Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/osgeo4w.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ jobs:
libsystre
libtre
libwinpthread-git
openblas
pcre
pkgconf
toolchain
Expand Down
2 changes: 1 addition & 1 deletion mswindows/osgeo4w/build_osgeo4w.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ CXXFLAGS="$CXXFLAGS -pipe" \
--includedir=${OSGEO4W_ROOT_MSYS}/include \
--libexecdir=${OSGEO4W_ROOT_MSYS}/bin \
--prefix=${OSGEO4W_ROOT_MSYS}/apps/grass \
--with-blas \
--without-blas \
--with-bzlib \
--with-cairo \
--with-cairo-includes=${OSGEO4W_ROOT_MSYS}/include/cairo \
Expand Down
12 changes: 9 additions & 3 deletions python/grass/pygrass/messages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
from __future__ import annotations

import sys
from multiprocessing import Lock, Pipe, Process
from multiprocessing import Lock, Pipe
from typing import TYPE_CHECKING, Literal, NoReturn

import grass.lib.gis as libgis
from grass.exceptions import FatalError
from grass.script.utils import get_fork_context

if TYPE_CHECKING:
from multiprocessing import Process
from multiprocessing.connection import Connection
from multiprocessing.context import _LockLike

Expand Down Expand Up @@ -181,15 +183,19 @@ def __init__(self, raise_on_error: bool = False) -> None:
self.raise_on_error = raise_on_error
self.client_conn, self.server_conn = Pipe()
self.lock = Lock()
self.server = Process(target=message_server, args=(self.lock, self.server_conn))
self.server = get_fork_context().Process(
target=message_server, args=(self.lock, self.server_conn)
)
self.server.daemon = True
self.server.start()

def start_server(self) -> None:
"""Start the messenger server and open the pipe"""
self.client_conn, self.server_conn = Pipe()
self.lock = Lock()
self.server = Process(target=message_server, args=(self.lock, self.server_conn))
self.server = get_fork_context().Process(
target=message_server, args=(self.lock, self.server_conn)
)
self.server.daemon = True
self.server.start()

Expand Down
6 changes: 3 additions & 3 deletions python/grass/pygrass/modules/interface/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ class MultiModule:

>>> from grass.pygrass.modules import Module
>>> from grass.pygrass.modules import MultiModule
>>> from multiprocessing import Process
>>> from multiprocessing.process import BaseProcess
>>> import copy

Synchronous module run
Expand Down Expand Up @@ -950,7 +950,7 @@ class MultiModule:
... sync=False,
... )
>>> t = mm.run()
>>> isinstance(t, Process)
>>> isinstance(t, BaseProcess)
True
>>> m_list = mm.wait()
>>> m_list[0].returncode
Expand All @@ -976,7 +976,7 @@ class MultiModule:
>>> str(mm)
'g.region format=plain -p ; g.region format=plain -p ; g.region format=plain -p ; g.region format=plain -p ; g.region format=plain -p'
>>> t = mm.run()
>>> isinstance(t, Process)
>>> isinstance(t, BaseProcess)
True
>>> m_list = mm.wait()
>>> m_list[0].returncode
Expand Down
7 changes: 5 additions & 2 deletions python/grass/pygrass/rpc/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
import sys
import threading
import time
from multiprocessing import Lock, Pipe, Process
from multiprocessing import Lock, Pipe
from typing import TYPE_CHECKING, NoReturn

from grass.exceptions import FatalError
from grass.script.utils import get_fork_context

if TYPE_CHECKING:
from multiprocessing.connection import Connection
Expand Down Expand Up @@ -142,7 +143,9 @@ def start_server(self):

self.client_conn, self.server_conn = Pipe(True)
self.lock = Lock()
self.server = Process(target=dummy_server, args=(self.lock, self.server_conn))
self.server = get_fork_context().Process(
target=dummy_server, args=(self.lock, self.server_conn)
)
self.server.daemon = True
self.server.start()

Expand Down
2 changes: 2 additions & 0 deletions python/grass/script/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
diff_files,
encode,
float_or_dms,
get_fork_context,
get_lib_path,
get_num_suffix,
legalize_vector_name,
Expand Down Expand Up @@ -159,6 +160,7 @@
"float_or_dms",
"get_capture_stderr",
"get_commands",
"get_fork_context",
"get_lib_path",
"get_num_suffix",
"get_raise_on_error",
Expand Down
19 changes: 19 additions & 0 deletions python/grass/script/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import uuid
import random
import string
import sys

from pathlib import Path
from typing import TYPE_CHECKING, AnyStr, TypeVar, cast, overload
Expand Down Expand Up @@ -125,6 +126,24 @@ def resolve_nprocs(nprocs: int | str) -> int:
return max(1, available + n)


def get_fork_context():
"""Return a suitable :mod:`multiprocessing` fork context.

The GRASS Python APIs does not yet support `spawn` and `forkserver`
start-methods for multiprocessing, which became new default in
Python 3.14. This function provides a temporary workaround that
switches back to `fork` on Linux until the new default methods are
supported.
The function should be removed as soon as the GRASS Python APIs support
the new default start-methods.
"""
import multiprocessing

if sys.platform != "darwin" and "fork" in multiprocessing.get_all_start_methods():
return multiprocessing.get_context("fork")
return multiprocessing.get_context()


def diff_files(
filename_a: FileDescriptorOrPath, filename_b: FileDescriptorOrPath
) -> list[str]:
Expand Down
4 changes: 2 additions & 2 deletions python/grass/temporal/c_libraries_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import sys
from ctypes import CFUNCTYPE, POINTER, byref, c_int, c_void_p, cast
from datetime import datetime
from multiprocessing import Lock, Pipe, Process
from multiprocessing import Lock, Pipe
from typing import TYPE_CHECKING, Any, Literal

import grass.lib.date as libdate
Expand Down Expand Up @@ -1471,7 +1471,7 @@ def __init__(self) -> None:
def start_server(self) -> None:
self.client_conn, self.server_conn = Pipe(True)
self.lock = Lock()
self.server = Process(
self.server = gs.get_fork_context().Process(
target=c_library_server, args=(self.lock, self.server_conn)
)
self.server.daemon = True
Expand Down
10 changes: 5 additions & 5 deletions python/grass/temporal/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import re
import sys
from multiprocessing import Process

import grass.script as gs
from grass.exceptions import CalledModuleError
Expand Down Expand Up @@ -207,6 +206,7 @@ def extract_dataset(

# Run the mapcalc expression
if expression:
mp_context = gs.get_fork_context()
proc_count = 0
proc_list = []

Expand Down Expand Up @@ -262,15 +262,15 @@ def extract_dataset(
# Add process to the process list
if type == "raster":
msgr.verbose(_('Applying r.mapcalc expression: "%s"') % expr)
proc_list.append(Process(target=run_mapcalc2d, args=(expr,)))
proc_list.append(mp_context.Process(target=run_mapcalc2d, args=(expr,)))
elif type == "raster3d":
msgr.verbose(_('Applying r3.mapcalc expression: "%s"') % expr)
proc_list.append(Process(target=run_mapcalc3d, args=(expr,)))
proc_list.append(mp_context.Process(target=run_mapcalc3d, args=(expr,)))
elif type == "vector":
msgr.verbose(_('Applying v.extract where statement: "%s"') % expression)
if row["layer"]:
proc_list.append(
Process(
mp_context.Process(
target=run_vector_extraction,
args=(
row["name"] + "@" + row["mapset"],
Expand All @@ -283,7 +283,7 @@ def extract_dataset(
)
else:
proc_list.append(
Process(
mp_context.Process(
target=run_vector_extraction,
args=(
row["name"] + "@" + row["mapset"],
Expand Down
10 changes: 7 additions & 3 deletions python/grass/temporal/mapcalc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import copy
import sys
from datetime import datetime
from multiprocessing import Process

import grass.script as gs
from grass.exceptions import CalledModuleError
Expand Down Expand Up @@ -234,6 +233,7 @@ def dataset_mapcalculator(
num = len(map_matrix[0])

# Parallel processing
mp_context = gs.get_fork_context()
proc_list = []
proc_count = 0

Expand Down Expand Up @@ -309,9 +309,13 @@ def dataset_mapcalculator(

# Start the parallel r.mapcalc computation
if type == "raster":
proc_list.append(Process(target=_run_mapcalc2d, args=(expr,)))
proc_list.append(
mp_context.Process(target=_run_mapcalc2d, args=(expr,))
)
else:
proc_list.append(Process(target=_run_mapcalc3d, args=(expr,)))
proc_list.append(
mp_context.Process(target=_run_mapcalc3d, args=(expr,))
)
proc_list[proc_count].start()
proc_count += 1

Expand Down
Loading