summaryrefslogtreecommitdiffstats
path: root/Python/pystate.c
diff options
context:
space:
mode:
authorBrett Simmers <swtaarrs@users.noreply.github.com>2024-02-20 14:57:48 (GMT)
committerGitHub <noreply@github.com>2024-02-20 14:57:48 (GMT)
commit0749244d13412d7cb5b53d834f586f2198f5b9a6 (patch)
tree06387c5b41cd14cdf230f71eab6fd92873c3d5a9 /Python/pystate.c
parente71468ba4f5fb2da0cefe9e923b01811cb53fb5f (diff)
downloadcpython-0749244d13412d7cb5b53d834f586f2198f5b9a6.zip
cpython-0749244d13412d7cb5b53d834f586f2198f5b9a6.tar.gz
cpython-0749244d13412d7cb5b53d834f586f2198f5b9a6.tar.bz2
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.
Diffstat (limited to 'Python/pystate.c')
-rw-r--r--Python/pystate.c16
1 files changed, 7 insertions, 9 deletions
diff --git a/Python/pystate.c b/Python/pystate.c
index 3484bea..4cd975a 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -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
//---------------------------------