]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-134381: Fix RuntimeError when starting not-yet started Thread after fork (gh-134514)
authorJiucheng(Oliver) <git.jiucheng@gmail.com>
Fri, 23 May 2025 19:22:14 +0000 (15:22 -0400)
committerGitHub <noreply@github.com>
Fri, 23 May 2025 19:22:14 +0000 (15:22 -0400)
Lib/test/_test_multiprocessing.py
Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-14-48-19.gh-issue-134381.2BXhth.rst [new file with mode: 0644]
Modules/_threadmodule.c

index 6a20a1eb03e32bdf2413861033ca5f0e80525094..75f31d858d3306c076819fbaa8cbdac4ae1f794f 100644 (file)
@@ -6844,6 +6844,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 (file)
index 0000000..aa89002
--- /dev/null
@@ -0,0 +1 @@
+Fix :exc:`RuntimeError` when using a not-started :class:`threading.Thread` after calling :func:`os.fork`
index 74b972b201a546849aa5c24ecfacd93f64a05393..cc83be4b5ff311e6d62864c25626c349797f4486 100644 (file)
@@ -296,6 +296,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.