gh-154937: Fix _thread._shutdown() racing with _thread.start_joinable_thread on ThreadHandle.ident - #155085
Conversation
|
Hi @aisk , Could you help take a look at this patch at your convenience? Thank you, and have a great day! |
|
Hi @aisk , Thanks very much for your review and suggestions! I've updated the patch based on the feedback. Have a wonderful day! |
|
The data race is trivial, I suggest to not add such heavy test for it, you can remove the test. |
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! |
|
(Checked that this PR can be backported to 3.13-3.15 without conflict via opencode + glm5.2) |
|
Thanks @LindaSummer for the PR, and @kumaraditya303 for merging it 🌮🎉.. I'm working now to backport this PR to: 3.13, 3.14, 3.15. |
|
GH-155123 is a backport of this pull request to the 3.15 branch. |
|
GH-155124 is a backport of this pull request to the 3.14 branch. |
|
GH-155125 is a backport of this pull request to the 3.13 branch. |
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