GeezerVison is showing github.com — gh-154348: fix data race on nogil build about `sys.monitoring.use_tool_id()` by weixlu · Pull Request #154459 · python/cpython
Conversation
weixlu
commented
Jul 22, 2026
•
edited by bedevere-app
Bot
Loading
Uh oh!
There was an error while loading. Please reload this page.
As reported in #154348 (found by devdanzin's fusil), interp->monitoring_tool_names[tool_id] for sys.monitoring is accessed without any synchronization, which is a data race on free-threaded build.
The fix is straightforward, just add a dedicated PyMutex and serialize all access on monitoring_tool_names
Testing
- The reproducer (8 threads × 6000 rounds) now runs clean under a TSAN build.
- This PR only adds a mutex, so basic functional behavior is unchanged.
Notes for reviewer:
- Of course the writer need a lock, but why reader (i.e.
monitoring.get_tool) needs one as well? Because it reads the pointer and then increfs it. A concurrent free_tool_id can run Py_CLEAR (drop the last reference and free the object) in between, turning the Py_NewRef into a use-after-free.
- The clear/free paths calls
_PyMonitoring_ClearToolId, which internally does a stop-the-world. Is it safe to hold a PyMutex across STW? This is safe, because a thread blocked on PyMutex_Lock detaches its thread state, so it is seen as stopped.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
I discussed this with @gaogaotiantian , and he'd like to see if there are approaches other than a lock to handle the data race. I've put together a few options:
-
The current approach — use a dedicated PyMutex.
-
Use Py_BEGIN_CRITICAL_SECTION to declare a critical section. The underlying mechanism is still a lock, and there's already similar usage in instrumentation.c, see:
#define LOCK_CODE(code) \
assert(!_PyInterpreterState_GET()->stoptheworld.world_stopped); \
Py_BEGIN_CRITICAL_SECTION(code)
- Use atomic operations (CAS, etc.), which would avoid the lock overhead and give better performance. That said, I think monitoring_tool is a fairly cold path, so the benefit of
going lock-free isn't significant, and atomic op is more error-prone. Of course, if there's a need for it, I'm happy to implement and post it for review.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
This PR is stale because it has been open for 90 days with no activity.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
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.
|