https://github.com/python/cpython/commit/12af26d17e4343c1f4ea3a1019bcc34ee76671c3
commit: 12af26d17e4343c1f4ea3a1019bcc34ee76671c3
branch: main
author: Edward Xu <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-06-06T17:03:04Z
summary:

gh-150411: fix `gc_generation.count` race in free-threading (#150413)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst
M Lib/test/test_free_threading/test_gc.py
M Modules/gcmodule.c

diff --git a/Lib/test/test_free_threading/test_gc.py 
b/Lib/test/test_free_threading/test_gc.py
index cc1888dae48bc0..39901023450940 100644
--- a/Lib/test/test_free_threading/test_gc.py
+++ b/Lib/test/test_free_threading/test_gc.py
@@ -124,6 +124,35 @@ def setter():
         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
index 00000000000000..5b19a4fff5ddc7
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst
@@ -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.
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 8762e592b25810..0093995441e390 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -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

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to