Skip to content

gh-154524: Fix race array.array export counter in free threading build - #157759

Open
LindaSummer wants to merge 3 commits into
python:mainfrom
LindaSummer:fix/array-export-race
Open

LindaSummer wants to merge 3 commits into
python:mainfrom
LindaSummer:fix/array-export-race

Conversation

@LindaSummer

@LindaSummer LindaSummer commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Proposed Changes

Guard arrayobject.ob_exports with atomic in free-threading build to fix racing during memoryview construction and release.

Root cause

During investigation array.array in #154524 , I find that the arrayobject.ob_exports is not protected in array_buffer_getbuf and array_buffer_relbuf.

Here is my test case in TSAN free-threading build on main.

from array import array
import threading

arr = array('i', [1, 2, 3, 4, 5])

ITER_TIMES = 1000
THREAD_NUMS = 4
def incre_export(arr: array):
    for _ in range(ITER_TIMES):
        view = memoryview(arr)
        view.release()

threads = [threading.Thread(target=incre_export, args=(arr,)) for _ in range(THREAD_NUMS)]
for t in threads:
    t.start()
for t in threads:
    t.join()

Here is the TSAN report of this case.

==================
WARNING: ThreadSanitizer: data race (pid=1161201)
  Write of size 8 at 0x7f799e715b88 by thread T1:
    #0 array_buffer_relbuf /home/someuser/projects/cpython/cpython-cdata-154525/./Modules/arraymodule.c:2921:21 (array.cpython-316t-x86_64-linux-gnu.so+0xc7d6)
    #1 PyBuffer_Release /home/someuser/projects/cpython/cpython-cdata-154525/Objects/abstract.c:825:9 (python+0x1eaf16)
    #2 mbuf_release /home/someuser/projects/cpython/cpython-cdata-154525/Objects/memoryobject.c:116:5 (python+0x2df7ef)
    #3 _memory_release /home/someuser/projects/cpython/cpython-cdata-154525/Objects/memoryobject.c:1118:9 (python+0x2df7ef)
    #4 memoryview_release_impl /home/someuser/projects/cpython/cpython-cdata-154525/Objects/memoryobject.c:1134:9 (python+0x2df7ef)
    #5 memoryview_release /home/someuser/projects/cpython/cpython-cdata-154525/Objects/clinic/memoryobject.c.h:147:12 (python+0x2df7ef)
    #6 _PyEval_EvalFrameDefault /home/someuser/projects/cpython/cpython-cdata-154525/Python/generated_cases.c.h:4330:35 (python+0x46b71d)
    #7 _PyEval_EvalFrame /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_ceval.h:122:16 (python+0x45dbc7)
    #8 _PyEval_Vector /home/someuser/projects/cpython/cpython-cdata-154525/Python/ceval.c:2174:12 (python+0x45dbc7)
    #9 _PyFunction_Vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c (python+0x227e77)
    #10 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_call.h:144:11 (python+0x229bb4)
    #11 _PyObject_VectorcallPrepend /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:855:20 (python+0x229bb4)
    #12 method_vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/classobject.c:55:12 (python+0x22d606)
    #13 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_call.h:144:11 (python+0x4bb839)
    #14 context_run /home/someuser/projects/cpython/cpython-cdata-154525/Python/context.c:802:29 (python+0x4bb839)
    #15 method_vectorcall_FASTCALL_KEYWORDS /home/someuser/projects/cpython/cpython-cdata-154525/Objects/descrobject.c:421:24 (python+0x242b90)
    #16 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_call.h:144:11 (python+0x2277ac)
    #17 PyObject_Vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:327:12 (python+0x2277ac)
    #18 _Py_VectorCallInstrumentation_StackRefSteal /home/someuser/projects/cpython/cpython-cdata-154525/Python/ceval.c:768:11 (python+0x45e934)
    #19 _PyEval_EvalFrameDefault /home/someuser/projects/cpython/cpython-cdata-154525/Python/generated_cases.c.h:1906:35 (python+0x46511a)
    #20 _PyEval_EvalFrame /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_ceval.h:122:16 (python+0x45dbc7)
    #21 _PyEval_Vector /home/someuser/projects/cpython/cpython-cdata-154525/Python/ceval.c:2174:12 (python+0x45dbc7)
    #22 _PyFunction_Vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c (python+0x227e77)
    #23 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_call.h:144:11 (python+0x229bb4)
    #24 _PyObject_VectorcallPrepend /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:855:20 (python+0x229bb4)
    #25 method_vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/classobject.c:55:12 (python+0x22d606)
    #26 _PyVectorcall_Call /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:273:16 (python+0x227ac6)
    #27 _PyObject_Call /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:348:16 (python+0x227ac6)
    #28 PyObject_Call /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:373:12 (python+0x227b3b)
    #29 thread_run /home/someuser/projects/cpython/cpython-cdata-154525/./Modules/_threadmodule.c:388:21 (python+0x65e9bc)
    #30 pythread_wrapper /home/someuser/projects/cpython/cpython-cdata-154525/Python/thread_pthread.h:236:5 (python+0x57caef)

  Previous write of size 8 at 0x7f799e715b88 by thread T2:
    #0 array_buffer_getbuf /home/someuser/projects/cpython/cpython-cdata-154525/./Modules/arraymodule.c:2913:21 (array.cpython-316t-x86_64-linux-gnu.so+0xc794)
    #1 PyObject_GetBuffer /home/someuser/projects/cpython/cpython-cdata-154525/Objects/abstract.c:455:15 (python+0x1eb5b5)
    #2 _PyManagedBuffer_FromObject /home/someuser/projects/cpython/cpython-cdata-154525/Objects/memoryobject.c:97:9 (python+0x2d7c56)
    #3 PyMemoryView_FromObjectAndFlags /home/someuser/projects/cpython/cpython-cdata-154525/Objects/memoryobject.c:813:42 (python+0x2d7c56)
    #4 PyMemoryView_FromObject /home/someuser/projects/cpython/cpython-cdata-154525/Objects/memoryobject.c:856:12 (python+0x2db190)
    #5 memoryview_impl /home/someuser/projects/cpython/cpython-cdata-154525/Objects/memoryobject.c:1017:12 (python+0x2db190)
    #6 memoryview /home/someuser/projects/cpython/cpython-cdata-154525/Objects/clinic/memoryobject.c.h:63:20 (python+0x2db190)
    #7 type_call /home/someuser/projects/cpython/cpython-cdata-154525/Objects/typeobject.c:2442:11 (python+0x3513bf)
    #8 _PyObject_MakeTpCall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:242:18 (python+0x226963)
    #9 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_call.h:142:16 (python+0x227874)
    #10 PyObject_Vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:327:12 (python+0x227874)
    #11 _Py_VectorCallInstrumentation_StackRefSteal /home/someuser/projects/cpython/cpython-cdata-154525/Python/ceval.c:768:11 (python+0x45e934)
    #12 _PyEval_EvalFrameDefault /home/someuser/projects/cpython/cpython-cdata-154525/Python/generated_cases.c.h:1906:35 (python+0x46511a)
    #13 _PyEval_EvalFrame /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_ceval.h:122:16 (python+0x45dbc7)
    #14 _PyEval_Vector /home/someuser/projects/cpython/cpython-cdata-154525/Python/ceval.c:2174:12 (python+0x45dbc7)
    #15 _PyFunction_Vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c (python+0x227e77)
    #16 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_call.h:144:11 (python+0x229bb4)
    #17 _PyObject_VectorcallPrepend /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:855:20 (python+0x229bb4)
    #18 method_vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/classobject.c:55:12 (python+0x22d606)
    #19 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_call.h:144:11 (python+0x4bb839)
    #20 context_run /home/someuser/projects/cpython/cpython-cdata-154525/Python/context.c:802:29 (python+0x4bb839)
    #21 method_vectorcall_FASTCALL_KEYWORDS /home/someuser/projects/cpython/cpython-cdata-154525/Objects/descrobject.c:421:24 (python+0x242b90)
    #22 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_call.h:144:11 (python+0x2277ac)
    #23 PyObject_Vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:327:12 (python+0x2277ac)
    #24 _Py_VectorCallInstrumentation_StackRefSteal /home/someuser/projects/cpython/cpython-cdata-154525/Python/ceval.c:768:11 (python+0x45e934)
    #25 _PyEval_EvalFrameDefault /home/someuser/projects/cpython/cpython-cdata-154525/Python/generated_cases.c.h:1906:35 (python+0x46511a)
    #26 _PyEval_EvalFrame /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_ceval.h:122:16 (python+0x45dbc7)
    #27 _PyEval_Vector /home/someuser/projects/cpython/cpython-cdata-154525/Python/ceval.c:2174:12 (python+0x45dbc7)
    #28 _PyFunction_Vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c (python+0x227e77)
    #29 _PyObject_VectorcallTstate /home/someuser/projects/cpython/cpython-cdata-154525/./Include/internal/pycore_call.h:144:11 (python+0x229bb4)
    #30 _PyObject_VectorcallPrepend /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:855:20 (python+0x229bb4)
    #31 method_vectorcall /home/someuser/projects/cpython/cpython-cdata-154525/Objects/classobject.c:55:12 (python+0x22d606)
    #32 _PyVectorcall_Call /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:273:16 (python+0x227ac6)
    #33 _PyObject_Call /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:348:16 (python+0x227ac6)
    #34 PyObject_Call /home/someuser/projects/cpython/cpython-cdata-154525/Objects/call.c:373:12 (python+0x227b3b)
    #35 thread_run /home/someuser/projects/cpython/cpython-cdata-154525/./Modules/_threadmodule.c:388:21 (python+0x65e9bc)
    #36 pythread_wrapper /home/someuser/projects/cpython/cpython-cdata-154525/Python/thread_pthread.h:236:5 (python+0x57caef)

So I added guard for all read and write patch for arrayobject.ob_exports to solve the problem.

From my understanding, this is a counter variable so an atomic guard should solve the problem.

@LindaSummer

Copy link
Copy Markdown
Contributor Author

Hi @devdanzin ,

Please help take a look when you have a chance.

By the way, for the CData resize problem, should we add an exporter counter and check it before resizing just like array.array's behavior?

Thanks very much and wish you a great day!

@LindaSummer LindaSummer changed the title gh-154524: Fix race array.arry export counter in free threading build gh-154524: Fix race array.array export counter in free threading build Sep 18, 2026
@ZeroIntensity

Copy link
Copy Markdown
Member

For the record, there's an existing PR that makes array thread-safe: #130771

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants