]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-108520: Fix bad fork detection in nested multiprocessing use case (#108568)
authoralbanD <desmaison.alban@gmail.com>
Wed, 30 Aug 2023 17:07:41 +0000 (13:07 -0400)
committerGitHub <noreply@github.com>
Wed, 30 Aug 2023 17:07:41 +0000 (17:07 +0000)
gh-107275 introduced a regression where a SemLock would fail being passed along nested child processes, as the `is_fork_ctx` attribute would be left missing after the first deserialization.

---------

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
Lib/multiprocessing/synchronize.py
Lib/test/_test_multiprocessing.py
Misc/NEWS.d/next/Core and Builtins/2023-08-30-15-41-47.gh-issue-108520.u0ZGP_.rst [new file with mode: 0644]

index 2328d33212308256bf9d2d2e8f2d562263157350..3ccbfe311c71f37e799eb8444af84677881116b8 100644 (file)
@@ -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():
index 343e9dcf769e8604a09a20e8aa2e7fb48cc4fa7d..7a851d3df787817b3c007278de40612fa8f30ae1 100644 (file)
@@ -5443,6 +5443,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 (file)
index 0000000..44131fb
--- /dev/null
@@ -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.\r
+\r
+Rename :attr:`multiprocessing.synchronize.SemLock.is_fork_ctx` to :attr:`multiprocessing.synchronize.SemLock._is_fork_ctx` to avoid exposing it as public API.\r