Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
9 changes: 7 additions & 2 deletions Lib/multiprocessing/synchronize.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ class SemLock(object):
def __init__(self, kind, value, maxvalue, *, ctx):
if ctx is None:
ctx = context._default_context.get_context()
name = ctx.get_start_method()
unlink_now = sys.platform == 'win32' or name == 'fork'
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(
Expand Down Expand Up @@ -103,6 +103,11 @@ def __getstate__(self):
if sys.platform == 'win32':
h = context.get_spawning_popen().duplicate_for_child(sl.handle)
else:
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 '
'multiprocessing objects and Process.')
h = sl.handle
return (h, sl.kind, sl.maxvalue, sl.name)

Expand Down
18 changes: 18 additions & 0 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5421,6 +5421,24 @@ def test_preload_resources(self):
print(err)
self.fail("failed spawning forkserver or grandchild")

@unittest.skipIf(sys.platform == "win32", "Only Spawn on windows so no risk of mixing")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit, but can you try to wrap the lines below around ~80 characters max?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!
Thanks for the update on the blurb, I wasn't sure what to put there

@only_run_in_spawn_testsuite("avoids redundant testing since we ignore the global context.")
def test_mixed_startmethod(self):
# Fork-based locks cannot be used with spawned process
for process_method in ["spawn", "forkserver"]:
queue = multiprocessing.get_context("fork").Queue()
p = multiprocessing.get_context(process_method).Process(target=close_queue, args=(queue,))
with self.assertRaisesRegex(RuntimeError, "A SemLock created in a fork"):
p.start()

# non-fork-based locks can be used with all other start methods
for queue_method in ["spawn", "forkserver"]:
for process_method in multiprocessing.get_all_start_methods():
queue = multiprocessing.get_context(queue_method).Queue()
p = multiprocessing.get_context(process_method).Process(target=close_queue, args=(queue,))
p.start()
p.join()


@unittest.skipIf(sys.platform == "win32",
"test semantics don't make sense on Windows")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure that multiprocessing synchronization objects created in a fork context are not sent to a different process created in a spawn context. This changes a segfault into an actionable RuntimeError in the parent process.