gh-154937: Fix _thread._shutdown() racing with _thread.start_joinable_thread on ThreadHandle.ident - #155085
Open
LindaSummer wants to merge 8 commits into
Open
gh-154937: Fix _thread._shutdown() racing with _thread.start_joinable_thread on ThreadHandle.ident#155085LindaSummer wants to merge 8 commits into
_thread._shutdown() racing with _thread.start_joinable_thread on ThreadHandle.ident#155085LindaSummer wants to merge 8 commits into
Conversation
Contributor
Author
|
Hi @aisk , Could you help take a look at this patch at your convenience? Thank you, and have a great day! |
aisk
reviewed
Aug 2, 2026
aisk
reviewed
Aug 2, 2026
Contributor
Author
|
Hi @aisk , Thanks very much for your review and suggestions! I've updated the patch based on the feedback. Have a wonderful day! |
aisk
approved these changes
Aug 3, 2026
Contributor
|
The data race is trivial, I suggest to not add such heavy test for it, you can remove the test. |
Contributor
Author
Hi @kumaraditya303 , Thanks very much for your review! I have removed the heavy 4*1000 threads unit test. Please help take a look at your convenience. Have a great day! |
kumaraditya303
approved these changes
Aug 3, 2026
kumaraditya303
enabled auto-merge (squash)
August 3, 2026 15:04
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.
Issue
Close #154937
Proposed Changes
The
ThreadHandle.identshould be guarded byThreadHandle.mutexas the below comment.cpython/Modules/_threadmodule.c
Lines 132 to 136 in 083e038
Root Cause
The
thread.Threadwill invoke_start_joinable_threadwith the same flag as the invoke thread. The main thread is a non-daemon thread.cpython/Lib/threading.py
Lines 1111 to 1147 in 083e038
cpython/Lib/threading.py
Lines 1527 to 1537 in 083e038
In
_start_joinable_thread=>do_start_new_thread, the non-daemon thread will add to the shutdown_handles.cpython/Modules/_threadmodule.c
Lines 1912 to 1917 in 083e038
After being added to shutdown_handlers,
do_start_new_threadwill invokeThreadHandle_start, where the racing happened.cpython/Modules/_threadmodule.c
Lines 483 to 490 in 083e038
At the same time,
_shutdownwill read theidentfor shutting down without a lock with theThreadHandle.mutex.cpython/Modules/_threadmodule.c
Lines 2403 to 2422 in 083e038
Comment
Referring to the above root cause analytics, I find the original test case may not be precise.
The original case in #154937 uses
_thread.start_new_threadalongsidethreading.Thread.The race was caused by
threading.Threadnot_thread.start_new_thread.So, I changed the original case to below with
daemon=True, and the racing doesn't happen.I created another case that aligns with the PR's unit test, and the problem is reproduced again.
Here is the TSAN report.
WARNING: ThreadSanitizer: data race (pid=2708706) Read of size 8 at 0x7fca36010070 by thread T2: #0 thread_shutdown /home/someuser/projects/cpython/cpython-upstream-main/./Modules/_threadmodule.c:2416:22 (python3.16+0x653ba9) #1 cfunction_vectorcall_NOARGS /home/someuser/projects/cpython/cpython-upstream-main/Objects/methodobject.c:508:24 (python3.16+0x2ddf41) #2 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-upstream-main/./Include/internal/pycore_call.h:144:11 (python3.16+0x2240fc) #3 PyObject_Vectorcall /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c:327:12 (python3.16+0x2240fc) #4 _Py_VectorCall_StackRefSteal /home/someuser/projects/cpython/cpython-upstream-main/Python/ceval.c:726:11 (python3.16+0x457e76) #5 _PyEval_EvalFrameDefault /home/someuser/projects/cpython/cpython-upstream-main/Python/generated_cases.c.h:4559:35 (python3.16+0x465a0c) #6 _PyEval_EvalFrame /home/someuser/projects/cpython/cpython-upstream-main/./Include/internal/pycore_ceval.h:122:16 (python3.16+0x457987) #7 _PyEval_Vector /home/someuser/projects/cpython/cpython-upstream-main/Python/ceval.c:2172:12 (python3.16+0x457987) #8 _PyFunction_Vectorcall /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c (python3.16+0x2247c7) #9 _PyVectorcall_Call /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c:273:16 (python3.16+0x224416) #10 _PyObject_Call /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c:348:16 (python3.16+0x224416) #11 PyObject_Call /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c:373:12 (python3.16+0x22448b) #12 thread_run /home/someuser/projects/cpython/cpython-upstream-main/./Modules/_threadmodule.c:388:21 (python3.16+0x654aec) #13 pythread_wrapper /home/someuser/projects/cpython/cpython-upstream-main/Python/thread_pthread.h:234:5 (python3.16+0x574def) Previous write of size 8 at 0x7fca36010070 by thread T1: #0 ThreadHandle_start /home/someuser/projects/cpython/cpython-upstream-main/./Modules/_threadmodule.c:486:17 (python3.16+0x654966) #1 do_start_new_thread /home/someuser/projects/cpython/cpython-upstream-main/./Modules/_threadmodule.c:1919:9 (python3.16+0x6542ef) #2 thread_PyThread_start_joinable_thread /home/someuser/projects/cpython/cpython-upstream-main/./Modules/_threadmodule.c:2042:14 (python3.16+0x653268) #3 cfunction_call /home/someuser/projects/cpython/cpython-upstream-main/Objects/methodobject.c:564:18 (python3.16+0x2de9c5) #4 _PyObject_MakeTpCall /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c:242:18 (python3.16+0x2232b3) #5 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-upstream-main/./Include/internal/pycore_call.h:142:16 (python3.16+0x2241c4) #6 PyObject_Vectorcall /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c:327:12 (python3.16+0x2241c4) #7 _Py_VectorCallInstrumentation_StackRefSteal /home/someuser/projects/cpython/cpython-upstream-main/Python/ceval.c:768:11 (python3.16+0x4586f4) #8 _PyEval_EvalFrameDefault /home/someuser/projects/cpython/cpython-upstream-main/Python/generated_cases.c.h:3474:35 (python3.16+0x463029) #9 _PyEval_EvalFrame /home/someuser/projects/cpython/cpython-upstream-main/./Include/internal/pycore_ceval.h:122:16 (python3.16+0x457987) #10 _PyEval_Vector /home/someuser/projects/cpython/cpython-upstream-main/Python/ceval.c:2172:12 (python3.16+0x457987) #11 _PyFunction_Vectorcall /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c (python3.16+0x2247c7) #12 _PyVectorcall_Call /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c:273:16 (python3.16+0x224416) #13 _PyObject_Call /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c:348:16 (python3.16+0x224416) #14 PyObject_Call /home/someuser/projects/cpython/cpython-upstream-main/Objects/call.c:373:12 (python3.16+0x22448b) #15 thread_run /home/someuser/projects/cpython/cpython-upstream-main/./Modules/_threadmodule.c:388:21 (python3.16+0x654aec) #16 pythread_wrapper /home/someuser/projects/cpython/cpython-upstream-main/Python/thread_pthread.h:234:5 (python3.16+0x574def)ThreadHandle.identbetweenthread_shutdownandThreadHandle_startunder free-threading #154937