Skip to content

Data race between record_allocation and gc_get_count_impl #150411

@LindaSummer

Description

@LindaSummer

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.

cpython/Modules/gcmodule.c

Lines 211 to 237 in c714b56

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirtype-bugAn unexpected behavior, bug, or error
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions