Skip to content

gh-153014: Fix data race on the GC debug flag in free-threading builds#153015

Open
tonghuaroot wants to merge 2 commits into
python:mainfrom
tonghuaroot:gc-debug-ft-race
Open

gh-153014: Fix data race on the GC debug flag in free-threading builds#153015
tonghuaroot wants to merge 2 commits into
python:mainfrom
tonghuaroot:gc-debug-ft-race

Conversation

@tonghuaroot

@tonghuaroot tonghuaroot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

In a free-threading build, gc.set_debug() stored gcstate->debug with a
plain write while gc.get_debug() and the collector read it without
synchronisation. gc.set_debug() takes no lock and, unlike
gc_set_threshold_impl(), does not run under _PyEval_StopTheWorld(), so
ThreadSanitizer reports a write/write race on gcstate->debug.

This accesses the flag with FT_ATOMIC_STORE_INT_RELAXED /
FT_ATOMIC_LOAD_INT_RELAXED in gc_set_debug_impl() / gc_get_debug_impl()
and in the collector reads in Python/gc_free_threading.c. Four of those
collector reads run outside stop-the-world (in delete_garbage(),
handle_legacy_finalizers(), and two stats reads in gc_collect_main()) and
race the write directly; the read in gc_collect_internal() runs under
stop-the-world and is made atomic only for consistency. Relaxed ordering is
correct for an independent int flag, matching how gcstate->enabled and
sys dlopenflags (gh-151644) are already handled.

Python/gc.c (the default GIL build) is left unchanged: it has no concurrent
access, and FT_ATOMIC_* compiles to a plain load/store there anyway.

Verified with a --with-thread-sanitizer --disable-gil build stressing
concurrent gc.set_debug() / gc.get_debug() plus a thread churning cyclic
garbage:

Before: WARNING: ThreadSanitizer: data race ... Write of size 4 ... gc_set_debug (2 reports)
After:  0 data races

A regression test is added under Lib/test/test_free_threading/.

… builds

In free-threading builds gc.set_debug() stored gcstate->debug with a plain
write while gc.get_debug() and the collector read it without synchronisation,
which ThreadSanitizer reports as a data race.  Access the flag with
FT_ATOMIC_STORE/LOAD_INT_RELAXED, consistent with how gc.enable()/disable()
already handle gcstate->enabled.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant