Skip to content

Commit da902cf

Browse files
committed
gh-157377: Don't flush the thread-local allocation count in gc.get_count()
1 parent 121e27c commit da902cf

3 files changed

Lines changed: 21 additions & 4 deletions

File tree

Lib/test/test_gc.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,19 @@ def test_indirect_calls_with_gc_disabled(self):
16581658
finally:
16591659
gc.enable()
16601660

1661+
@gc_threshold(1000, 0, 0)
1662+
def test_get_count_does_not_prevent_collection(self):
1663+
junk = []
1664+
gc.collect()
1665+
detector = GC_Detector()
1666+
for _ in range(10000):
1667+
junk.append([])
1668+
gc.get_count()
1669+
if detector.gc_happened:
1670+
break
1671+
else:
1672+
self.fail("gc didn't happen after 10000 iterations")
1673+
16611674
# Ensure that setting *threshold0* to zero disables collection.
16621675
@gc_threshold(0)
16631676
def test_threshold_zero(self):
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix :func:`gc.get_count` on the free-threaded build resetting the thread-local
2+
allocation counter that schedules automatic garbage collection. A thread that
3+
called it while allocating could prevent cyclic garbage from ever being
4+
collected.

Modules/gcmodule.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,9 @@ gc_get_count_impl(PyObject *module)
221221
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
222222
struct _gc_thread_state *gc = &tstate->gc;
223223

224-
// Flush the local allocation count to the global count
225-
_Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count);
226-
gc->alloc_count = 0;
224+
// Don't flush: record_allocation() checks the threshold only when it fills.
225+
int young = _Py_atomic_load_int_relaxed(&gcstate->young.count);
226+
young += (int)gc->alloc_count;
227227
#endif
228228

229229
#ifndef Py_GIL_DISABLED
@@ -233,7 +233,7 @@ gc_get_count_impl(PyObject *module)
233233
gcstate->generations[2].count);
234234
#else
235235
return Py_BuildValue("(iii)",
236-
_Py_atomic_load_int_relaxed(&gcstate->young.count),
236+
young,
237237
gcstate->old[0].count,
238238
gcstate->old[1].count);
239239
#endif

0 commit comments

Comments
 (0)