https://github.com/python/cpython/commit/0ed896fb54aeb54b53f6e0af6b7b73c37f444854 commit: 0ed896fb54aeb54b53f6e0af6b7b73c37f444854 branch: 3.15 author: Miss Islington (bot) <[email protected]> committer: hugovk <[email protected]> date: 2026-09-16T11:13:28+03:00 summary:
[3.15] gh-157377: Don't flush the thread-local allocation count in gc.get_count (GH-157381) (#157505) Co-authored-by: Nathan Goldbaum <[email protected]> files: A Misc/NEWS.d/next/Core_and_Builtins/2026-09-12-22-29-51.gh-issue-157377.O6WYlM.rst M Lib/test/test_gc.py M Modules/gcmodule.c diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 3fc084ea6e9c6e..7e86f6123b970e 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1662,6 +1662,25 @@ def test_indirect_calls_with_gc_disabled(self): finally: gc.enable() + def test_get_count_nonnegative(self): + xs = [[] for _ in range(1500)] + gc.collect() + del xs[:1000] + self.assertGreaterEqual(gc.get_count()[0], 0) + + @gc_threshold(1000, 0, 0) + def test_get_count_does_not_prevent_collection(self): + junk = [] + gc.collect() + detector = GC_Detector() + for _ in range(10000): + junk.append([]) + gc.get_count() + if detector.gc_happened: + break + else: + self.fail("gc didn't happen after 10000 iterations") + # Ensure that setting *threshold0* to zero disables collection. @gc_threshold(0) def test_threshold_zero(self): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-12-22-29-51.gh-issue-157377.O6WYlM.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-12-22-29-51.gh-issue-157377.O6WYlM.rst new file mode 100644 index 00000000000000..0e8c5584a316a5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-12-22-29-51.gh-issue-157377.O6WYlM.rst @@ -0,0 +1,4 @@ +Fix :func:`gc.get_count` on the free-threaded build resetting the thread-local +allocation counter that schedules automatic garbage collection. A thread that +called it while allocating could prevent cyclic garbage from ever being +collected. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 0093995441e390..566ecae4ebe1f1 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -221,9 +221,12 @@ gc_get_count_impl(PyObject *module) _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET(); struct _gc_thread_state *gc = &tstate->gc; - // Flush the local allocation count to the global count - _Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count); - gc->alloc_count = 0; + // Don't flush: record_allocation() checks the threshold only when it fills. + int young = _Py_atomic_load_int_relaxed(&gcstate->young.count); + young += (int)gc->alloc_count; + if (young < 0) { + young = 0; + } #endif #ifndef Py_GIL_DISABLED @@ -233,7 +236,7 @@ gc_get_count_impl(PyObject *module) gcstate->generations[2].count); #else return Py_BuildValue("(iii)", - _Py_atomic_load_int_relaxed(&gcstate->young.count), + young, 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]
