summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/multiprocessing/synchronize.py9
-rw-r--r--Lib/test/_test_multiprocessing.py22
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst1
3 files changed, 30 insertions, 2 deletions
diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py
index 42624b5..2328d33 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()
- 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(
@@ -103,6 +103,11 @@ class SemLock(object):
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)
diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py
index 29dc386..9bc8242 100644
--- a/Lib/test/_test_multiprocessing.py
+++ b/Lib/test/_test_multiprocessing.py
@@ -5345,6 +5345,28 @@ class TestStartMethod(unittest.TestCase):
print(err)
self.fail("failed spawning forkserver or grandchild")
+ @unittest.skipIf(sys.platform == "win32",
+ "Only Spawn on windows so no risk of mixing")
+ @only_run_in_spawn_testsuite("avoids redundant testing.")
+ 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()
+ process_ctx = multiprocessing.get_context(process_method)
+ p = process_ctx.Process(target=close_queue, args=(queue,))
+ err_msg = "A SemLock created in a fork"
+ with self.assertRaisesRegex(RuntimeError, err_msg):
+ 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()
+ process_ctx = multiprocessing.get_context(process_method)
+ p = process_ctx.Process(target=close_queue, args=(queue,))
+ p.start()
+ p.join()
+
@unittest.skipIf(sys.platform == "win32",
"test semantics don't make sense on Windows")
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst
new file mode 100644
index 0000000..194851d
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-25-22-35-35.gh-issue-77377.EHAbXx.rst
@@ -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.