]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-145036: Fix data race for list capacity in free-threading (#145365)
authorbkap123 <97006829+bkap123@users.noreply.github.com>
Tue, 10 Mar 2026 16:30:11 +0000 (12:30 -0400)
committerGitHub <noreply@github.com>
Tue, 10 Mar 2026 16:30:11 +0000 (22:00 +0530)
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 27ddc9c2d26dfbc3e992ad57466e11a2baf1e9e6..0ede4df103f72819acce1a5b5da0d1927034c732 100644 (file)
@@ -149,6 +149,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 7fc21907fefd314267c3794097ec723662a906fd..62d04ab150a77a0e4e2fcd3f0a90a0b56e4565f7 100644 (file)
@@ -3582,8 +3582,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);
 }