Skip to content

Data race between record_allocation and gc_get_count_impl #150411

Description

@LindaSummer

Bug report

Bug description:

I found this during solve #148613.

The gc_generation.count has one read not protected by atomic operation.

importgcfromthreadingimportBarrier, ThreadclassCyclicReference:
def__init__(self):
self.ref=selfN_ALLOCATORS=7N_READERS=1N_THREADS=N_ALLOCATORS+N_READERSN_ITERS=200_000barrier=Barrier(N_THREADS)
defallocator():
barrier.wait()
for_inrange(N_ITERS):
CyclicReference()
defreader():
barrier.wait()
for_inrange(N_ITERS):
gc.get_count()
threads= [Thread(target=allocator) for_inrange(N_ALLOCATORS)]
threads.extend(Thread(target=reader) for_inrange(N_READERS))
forthreadinthreads:
thread.start()
forthreadinthreads:
thread.join()

Here is tsan report.

==================
WARNING: ThreadSanitizer: data race (pid=2274087)
Atomic write of size 4 at 0x55a842f6ab04 by thread T2:
#0 _Py_atomic_add_int Include/cpython/pyatomic_gcc.h:15 (python3.16+0x45a310)
#1 record_allocation Python/gc_free_threading.c:2028 (python3.16+0x45a310)
#2 _PyObject_GC_Link Python/gc_free_threading.c:2707 (python3.16+0x45a310)
#3 _PyType_AllocNoTrack Objects/typeobject.c:2533 (python3.16+0x299867)
#4 PyType_GenericAlloc Objects/typeobject.c:2554 (python3.16+0x2998d4)
#5 _PyEval_EvalFrameDefault Python/generated_cases.c.h:1955 (python3.16+0x3e0047)
#6 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee7b7)
#7 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee7b7)
#8 _PyFunction_Vectorcall Objects/call.c:413 (python3.16+0x152190)
#9 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x15379f)
#10 _PyObject_VectorcallPrepend Objects/call.c:855 (python3.16+0x15379f)
#11 method_vectorcall Objects/classobject.c:55 (python3.16+0x158676)
#12 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x42927b)
#13 context_run Python/context.c:728 (python3.16+0x42927b)
#14 method_vectorcall_FASTCALL_KEYWORDS Objects/descrobject.c:421 (python3.16+0x174575)
#15 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x151d7c)
#16 PyObject_Vectorcall Objects/call.c:327 (python3.16+0x151d7c)
#17 _Py_VectorCallInstrumentation_StackRefSteal Python/ceval.c:775 (python3.16+0x3c13f5)
#18 _PyEval_EvalFrameDefault Python/generated_cases.c.h:1846 (python3.16+0x3cee3f)
#19 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee7b7)
#20 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee7b7)
#21 _PyFunction_Vectorcall Objects/call.c:413 (python3.16+0x152190)
#22 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x15379f)
#23 _PyObject_VectorcallPrepend Objects/call.c:855 (python3.16+0x15379f)
#24 method_vectorcall Objects/classobject.c:55 (python3.16+0x158676)
#25 _PyVectorcall_Call Objects/call.c:273 (python3.16+0x154c78)
#26 _PyObject_Call Objects/call.c:348 (python3.16+0x1550b1)
#27 PyObject_Call Objects/call.c:373 (python3.16+0x1550b1)
#28 thread_run Modules/_threadmodule.c:388 (python3.16+0x609d30)
#29 pythread_wrapper Python/thread_pthread.h:234 (python3.16+0x508c52)
#30 <null> <null> (libtsan.so.2+0x4cf1a)
Previous read of size 4 at 0x55a842f6ab04 by thread T8:
#0 gc_get_count_impl Modules/gcmodule.c:232 (python3.16+0x52aef1)
#1 gc_get_count Modules/clinic/gcmodule.c.h:305 (python3.16+0x52aef1)
#2 cfunction_vectorcall_NOARGS Objects/methodobject.c:508 (python3.16+0x21a586)
#3 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x151d7c)
#4 PyObject_Vectorcall Objects/call.c:327 (python3.16+0x151d7c)
#5 _Py_VectorCall_StackRefSteal Python/ceval.c:733 (python3.16+0x3c0fc5)
#6 _PyEval_EvalFrameDefault Python/generated_cases.c.h:4362 (python3.16+0x3cf022)
#7 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee7b7)
#8 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee7b7)
#9 _PyFunction_Vectorcall Objects/call.c:413 (python3.16+0x152190)
#10 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x15379f)
#11 _PyObject_VectorcallPrepend Objects/call.c:855 (python3.16+0x15379f)
#12 method_vectorcall Objects/classobject.c:55 (python3.16+0x158676)
#13 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x42927b)
#14 context_run Python/context.c:728 (python3.16+0x42927b)
#15 method_vectorcall_FASTCALL_KEYWORDS Objects/descrobject.c:421 (python3.16+0x174575)
#16 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x151d7c)
#17 PyObject_Vectorcall Objects/call.c:327 (python3.16+0x151d7c)
#18 _Py_VectorCallInstrumentation_StackRefSteal Python/ceval.c:775 (python3.16+0x3c13f5)
#19 _PyEval_EvalFrameDefault Python/generated_cases.c.h:1846 (python3.16+0x3cee3f)
#20 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee7b7)
#21 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee7b7)
#22 _PyFunction_Vectorcall Objects/call.c:413 (python3.16+0x152190)
#23 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x15379f)
#24 _PyObject_VectorcallPrepend Objects/call.c:855 (python3.16+0x15379f)
#25 method_vectorcall Objects/classobject.c:55 (python3.16+0x158676)
#26 _PyVectorcall_Call Objects/call.c:273 (python3.16+0x154c78)
#27 _PyObject_Call Objects/call.c:348 (python3.16+0x1550b1)
#28 PyObject_Call Objects/call.c:373 (python3.16+0x1550b1)
#29 thread_run Modules/_threadmodule.c:388 (python3.16+0x609d30)
#30 pythread_wrapper Python/thread_pthread.h:234 (python3.16+0x508c52)
#31 <null> <null> (libtsan.so.2+0x4cf1a)
Location is global '_PyRuntime' of size 405696 at 0x55a842f42240 (python3.16+0x89fb04)
Thread T2 'Thread-2 (alloc' (tid=2274090, running) created by main thread at:
#0 pthread_create <null> (libtsan.so.2+0x58491)
#1 do_start_joinable_thread Python/thread_pthread.h:281 (python3.16+0x508d67)
#2 PyThread_start_joinable_thread Python/thread_pthread.h:323 (python3.16+0x5090ab)
#3 ThreadHandle_start Modules/_threadmodule.c:475 (python3.16+0x60aca9)
#4 do_start_new_thread Modules/_threadmodule.c:1919 (python3.16+0x60aca9)
#5 thread_PyThread_start_joinable_thread Modules/_threadmodule.c:2042 (python3.16+0x60b5b9)
#6 cfunction_call Objects/methodobject.c:564 (python3.16+0x2192fd)
#7 _PyObject_MakeTpCall Objects/call.c:242 (python3.16+0x150e5b)
#8 _PyObject_VectorcallTstate Include/internal/pycore_call.h:142 (python3.16+0x151de1)
#9 _PyObject_VectorcallTstate Include/internal/pycore_call.h:129 (python3.16+0x151de1)
#10 PyObject_Vectorcall Objects/call.c:327 (python3.16+0x151de1)
#11 _Py_VectorCall_StackRefSteal Python/ceval.c:733 (python3.16+0x3c0fc5)
#12 _PyEval_EvalFrameDefault Python/generated_cases.c.h:3528 (python3.16+0x3d8f55)
#13 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee06c)
#14 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee06c)
#15 PyEval_EvalCode Python/ceval.c:686 (python3.16+0x3ee06c)
#16 run_eval_code_obj Python/pythonrun.c:1369 (python3.16+0x4d55c2)
#17 run_mod Python/pythonrun.c:1472 (python3.16+0x4d5b88)
#18 pyrun_file Python/pythonrun.c:1296 (python3.16+0x4d9913)
#19 _PyRun_SimpleFileObject Python/pythonrun.c:518 (python3.16+0x4d9913)
#20 _PyRun_AnyFileObject Python/pythonrun.c:81 (python3.16+0x4da3c5)
#21 pymain_run_file_obj Modules/main.c:411 (python3.16+0x52940d)
#22 pymain_run_file Modules/main.c:430 (python3.16+0x52940d)
#23 pymain_run_python Modules/main.c:715 (python3.16+0x52940d)
#24 Py_RunMain Modules/main.c:796 (python3.16+0x52a2fd)
#25 pymain_main Modules/main.c:826 (python3.16+0x52a2fd)
#26 Py_BytesMain Modules/main.c:850 (python3.16+0x52a2fd)
#27 main Programs/python.c:15 (python3.16+0xa0786)
Thread T8 'Thread-8 (reade' (tid=2274096, running) created by main thread at:
#0 pthread_create <null> (libtsan.so.2+0x58491)
#1 do_start_joinable_thread Python/thread_pthread.h:281 (python3.16+0x508d67)
#2 PyThread_start_joinable_thread Python/thread_pthread.h:323 (python3.16+0x5090ab)
#3 ThreadHandle_start Modules/_threadmodule.c:475 (python3.16+0x60aca9)
#4 do_start_new_thread Modules/_threadmodule.c:1919 (python3.16+0x60aca9)
#5 thread_PyThread_start_joinable_thread Modules/_threadmodule.c:2042 (python3.16+0x60b5b9)
#6 cfunction_call Objects/methodobject.c:564 (python3.16+0x2192fd)
#7 _PyObject_MakeTpCall Objects/call.c:242 (python3.16+0x150e5b)
#8 _PyObject_VectorcallTstate Include/internal/pycore_call.h:142 (python3.16+0x151de1)
#9 _PyObject_VectorcallTstate Include/internal/pycore_call.h:129 (python3.16+0x151de1)
#10 PyObject_Vectorcall Objects/call.c:327 (python3.16+0x151de1)
#11 _Py_VectorCall_StackRefSteal Python/ceval.c:733 (python3.16+0x3c0fc5)
#12 _PyEval_EvalFrameDefault Python/generated_cases.c.h:3528 (python3.16+0x3d8f55)
#13 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee06c)
#14 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee06c)
#15 PyEval_EvalCode Python/ceval.c:686 (python3.16+0x3ee06c)
#16 run_eval_code_obj Python/pythonrun.c:1369 (python3.16+0x4d55c2)
#17 run_mod Python/pythonrun.c:1472 (python3.16+0x4d5b88)
#18 pyrun_file Python/pythonrun.c:1296 (python3.16+0x4d9913)
#19 _PyRun_SimpleFileObject Python/pythonrun.c:518 (python3.16+0x4d9913)
#20 _PyRun_AnyFileObject Python/pythonrun.c:81 (python3.16+0x4da3c5)
#21 pymain_run_file_obj Modules/main.c:411 (python3.16+0x52940d)
#22 pymain_run_file Modules/main.c:430 (python3.16+0x52940d)
#23 pymain_run_python Modules/main.c:715 (python3.16+0x52940d)
#24 Py_RunMain Modules/main.c:796 (python3.16+0x52a2fd)
#25 pymain_main Modules/main.c:826 (python3.16+0x52a2fd)
#26 Py_BytesMain Modules/main.c:850 (python3.16+0x52a2fd)
#27 main Programs/python.c:15 (python3.16+0xa0786)
SUMMARY: ThreadSanitizer: data race Include/cpython/pyatomic_gcc.h:15 in _Py_atomic_add_int
==================
==================
WARNING: ThreadSanitizer: data race (pid=2274087)
Atomic write of size 4 at 0x55a842f6ab04 by thread T3:
#0 _Py_atomic_compare_exchange_int Include/cpython/pyatomic_gcc.h:70 (python3.16+0x45b47f)
#1 record_deallocation Python/gc_free_threading.c:2057 (python3.16+0x45b47f)
#2 PyObject_GC_Del Python/gc_free_threading.c:2831 (python3.16+0x45b47f)
#3 object_dealloc Objects/typeobject.c:7463 (python3.16+0x286bab)
#4 subtype_dealloc Objects/typeobject.c:2871 (python3.16+0x28e8b7)
#5 _Py_Dealloc Objects/object.c:3312 (python3.16+0x22599b)
#6 _Py_DecRefSharedDebug Objects/object.c:426 (python3.16+0x225d5d)
#7 _Py_DecRefShared Objects/object.c:433 (python3.16+0x225d5d)
#8 Py_DECREF Include/refcount.h:385 (python3.16+0x45ca4c)
#9 delete_garbage Python/gc_free_threading.c:1769 (python3.16+0x45ca4c)
#10 gc_collect_internal Python/gc_free_threading.c:2176 (python3.16+0x45ca4c)
#11 gc_collect_main Python/gc_free_threading.c:2257 (python3.16+0x45d5d6)
#12 _Py_RunGC Python/gc_free_threading.c:2716 (python3.16+0x45dbb6)
#13 _Py_HandlePending Python/ceval_gil.c:1399 (python3.16+0x46d393)
#14 check_periodics Python/ceval_macros.h:524 (python3.16+0x3e9e88)
#15 _PyEval_EvalFrameDefault Python/generated_cases.c.h:10876 (python3.16+0x3e9e88)
#16 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee7b7)
#17 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee7b7)
#18 _PyFunction_Vectorcall Objects/call.c:413 (python3.16+0x152190)
#19 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x15379f)
#20 _PyObject_VectorcallPrepend Objects/call.c:855 (python3.16+0x15379f)
#21 method_vectorcall Objects/classobject.c:55 (python3.16+0x158676)
#22 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x42927b)
#23 context_run Python/context.c:728 (python3.16+0x42927b)
#24 method_vectorcall_FASTCALL_KEYWORDS Objects/descrobject.c:421 (python3.16+0x174575)
#25 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x151d7c)
#26 PyObject_Vectorcall Objects/call.c:327 (python3.16+0x151d7c)
#27 _Py_VectorCallInstrumentation_StackRefSteal Python/ceval.c:775 (python3.16+0x3c13f5)
#28 _PyEval_EvalFrameDefault Python/generated_cases.c.h:1846 (python3.16+0x3cee3f)
#29 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee7b7)
#30 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee7b7)
#31 _PyFunction_Vectorcall Objects/call.c:413 (python3.16+0x152190)
#32 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x15379f)
#33 _PyObject_VectorcallPrepend Objects/call.c:855 (python3.16+0x15379f)
#34 method_vectorcall Objects/classobject.c:55 (python3.16+0x158676)
#35 _PyVectorcall_Call Objects/call.c:273 (python3.16+0x154c78)
#36 _PyObject_Call Objects/call.c:348 (python3.16+0x1550b1)
#37 PyObject_Call Objects/call.c:373 (python3.16+0x1550b1)
#38 thread_run Modules/_threadmodule.c:388 (python3.16+0x609d30)
#39 pythread_wrapper Python/thread_pthread.h:234 (python3.16+0x508c52)
#40 <null> <null> (libtsan.so.2+0x4cf1a)
Previous read of size 4 at 0x55a842f6ab04 by thread T8:
#0 gc_get_count_impl Modules/gcmodule.c:232 (python3.16+0x52aef1)
#1 gc_get_count Modules/clinic/gcmodule.c.h:305 (python3.16+0x52aef1)
#2 cfunction_vectorcall_NOARGS Objects/methodobject.c:508 (python3.16+0x21a586)
#3 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x151d7c)
#4 PyObject_Vectorcall Objects/call.c:327 (python3.16+0x151d7c)
#5 _Py_VectorCall_StackRefSteal Python/ceval.c:733 (python3.16+0x3c0fc5)
#6 _PyEval_EvalFrameDefault Python/generated_cases.c.h:4362 (python3.16+0x3cf022)
#7 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee7b7)
#8 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee7b7)
#9 _PyFunction_Vectorcall Objects/call.c:413 (python3.16+0x152190)
#10 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x15379f)
#11 _PyObject_VectorcallPrepend Objects/call.c:855 (python3.16+0x15379f)
#12 method_vectorcall Objects/classobject.c:55 (python3.16+0x158676)
#13 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x42927b)
#14 context_run Python/context.c:728 (python3.16+0x42927b)
#15 method_vectorcall_FASTCALL_KEYWORDS Objects/descrobject.c:421 (python3.16+0x174575)
#16 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x151d7c)
#17 PyObject_Vectorcall Objects/call.c:327 (python3.16+0x151d7c)
#18 _Py_VectorCallInstrumentation_StackRefSteal Python/ceval.c:775 (python3.16+0x3c13f5)
#19 _PyEval_EvalFrameDefault Python/generated_cases.c.h:1846 (python3.16+0x3cee3f)
#20 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee7b7)
#21 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee7b7)
#22 _PyFunction_Vectorcall Objects/call.c:413 (python3.16+0x152190)
#23 _PyObject_VectorcallTstate Include/internal/pycore_call.h:144 (python3.16+0x15379f)
#24 _PyObject_VectorcallPrepend Objects/call.c:855 (python3.16+0x15379f)
#25 method_vectorcall Objects/classobject.c:55 (python3.16+0x158676)
#26 _PyVectorcall_Call Objects/call.c:273 (python3.16+0x154c78)
#27 _PyObject_Call Objects/call.c:348 (python3.16+0x1550b1)
#28 PyObject_Call Objects/call.c:373 (python3.16+0x1550b1)
#29 thread_run Modules/_threadmodule.c:388 (python3.16+0x609d30)
#30 pythread_wrapper Python/thread_pthread.h:234 (python3.16+0x508c52)
#31 <null> <null> (libtsan.so.2+0x4cf1a)
Location is global '_PyRuntime' of size 405696 at 0x55a842f42240 (python3.16+0x89fb04)
Thread T3 'Thread-3 (alloc' (tid=2274091, running) created by main thread at:
#0 pthread_create <null> (libtsan.so.2+0x58491)
#1 do_start_joinable_thread Python/thread_pthread.h:281 (python3.16+0x508d67)
#2 PyThread_start_joinable_thread Python/thread_pthread.h:323 (python3.16+0x5090ab)
#3 ThreadHandle_start Modules/_threadmodule.c:475 (python3.16+0x60aca9)
#4 do_start_new_thread Modules/_threadmodule.c:1919 (python3.16+0x60aca9)
#5 thread_PyThread_start_joinable_thread Modules/_threadmodule.c:2042 (python3.16+0x60b5b9)
#6 cfunction_call Objects/methodobject.c:564 (python3.16+0x2192fd)
#7 _PyObject_MakeTpCall Objects/call.c:242 (python3.16+0x150e5b)
#8 _PyObject_VectorcallTstate Include/internal/pycore_call.h:142 (python3.16+0x151de1)
#9 _PyObject_VectorcallTstate Include/internal/pycore_call.h:129 (python3.16+0x151de1)
#10 PyObject_Vectorcall Objects/call.c:327 (python3.16+0x151de1)
#11 _Py_VectorCall_StackRefSteal Python/ceval.c:733 (python3.16+0x3c0fc5)
#12 _PyEval_EvalFrameDefault Python/generated_cases.c.h:3528 (python3.16+0x3d8f55)
#13 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee06c)
#14 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee06c)
#15 PyEval_EvalCode Python/ceval.c:686 (python3.16+0x3ee06c)
#16 run_eval_code_obj Python/pythonrun.c:1369 (python3.16+0x4d55c2)
#17 run_mod Python/pythonrun.c:1472 (python3.16+0x4d5b88)
#18 pyrun_file Python/pythonrun.c:1296 (python3.16+0x4d9913)
#19 _PyRun_SimpleFileObject Python/pythonrun.c:518 (python3.16+0x4d9913)
#20 _PyRun_AnyFileObject Python/pythonrun.c:81 (python3.16+0x4da3c5)
#21 pymain_run_file_obj Modules/main.c:411 (python3.16+0x52940d)
#22 pymain_run_file Modules/main.c:430 (python3.16+0x52940d)
#23 pymain_run_python Modules/main.c:715 (python3.16+0x52940d)
#24 Py_RunMain Modules/main.c:796 (python3.16+0x52a2fd)
#25 pymain_main Modules/main.c:826 (python3.16+0x52a2fd)
#26 Py_BytesMain Modules/main.c:850 (python3.16+0x52a2fd)
#27 main Programs/python.c:15 (python3.16+0xa0786)
Thread T8 'Thread-8 (reade' (tid=2274096, running) created by main thread at:
#0 pthread_create <null> (libtsan.so.2+0x58491)
#1 do_start_joinable_thread Python/thread_pthread.h:281 (python3.16+0x508d67)
#2 PyThread_start_joinable_thread Python/thread_pthread.h:323 (python3.16+0x5090ab)
#3 ThreadHandle_start Modules/_threadmodule.c:475 (python3.16+0x60aca9)
#4 do_start_new_thread Modules/_threadmodule.c:1919 (python3.16+0x60aca9)
#5 thread_PyThread_start_joinable_thread Modules/_threadmodule.c:2042 (python3.16+0x60b5b9)
#6 cfunction_call Objects/methodobject.c:564 (python3.16+0x2192fd)
#7 _PyObject_MakeTpCall Objects/call.c:242 (python3.16+0x150e5b)
#8 _PyObject_VectorcallTstate Include/internal/pycore_call.h:142 (python3.16+0x151de1)
#9 _PyObject_VectorcallTstate Include/internal/pycore_call.h:129 (python3.16+0x151de1)
#10 PyObject_Vectorcall Objects/call.c:327 (python3.16+0x151de1)
#11 _Py_VectorCall_StackRefSteal Python/ceval.c:733 (python3.16+0x3c0fc5)
#12 _PyEval_EvalFrameDefault Python/generated_cases.c.h:3528 (python3.16+0x3d8f55)
#13 _PyEval_EvalFrame Include/internal/pycore_ceval.h:122 (python3.16+0x3ee06c)
#14 _PyEval_Vector Python/ceval.c:2156 (python3.16+0x3ee06c)
#15 PyEval_EvalCode Python/ceval.c:686 (python3.16+0x3ee06c)
#16 run_eval_code_obj Python/pythonrun.c:1369 (python3.16+0x4d55c2)
#17 run_mod Python/pythonrun.c:1472 (python3.16+0x4d5b88)
#18 pyrun_file Python/pythonrun.c:1296 (python3.16+0x4d9913)
#19 _PyRun_SimpleFileObject Python/pythonrun.c:518 (python3.16+0x4d9913)
#20 _PyRun_AnyFileObject Python/pythonrun.c:81 (python3.16+0x4da3c5)
#21 pymain_run_file_obj Modules/main.c:411 (python3.16+0x52940d)
#22 pymain_run_file Modules/main.c:430 (python3.16+0x52940d)
#23 pymain_run_python Modules/main.c:715 (python3.16+0x52940d)
#24 Py_RunMain Modules/main.c:796 (python3.16+0x52a2fd)
#25 pymain_main Modules/main.c:826 (python3.16+0x52a2fd)
#26 Py_BytesMain Modules/main.c:850 (python3.16+0x52a2fd)
#27 main Programs/python.c:15 (python3.16+0xa0786)
SUMMARY: ThreadSanitizer: data race Include/cpython/pyatomic_gcc.h:70 in _Py_atomic_compare_exchange_int
==================
ThreadSanitizer: reported 2 warnings

Root Cause

This problem would happen when cyclic references allocation frequently in free threading build.

The alloc_count would be reset when gc->alloc_count >= LOCAL_ALLOC_COUNT_THRESHOLD and the gcstate->young.count would increase.

if (gc->alloc_count >= LOCAL_ALLOC_COUNT_THRESHOLD) {
// TODO: Use Py_ssize_t for the generation count.
GCState*gcstate=&tstate->interp->gc;
_Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count);
gc->alloc_count=0;

And we read the count in gc_get_count_impl, it was partially protected by atomic.

staticPyObject*
gc_get_count_impl(PyObject*module)
/*[clinic end generated code: output=354012e67b16398f input=a392794a08251751]*/
{
GCState*gcstate=get_gc_state();
#ifdefPy_GIL_DISABLED
_PyThreadStateImpl*tstate= (_PyThreadStateImpl*)_PyThreadState_GET();
struct_gc_thread_state*gc=&tstate->gc;
// Flush the local allocation count to the global count
_Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count);
gc->alloc_count=0;
#endif
#ifndefPy_GIL_DISABLED
returnPy_BuildValue("(iii)",
gcstate->generations[0].count,
gcstate->generations[1].count,
gcstate->generations[2].count);
#else
returnPy_BuildValue("(iii)",
gcstate->young.count,
gcstate->old[0].count,
gcstate->old[1].count);
#endif
}

Solution

So we should add atomic load for the read action.

For count in gc_collect_internal, they are guarded by STW so I think they should be safe.

gc_collect_internal(PyInterpreterState*interp, structcollection_state*state, intgeneration)
{
_PyEval_StopTheWorld(interp);
// update collection and allocation counters
if (generation+1<NUM_GENERATIONS) {
state->gcstate->old[generation].count+=1;
}
state->gcstate->young.count=0;
for (inti=1; i <= generation; ++i) {
state->gcstate->old[i-1].count=0;
}

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions