FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

gh-153014: Fix data race on the GC debug flag in free-threading builds by tonghuaroot · Pull Request #153015 · python/cpython · GitHub

Repository navigation

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

Merged
nascheme merged 10 commits into
python:mainfrom
tonghuaroot:gc-debug-ft-race
Oct 21, 2026
Merged

nascheme merged 10 commits into
python:mainfrom
tonghuaroot:gc-debug-ft-race

Conversation

tonghuaroot commented Jul 4, 2026 •
edited by bedevere-app Bot
Loading

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.
Comment thread Python/gc_free_threading.c Outdated

Copy link
Copy Markdown
Contributor

cc @nascheme as a GC-FT expert

nascheme commented Jul 8, 2026

Copy link
Copy Markdown
Member

This looks worth fixing. Some suggested improvements:

  • Use threading_helper.run_concurrently()
  • Remove the thread that runs GC collections. I don't think that's useful since it stops-the-world.
  • I'd use one writer and one reader. It would be enough to trigger the TSAN warnings.
  • If gc->debug is read more than once inside a function, read it into a local variable.

Copy link
Copy Markdown

This PR is stale because it has been open for 90 days with no activity.

github-actions Bot added the stale Stale PR or inactive for long period of time. label Oct 11, 2026
- Read gcstate->debug once into a local in delete_garbage,
  handle_legacy_finalizers, gc_collect_main and _PyGC_DumpShutdownStats.
- Simplify the regression test to one gc.set_debug() writer racing one
  gc.get_debug() reader via run_concurrently(), dropping the collector
  thread.

Copy link
Copy Markdown
Contributor Author

Done: gcstate->debug is now read once into a local in the four collector functions, and the test is one gc.set_debug() writer racing one gc.get_debug() reader via run_concurrently() (no collector thread). Verified under --disable-gil --with-thread-sanitizer: TSAN flags the race without the fix and is clean with it. Also merged main to clear the conflict.

I have made the requested changes; please review again

bedevere-app Bot commented Oct 12, 2026

Copy link
Copy Markdown

Thanks for making the requested changes!

: please review the changes made to this pull request.

Copy link
Copy Markdown
Member

Hi tonghuaroot, thanks for making the changes requested. This looks good to merge now. After reconsidering the unit test, I think it's better we just remove it (I pushed a change). In terms of CI cost vs what this test buys us, I think it's not worth it. This race is not very serious. Fixing the code so we don't get a TSAN warning is good. Sorry for asking you to fix the test on the first review, normally we do want unit tests when bugs are fixed.

nascheme enabled auto-merge (squash) October 12, 2026 18:22

Copy link
Copy Markdown
Contributor Author

looks like this PR needs your approval @nascheme

sergey-miryanov removed the stale Stale PR or inactive for long period of time. label Oct 15, 2026
nascheme disabled auto-merge October 20, 2026 05:52

Copy link
Copy Markdown
Member

@nascheme you'll have to approve the PR for the bot to change the label, or change it manually.

nascheme left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

LGTM!

nascheme added type-bug An unexpected behavior, bug, or error topic-free-threading labels Oct 21, 2026
nascheme merged commit ed144b7 into python:main Oct 21, 2026
63 of 64 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic-free-threading type-bug An unexpected behavior, bug, or error

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants


Back | FazBrowse Home | New Git URL