mirror of
https://github.com/python/cpython.git
synced 2025-11-24 20:30:18 +00:00
gh-112175: Add eval_breaker to PyThreadState (#115194)
This change adds an `eval_breaker` field to `PyThreadState`. 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. The source of truth for the global instrumentation version is stored in the `instrumentation_version` field in PyInterpreterState. Threads usually read the version from their local `eval_breaker`, where it continues to be colocated with the eval breaker bits.
This commit is contained in:
parent
e71468ba4f
commit
0749244d13
19 changed files with 265 additions and 172 deletions
|
|
@ -794,9 +794,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
|
|||
|
||||
Py_CLEAR(interp->audit_hooks);
|
||||
|
||||
// At this time, all the threads should be cleared so we don't need
|
||||
// atomic operations for eval_breaker
|
||||
interp->ceval.eval_breaker = 0;
|
||||
interp->ceval.instrumentation_version = 0;
|
||||
|
||||
for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) {
|
||||
interp->monitors.tools[i] = 0;
|
||||
|
|
@ -1318,6 +1316,8 @@ init_threadstate(_PyThreadStateImpl *_tstate,
|
|||
|
||||
assert(interp != NULL);
|
||||
tstate->interp = interp;
|
||||
tstate->eval_breaker =
|
||||
_Py_atomic_load_uintptr_relaxed(&interp->ceval.instrumentation_version);
|
||||
|
||||
// next/prev are set in add_threadstate().
|
||||
assert(tstate->next == NULL);
|
||||
|
|
@ -2021,8 +2021,7 @@ park_detached_threads(struct _stoptheworld_state *stw)
|
|||
}
|
||||
}
|
||||
else if (state == _Py_THREAD_ATTACHED && t != stw->requester) {
|
||||
// TODO: set this per-thread, rather than per-interpreter.
|
||||
_Py_set_eval_breaker_bit(t->interp, _PY_EVAL_PLEASE_STOP_BIT, 1);
|
||||
_Py_set_eval_breaker_bit(t, _PY_EVAL_PLEASE_STOP_BIT);
|
||||
}
|
||||
}
|
||||
stw->thread_countdown -= num_parked;
|
||||
|
|
@ -2186,19 +2185,18 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)
|
|||
* deadlock, we need to release head_mutex before
|
||||
* the decref.
|
||||
*/
|
||||
PyObject *old_exc = tstate->async_exc;
|
||||
tstate->async_exc = Py_XNewRef(exc);
|
||||
Py_XINCREF(exc);
|
||||
PyObject *old_exc = _Py_atomic_exchange_ptr(&tstate->async_exc, exc);
|
||||
HEAD_UNLOCK(runtime);
|
||||
|
||||
Py_XDECREF(old_exc);
|
||||
_PyEval_SignalAsyncExc(tstate->interp);
|
||||
_Py_set_eval_breaker_bit(tstate, _PY_ASYNC_EXCEPTION_BIT);
|
||||
return 1;
|
||||
}
|
||||
HEAD_UNLOCK(runtime);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------
|
||||
// API for the current thread state
|
||||
//---------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue