Repository navigation
PyEval_SetProfileAllThreads is racy under free-threading #132817
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Apr 22, 2025 Very similar to #132296. I'm not totally sure what the best way to deal with this is. Stop-the-world isn't really an option because
_PyEval_SetProfileis re-entrant.- addedinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixes
on Apr 22, 2025 Without knowing this code well, I wonder if you could keep the thread states (the list of which is read-mostly?) in a QSBR-style data structure so it's safe to iterate on them even in the presence of concurrent list updates.
There's a few ways to deal with the concurrent deletion of the thread state, but the much bigger problem is that thread states are, by design, not thread-safe, because they're supposed to be thread-local.
A crazy idea that could possibly work: I guess we could re-enable the GIL right before the loop to prevent other threads from messing things up, and then disable it again at the end.
The whole iteration over thread states isn't safe even in the GIL-enabled build. It's a mess. I think the "right" way to do this is something like:
- Move the
_PySys_Audit()earlier and call it only once instead of once per-thread - Stop the world before iteration over thread states (or at least hold the
HEAD_LOCK()for the entire loop) - Make sure that nothing else in
_PyEval_SetProfile()is possibly reentrant
I wonder if you could keep the thread states (the list of which is read-mostly?) in a QSBR-style data structure...
I've played around with this before and it started to get really complicated. I think we may want to pursue this eventually if we contention on
HEAD_LOCK()becomes a problem, but probably not immediately.Reacted by Peter Bierma- Move the
- added a commit that references this issue
on May 9, 2025 - added a commit that references this issue
on Jun 19, 2025 - marked Concurrent deallocation of threads while calling
PyEval_SetTrace#132296 as a duplicate of this issueon Aug 1, 2025 This should be fixed now with the fixes in #137518
Bug report
Bug description:
PyEval_SetProfileAllThreadsiterates over the list of thread-states without doing anything to stop the world:cpython/Python/ceval.c
Line 2484 in 87b1ea0
In jax-ml/jax#28142, we saw the following TSAN report from a 3.14 freethreaded build:
and reading the code this makes sense;
PyEval_SetProfileAllThreadsdoes nothing to prevent threads from starting or stopping during its execution.CPython versions tested on:
3.14
Operating systems tested on:
Linux