Skip to content

Commit 2af2a38

Browse files
[3.14] gh-145036: Fix data race for list capacity in free-threading (GH-145365) (#145881)
(cherry picked from commit 9e08023) Co-authored-by: Kumar Aditya <kumaraditya@python.org>
1 parent 8c0a190 commit 2af2a38

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

Lib/test/test_free_threading/test_list.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,27 @@ def copy_back_and_forth(b, l):
9191
with threading_helper.start_threads(threads):
9292
pass
9393

94+
# gh-145036: race condition with list.__sizeof__()
95+
def test_list_sizeof_free_threaded_build(self):
96+
L = []
97+
98+
def mutate_function():
99+
for _ in range(100):
100+
L.append(1)
101+
L.pop()
102+
103+
def size_function():
104+
for _ in range(100):
105+
L.__sizeof__()
106+
107+
threads = []
108+
for _ in range(4):
109+
threads.append(Thread(target=mutate_function))
110+
threads.append(Thread(target=size_function))
111+
112+
with threading_helper.start_threads(threads):
113+
pass
114+
94115

95116
if __name__ == "__main__":
96117
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
In free-threaded build, fix race condition when calling :meth:`!__sizeof__` on a :class:`list`

Objects/listobject.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3538,8 +3538,14 @@ list___sizeof___impl(PyListObject *self)
35383538
/*[clinic end generated code: output=3417541f95f9a53e input=b8030a5d5ce8a187]*/
35393539
{
35403540
size_t res = _PyObject_SIZE(Py_TYPE(self));
3541-
Py_ssize_t allocated = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->allocated);
3542-
res += (size_t)allocated * sizeof(void*);
3541+
#ifdef Py_GIL_DISABLED
3542+
PyObject **ob_item = _Py_atomic_load_ptr(&self->ob_item);
3543+
if (ob_item != NULL) {
3544+
res += list_capacity(ob_item) * sizeof(PyObject *);
3545+
}
3546+
#else
3547+
res += (size_t)self->allocated * sizeof(PyObject *);
3548+
#endif
35433549
return PyLong_FromSize_t(res);
35443550
}
35453551

0 commit comments

Comments
 (0)