summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/_test_multiprocessing.py22
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-14-48-19.gh-issue-134381.2BXhth.rst1
-rw-r--r--Modules/_threadmodule.c6
3 files changed, 29 insertions, 0 deletions
diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py
index 8cc4e46..63f522f 100644
--- a/Lib/test/_test_multiprocessing.py
+++ b/Lib/test/_test_multiprocessing.py
@@ -6832,6 +6832,28 @@ class MiscTestCase(unittest.TestCase):
self.assertEqual("332833500", out.decode('utf-8').strip())
self.assertFalse(err, msg=err.decode('utf-8'))
+ def test_forked_thread_not_started(self):
+ # gh-134381: Ensure that a thread that has not been started yet in
+ # the parent process can be started within a forked child process.
+
+ if multiprocessing.get_start_method() != "fork":
+ self.skipTest("fork specific test")
+
+ q = multiprocessing.Queue()
+ t = threading.Thread(target=lambda: q.put("done"), daemon=True)
+
+ def child():
+ t.start()
+ t.join()
+
+ p = multiprocessing.Process(target=child)
+ p.start()
+ p.join(support.SHORT_TIMEOUT)
+
+ self.assertEqual(p.exitcode, 0)
+ self.assertEqual(q.get_nowait(), "done")
+ close_queue(q)
+
#
# Mixins
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-14-48-19.gh-issue-134381.2BXhth.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-14-48-19.gh-issue-134381.2BXhth.rst
new file mode 100644
index 0000000..aa89002
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-14-48-19.gh-issue-134381.2BXhth.rst
@@ -0,0 +1 @@
+Fix :exc:`RuntimeError` when using a not-started :class:`threading.Thread` after calling :func:`os.fork`
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index f676659..73739d2 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -281,6 +281,12 @@ _PyThread_AfterFork(struct _pythread_runtime_state *state)
continue;
}
+ // Keep handles for threads that have not been started yet. They are
+ // safe to start in the child process.
+ if (handle->state == THREAD_HANDLE_NOT_STARTED) {
+ continue;
+ }
+
// Mark all threads as done. Any attempts to join or detach the
// underlying OS thread (if any) could crash. We are the only thread;
// it's safe to set this non-atomically.