Bug report
Bug description:
I found this during solve #148613.
The gc_generation.count has one read not protected by atomic operation.
import gc
from threading import Barrier, Thread
class CyclicReference:
def __init__(self):
self.ref = self
N_ALLOCATORS = 7
N_READERS = 1
N_THREADS = N_ALLOCATORS + N_READERS
N_ITERS = 200_000
barrier = Barrier(N_THREADS)
def allocator():
barrier.wait()
for _ in range(N_ITERS):
CyclicReference()
def reader():
barrier.wait()
for _ in range(N_ITERS):
gc.get_count()
threads = [Thread(target=allocator) for _ in range(N_ALLOCATORS)]
threads.extend(Thread(target=reader) for _ in range(N_READERS))
for thread in threads:
thread.start()
for thread in threads:
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.
|
static PyObject * |
|
gc_get_count_impl(PyObject *module) |
|
/*[clinic end generated code: output=354012e67b16398f input=a392794a08251751]*/ |
|
{ |
|
GCState *gcstate = get_gc_state(); |
|
|
|
#ifdef Py_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 |
|
|
|
#ifndef Py_GIL_DISABLED |
|
return Py_BuildValue("(iii)", |
|
gcstate->generations[0].count, |
|
gcstate->generations[1].count, |
|
gcstate->generations[2].count); |
|
#else |
|
return Py_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, struct collection_state *state, int generation) |
|
{ |
|
_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 (int i = 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
Bug report
Bug description:
I found this during solve #148613.
The
gc_generation.counthas one read not protected by atomic operation.Here is tsan report.
Root Cause
This problem would happen when cyclic references allocation frequently in free threading build.
The
alloc_countwould be reset whengc->alloc_count >= LOCAL_ALLOC_COUNT_THRESHOLDand thegcstate->young.countwould increase.cpython/Python/gc_free_threading.c
Lines 2025 to 2029 in c714b56
And we read the count in
gc_get_count_impl, it was partially protected by atomic.cpython/Modules/gcmodule.c
Lines 211 to 237 in c714b56
Solution
So we should add atomic load for the read action.
For
countingc_collect_internal, they are guarded by STW so I think they should be safe.cpython/Python/gc_free_threading.c
Lines 2065 to 2077 in c714b56
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
gc_generation.countrace #150413