diff --git a/Lib/test/test_free_threading/test_list.py b/Lib/test/test_free_threading/test_list.py index 27ddc9c2d26dfb..0ede4df103f728 100644 --- a/Lib/test/test_free_threading/test_list.py +++ b/Lib/test/test_free_threading/test_list.py @@ -149,6 +149,27 @@ def reader_list(b, l): with threading_helper.start_threads(threads): pass + # gh-145036: race condition with list.__sizeof__() + def test_list_sizeof_free_threaded_build(self): + L = [] + + def mutate_function(): + for _ in range(100): + L.append(1) + L.pop() + + def size_function(): + for _ in range(100): + L.__sizeof__() + + threads = [] + for _ in range(4): + threads.append(Thread(target=mutate_function)) + threads.append(Thread(target=size_function)) + + with threading_helper.start_threads(threads): + pass + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-28-18-42-36.gh-issue-145036.70Kbfz.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-28-18-42-36.gh-issue-145036.70Kbfz.rst new file mode 100644 index 00000000000000..2a565c1d02bc2e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-28-18-42-36.gh-issue-145036.70Kbfz.rst @@ -0,0 +1 @@ +In free-threaded build, fix race condition when calling :meth:`!__sizeof__` on a :class:`list` diff --git a/Objects/listobject.c b/Objects/listobject.c index 4a98c8e54ab03f..c472543dc6a74d 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -3558,8 +3558,14 @@ list___sizeof___impl(PyListObject *self) /*[clinic end generated code: output=3417541f95f9a53e input=b8030a5d5ce8a187]*/ { size_t res = _PyObject_SIZE(Py_TYPE(self)); - Py_ssize_t allocated = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->allocated); - res += (size_t)allocated * sizeof(void*); +#ifdef Py_GIL_DISABLED + PyObject **ob_item = _Py_atomic_load_ptr(&self->ob_item); + if (ob_item != NULL) { + res += list_capacity(ob_item) * sizeof(PyObject *); + } +#else + res += (size_t)self->allocated * sizeof(PyObject *); +#endif return PyLong_FromSize_t(res); }