bpo-42296: On Windows, fix CTRL+C regression (GH-23257)
On Windows, fix a regression in signal handling which prevented to interrupt a program using CTRL+C. The signal handler can be run in a thread different than the Python thread, in which case the test deciding if the thread can handle signals is wrong. On Windows, _PyEval_SignalReceived() now always sets eval_breaker to 1 since it cannot test _Py_ThreadCanHandleSignals(), and eval_frame_handle_pending() always calls _Py_ThreadCanHandleSignals() to recompute eval_breaker.
This commit is contained in:
parent
0cec97eb6a
commit
d96a7a8313
|
@ -0,0 +1,4 @@
|
||||||
|
On Windows, fix a regression in signal handling which prevented to interrupt
|
||||||
|
a program using CTRL+C. The signal handler can be run in a thread different
|
||||||
|
than the Python thread, in which case the test deciding if the thread can
|
||||||
|
handle signals is wrong.
|
|
@ -203,13 +203,18 @@ UNSIGNAL_PENDING_CALLS(PyInterpreterState *interp)
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp)
|
SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp, int force)
|
||||||
{
|
{
|
||||||
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
|
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
|
||||||
struct _ceval_state *ceval2 = &interp->ceval;
|
struct _ceval_state *ceval2 = &interp->ceval;
|
||||||
_Py_atomic_store_relaxed(&ceval->signals_pending, 1);
|
_Py_atomic_store_relaxed(&ceval->signals_pending, 1);
|
||||||
/* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
|
if (force) {
|
||||||
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
|
_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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -559,10 +564,22 @@ PyEval_RestoreThread(PyThreadState *tstate)
|
||||||
void
|
void
|
||||||
_PyEval_SignalReceived(PyInterpreterState *interp)
|
_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
|
/* bpo-30703: Function called when the C signal handler of Python gets a
|
||||||
signal. We cannot queue a callback using _PyEval_AddPendingCall() since
|
signal. We cannot queue a callback using _PyEval_AddPendingCall() since
|
||||||
that function is not async-signal-safe. */
|
that function is not async-signal-safe. */
|
||||||
SIGNAL_PENDING_SIGNALS(interp);
|
SIGNAL_PENDING_SIGNALS(interp, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push one item onto the queue while holding the lock. */
|
/* Push one item onto the queue while holding the lock. */
|
||||||
|
@ -662,7 +679,7 @@ handle_signals(PyThreadState *tstate)
|
||||||
UNSIGNAL_PENDING_SIGNALS(tstate->interp);
|
UNSIGNAL_PENDING_SIGNALS(tstate->interp);
|
||||||
if (_PyErr_CheckSignalsTstate(tstate) < 0) {
|
if (_PyErr_CheckSignalsTstate(tstate) < 0) {
|
||||||
/* On failure, re-schedule a call to handle_signals(). */
|
/* On failure, re-schedule a call to handle_signals(). */
|
||||||
SIGNAL_PENDING_SIGNALS(tstate->interp);
|
SIGNAL_PENDING_SIGNALS(tstate->interp, 0);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -948,6 +965,17 @@ eval_frame_handle_pending(PyThreadState *tstate)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
// 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, ceval2);
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue