Skip to content

Commit e5729ae

Browse files
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. (cherry picked from commit d96a7a8) Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent bc77704 commit e5729ae

2 files changed

Lines changed: 37 additions & 5 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
On Windows, fix a regression in signal handling which prevented to interrupt
2+
a program using CTRL+C. The signal handler can be run in a thread different
3+
than the Python thread, in which case the test deciding if the thread can
4+
handle signals is wrong.

Python/ceval.c

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,18 @@ UNSIGNAL_PENDING_CALLS(PyInterpreterState *interp)
196196

197197

198198
static inline void
199-
SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp)
199+
SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp, int force)
200200
{
201201
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
202202
struct _ceval_state *ceval2 = &interp->ceval;
203203
_Py_atomic_store_relaxed(&ceval->signals_pending, 1);
204-
/* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
205-
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
204+
if (force) {
205+
_Py_atomic_store_relaxed(&ceval2->eval_breaker, 1);
206+
}
207+
else {
208+
/* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
209+
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
210+
}
206211
}
207212

208213

@@ -491,10 +496,22 @@ PyEval_RestoreThread(PyThreadState *tstate)
491496
void
492497
_PyEval_SignalReceived(PyInterpreterState *interp)
493498
{
499+
#ifdef MS_WINDOWS
500+
// bpo-42296: On Windows, _PyEval_SignalReceived() is called from a signal
501+
// handler which can run in a thread different than the Python thread, in
502+
// which case _Py_ThreadCanHandleSignals() is wrong. Ignore
503+
// _Py_ThreadCanHandleSignals() and always set eval_breaker to 1.
504+
//
505+
// The next eval_frame_handle_pending() call will call
506+
// _Py_ThreadCanHandleSignals() to recompute eval_breaker.
507+
int force = 1;
508+
#else
509+
int force = 0;
510+
#endif
494511
/* bpo-30703: Function called when the C signal handler of Python gets a
495512
signal. We cannot queue a callback using _PyEval_AddPendingCall() since
496513
that function is not async-signal-safe. */
497-
SIGNAL_PENDING_SIGNALS(interp);
514+
SIGNAL_PENDING_SIGNALS(interp, force);
498515
}
499516

500517
/* Push one item onto the queue while holding the lock. */
@@ -594,7 +611,7 @@ handle_signals(PyThreadState *tstate)
594611
UNSIGNAL_PENDING_SIGNALS(tstate->interp);
595612
if (_PyErr_CheckSignalsTstate(tstate) < 0) {
596613
/* On failure, re-schedule a call to handle_signals(). */
597-
SIGNAL_PENDING_SIGNALS(tstate->interp);
614+
SIGNAL_PENDING_SIGNALS(tstate->interp, 0);
598615
return -1;
599616
}
600617
return 0;
@@ -883,6 +900,17 @@ eval_frame_handle_pending(PyThreadState *tstate)
883900
return -1;
884901
}
885902

903+
#ifdef MS_WINDOWS
904+
// bpo-42296: On Windows, _PyEval_SignalReceived() can be called in a
905+
// different thread than the Python thread, in which case
906+
// _Py_ThreadCanHandleSignals() is wrong. Recompute eval_breaker in the
907+
// current Python thread with the correct _Py_ThreadCanHandleSignals()
908+
// value. It prevents to interrupt the eval loop at every instruction if
909+
// the current Python thread cannot handle signals (if
910+
// _Py_ThreadCanHandleSignals() is false).
911+
COMPUTE_EVAL_BREAKER(tstate->interp, ceval, ceval2);
912+
#endif
913+
886914
return 0;
887915
}
888916

0 commit comments

Comments
 (0)