mirror of
https://github.com/python/cpython.git
synced 2025-08-23 02:04:56 +00:00
GH-109369: Merge all eval-breaker flags and monitoring version into one word. (GH-109846)
This commit is contained in:
parent
7c149a76b2
commit
bf4bc36069
13 changed files with 188 additions and 234 deletions
|
@ -57,113 +57,62 @@
|
|||
#define _Py_atomic_load_relaxed_int32(ATOMIC_VAL) _Py_atomic_load_relaxed(ATOMIC_VAL)
|
||||
#endif
|
||||
|
||||
/* This can set eval_breaker to 0 even though gil_drop_request became
|
||||
1. We believe this is all right because the eval loop will release
|
||||
the GIL eventually anyway. */
|
||||
/* bpo-40010: eval_breaker should be recomputed if there
|
||||
is a pending signal: signal received by another thread which cannot
|
||||
handle signals.
|
||||
Similarly, we set CALLS_TO_DO and ASYNC_EXCEPTION to match the thread.
|
||||
*/
|
||||
static inline void
|
||||
COMPUTE_EVAL_BREAKER(PyInterpreterState *interp,
|
||||
struct _ceval_runtime_state *ceval,
|
||||
struct _ceval_state *ceval2)
|
||||
update_eval_breaker_from_thread(PyInterpreterState *interp, PyThreadState *tstate)
|
||||
{
|
||||
_Py_atomic_store_relaxed(&ceval2->eval_breaker,
|
||||
_Py_atomic_load_relaxed_int32(&ceval2->gil_drop_request)
|
||||
| (_Py_atomic_load_relaxed_int32(&ceval->signals_pending)
|
||||
&& _Py_ThreadCanHandleSignals(interp))
|
||||
| (_Py_atomic_load_relaxed_int32(&ceval2->pending.calls_to_do))
|
||||
| (_Py_IsMainThread() && _Py_IsMainInterpreter(interp)
|
||||
&&_Py_atomic_load_relaxed_int32(&ceval->pending_mainthread.calls_to_do))
|
||||
| ceval2->pending.async_exc
|
||||
| _Py_atomic_load_relaxed_int32(&ceval2->gc_scheduled));
|
||||
}
|
||||
if (tstate == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_Py_IsMainThread()) {
|
||||
int32_t calls_to_do = _Py_atomic_load_int32_relaxed(
|
||||
&_PyRuntime.ceval.pending_mainthread.calls_to_do);
|
||||
if (calls_to_do) {
|
||||
_Py_set_eval_breaker_bit(interp, _PY_CALLS_TO_DO_BIT, 1);
|
||||
}
|
||||
if (_Py_ThreadCanHandleSignals(interp)) {
|
||||
if (_Py_atomic_load(&_PyRuntime.signals.is_tripped)) {
|
||||
_Py_set_eval_breaker_bit(interp, _PY_SIGNALS_PENDING_BIT, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tstate->async_exc != NULL) {
|
||||
_Py_set_eval_breaker_bit(interp, _PY_ASYNC_EXCEPTION_BIT, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
SET_GIL_DROP_REQUEST(PyInterpreterState *interp)
|
||||
{
|
||||
struct _ceval_state *ceval2 = &interp->ceval;
|
||||
_Py_atomic_store_relaxed(&ceval2->gil_drop_request, 1);
|
||||
_Py_atomic_store_relaxed(&ceval2->eval_breaker, 1);
|
||||
_Py_set_eval_breaker_bit(interp, _PY_GIL_DROP_REQUEST_BIT, 1);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
RESET_GIL_DROP_REQUEST(PyInterpreterState *interp)
|
||||
{
|
||||
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
|
||||
struct _ceval_state *ceval2 = &interp->ceval;
|
||||
_Py_atomic_store_relaxed(&ceval2->gil_drop_request, 0);
|
||||
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
|
||||
_Py_set_eval_breaker_bit(interp, _PY_GIL_DROP_REQUEST_BIT, 0);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
SIGNAL_PENDING_CALLS(struct _pending_calls *pending, PyInterpreterState *interp)
|
||||
SIGNAL_PENDING_CALLS(PyInterpreterState *interp)
|
||||
{
|
||||
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
|
||||
struct _ceval_state *ceval2 = &interp->ceval;
|
||||
_Py_atomic_store_relaxed(&pending->calls_to_do, 1);
|
||||
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
|
||||
_Py_set_eval_breaker_bit(interp, _PY_CALLS_TO_DO_BIT, 1);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
UNSIGNAL_PENDING_CALLS(PyInterpreterState *interp)
|
||||
{
|
||||
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
|
||||
struct _ceval_state *ceval2 = &interp->ceval;
|
||||
if (_Py_IsMainThread() && _Py_IsMainInterpreter(interp)) {
|
||||
_Py_atomic_store_relaxed(&ceval->pending_mainthread.calls_to_do, 0);
|
||||
}
|
||||
_Py_atomic_store_relaxed(&ceval2->pending.calls_to_do, 0);
|
||||
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
|
||||
_Py_set_eval_breaker_bit(interp, _PY_CALLS_TO_DO_BIT, 0);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp, int force)
|
||||
{
|
||||
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
|
||||
struct _ceval_state *ceval2 = &interp->ceval;
|
||||
_Py_atomic_store_relaxed(&ceval->signals_pending, 1);
|
||||
if (force) {
|
||||
_Py_atomic_store_relaxed(&ceval2->eval_breaker, 1);
|
||||
}
|
||||
else {
|
||||
/* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
|
||||
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
UNSIGNAL_PENDING_SIGNALS(PyInterpreterState *interp)
|
||||
{
|
||||
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
|
||||
struct _ceval_state *ceval2 = &interp->ceval;
|
||||
_Py_atomic_store_relaxed(&ceval->signals_pending, 0);
|
||||
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
SIGNAL_ASYNC_EXC(PyInterpreterState *interp)
|
||||
{
|
||||
struct _ceval_state *ceval2 = &interp->ceval;
|
||||
ceval2->pending.async_exc = 1;
|
||||
_Py_atomic_store_relaxed(&ceval2->eval_breaker, 1);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
UNSIGNAL_ASYNC_EXC(PyInterpreterState *interp)
|
||||
{
|
||||
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
|
||||
struct _ceval_state *ceval2 = &interp->ceval;
|
||||
ceval2->pending.async_exc = 0;
|
||||
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Implementation of the Global Interpreter Lock (GIL).
|
||||
*/
|
||||
|
@ -271,8 +220,9 @@ static void recreate_gil(struct _gil_runtime_state *gil)
|
|||
#endif
|
||||
|
||||
static void
|
||||
drop_gil(struct _ceval_state *ceval, PyThreadState *tstate)
|
||||
drop_gil(PyInterpreterState *interp, PyThreadState *tstate)
|
||||
{
|
||||
struct _ceval_state *ceval = &interp->ceval;
|
||||
/* If tstate is NULL, the caller is indicating that we're releasing
|
||||
the GIL for the last time in this thread. This is particularly
|
||||
relevant when the current thread state is finalizing or its
|
||||
|
@ -310,7 +260,7 @@ drop_gil(struct _ceval_state *ceval, PyThreadState *tstate)
|
|||
the GIL, and that's the only time we might delete the
|
||||
interpreter, so checking tstate first prevents the crash.
|
||||
See https://github.com/python/cpython/issues/104341. */
|
||||
if (tstate != NULL && _Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
|
||||
if (tstate != NULL && _Py_eval_breaker_bit_is_set(interp, _PY_GIL_DROP_REQUEST_BIT)) {
|
||||
MUTEX_LOCK(gil->switch_mutex);
|
||||
/* Not switched yet => wait */
|
||||
if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate)
|
||||
|
@ -356,8 +306,7 @@ take_gil(PyThreadState *tstate)
|
|||
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
PyInterpreterState *interp = tstate->interp;
|
||||
struct _ceval_state *ceval = &interp->ceval;
|
||||
struct _gil_runtime_state *gil = ceval->gil;
|
||||
struct _gil_runtime_state *gil = interp->ceval.gil;
|
||||
|
||||
/* Check that _PyEval_InitThreads() was called to create the lock */
|
||||
assert(gil_created(gil));
|
||||
|
@ -431,27 +380,13 @@ _ready:
|
|||
in take_gil() while the main thread called
|
||||
wait_for_thread_shutdown() from Py_Finalize(). */
|
||||
MUTEX_UNLOCK(gil->mutex);
|
||||
drop_gil(ceval, tstate);
|
||||
drop_gil(interp, tstate);
|
||||
PyThread_exit_thread();
|
||||
}
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
|
||||
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
|
||||
RESET_GIL_DROP_REQUEST(interp);
|
||||
}
|
||||
else {
|
||||
/* bpo-40010: eval_breaker should be recomputed to be set to 1 if there
|
||||
is a pending signal: signal received by another thread which cannot
|
||||
handle signals.
|
||||
|
||||
Note: RESET_GIL_DROP_REQUEST() calls COMPUTE_EVAL_BREAKER(). */
|
||||
COMPUTE_EVAL_BREAKER(interp, &_PyRuntime.ceval, ceval);
|
||||
}
|
||||
|
||||
/* Don't access tstate if the thread must exit */
|
||||
if (tstate->async_exc != NULL) {
|
||||
_PyEval_SignalAsyncExc(tstate->interp);
|
||||
}
|
||||
RESET_GIL_DROP_REQUEST(interp);
|
||||
update_eval_breaker_from_thread(interp, tstate);
|
||||
|
||||
MUTEX_UNLOCK(gil->mutex);
|
||||
|
||||
|
@ -611,8 +546,7 @@ PyEval_ReleaseLock(void)
|
|||
/* This function must succeed when the current thread state is NULL.
|
||||
We therefore avoid PyThreadState_Get() which dumps a fatal error
|
||||
in debug mode. */
|
||||
struct _ceval_state *ceval = &tstate->interp->ceval;
|
||||
drop_gil(ceval, tstate);
|
||||
drop_gil(tstate->interp, tstate);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -628,8 +562,7 @@ _PyEval_ReleaseLock(PyInterpreterState *interp, PyThreadState *tstate)
|
|||
/* If tstate is NULL then we do not expect the current thread
|
||||
to acquire the GIL ever again. */
|
||||
assert(tstate == NULL || tstate->interp == interp);
|
||||
struct _ceval_state *ceval = &interp->ceval;
|
||||
drop_gil(ceval, tstate);
|
||||
drop_gil(interp, tstate);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -653,8 +586,7 @@ PyEval_ReleaseThread(PyThreadState *tstate)
|
|||
if (new_tstate != tstate) {
|
||||
Py_FatalError("wrong thread state");
|
||||
}
|
||||
struct _ceval_state *ceval = &tstate->interp->ceval;
|
||||
drop_gil(ceval, tstate);
|
||||
drop_gil(tstate->interp, tstate);
|
||||
}
|
||||
|
||||
#ifdef HAVE_FORK
|
||||
|
@ -691,7 +623,7 @@ _PyEval_ReInitThreads(PyThreadState *tstate)
|
|||
void
|
||||
_PyEval_SignalAsyncExc(PyInterpreterState *interp)
|
||||
{
|
||||
SIGNAL_ASYNC_EXC(interp);
|
||||
_Py_set_eval_breaker_bit(interp, _PY_ASYNC_EXCEPTION_BIT, 1);
|
||||
}
|
||||
|
||||
PyThreadState *
|
||||
|
@ -700,9 +632,8 @@ PyEval_SaveThread(void)
|
|||
PyThreadState *tstate = _PyThreadState_SwapNoGIL(NULL);
|
||||
_Py_EnsureTstateNotNULL(tstate);
|
||||
|
||||
struct _ceval_state *ceval = &tstate->interp->ceval;
|
||||
assert(gil_created(ceval->gil));
|
||||
drop_gil(ceval, tstate);
|
||||
assert(gil_created(tstate->interp->ceval.gil));
|
||||
drop_gil(tstate->interp, tstate);
|
||||
return tstate;
|
||||
}
|
||||
|
||||
|
@ -742,22 +673,9 @@ PyEval_RestoreThread(PyThreadState *tstate)
|
|||
void
|
||||
_PyEval_SignalReceived(PyInterpreterState *interp)
|
||||
{
|
||||
#ifdef MS_WINDOWS
|
||||
// bpo-42296: On Windows, _PyEval_SignalReceived() is called from a signal
|
||||
// handler which can run in a thread different than the Python thread, in
|
||||
// which case _Py_ThreadCanHandleSignals() is wrong. Ignore
|
||||
// _Py_ThreadCanHandleSignals() and always set eval_breaker to 1.
|
||||
//
|
||||
// The next eval_frame_handle_pending() call will call
|
||||
// _Py_ThreadCanHandleSignals() to recompute eval_breaker.
|
||||
int force = 1;
|
||||
#else
|
||||
int force = 0;
|
||||
#endif
|
||||
/* bpo-30703: Function called when the C signal handler of Python gets a
|
||||
signal. We cannot queue a callback using _PyEval_AddPendingCall() since
|
||||
that function is not async-signal-safe. */
|
||||
SIGNAL_PENDING_SIGNALS(interp, force);
|
||||
if (_Py_ThreadCanHandleSignals(interp)) {
|
||||
_Py_set_eval_breaker_bit(interp, _PY_SIGNALS_PENDING_BIT, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Push one item onto the queue while holding the lock. */
|
||||
|
@ -773,6 +691,8 @@ _push_pending_call(struct _pending_calls *pending,
|
|||
pending->calls[i].func = func;
|
||||
pending->calls[i].arg = arg;
|
||||
pending->last = j;
|
||||
assert(pending->calls_to_do < NPENDINGCALLS);
|
||||
pending->calls_to_do++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -800,6 +720,8 @@ _pop_pending_call(struct _pending_calls *pending,
|
|||
if (i >= 0) {
|
||||
pending->calls[i] = (struct _pending_call){0};
|
||||
pending->first = (i + 1) % NPENDINGCALLS;
|
||||
assert(pending->calls_to_do > 0);
|
||||
pending->calls_to_do--;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -829,7 +751,7 @@ _PyEval_AddPendingCall(PyInterpreterState *interp,
|
|||
PyThread_release_lock(pending->lock);
|
||||
|
||||
/* signal main loop */
|
||||
SIGNAL_PENDING_CALLS(pending, interp);
|
||||
SIGNAL_PENDING_CALLS(interp);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -846,33 +768,18 @@ static int
|
|||
handle_signals(PyThreadState *tstate)
|
||||
{
|
||||
assert(_PyThreadState_CheckConsistency(tstate));
|
||||
_Py_set_eval_breaker_bit(tstate->interp, _PY_SIGNALS_PENDING_BIT, 0);
|
||||
if (!_Py_ThreadCanHandleSignals(tstate->interp)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
UNSIGNAL_PENDING_SIGNALS(tstate->interp);
|
||||
if (_PyErr_CheckSignalsTstate(tstate) < 0) {
|
||||
/* On failure, re-schedule a call to handle_signals(). */
|
||||
SIGNAL_PENDING_SIGNALS(tstate->interp, 0);
|
||||
_Py_set_eval_breaker_bit(tstate->interp, _PY_SIGNALS_PENDING_BIT, 1);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
maybe_has_pending_calls(PyInterpreterState *interp)
|
||||
{
|
||||
struct _pending_calls *pending = &interp->ceval.pending;
|
||||
if (_Py_atomic_load_relaxed_int32(&pending->calls_to_do)) {
|
||||
return 1;
|
||||
}
|
||||
if (!_Py_IsMainThread() || !_Py_IsMainInterpreter(interp)) {
|
||||
return 0;
|
||||
}
|
||||
pending = &_PyRuntime.ceval.pending_mainthread;
|
||||
return _Py_atomic_load_relaxed_int32(&pending->calls_to_do);
|
||||
}
|
||||
|
||||
static int
|
||||
_make_pending_calls(struct _pending_calls *pending)
|
||||
{
|
||||
|
@ -930,7 +837,7 @@ make_pending_calls(PyInterpreterState *interp)
|
|||
if (_make_pending_calls(pending) != 0) {
|
||||
pending->busy = 0;
|
||||
/* There might not be more calls to make, but we play it safe. */
|
||||
SIGNAL_PENDING_CALLS(pending, interp);
|
||||
SIGNAL_PENDING_CALLS(interp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -938,7 +845,7 @@ make_pending_calls(PyInterpreterState *interp)
|
|||
if (_make_pending_calls(pending_main) != 0) {
|
||||
pending->busy = 0;
|
||||
/* There might not be more calls to make, but we play it safe. */
|
||||
SIGNAL_PENDING_CALLS(pending_main, interp);
|
||||
SIGNAL_PENDING_CALLS(interp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -1083,38 +990,35 @@ _PyEval_FiniState(struct _ceval_state *ceval)
|
|||
int
|
||||
_Py_HandlePending(PyThreadState *tstate)
|
||||
{
|
||||
_PyRuntimeState * const runtime = &_PyRuntime;
|
||||
struct _ceval_runtime_state *ceval = &runtime->ceval;
|
||||
struct _ceval_state *interp_ceval_state = &tstate->interp->ceval;
|
||||
PyInterpreterState *interp = tstate->interp;
|
||||
|
||||
/* Pending signals */
|
||||
if (_Py_atomic_load_relaxed_int32(&ceval->signals_pending)) {
|
||||
if (_Py_eval_breaker_bit_is_set(interp, _PY_SIGNALS_PENDING_BIT)) {
|
||||
if (handle_signals(tstate) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pending calls */
|
||||
if (maybe_has_pending_calls(tstate->interp)) {
|
||||
if (make_pending_calls(tstate->interp) != 0) {
|
||||
if (_Py_eval_breaker_bit_is_set(interp, _PY_CALLS_TO_DO_BIT)) {
|
||||
if (make_pending_calls(interp) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* GC scheduled to run */
|
||||
if (_Py_atomic_load_relaxed_int32(&interp_ceval_state->gc_scheduled)) {
|
||||
_Py_atomic_store_relaxed(&interp_ceval_state->gc_scheduled, 0);
|
||||
COMPUTE_EVAL_BREAKER(tstate->interp, ceval, interp_ceval_state);
|
||||
if (_Py_eval_breaker_bit_is_set(interp, _PY_GC_SCHEDULED_BIT)) {
|
||||
_Py_set_eval_breaker_bit(interp, _PY_GC_SCHEDULED_BIT, 0);
|
||||
_Py_RunGC(tstate);
|
||||
}
|
||||
|
||||
/* GIL drop request */
|
||||
if (_Py_atomic_load_relaxed_int32(&interp_ceval_state->gil_drop_request)) {
|
||||
if (_Py_eval_breaker_bit_is_set(interp, _PY_GIL_DROP_REQUEST_BIT)) {
|
||||
/* Give another thread a chance */
|
||||
if (_PyThreadState_SwapNoGIL(NULL) != tstate) {
|
||||
Py_FatalError("tstate mix-up");
|
||||
}
|
||||
drop_gil(interp_ceval_state, tstate);
|
||||
drop_gil(interp, tstate);
|
||||
|
||||
/* Other threads may run now */
|
||||
|
||||
|
@ -1126,27 +1030,16 @@ _Py_HandlePending(PyThreadState *tstate)
|
|||
}
|
||||
|
||||
/* Check for asynchronous exception. */
|
||||
if (tstate->async_exc != NULL) {
|
||||
PyObject *exc = tstate->async_exc;
|
||||
tstate->async_exc = NULL;
|
||||
UNSIGNAL_ASYNC_EXC(tstate->interp);
|
||||
_PyErr_SetNone(tstate, exc);
|
||||
Py_DECREF(exc);
|
||||
return -1;
|
||||
if (_Py_eval_breaker_bit_is_set(interp, _PY_ASYNC_EXCEPTION_BIT)) {
|
||||
_Py_set_eval_breaker_bit(interp, _PY_ASYNC_EXCEPTION_BIT, 0);
|
||||
if (tstate->async_exc != NULL) {
|
||||
PyObject *exc = tstate->async_exc;
|
||||
tstate->async_exc = NULL;
|
||||
_PyErr_SetNone(tstate, exc);
|
||||
Py_DECREF(exc);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// It is possible that some of the conditions that trigger the eval breaker
|
||||
// are called in a different thread than the Python thread. An example of
|
||||
// this is bpo-42296: On Windows, _PyEval_SignalReceived() can be called in
|
||||
// a different thread than the Python thread, in which case
|
||||
// _Py_ThreadCanHandleSignals() is wrong. Recompute eval_breaker in the
|
||||
// current Python thread with the correct _Py_ThreadCanHandleSignals()
|
||||
// value. It prevents to interrupt the eval loop at every instruction if
|
||||
// the current Python thread cannot handle signals (if
|
||||
// _Py_ThreadCanHandleSignals() is false).
|
||||
COMPUTE_EVAL_BREAKER(tstate->interp, ceval, interp_ceval_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue