diff options
Diffstat (limited to 'Python/ceval.c')
-rw-r--r-- | Python/ceval.c | 75 |
1 files changed, 54 insertions, 21 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index 3243a4f..0f6978e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -140,6 +140,15 @@ static long dxp[256]; do { pending_async_exc = 0; COMPUTE_EVAL_BREAKER(); } while (0) +/* This single variable consolidates all requests to break out of the fast path + in the eval loop. */ +static _Py_atomic_int eval_breaker = {0}; +/* Request for running pending calls. */ +static _Py_atomic_int pendingcalls_to_do = {0}; +/* Request for looking at the `async_exc` field of the current thread state. + Guarded by the GIL. */ +static int pending_async_exc = 0; + #ifdef WITH_THREAD #ifdef HAVE_ERRNO_H @@ -149,16 +158,8 @@ static long dxp[256]; static PyThread_type_lock pending_lock = 0; /* for pending calls */ static unsigned long main_thread = 0; -/* This single variable consolidates all requests to break out of the fast path - in the eval loop. */ -static _Py_atomic_int eval_breaker = {0}; /* Request for dropping the GIL */ static _Py_atomic_int gil_drop_request = {0}; -/* Request for running pending calls. */ -static _Py_atomic_int pendingcalls_to_do = {0}; -/* Request for looking at the `async_exc` field of the current thread state. - Guarded by the GIL. */ -static int pending_async_exc = 0; #include "ceval_gil.h" @@ -253,9 +254,6 @@ PyEval_ReInitThreads(void) _PyThreadState_DeleteExcept(current_tstate); } -#else -static _Py_atomic_int eval_breaker = {0}; -static int pending_async_exc = 0; #endif /* WITH_THREAD */ /* This function is used to signal that async exceptions are waiting to be @@ -330,6 +328,15 @@ PyEval_RestoreThread(PyThreadState *tstate) #endif */ +void +_PyEval_SignalReceived(void) +{ + /* bpo-30703: Function called when the C signal handler of Python gets a + signal. We cannot queue a callback using Py_AddPendingCall() since + that function is not async-signal-safe. */ + SIGNAL_PENDING_CALLS(); +} + #ifdef WITH_THREAD /* The WITH_THREAD implementation is thread-safe. It allows @@ -394,6 +401,8 @@ Py_MakePendingCalls(void) int i; int r = 0; + assert(PyGILState_Check()); + if (!pending_lock) { /* initial allocation of the lock */ pending_lock = PyThread_allocate_lock(); @@ -408,6 +417,16 @@ Py_MakePendingCalls(void) if (busy) return 0; busy = 1; + /* unsignal before starting to call callbacks, so that any callback + added in-between re-signals */ + UNSIGNAL_PENDING_CALLS(); + + /* Python signal handler doesn't really queue a callback: it only signals + that a signal was received, see _PyEval_SignalReceived(). */ + if (PyErr_CheckSignals() < 0) { + goto error; + } + /* perform a bounded number of calls, in case of recursion */ for (i=0; i<NPENDINGCALLS; i++) { int j; @@ -424,20 +443,23 @@ Py_MakePendingCalls(void) arg = pendingcalls[j].arg; pendingfirst = (j + 1) % NPENDINGCALLS; } - if (pendingfirst != pendinglast) - SIGNAL_PENDING_CALLS(); - else - UNSIGNAL_PENDING_CALLS(); PyThread_release_lock(pending_lock); /* having released the lock, perform the callback */ if (func == NULL) break; r = func(arg); - if (r) - break; + if (r) { + goto error; + } } + busy = 0; return r; + +error: + busy = 0; + SIGNAL_PENDING_CALLS(); /* We're not done yet */ + return -1; } #else /* if ! defined WITH_THREAD */ @@ -472,7 +494,6 @@ static struct { } pendingcalls[NPENDINGCALLS]; static volatile int pendingfirst = 0; static volatile int pendinglast = 0; -static _Py_atomic_int pendingcalls_to_do = {0}; int Py_AddPendingCall(int (*func)(void *), void *arg) @@ -506,7 +527,16 @@ Py_MakePendingCalls(void) if (busy) return 0; busy = 1; + + /* unsignal before starting to call callbacks, so that any callback + added in-between re-signals */ UNSIGNAL_PENDING_CALLS(); + /* Python signal handler doesn't really queue a callback: it only signals + that a signal was received, see _PyEval_SignalReceived(). */ + if (PyErr_CheckSignals() < 0) { + goto error; + } + for (;;) { int i; int (*func)(void *); @@ -518,13 +548,16 @@ Py_MakePendingCalls(void) arg = pendingcalls[i].arg; pendingfirst = (i + 1) % NPENDINGCALLS; if (func(arg) < 0) { - busy = 0; - SIGNAL_PENDING_CALLS(); /* We're not done yet */ - return -1; + goto error: } } busy = 0; return 0; + +error: + busy = 0; + SIGNAL_PENDING_CALLS(); /* We're not done yet */ + return -1; } #endif /* WITH_THREAD */ |