Skip to content

Commit 385b195

Browse files
miss-islingtonalbanDblurb-it[bot]pitrou
authored
[3.11] gh-108520: Fix bad fork detection in nested multiprocessing use case (GH-108568) (#108692)
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. --------- (cherry picked from commit add8d45) Co-authored-by: albanD <[email protected]> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Antoine Pitrou <[email protected]>
1 parent 6f24420 commit 385b195

File tree

3 files changed

+34
-3
lines changed

3 files changed

+34
-3
lines changed

Lib/multiprocessing/synchronize.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ class SemLock(object):
5050
def __init__(self, kind, value, maxvalue, *, ctx):
5151
if ctx is None:
5252
ctx = context._default_context.get_context()
53-
self.is_fork_ctx = ctx.get_start_method() == 'fork'
54-
unlink_now = sys.platform == 'win32' or self.is_fork_ctx
53+
self._is_fork_ctx = ctx.get_start_method() == 'fork'
54+
unlink_now = sys.platform == 'win32' or self._is_fork_ctx
5555
for i in range(100):
5656
try:
5757
sl = self._semlock = _multiprocessing.SemLock(
@@ -103,7 +103,7 @@ def __getstate__(self):
103103
if sys.platform == 'win32':
104104
h = context.get_spawning_popen().duplicate_for_child(sl.handle)
105105
else:
106-
if self.is_fork_ctx:
106+
if self._is_fork_ctx:
107107
raise RuntimeError('A SemLock created in a fork context is being '
108108
'shared with a process in a spawn context. This is '
109109
'not supported. Please use the same context to create '
@@ -115,6 +115,8 @@ def __setstate__(self, state):
115115
self._semlock = _multiprocessing.SemLock._rebuild(*state)
116116
util.debug('recreated blocker with handle %r' % state[0])
117117
self._make_methods()
118+
# Ensure that deserialized SemLock can be serialized again (gh-108520).
119+
self._is_fork_ctx = False
118120

119121
@staticmethod
120122
def _make_name():

Lib/test/_test_multiprocessing.py

+26
Original file line numberDiff line numberDiff line change
@@ -5367,6 +5367,32 @@ def test_mixed_startmethod(self):
53675367
p.start()
53685368
p.join()
53695369

5370+
@classmethod
5371+
def _put_one_in_queue(cls, queue):
5372+
queue.put(1)
5373+
5374+
@classmethod
5375+
def _put_two_and_nest_once(cls, queue):
5376+
queue.put(2)
5377+
process = multiprocessing.Process(target=cls._put_one_in_queue, args=(queue,))
5378+
process.start()
5379+
process.join()
5380+
5381+
def test_nested_startmethod(self):
5382+
# gh-108520: Regression test to ensure that child process can send its
5383+
# arguments to another process
5384+
queue = multiprocessing.Queue()
5385+
5386+
process = multiprocessing.Process(target=self._put_two_and_nest_once, args=(queue,))
5387+
process.start()
5388+
process.join()
5389+
5390+
results = []
5391+
while not queue.empty():
5392+
results.append(queue.get())
5393+
5394+
self.assertEqual(results, [2, 1])
5395+
53705396

53715397
@unittest.skipIf(sys.platform == "win32",
53725398
"test semantics don't make sense on Windows")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
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.
2+
3+
Rename :attr:`multiprocessing.synchronize.SemLock.is_fork_ctx` to :attr:`multiprocessing.synchronize.SemLock._is_fork_ctx` to avoid exposing it as public API.

0 commit comments

Comments
 (0)