]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-150411: fix `gc_generation.count` race in free-threading (#150413)
authorEdward Xu <xuxiangad@gmail.com>
Sat, 6 Jun 2026 17:03:04 +0000 (01:03 +0800)
committerGitHub <noreply@github.com>
Sat, 6 Jun 2026 17:03:04 +0000 (17:03 +0000)
Lib/test/test_free_threading/test_gc.py
Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst [new file with mode: 0644]
Modules/gcmodule.c

index cc1888dae48bc0338d1e61b22932e2b67d24865a..399010234509408eb8bbeae833be684010d0207b 100644 (file)
@@ -124,6 +124,35 @@ class TestGC(TestCase):
         finally:
             gc.set_threshold(*current_threshold)
 
+    def test_get_count(self):
+        class CyclicReference:
+            def __init__(self):
+                self.ref = self
+
+        NUM_ALLOCATORS = 7
+        NUM_READERS = 1
+        NUM_THREADS = NUM_ALLOCATORS + NUM_READERS
+        NUM_ITERS = 1000
+
+        barrier = threading.Barrier(NUM_THREADS)
+
+        def allocator():
+            barrier.wait()
+            for _ in range(NUM_ITERS):
+                CyclicReference()
+
+
+        def reader():
+            barrier.wait()
+            for _ in range(NUM_ITERS):
+                gc.get_count()
+
+        threads = [Thread(target=allocator) for _ in range(NUM_ALLOCATORS)]
+        threads.extend(Thread(target=reader) for _ in range(NUM_READERS))
+
+        with threading_helper.start_threads(threads):
+            pass
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst
new file mode 100644 (file)
index 0000000..5b19a4f
--- /dev/null
@@ -0,0 +1,2 @@
+Fix a data race in the free-threaded build when :func:`gc.get_count` reads
+the young generation allocation count while another thread updates it.
index 8762e592b2581043af03c974a4aea32c6d2c49b6..0093995441e390d46f0381f03eee51f10d9d7fd6 100644 (file)
@@ -233,7 +233,7 @@ gc_get_count_impl(PyObject *module)
                          gcstate->generations[2].count);
 #else
     return Py_BuildValue("(iii)",
-                         gcstate->young.count,
+                         _Py_atomic_load_int_relaxed(&gcstate->young.count),
                          gcstate->old[0].count,
                          gcstate->old[1].count);
 #endif