Repository navigation
gh-112175: Add eval_breaker to PyThreadState
#115194
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
bc613e4
gh-112175: Add `eval_breaker` to `PyThreadState`
swtaarrs 76fed4e
📜🤖 Added by blurb_it.
blurb-it[bot] 321c153
Remove unnecessary test
swtaarrs f78c505
Review comments from Sam
swtaarrs 58f679c
Add !IsSignalled check to free-threaded _Py_ScheduleGC to match norma…
swtaarrs cf2a8a4
Move breaker bits/functions back to pycore_ceval.h, restore old namin…
swtaarrs 470727e
Merge remote-tracking branch 'upstream/main' into cpython-eval-breaker.
swtaarrs f129fd6
Back to signed 1 in _PY_EVAL_EVENTS_MASK
swtaarrs 4260410
Don't copy _PY_GC_SCHEDULED_BIT to/from interp_eval_breaker
swtaarrs 0db460e
Merge remote-tracking branch 'upstream/main' into cpython-eval-breaker
swtaarrs 0e1055f
Remove unnecessary NULL check, add uint32_t cast
swtaarrs 2139ed4
Merge remote-tracking branch 'upstream/main' into cpython-eval-breaker
swtaarrs 90450ce
Merge remote-tracking branch 'upstream/main' into cpython-eval-breaker
swtaarrs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next
Next commit
This change adds an `eval_breaker` field to `PyThreadState`, renaming the existing `eval_breaker` to
`interp_eval_breaker` (its uses are explained further down). The primary motivation is for
performance in free-threaded builds: with thread-local eval breakers, we can stop a specific thread
(e.g., for an async exception) without interrupting other threads.
There are still two situations where we want the first available thread to handle a request:
- Running a garbage collection: In normal builds, we set `_PY_GC_SCHEDULED_BIT` on the current
thread. In case a thread suspends before handling the collection, the bit is copied to and from
`interp_eval_breaker` on thread suspend and resume, respectively. In a free-threaded build, we
simply iterate over all threads and set the bit. The first thread to check its eval breaker runs
the collection, unsetting the bit on all threads.
- Free-threaded builds could have multiple threads attempt a GC from one trigger if we get very
unlucky with thread scheduling. I didn't put any protections against this in place because a)
the consequences of it happening are just that one or more threads will check the GC thresholds
right after a collection finishes, which won't affect correctness and b) it's incredibly,
vanishingly unlikely.
- Pending calls not limited to the main thread (possible since 757b402ea1c2). This is
a little tricker, since the callback can be added from any thread, with or without the GIL
held. If the targeted interpreter's GIL is locked, we signal the holding thread. When a thread is
resumed, its `_PY_CALLS_TO_DO` bit is derived from the source of truth for pending calls (one of
two `_pending_calls` structs). This handles situations where no thread held the GIL when the call
was first added, or if the active thread did not handle the call before releasing the GIL. In a
free-threaded build, all threads all signaled, similar to scheduling a GC.
The source of truth for the global instrumentation version is still in `interp_eval_breaker`, in
both normal and free-threaded builds. Threads usually read the version from their local
`eval_breaker`, where it continues to be colocated with the eval breaker bits, and the method for
keeping it up to date depends on build type. All builds first update the version in
`interp_eval_breaker`, and then:
- Normal builds update the version in the current thread's `eval_breaker`. When a thread takes the
GIL, it copies the current version from `interp_eval_breaker` as part of the same operation that
copies `_PY_GC_SCHEDULED_BIT`.
- Free-threaded builds again iterate over all threads in the current interpreter, updating the
version on each one.
Instrumentation (and the specializing interpreter more generally) will need more work to be
compatible with free-threaded builds, so these changes are just intended to maintain the status quo
in normal builds for now.
Other notable changes are:
- The `_PY_*_BIT` macros now expand to the actual bit being set, rather than the bit's index. I
think this is simpler overall. I also moved their definitions from `pycore_ceval.h` to
`pycore_pystate.h`, since their main usage is on `PyThreadState`s now.
- Most manipulations of `eval_breaker` are done with a new pair of functions:
`_PyThreadState_Signal()` and `_PyThreadState_Unsignal()`. Having two separate functions to
set/unset a bit, rather than one function that takes the bit value to use, lets us use a single
atomic `or`/`and`, rather than a loop around an atomic compare/exchange like the old
`_Py_set_eval_breaker_bit` function.
Existing tests provide pretty good coverage for most of this functionality. The one new test I added
is to make sure a GC still happens if a thread schedules it then drops the GIL before the GC runs. I
don't love how complicated this test ended up so I'm open to other ideas for how to test this (or
other things to test in general).- Loading branch information
commit bc613e4ecb3c8f6439e666b1ddd13ac533dcef95
There are no files selected for viewing
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
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
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
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
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
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
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
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
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.