Skip to content

Commit 28495f2

Browse files
committed
gh-155628: Use relaxed adds for the GC allocation counter
The free-threaded build buffers per-thread allocation counts and flushes them to gcstate->young.count in three places: when the local threshold is reached, when a thread state is cleared, and in gc.get_count(). The counter is a collection heuristic: its readers use relaxed loads (gc_should_collect()) or a compare-exchange loop, it is reset during a stop-the-world pause, and it publishes no other memory, so the flushes need atomicity but no ordering.
1 parent 1bd9bcd commit 28495f2

3 files changed

Lines changed: 4 additions & 4 deletions

File tree

Modules/gcmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ gc_get_count_impl(PyObject *module)
222222
struct _gc_thread_state *gc = &tstate->gc;
223223

224224
// Flush the local allocation count to the global count
225-
_Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count);
225+
_Py_atomic_add_int_relaxed(&gcstate->young.count, (int)gc->alloc_count);
226226
gc->alloc_count = 0;
227227
#endif
228228

Python/gc_free_threading.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2025,7 +2025,7 @@ record_allocation(PyThreadState *tstate)
20252025
if (gc->alloc_count >= LOCAL_ALLOC_COUNT_THRESHOLD) {
20262026
// TODO: Use Py_ssize_t for the generation count.
20272027
GCState *gcstate = &tstate->interp->gc;
2028-
_Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count);
2028+
_Py_atomic_add_int_relaxed(&gcstate->young.count, (int)gc->alloc_count);
20292029
gc->alloc_count = 0;
20302030

20312031
if (gc_should_collect(gcstate) &&

Python/pystate.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,8 +1879,8 @@ PyThreadState_Clear(PyThreadState *tstate)
18791879
// Flush the thread's local GC allocation count to the global count
18801880
// before the thread state is cleared, otherwise the count is lost.
18811881
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
1882-
_Py_atomic_add_int(&tstate->interp->gc.young.count,
1883-
(int)tstate_impl->gc.alloc_count);
1882+
_Py_atomic_add_int_relaxed(&tstate->interp->gc.young.count,
1883+
(int)tstate_impl->gc.alloc_count);
18841884
tstate_impl->gc.alloc_count = 0;
18851885

18861886
// Merge our thread-local refcounts into the type's own refcount and

0 commit comments

Comments
 (0)