aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2020-11-13 14:44:42 +0100
committerGitHub <noreply@github.com>2020-11-13 14:44:42 +0100
commitd96a7a83133250377219227b5cfab4dbdddc5d3a (patch)
tree5f040cee144aa498889578d7a1ef12d9751c7f89 /Python/ceval.c
parentbpo-38823: Fix compiler warning in _ctypes on Windows (GH-23258) (diff)
downloadcpython-d96a7a83133250377219227b5cfab4dbdddc5d3a.tar.gz
cpython-d96a7a83133250377219227b5cfab4dbdddc5d3a.tar.bz2
cpython-d96a7a83133250377219227b5cfab4dbdddc5d3a.zip
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.
Diffstat (limited to 'Python/ceval.c')
-rw-r--r--Python/ceval.c38
1 files changed, 33 insertions, 5 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index d6b786dc2cd..3d65e161302 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -203,13 +203,18 @@ UNSIGNAL_PENDING_CALLS(PyInterpreterState *interp)
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_state *ceval2 = &interp->ceval;
_Py_atomic_store_relaxed(&ceval->signals_pending, 1);
- /* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
- COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
+ 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);
+ }
}
@@ -559,10 +564,22 @@ 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);
+ SIGNAL_PENDING_SIGNALS(interp, force);
}
/* Push one item onto the queue while holding the lock. */
@@ -662,7 +679,7 @@ handle_signals(PyThreadState *tstate)
UNSIGNAL_PENDING_SIGNALS(tstate->interp);
if (_PyErr_CheckSignalsTstate(tstate) < 0) {
/* On failure, re-schedule a call to handle_signals(). */
- SIGNAL_PENDING_SIGNALS(tstate->interp);
+ SIGNAL_PENDING_SIGNALS(tstate->interp, 0);
return -1;
}
return 0;
@@ -948,6 +965,17 @@ eval_frame_handle_pending(PyThreadState *tstate)
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;
}