Conversation
…hread The test script ended at ``w.start()``, so the ``os.fork()`` in ``worker()`` raced the interpreter shutdown that begins as soon as the main thread falls off the end of the script. When the fork loses that race it happens inside ``threading._shutdown()``, which marks the main thread as stopped before joining non-daemon threads. The child then inherits a main thread that was already marked as stopped in the parent, so ``mainthread.join()`` in ``joiningfunc()`` returns for the wrong reason and the test stops checking what its own comment describes: "In the forked process, the main Thread object must be marked as stopped." Join ``w`` so that the main thread stays alive until the fork has happened, and check in ``worker()`` that the main thread really is still alive before forking. Without that check the test passes either way, so losing the join again would go unnoticed. With ``time.sleep(0.1)`` added at the start of ``worker()`` (the reproducer from the issue), the fork previously saw ``threading._SHUTTING_DOWN`` true and the main thread already marked done; it now sees both false, and the main thread handle is marked done only in the child.
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
v0ropaev
force-pushed
the
gh-116612-join-forked-thread
branch
from
September 28, 2026 15:47
05190d2 to
9ef91ca
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #116612.
test_3_join_in_forked_from_threadforks from a worker thread while the main thread isrunning off the end of the script, so whether the fork lands before or after interpreter
shutdown begins is a race. When it lands after, the main thread is already marked stopped in
the parent, the child inherits that, and
mainthread.join()in the child returns for thewrong reason, so the test passes without testing anything.
Two lines of substance in
Lib/test/test_threading.py. Aw.join()at the end of the scriptkeeps the main thread alive until the worker has forked, which is what actually establishes
the ordering. And a check in the worker that the main thread is still alive immediately
before
fork(), exiting 3 and printing to stderr if it is not, which is what makes a futureregression loud instead of silently green.
Worth being upfront about one thing. The race does not reproduce unaided on this machine.
With
w.join()removed the test still passed 12 runs out of 12, the fork wins every timehere, so there is no red to green transition to show. Delaying the fork by 0.5s inside the
worker is enough to bring it out, which is what a loaded machine or a slower interpreter does
for free. With
w.join()removed and that delay in place it fails withmain thread stopped before fork(); withw.join()present and the same delay it passes. Sothe main thread really can be gone by the time the worker forks,
w.join()is what preventsit, and without the guard that state would have gone unnoticed.
./python.exe -m test test_threadingpasses.