bpo-35423: Stop using the "pending calls" machinery for signals. (gh-10972)
This change separates the signal handling trigger in the eval loop from the "pending calls" machinery. There is no semantic change and the difference in performance is insignificant. The change makes both components less confusing. It also eliminates the risk of changes to the pending calls affecting signal handling. This is particularly relevant for some upcoming pending calls changes I have in the works.
This commit is contained in:
parent
a909460a09
commit
fdf282d609
|
@ -45,6 +45,8 @@ struct _ceval_runtime_state {
|
||||||
/* Request for dropping the GIL */
|
/* Request for dropping the GIL */
|
||||||
_Py_atomic_int gil_drop_request;
|
_Py_atomic_int gil_drop_request;
|
||||||
struct _pending_calls pending;
|
struct _pending_calls pending;
|
||||||
|
/* Request for checking signals. */
|
||||||
|
_Py_atomic_int signals_pending;
|
||||||
struct _gil_runtime_state gil;
|
struct _gil_runtime_state gil;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Separate the signal handling trigger in the eval loop from the "pending
|
||||||
|
calls" machinery. There is no semantic change and the difference in
|
||||||
|
performance is insignificant.
|
|
@ -202,12 +202,15 @@ It raises KeyboardInterrupt.");
|
||||||
static int
|
static int
|
||||||
report_wakeup_write_error(void *data)
|
report_wakeup_write_error(void *data)
|
||||||
{
|
{
|
||||||
|
PyObject *exc, *val, *tb;
|
||||||
int save_errno = errno;
|
int save_errno = errno;
|
||||||
errno = (int) (intptr_t) data;
|
errno = (int) (intptr_t) data;
|
||||||
|
PyErr_Fetch(&exc, &val, &tb);
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
PySys_WriteStderr("Exception ignored when trying to write to the "
|
PySys_WriteStderr("Exception ignored when trying to write to the "
|
||||||
"signal wakeup fd:\n");
|
"signal wakeup fd:\n");
|
||||||
PyErr_WriteUnraisable(NULL);
|
PyErr_WriteUnraisable(NULL);
|
||||||
|
PyErr_Restore(exc, val, tb);
|
||||||
errno = save_errno;
|
errno = save_errno;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -216,6 +219,8 @@ report_wakeup_write_error(void *data)
|
||||||
static int
|
static int
|
||||||
report_wakeup_send_error(void* data)
|
report_wakeup_send_error(void* data)
|
||||||
{
|
{
|
||||||
|
PyObject *exc, *val, *tb;
|
||||||
|
PyErr_Fetch(&exc, &val, &tb);
|
||||||
/* PyErr_SetExcFromWindowsErr() invokes FormatMessage() which
|
/* PyErr_SetExcFromWindowsErr() invokes FormatMessage() which
|
||||||
recognizes the error codes used by both GetLastError() and
|
recognizes the error codes used by both GetLastError() and
|
||||||
WSAGetLastError */
|
WSAGetLastError */
|
||||||
|
@ -223,6 +228,7 @@ report_wakeup_send_error(void* data)
|
||||||
PySys_WriteStderr("Exception ignored when trying to send to the "
|
PySys_WriteStderr("Exception ignored when trying to send to the "
|
||||||
"signal wakeup fd:\n");
|
"signal wakeup fd:\n");
|
||||||
PyErr_WriteUnraisable(NULL);
|
PyErr_WriteUnraisable(NULL);
|
||||||
|
PyErr_Restore(exc, val, tb);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* MS_WINDOWS */
|
#endif /* MS_WINDOWS */
|
||||||
|
|
104
Python/ceval.c
104
Python/ceval.c
|
@ -100,6 +100,7 @@ static long dxp[256];
|
||||||
_Py_atomic_store_relaxed( \
|
_Py_atomic_store_relaxed( \
|
||||||
&_PyRuntime.ceval.eval_breaker, \
|
&_PyRuntime.ceval.eval_breaker, \
|
||||||
GIL_REQUEST | \
|
GIL_REQUEST | \
|
||||||
|
_Py_atomic_load_relaxed(&_PyRuntime.ceval.signals_pending) | \
|
||||||
_Py_atomic_load_relaxed(&_PyRuntime.ceval.pending.calls_to_do) | \
|
_Py_atomic_load_relaxed(&_PyRuntime.ceval.pending.calls_to_do) | \
|
||||||
_PyRuntime.ceval.pending.async_exc)
|
_PyRuntime.ceval.pending.async_exc)
|
||||||
|
|
||||||
|
@ -128,6 +129,18 @@ static long dxp[256];
|
||||||
COMPUTE_EVAL_BREAKER(); \
|
COMPUTE_EVAL_BREAKER(); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define SIGNAL_PENDING_SIGNALS() \
|
||||||
|
do { \
|
||||||
|
_Py_atomic_store_relaxed(&_PyRuntime.ceval.signals_pending, 1); \
|
||||||
|
_Py_atomic_store_relaxed(&_PyRuntime.ceval.eval_breaker, 1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define UNSIGNAL_PENDING_SIGNALS() \
|
||||||
|
do { \
|
||||||
|
_Py_atomic_store_relaxed(&_PyRuntime.ceval.signals_pending, 0); \
|
||||||
|
COMPUTE_EVAL_BREAKER(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define SIGNAL_ASYNC_EXC() \
|
#define SIGNAL_ASYNC_EXC() \
|
||||||
do { \
|
do { \
|
||||||
_PyRuntime.ceval.pending.async_exc = 1; \
|
_PyRuntime.ceval.pending.async_exc = 1; \
|
||||||
|
@ -306,7 +319,7 @@ _PyEval_SignalReceived(void)
|
||||||
/* bpo-30703: Function called when the C signal handler of Python gets a
|
/* bpo-30703: Function called when the C signal handler of Python gets a
|
||||||
signal. We cannot queue a callback using Py_AddPendingCall() since
|
signal. We cannot queue a callback using Py_AddPendingCall() since
|
||||||
that function is not async-signal-safe. */
|
that function is not async-signal-safe. */
|
||||||
SIGNAL_PENDING_CALLS();
|
SIGNAL_PENDING_SIGNALS();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This implementation is thread-safe. It allows
|
/* This implementation is thread-safe. It allows
|
||||||
|
@ -356,21 +369,28 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
Py_MakePendingCalls(void)
|
handle_signals(void)
|
||||||
{
|
{
|
||||||
static int busy = 0;
|
/* Only handle signals on main thread. */
|
||||||
int i;
|
if (_PyRuntime.ceval.pending.main_thread &&
|
||||||
int r = 0;
|
PyThread_get_thread_ident() != _PyRuntime.ceval.pending.main_thread)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
assert(PyGILState_Check());
|
UNSIGNAL_PENDING_SIGNALS();
|
||||||
|
if (PyErr_CheckSignals() < 0) {
|
||||||
if (!_PyRuntime.ceval.pending.lock) {
|
SIGNAL_PENDING_SIGNALS(); /* We're not done yet */
|
||||||
/* initial allocation of the lock */
|
|
||||||
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
|
|
||||||
if (_PyRuntime.ceval.pending.lock == NULL)
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
make_pending_calls(void)
|
||||||
|
{
|
||||||
|
static int busy = 0;
|
||||||
|
|
||||||
/* only service pending calls on main thread */
|
/* only service pending calls on main thread */
|
||||||
if (_PyRuntime.ceval.pending.main_thread &&
|
if (_PyRuntime.ceval.pending.main_thread &&
|
||||||
|
@ -378,22 +398,28 @@ Py_MakePendingCalls(void)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* don't perform recursive pending calls */
|
/* don't perform recursive pending calls */
|
||||||
if (busy)
|
if (busy) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
busy = 1;
|
busy = 1;
|
||||||
/* unsignal before starting to call callbacks, so that any callback
|
/* unsignal before starting to call callbacks, so that any callback
|
||||||
added in-between re-signals */
|
added in-between re-signals */
|
||||||
UNSIGNAL_PENDING_CALLS();
|
UNSIGNAL_PENDING_CALLS();
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
/* Python signal handler doesn't really queue a callback: it only signals
|
if (!_PyRuntime.ceval.pending.lock) {
|
||||||
that a signal was received, see _PyEval_SignalReceived(). */
|
/* initial allocation of the lock */
|
||||||
if (PyErr_CheckSignals() < 0) {
|
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
|
||||||
|
if (_PyRuntime.ceval.pending.lock == NULL) {
|
||||||
|
res = -1;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* perform a bounded number of calls, in case of recursion */
|
/* perform a bounded number of calls, in case of recursion */
|
||||||
for (i=0; i<NPENDINGCALLS; i++) {
|
for (int i=0; i<NPENDINGCALLS; i++) {
|
||||||
int j;
|
int j;
|
||||||
int (*func)(void *);
|
int (*func)(void *);
|
||||||
void *arg = NULL;
|
void *arg = NULL;
|
||||||
|
@ -412,19 +438,41 @@ Py_MakePendingCalls(void)
|
||||||
/* having released the lock, perform the callback */
|
/* having released the lock, perform the callback */
|
||||||
if (func == NULL)
|
if (func == NULL)
|
||||||
break;
|
break;
|
||||||
r = func(arg);
|
res = func(arg);
|
||||||
if (r) {
|
if (res) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
busy = 0;
|
busy = 0;
|
||||||
return r;
|
return res;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
busy = 0;
|
busy = 0;
|
||||||
SIGNAL_PENDING_CALLS(); /* We're not done yet */
|
SIGNAL_PENDING_CALLS();
|
||||||
return -1;
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Py_MakePendingCalls() is a simple wrapper for the sake
|
||||||
|
of backward-compatibility. */
|
||||||
|
int
|
||||||
|
Py_MakePendingCalls(void)
|
||||||
|
{
|
||||||
|
assert(PyGILState_Check());
|
||||||
|
|
||||||
|
/* Python signal handler doesn't really queue a callback: it only signals
|
||||||
|
that a signal was received, see _PyEval_SignalReceived(). */
|
||||||
|
int res = handle_signals();
|
||||||
|
if (res != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = make_pending_calls();
|
||||||
|
if (res != 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The interpreter's recursion limit */
|
/* The interpreter's recursion limit */
|
||||||
|
@ -957,12 +1005,22 @@ main_loop:
|
||||||
*/
|
*/
|
||||||
goto fast_next_opcode;
|
goto fast_next_opcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_Py_atomic_load_relaxed(
|
||||||
|
&_PyRuntime.ceval.signals_pending))
|
||||||
|
{
|
||||||
|
if (handle_signals() != 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (_Py_atomic_load_relaxed(
|
if (_Py_atomic_load_relaxed(
|
||||||
&_PyRuntime.ceval.pending.calls_to_do))
|
&_PyRuntime.ceval.pending.calls_to_do))
|
||||||
{
|
{
|
||||||
if (Py_MakePendingCalls() < 0)
|
if (make_pending_calls() != 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_Py_atomic_load_relaxed(
|
if (_Py_atomic_load_relaxed(
|
||||||
&_PyRuntime.ceval.gil_drop_request))
|
&_PyRuntime.ceval.gil_drop_request))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue