]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-145036: Fix data race for list capacity in free-threading (GH-145365) ...
authorbkap123 <97006829+bkap123@users.noreply.github.com>
Fri, 13 Mar 2026 02:06:12 +0000 (22:06 -0400)
committerGitHub <noreply@github.com>
Fri, 13 Mar 2026 02:06:12 +0000 (22:06 -0400)
(cherry picked from commit 9e0802330caca51fed7fc0c8c1dcce2daf03d8bd)

Co-authored-by: Kumar Aditya <kumaraditya@python.org>
Lib/test/test_free_threading/test_list.py
Misc/NEWS.d/next/Core_and_Builtins/2026-02-28-18-42-36.gh-issue-145036.70Kbfz.rst [new file with mode: 0644]
Objects/listobject.c

index 44c0ac74e02aa35f096736c9f4a073c5fce55c54..4e68dde494118ef41998b2c7874be6de8a945298 100644 (file)
@@ -91,6 +91,27 @@ class TestList(TestCase):
         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 (file)
index 0000000..2a565c1
--- /dev/null
@@ -0,0 +1 @@
+In free-threaded build, fix race condition when calling :meth:`!__sizeof__` on a :class:`list`
index 23d3472b6d41535f91c03411a1bb1d8de7717c15..98c90665be5721aa03847240488a9abc3040e5fb 100644 (file)
@@ -3538,8 +3538,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);
 }