summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/multiprocessing/synchronize.py8
-rw-r--r--Lib/test/_test_multiprocessing.py26
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst3
3 files changed, 34 insertions, 3 deletions
diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py
index 2328d33..3ccbfe3 100644
--- a/Lib/multiprocessing/synchronize.py
+++ b/Lib/multiprocessing/synchronize.py
@@ -50,8 +50,8 @@ class SemLock(object):
def __init__(self, kind, value, maxvalue, *, ctx):
if ctx is None:
ctx = context._default_context.get_context()
- self.is_fork_ctx = ctx.get_start_method() == 'fork'
- unlink_now = sys.platform == 'win32' or self.is_fork_ctx
+ self._is_fork_ctx = ctx.get_start_method() == 'fork'
+ unlink_now = sys.platform == 'win32' or self._is_fork_ctx
for i in range(100):
try:
sl = self._semlock = _multiprocessing.SemLock(
@@ -103,7 +103,7 @@ class SemLock(object):
if sys.platform == 'win32':
h = context.get_spawning_popen().duplicate_for_child(sl.handle)
else:
- if self.is_fork_ctx:
+ if self._is_fork_ctx:
raise RuntimeError('A SemLock created in a fork context is being '
'shared with a process in a spawn context. This is '
'not supported. Please use the same context to create '
@@ -115,6 +115,8 @@ class SemLock(object):
self._semlock = _multiprocessing.SemLock._rebuild(*state)
util.debug('recreated blocker with handle %r' % state[0])
self._make_methods()
+ # Ensure that deserialized SemLock can be serialized again (gh-108520).
+ self._is_fork_ctx = False
@staticmethod
def _make_name():
diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py
index 9bc8242..3476371 100644
--- a/Lib/test/_test_multiprocessing.py
+++ b/Lib/test/_test_multiprocessing.py
@@ -5367,6 +5367,32 @@ class TestStartMethod(unittest.TestCase):
p.start()
p.join()
+ @classmethod
+ def _put_one_in_queue(cls, queue):
+ queue.put(1)
+
+ @classmethod
+ def _put_two_and_nest_once(cls, queue):
+ queue.put(2)
+ process = multiprocessing.Process(target=cls._put_one_in_queue, args=(queue,))
+ process.start()
+ process.join()
+
+ def test_nested_startmethod(self):
+ # gh-108520: Regression test to ensure that child process can send its
+ # arguments to another process
+ queue = multiprocessing.Queue()
+
+ process = multiprocessing.Process(target=self._put_two_and_nest_once, args=(queue,))
+ process.start()
+ process.join()
+
+ results = []
+ while not queue.empty():
+ results.append(queue.get())
+
+ self.assertEqual(results, [2, 1])
+
@unittest.skipIf(sys.platform == "win32",
"test semantics don't make sense on Windows")
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst b/Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst
new file mode 100644
index 0000000..44131fb
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst
@@ -0,0 +1,3 @@
+Fix :meth:`multiprocessing.synchronize.SemLock.__setstate__` to properly initialize :attr:`multiprocessing.synchronize.SemLock._is_fork_ctx`. This fixes a regression when passing a SemLock accross nested processes.
+
+Rename :attr:`multiprocessing.synchronize.SemLock.is_fork_ctx` to :attr:`multiprocessing.synchronize.SemLock._is_fork_ctx` to avoid exposing it as public API.