Repository navigation
data races in decimal module with global context #141148
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or errorextension-modulesC modules in the Modules dirC modules in the Modules dir
on Nov 6, 2025 I don't think you should be sharing contexts across threads.
https://docs.python.org/3/library/decimal.html#context-objects
"Each thread has its own current context which is accessed or changed using the getcontext() and setcontext() functions"
I'm not sure if we should explicitly disallow it or leave things as is.
Hmm... actually, I guess things like
decimal.BasicContextwill always be shared across threads.There is also section Working with threads. I don't think that OP example fits in this picture. (Though, maybe we should reclassify this as a documentation issue, instead of closing.)
BTW, what was original test failure?
Another reproducer which does not shares a global context:
from decimal import Decimal import numpy as np def test_array_gcd_lcm_decimal(): a = np.array([1, 1, -1, -1]) * Decimal('0.20')
Running this under pytest-run-parallel leads to similar data races.
Details
================== WARNING: ThreadSanitizer: data race (pid=10816) Write of size 4 at 0x00012305b23c by thread T1: #0 dec_addstatus <null> (_decimal.cpython-314t-darwin.so:arm64+0x98e4) #1 PyDecType_FromCStringExact <null> (_decimal.cpython-314t-darwin.so:arm64+0x197c8) #2 dec_new <null> (_decimal.cpython-314t-darwin.so:arm64+0x4e9c) #3 type_call <null> (libpython3.14t.dylib:arm64+0x18a600) #4 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #5 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #6 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27ce94) #7 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #8 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #9 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #10 context_run <null> (libpython3.14t.dylib:arm64+0x2c3eec) #11 method_vectorcall_FASTCALL_KEYWORDS <null> (libpython3.14t.dylib:arm64+0xa1c48) #12 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ab8c) #13 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27ce94) #14 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #15 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #16 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #17 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae40) #18 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #19 thread_run <null> (libpython3.14t.dylib:arm64+0x416b08) #20 pythread_wrapper <null> (libpython3.14t.dylib:arm64+0x368cac) Previous write of size 4 at 0x00012305b23c by thread T4: #0 dec_addstatus <null> (_decimal.cpython-314t-darwin.so:arm64+0x98e4) #1 PyDecType_FromCStringExact <null> (_decimal.cpython-314t-darwin.so:arm64+0x197c8) #2 dec_new <null> (_decimal.cpython-314t-darwin.so:arm64+0x4e9c) #3 type_call <null> (libpython3.14t.dylib:arm64+0x18a600) #4 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #5 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #6 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27ce94) #7 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #8 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #9 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #10 context_run <null> (libpython3.14t.dylib:arm64+0x2c3eec) #11 method_vectorcall_FASTCALL_KEYWORDS <null> (libpython3.14t.dylib:arm64+0xa1c48) #12 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ab8c) #13 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27ce94) #14 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #15 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #16 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #17 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae40) #18 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #19 thread_run <null> (libpython3.14t.dylib:arm64+0x416b08) #20 pythread_wrapper <null> (libpython3.14t.dylib:arm64+0x368cac) Thread T1 (tid=892055, running) created by main thread at: #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708) #1 do_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x36796c) #2 PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x3677b4) #3 do_start_new_thread <null> (libpython3.14t.dylib:arm64+0x4166bc) #4 thread_PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x415448) #5 cfunction_call <null> (libpython3.14t.dylib:arm64+0x12bfb4) #6 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #7 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #12 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #13 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #14 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #15 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #16 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #17 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #18 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #19 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #20 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #21 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #22 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #23 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #24 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae00) #25 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #26 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27fbc0) #27 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #28 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #29 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #30 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #31 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #32 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #33 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #34 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #35 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #36 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #37 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #38 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #39 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #40 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #41 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #42 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #43 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #44 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #45 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #46 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #47 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #48 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #49 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #50 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #51 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #52 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #53 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #54 PyEval_EvalCode <null> (libpython3.14t.dylib:arm64+0x27886c) #55 run_mod <null> (libpython3.14t.dylib:arm64+0x346724) #56 _PyRun_SimpleFileObject <null> (libpython3.14t.dylib:arm64+0x341e10) #57 _PyRun_AnyFileObject <null> (libpython3.14t.dylib:arm64+0x341564) #58 pymain_run_file <null> (libpython3.14t.dylib:arm64+0x382acc) #59 Py_RunMain <null> (libpython3.14t.dylib:arm64+0x381ef8) #60 pymain_main <null> (libpython3.14t.dylib:arm64+0x382364) #61 Py_BytesMain <null> (libpython3.14t.dylib:arm64+0x382438) #62 main <null> (python3.14:arm64+0x10000070c) Thread T4 (tid=892058, running) created by main thread at: #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708) #1 do_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x36796c) #2 PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x3677b4) #3 do_start_new_thread <null> (libpython3.14t.dylib:arm64+0x4166bc) #4 thread_PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x415448) #5 cfunction_call <null> (libpython3.14t.dylib:arm64+0x12bfb4) #6 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #7 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2819d0) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #12 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #13 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #14 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #15 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #16 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #17 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #18 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #19 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #20 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #21 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #22 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #23 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #24 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae00) #25 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #26 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27fbc0) #27 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #28 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #29 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #30 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #31 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #32 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #33 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #34 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #35 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #36 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #37 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #38 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #39 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #40 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #41 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #42 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #43 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #44 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #45 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #46 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #47 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #48 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #49 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #50 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #51 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #52 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #53 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #54 PyEval_EvalCode <null> (libpython3.14t.dylib:arm64+0x27886c) #55 run_mod <null> (libpython3.14t.dylib:arm64+0x346724) #56 _PyRun_SimpleFileObject <null> (libpython3.14t.dylib:arm64+0x341e10) #57 _PyRun_AnyFileObject <null> (libpython3.14t.dylib:arm64+0x341564) #58 pymain_run_file <null> (libpython3.14t.dylib:arm64+0x382acc) #59 Py_RunMain <null> (libpython3.14t.dylib:arm64+0x381ef8) #60 pymain_main <null> (libpython3.14t.dylib:arm64+0x382364) #61 Py_BytesMain <null> (libpython3.14t.dylib:arm64+0x382438) #62 main <null> (python3.14:arm64+0x10000070c) SUMMARY: ThreadSanitizer: data race (_decimal.cpython-314t-darwin.so:arm64+0x98e4) in dec_addstatus+0x3c ================== ================== WARNING: ThreadSanitizer: data race (pid=10816) Write of size 4 at 0x00012305b23c by thread T1: #0 dec_addstatus <null> (_decimal.cpython-314t-darwin.so:arm64+0x98e4) #1 PyDecType_FromLongExact <null> (_decimal.cpython-314t-darwin.so:arm64+0x9c2c) #2 nm_mpd_qmul <null> (_decimal.cpython-314t-darwin.so:arm64+0x6164) #3 binary_op1 <null> (libpython3.14t.dylib:arm64+0x5acd8) #4 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #5 PyUFunc_OO_O <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x78764) #6 PyObject_CallFunctionObjArgs <null> (libpython3.14t.dylib:arm64+0x8d110) #7 array_multiply <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x138880) #8 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #9 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x279c44) #10 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #11 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #12 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #13 context_run <null> (libpython3.14t.dylib:arm64+0x2c3eec) #14 method_vectorcall_FASTCALL_KEYWORDS <null> (libpython3.14t.dylib:arm64+0xa1c48) #15 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ab8c) #16 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27ce94) #17 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #18 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #19 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #20 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae40) #21 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #22 thread_run <null> (libpython3.14t.dylib:arm64+0x416b08) #23 pythread_wrapper <null> (libpython3.14t.dylib:arm64+0x368cac) Previous write of size 4 at 0x00012305b23c by thread T4: #0 dec_addstatus <null> (_decimal.cpython-314t-darwin.so:arm64+0x98e4) #1 PyDecType_FromLongExact <null> (_decimal.cpython-314t-darwin.so:arm64+0x9c2c) #2 nm_mpd_qmul <null> (_decimal.cpython-314t-darwin.so:arm64+0x6164) #3 binary_op1 <null> (libpython3.14t.dylib:arm64+0x5acd8) #4 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #5 PyUFunc_OO_O <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x78764) #6 PyObject_CallFunctionObjArgs <null> (libpython3.14t.dylib:arm64+0x8d110) #7 array_multiply <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x138880) #8 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #9 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x279c44) #10 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #11 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #12 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #13 context_run <null> (libpython3.14t.dylib:arm64+0x2c3eec) #14 method_vectorcall_FASTCALL_KEYWORDS <null> (libpython3.14t.dylib:arm64+0xa1c48) #15 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ab8c) #16 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27ce94) #17 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #18 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #19 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #20 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae40) #21 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #22 thread_run <null> (libpython3.14t.dylib:arm64+0x416b08) #23 pythread_wrapper <null> (libpython3.14t.dylib:arm64+0x368cac) Thread T1 (tid=892055, running) created by main thread at: #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708) #1 do_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x36796c) #2 PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x3677b4) #3 do_start_new_thread <null> (libpython3.14t.dylib:arm64+0x4166bc) #4 thread_PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x415448) #5 cfunction_call <null> (libpython3.14t.dylib:arm64+0x12bfb4) #6 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #7 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #12 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #13 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #14 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #15 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #16 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #17 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #18 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #19 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #20 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #21 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #22 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #23 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #24 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae00) #25 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #26 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27fbc0) #27 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #28 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #29 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #30 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #31 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #32 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #33 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #34 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #35 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #36 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #37 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #38 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #39 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #40 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #41 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #42 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #43 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #44 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #45 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #46 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #47 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #48 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #49 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #50 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #51 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #52 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #53 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #54 PyEval_EvalCode <null> (libpython3.14t.dylib:arm64+0x27886c) #55 run_mod <null> (libpython3.14t.dylib:arm64+0x346724) #56 _PyRun_SimpleFileObject <null> (libpython3.14t.dylib:arm64+0x341e10) #57 _PyRun_AnyFileObject <null> (libpython3.14t.dylib:arm64+0x341564) #58 pymain_run_file <null> (libpython3.14t.dylib:arm64+0x382acc) #59 Py_RunMain <null> (libpython3.14t.dylib:arm64+0x381ef8) #60 pymain_main <null> (libpython3.14t.dylib:arm64+0x382364) #61 Py_BytesMain <null> (libpython3.14t.dylib:arm64+0x382438) #62 main <null> (python3.14:arm64+0x10000070c) Thread T4 (tid=892058, running) created by main thread at: #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708) #1 do_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x36796c) #2 PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x3677b4) #3 do_start_new_thread <null> (libpython3.14t.dylib:arm64+0x4166bc) #4 thread_PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x415448) #5 cfunction_call <null> (libpython3.14t.dylib:arm64+0x12bfb4) #6 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #7 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2819d0) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #12 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #13 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #14 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #15 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #16 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #17 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #18 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #19 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #20 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #21 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #22 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #23 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #24 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae00) #25 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #26 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27fbc0) #27 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #28 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #29 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #30 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #31 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #32 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #33 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #34 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #35 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #36 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #37 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #38 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #39 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #40 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #41 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #42 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #43 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #44 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #45 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #46 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #47 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #48 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #49 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #50 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #51 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #52 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #53 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #54 PyEval_EvalCode <null> (libpython3.14t.dylib:arm64+0x27886c) #55 run_mod <null> (libpython3.14t.dylib:arm64+0x346724) #56 _PyRun_SimpleFileObject <null> (libpython3.14t.dylib:arm64+0x341e10) #57 _PyRun_AnyFileObject <null> (libpython3.14t.dylib:arm64+0x341564) #58 pymain_run_file <null> (libpython3.14t.dylib:arm64+0x382acc) #59 Py_RunMain <null> (libpython3.14t.dylib:arm64+0x381ef8) #60 pymain_main <null> (libpython3.14t.dylib:arm64+0x382364) #61 Py_BytesMain <null> (libpython3.14t.dylib:arm64+0x382438) #62 main <null> (python3.14:arm64+0x10000070c) SUMMARY: ThreadSanitizer: data race (_decimal.cpython-314t-darwin.so:arm64+0x98e4) in dec_addstatus+0x3c ================== ================== WARNING: ThreadSanitizer: data race (pid=10816) Write of size 4 at 0x00012305b23c by thread T4: #0 dec_addstatus <null> (_decimal.cpython-314t-darwin.so:arm64+0x98e4) #1 PyDecType_FromLongExact <null> (_decimal.cpython-314t-darwin.so:arm64+0x9c2c) #2 nm_mpd_qmul <null> (_decimal.cpython-314t-darwin.so:arm64+0x6164) #3 binary_op1 <null> (libpython3.14t.dylib:arm64+0x5acd8) #4 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #5 PyUFunc_OO_O <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x78764) #6 PyObject_CallFunctionObjArgs <null> (libpython3.14t.dylib:arm64+0x8d110) #7 array_multiply <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x138880) #8 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #9 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x279c44) #10 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #11 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #12 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #13 context_run <null> (libpython3.14t.dylib:arm64+0x2c3eec) #14 method_vectorcall_FASTCALL_KEYWORDS <null> (libpython3.14t.dylib:arm64+0xa1c48) #15 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ab8c) #16 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27ce94) #17 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #18 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #19 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #20 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae40) #21 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #22 thread_run <null> (libpython3.14t.dylib:arm64+0x416b08) #23 pythread_wrapper <null> (libpython3.14t.dylib:arm64+0x368cac) Previous write of size 4 at 0x00012305b23c by thread T1: #0 dec_addstatus <null> (_decimal.cpython-314t-darwin.so:arm64+0x98e4) #1 nm_mpd_qmul <null> (_decimal.cpython-314t-darwin.so:arm64+0x6468) #2 binary_op1 <null> (libpython3.14t.dylib:arm64+0x5acd8) #3 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #4 PyUFunc_OO_O <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x78764) #5 PyObject_CallFunctionObjArgs <null> (libpython3.14t.dylib:arm64+0x8d110) #6 array_multiply <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x138880) #7 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x279c44) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #12 context_run <null> (libpython3.14t.dylib:arm64+0x2c3eec) #13 method_vectorcall_FASTCALL_KEYWORDS <null> (libpython3.14t.dylib:arm64+0xa1c48) #14 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ab8c) #15 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27ce94) #16 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #17 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #18 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #19 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae40) #20 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #21 thread_run <null> (libpython3.14t.dylib:arm64+0x416b08) #22 pythread_wrapper <null> (libpython3.14t.dylib:arm64+0x368cac) Thread T4 (tid=892058, running) created by main thread at: #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708) #1 do_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x36796c) #2 PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x3677b4) #3 do_start_new_thread <null> (libpython3.14t.dylib:arm64+0x4166bc) #4 thread_PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x415448) #5 cfunction_call <null> (libpython3.14t.dylib:arm64+0x12bfb4) #6 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #7 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2819d0) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #12 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #13 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #14 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #15 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #16 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #17 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #18 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #19 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #20 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #21 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #22 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #23 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #24 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae00) #25 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #26 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27fbc0) #27 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #28 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #29 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #30 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #31 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #32 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #33 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #34 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #35 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #36 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #37 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #38 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #39 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #40 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #41 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #42 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #43 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #44 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #45 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #46 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #47 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #48 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #49 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #50 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #51 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #52 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #53 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #54 PyEval_EvalCode <null> (libpython3.14t.dylib:arm64+0x27886c) #55 run_mod <null> (libpython3.14t.dylib:arm64+0x346724) #56 _PyRun_SimpleFileObject <null> (libpython3.14t.dylib:arm64+0x341e10) #57 _PyRun_AnyFileObject <null> (libpython3.14t.dylib:arm64+0x341564) #58 pymain_run_file <null> (libpython3.14t.dylib:arm64+0x382acc) #59 Py_RunMain <null> (libpython3.14t.dylib:arm64+0x381ef8) #60 pymain_main <null> (libpython3.14t.dylib:arm64+0x382364) #61 Py_BytesMain <null> (libpython3.14t.dylib:arm64+0x382438) #62 main <null> (python3.14:arm64+0x10000070c) Thread T1 (tid=892055, running) created by main thread at: #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708) #1 do_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x36796c) #2 PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x3677b4) #3 do_start_new_thread <null> (libpython3.14t.dylib:arm64+0x4166bc) #4 thread_PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x415448) #5 cfunction_call <null> (libpython3.14t.dylib:arm64+0x12bfb4) #6 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #7 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #12 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #13 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #14 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #15 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #16 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #17 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #18 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #19 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #20 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #21 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #22 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #23 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #24 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae00) #25 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #26 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27fbc0) #27 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #28 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #29 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #30 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #31 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #32 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #33 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #34 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #35 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #36 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #37 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #38 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #39 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #40 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #41 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #42 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #43 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #44 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #45 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #46 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #47 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #48 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #49 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #50 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #51 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #52 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #53 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #54 PyEval_EvalCode <null> (libpython3.14t.dylib:arm64+0x27886c) #55 run_mod <null> (libpython3.14t.dylib:arm64+0x346724) #56 _PyRun_SimpleFileObject <null> (libpython3.14t.dylib:arm64+0x341e10) #57 _PyRun_AnyFileObject <null> (libpython3.14t.dylib:arm64+0x341564) #58 pymain_run_file <null> (libpython3.14t.dylib:arm64+0x382acc) #59 Py_RunMain <null> (libpython3.14t.dylib:arm64+0x381ef8) #60 pymain_main <null> (libpython3.14t.dylib:arm64+0x382364) #61 Py_BytesMain <null> (libpython3.14t.dylib:arm64+0x382438) #62 main <null> (python3.14:arm64+0x10000070c) SUMMARY: ThreadSanitizer: data race (_decimal.cpython-314t-darwin.so:arm64+0x98e4) in dec_addstatus+0x3c ================== ================== WARNING: ThreadSanitizer: data race (pid=10816) Write of size 4 at 0x00012305b23c by thread T1: #0 dec_addstatus <null> (_decimal.cpython-314t-darwin.so:arm64+0x98e4) #1 nm_mpd_qmul <null> (_decimal.cpython-314t-darwin.so:arm64+0x6468) #2 binary_op1 <null> (libpython3.14t.dylib:arm64+0x5acd8) #3 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #4 PyUFunc_OO_O <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x78764) #5 PyObject_CallFunctionObjArgs <null> (libpython3.14t.dylib:arm64+0x8d110) #6 array_multiply <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x138880) #7 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x279c44) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #12 context_run <null> (libpython3.14t.dylib:arm64+0x2c3eec) #13 method_vectorcall_FASTCALL_KEYWORDS <null> (libpython3.14t.dylib:arm64+0xa1c48) #14 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ab8c) #15 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27ce94) #16 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #17 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #18 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #19 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae40) #20 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #21 thread_run <null> (libpython3.14t.dylib:arm64+0x416b08) #22 pythread_wrapper <null> (libpython3.14t.dylib:arm64+0x368cac) Previous write of size 4 at 0x00012305b23c by thread T4: #0 dec_addstatus <null> (_decimal.cpython-314t-darwin.so:arm64+0x98e4) #1 nm_mpd_qmul <null> (_decimal.cpython-314t-darwin.so:arm64+0x6468) #2 binary_op1 <null> (libpython3.14t.dylib:arm64+0x5acd8) #3 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #4 PyUFunc_OO_O <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x78764) #5 PyObject_CallFunctionObjArgs <null> (libpython3.14t.dylib:arm64+0x8d110) #6 array_multiply <null> (_multiarray_umath.cpython-314t-darwin.so:arm64+0x138880) #7 PyNumber_Multiply <null> (libpython3.14t.dylib:arm64+0x5ada4) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x279c44) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #12 context_run <null> (libpython3.14t.dylib:arm64+0x2c3eec) #13 method_vectorcall_FASTCALL_KEYWORDS <null> (libpython3.14t.dylib:arm64+0xa1c48) #14 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ab8c) #15 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27ce94) #16 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #17 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #18 method_vectorcall <null> (libpython3.14t.dylib:arm64+0x8f638) #19 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae40) #20 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #21 thread_run <null> (libpython3.14t.dylib:arm64+0x416b08) #22 pythread_wrapper <null> (libpython3.14t.dylib:arm64+0x368cac) Thread T1 (tid=892055, running) created by main thread at: #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708) #1 do_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x36796c) #2 PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x3677b4) #3 do_start_new_thread <null> (libpython3.14t.dylib:arm64+0x4166bc) #4 thread_PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x415448) #5 cfunction_call <null> (libpython3.14t.dylib:arm64+0x12bfb4) #6 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #7 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #12 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #13 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #14 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #15 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #16 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #17 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #18 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #19 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #20 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #21 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #22 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #23 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #24 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae00) #25 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #26 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27fbc0) #27 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #28 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #29 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #30 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #31 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #32 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #33 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #34 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #35 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #36 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #37 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #38 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #39 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #40 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #41 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #42 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #43 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #44 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #45 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #46 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #47 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #48 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #49 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #50 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #51 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #52 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #53 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #54 PyEval_EvalCode <null> (libpython3.14t.dylib:arm64+0x27886c) #55 run_mod <null> (libpython3.14t.dylib:arm64+0x346724) #56 _PyRun_SimpleFileObject <null> (libpython3.14t.dylib:arm64+0x341e10) #57 _PyRun_AnyFileObject <null> (libpython3.14t.dylib:arm64+0x341564) #58 pymain_run_file <null> (libpython3.14t.dylib:arm64+0x382acc) #59 Py_RunMain <null> (libpython3.14t.dylib:arm64+0x381ef8) #60 pymain_main <null> (libpython3.14t.dylib:arm64+0x382364) #61 Py_BytesMain <null> (libpython3.14t.dylib:arm64+0x382438) #62 main <null> (python3.14:arm64+0x10000070c) Thread T4 (tid=892058, running) created by main thread at: #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708) #1 do_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x36796c) #2 PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x3677b4) #3 do_start_new_thread <null> (libpython3.14t.dylib:arm64+0x4166bc) #4 thread_PyThread_start_joinable_thread <null> (libpython3.14t.dylib:arm64+0x415448) #5 cfunction_call <null> (libpython3.14t.dylib:arm64+0x12bfb4) #6 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #7 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #8 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2819d0) #9 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #10 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #11 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #12 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #13 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #14 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #15 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #16 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #17 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #18 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #19 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #20 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #21 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #22 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #23 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #24 _PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8ae00) #25 PyObject_Call <null> (libpython3.14t.dylib:arm64+0x8aeb4) #26 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x27fbc0) #27 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #28 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #29 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #30 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #31 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #32 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #33 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #34 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #35 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #36 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #37 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #38 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #39 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #40 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #41 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #42 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #43 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #44 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #45 _PyEval_Vector <null> (libpython3.14t.dylib:arm64+0x278c90) #46 _PyFunction_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8b1cc) #47 _PyObject_VectorcallDictTstate <null> (libpython3.14t.dylib:arm64+0x89d98) #48 _PyObject_Call_Prepend <null> (libpython3.14t.dylib:arm64+0x8b7dc) #49 call_method <null> (libpython3.14t.dylib:arm64+0x196a54) #50 slot_tp_call <null> (libpython3.14t.dylib:arm64+0x196874) #51 _PyObject_MakeTpCall <null> (libpython3.14t.dylib:arm64+0x8a038) #52 PyObject_Vectorcall <null> (libpython3.14t.dylib:arm64+0x8ac28) #53 _PyEval_EvalFrameDefault <null> (libpython3.14t.dylib:arm64+0x2809ac) #54 PyEval_EvalCode <null> (libpython3.14t.dylib:arm64+0x27886c) #55 run_mod <null> (libpython3.14t.dylib:arm64+0x346724) #56 _PyRun_SimpleFileObject <null> (libpython3.14t.dylib:arm64+0x341e10) #57 _PyRun_AnyFileObject <null> (libpython3.14t.dylib:arm64+0x341564) #58 pymain_run_file <null> (libpython3.14t.dylib:arm64+0x382acc) #59 Py_RunMain <null> (libpython3.14t.dylib:arm64+0x381ef8) #60 pymain_main <null> (libpython3.14t.dylib:arm64+0x382364) #61 Py_BytesMain <null> (libpython3.14t.dylib:arm64+0x382438) #62 main <null> (python3.14:arm64+0x10000070c) SUMMARY: ThreadSanitizer: data race (_decimal.cpython-314t-darwin.so:arm64+0x98e4) in dec_addstatus+0x3c ================== ThreadSanitizer: reported 4 warnings
I confirm. This happens on v3.14.0 with (commented variant affected too):
# test_a.py from decimal import Decimal def test_array_gcd_lcm_decimal(): # a = 123 * Decimal('0.20') a = 123 * Decimal(123) assert a
(Edit: "fixed" by
export PYTHON_THREAD_INHERIT_CONTEXT=0.)This is tricky. I'm not sure what the fix should be, but I no longer think it's just a documentation issue (or API misuse).
I think the underlying problem is that the Decimal context is a single context var, so you don't get the thread-safety (or context safety) that you'd normally expect with context vars.
I think the underlying problem is that the Decimal context is a single context var
Hmm, this is same as for the gmpy2 extension module. Though, it works in a similar situation (status flags got updated in example below):
# test_b.py from gmpy2 import mpfr def test_gmpy2(): a = 1.1 * mpfr(123) assert a
Another reproducer which does not shares a global context:
I was able to fix this case with the following patch:
diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 0484d9896a1..8fc1183b648 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1941,7 +1941,24 @@ current_context(decimal_state *state) } if (tl_context != NULL) { - return tl_context; + // It is possible that the context was inherited from another thread + // e.g. if PYTHON_THREAD_INHERIT_CONTEXT=1 is set. + // In that case, we need to make a copy of context otherwise we + // would be sharing the same context as the parent thread. + // This is needed as decimal module mutates the context directly + // rather than setting a new context via PyContextVar_Set. + PyThreadState *tstate = _PyThreadState_GET(); + if (_PyDecContextObject_CAST(tl_context)->tstate == tstate) { + return tl_context; + } + PyObject *copy = context_copy(state, tl_context); + Py_INCREF(copy); + // Set the copy as the current context for this thread, + // so that subsequent calls get the correct context. + PyObject *tok = PyContextVar_Set(state->current_context_var, copy); + Py_DECREF(tok); + Py_DECREF(tl_context); + return copy; } return init_current_context(state);
With this I don't see any data races with the minimal reproducer which does not shares global context.
The copying in the patch is happening when the context is accessed. It can still be concurrently modified by the parent thread.
The copying in the patch is happening when the context is accessed. It can still be concurrently modified by the parent thread.
I think this case is already covered by docs:
To control the defaults so that each thread will use the same values throughout the application, directly modify the DefaultContext object. This should be done before any threads are started so that there won’t be a race condition between threads calling getcontext().
Hi @skirpichev @colesbury @kumaraditya303 ,
Are we still working on this issue?
If this problem needs to be solved, I want to have a try. 😊If this problem needs to be solved
I think so.
I want to have a try.
Feel free to do this. Beware, it may be not an easy issue.
Reacted by Edward XuAfter some experiments, I feel that #130010 breaks the decimal module. And not only it's C-version. Lets consider the pure-Python module.
Docs now says:
If setcontext() has not been called before getcontext(), then getcontext() will automatically create a new context for use in the current thread. New context objects have default values set from the decimal.DefaultContext object.
The sys.flags.thread_inherit_context flag affects the context for new threads. If the flag is false, new threads will start with an empty context. In this case, getcontext() will create a new context object when called and use the default values from DefaultContext. If the flag is true, new threads will start with a copy of context from the caller of threading.Thread.start().
First, the last sentence is misleading. The context, referred here ("a copy of") is not a decimal Context.
Second, in fact, with this settings --- thread rather inherits parents context, if we are talking about decimal's Context. That could be demonstrated by:
from threading import Thread, Barrier from _pydecimal import * #from decimal import * DefaultContext.prec = 12 setcontext(DefaultContext) n = 3 barrier = Barrier(n) def race(i, r): barrier.wait() ctx = getcontext() ctx.prec += i ctx.clear_flags() d = Decimal('1E-100000000') if i % 2 == 0: d = +d r[i] = (i, ctx.prec, id(ctx), int(ctx.flags[Underflow])) if __name__ == "__main__": for _ in range(n): r = list(range(n)) threads = [Thread(target=race, args=(i, r)) for i in range(n)] # threads = [Thread(target=race, args=(i, r), # context=contextvars.Context()) for i in range(n)] for thread in threads: thread.start() for thread in threads: thread.join() print(r)
Output:
$ PYTHON_THREAD_INHERIT_CONTEXT=0 ./python d.py [(0, 12, 139845881823648, 1), (1, 13, 139845948932448, 0), (2, 14, 139846015254944, 1)] [(0, 12, 139845949194592, 1), (1, 13, 139845882085728, 0), (2, 14, 139846016434528, 1)] [(0, 12, 139845948932448, 1), (1, 13, 139845881561376, 0), (2, 14, 139846016237920, 1)] $ PYTHON_THREAD_INHERIT_CONTEXT=1 ./python d.py [(0, 14, 140482748631840, 1), (1, 14, 140482748631840, 1), (2, 14, 140482748631840, 1)] [(0, 17, 140482748631840, 1), (1, 17, 140482748631840, 0), (2, 17, 140482748631840, 1)] [(0, 20, 140482748631840, 1), (1, 20, 140482748631840, 0), (2, 20, 140482748631840, 1)]For C-decimal module the last example will trigger TS failure like in the issue description
This looks as a regression, because the leading paragraph of the Working with threads section says:
The getcontext() function accesses a different Context object for each thread. Having separate thread contexts means that threads may make changes (such as getcontext().prec=10) without interfering with other threads.
Now this contract is broken with
PYTHON_THREAD_INHERIT_CONTEXT=1settings, i.e. per default on free-threading builds.CC @nascheme
BTW, workaround is commented out in the code (explicit settings of the context parameter for Thread). So, maybe it's still a documentation issue. Documentation must be fixed anyway, so I'll add a label.
Reacted by Edward XuYeah, there is bug here and it's not just a documentation issue. IMHO, the original change that switched
_current_context_varto becontextvars.ContextVarrather thanthreading.local()was the start of problem. Or, maybe you could say it was broken before that, since the decimal context did not function as expected when async tasks were involved. UsingContextVar()instead of thread-locals was an attempt make things work nicely with asyncio but didn't really do it.The problem with using
ContextVar()is that the copy operation done, via the HAMT data structure, applies to the bindings and not to the variables themselves. For the decimal module, the bound variable is aContext()instance and it itself is mutable.The names involved here are adding to the confusion. E.g. this text:
If the flag is true, new threads will start with a copy of context from the caller of threading.Thread.start().
Is true if we mean "context" as in a
contextvars.Context()instance. It's not true for the decimal Context() instance though. As you point out, this documentation is definitely not correct right now:The getcontext() function accesses a different Context object for each thread. Having separate thread contexts means that threads may make changes (such as getcontext().prec=10) without interfering with other threads.
I think I have a fix for this, will make a PR. It should both fix the problem with threads and also for asyncio.
Reacted by Sergey B Kirpichev and Edward XuIt seems GH-146482 will fix this race if we don't share a global context. My reproducer code (produces TSAN warnings without the PR and no warnings with):
from decimal import Decimal, getcontext import threading def decimal_work(): a = 123 * Decimal("0.20") assert a def race(): barrier.wait(timeout=2) for _ in range(100): decimal_work() if __name__ == "__main__": N_THREADS = 20 barrier = threading.Barrier(N_THREADS) for _ in range(10): ctx = getcontext() # need to set context in parent thread threads = [threading.Thread(target=race) for _ in range(N_THREADS)] for thread in threads: thread.start() for thread in threads: thread.join()
Just to flag a scope question for after #146482 lands:
The PR closes the inherited-context path —
getcontext()/current_context()now copy on first access in a new thread/task, so default arithmetic stops racing on a sharedmpd_context_t.It doesn't, however, close the underlying primitive race in
_decimal.c. Those accesses are still plain non-atomic reads/writes:dec_addstatus:ctx->status |= status;at_decimal.c:616Context.clear_flags:ctx->status = 0;at_decimal.c:1421Context.clear_traps:ctx->traps = 0;at_decimal.c:1407signaldict_setitem:SdFlags(self) |= flag;/SdFlags(self) &= ~flag;at_decimal.c:744/:747(this isctx->status/ctx->trapsreached viactx.flags[...] = True)signaldict_getitem:SdFlags(self) & flagat_decimal.c:715
So the race still fires whenever the same
Contextis reached without going throughcurrent_context(). In particular, this issue's own MRE:context = decimal.Context() context.prec = 28 ... def race(): barrier.wait() decimal.Decimal('1.0', context=context)
passes
context=contextexplicitly and so bypasses the newget_changed()-based copy. After #146482 merges that exact reproducer should still trip TSan at_decimal.c:616. Same for any of:shared_ctx.create_decimal(s)(routes through_decimal_Context_create_decimal_impl)shared_ctx.add(...)/multiply/divide/quantize/ etc.shared_ctx.flags[Inexact] = Trueconcurrent with arithmeticsetcontext(non_template_instance)from multiple threads (only the three module templates are auto-copied; other instances arePy_INCREF'd and stored as-is)
I'd suggest either keeping this issue open until those C-level sites are made FT-safe (atomic ops or a per-
ContextPyMutex), or, probably cleaner, letting #146482 close this one and tracking the primitive in a follow-up. Happy to file that follow-up with reproducers for each of the five sites if helpful.Drafted by Claude Code, reviewed by a human.
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsTodo
Reproducer:
Data race:
I discovered this while adding thread safety support to msgspec. Running its test suite under pytest-run-parallel leads to similar data races as present above from the minimal reproducer.
See https://gist.github.com/kumaraditya303/3c26f31eaf0beea39d7eeb539b662846 for full tsan report
Linked PRs