8000 bpo-42296: On Windows, fix CTRL+C regression (GH-23257) · python/cpython@d96a7a8 · GitHub
[go: up one dir, main page]

Skip to content

Commit d96a7a8

Browse files
authored
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.
1 parent 0cec97e commit d96a7a8

File tree

2 files changed

+37
-5
lines changed

2 files changed

+37
-5
lines changed
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
@@ -203,13 +203,18 @@ UNSIGNAL_PENDING_CALLS(PyInterpreterState *interp)
203203

204204

205205
static inline void
206-
SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp)
206+
SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp, int force)
207207
{
208208
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
209209
struct _ceval_state *ceval2 = &interp->ceval;
210210
_Py_atomic_store_relaxed(&ceval->signals_pending, 1);
211-
/* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
212-
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
211+
if (force) {
212+
_Py_atomic_store_relaxed(&ceval2->eval_breaker, 1);
213+
}
214+
else {
215+
/* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
216+
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
217+
}
213218
}
214219

215220

@@ -559,10 +564,22 @@ PyEval_RestoreThread(PyThreadState *tstate)
559564
void
560565
_PyEval_SignalReceived(PyInterpreterState *interp)
561566
{
567+
#ifdef MS_WINDOWS
568+
// bpo-42296: On Windows, _PyEval_SignalReceived() is called from a signal
569+
// handler which can run in a thread different than the Python thread, in
570+
// which case _Py_ThreadCanHandleSignals() is wrong. Ignore
571+
// _Py_ThreadCanHandleSignals() and always set eval_breaker to 1.
572+
//
573+
// The next eval_frame_handle_pending() call will call
574+
// _Py_ThreadCanHandleSignals() to recompute eval_breaker.
575+
int force = 1;
576+
#else
577+
int force = 0;
578+
#endif
562579
/* bpo-30703: Function called when the C signal handler of Python gets a
563580
signal. We cannot queue a callback using _PyEval_AddPendingCall() since
564581
that function is not async-signal-safe. */
565-
SIGNAL_PENDING_SIGNALS(interp);
582+
SIGNAL_PENDING_SIGNALS(interp, force);
566583
}
567584

568585
/* Push one item onto the queue while holding the lock. */
@@ -662,7 +679,7 @@ handle_signals(PyThreadState *tstate)
662679
UNSIGNAL_PENDING_SIGNALS(tstate->interp);
663680
if (_PyErr_CheckSignalsTstate(tstate) < 0) {
664681
/* On failure, re-schedule a call to handle_signals(). */
665-
SIGNAL_PENDING_SIGNALS(tstate->interp);
682+
SIGNAL_PENDING_SIGNALS(tstate->interp, 0);
666683
return -1;
667684
}
668685
return 0;
@@ -948,6 +965,17 @@ eval_frame_handle_pending(PyThreadState *tstate)
948965
return -1;
949966
}
950967

968+
#ifdef MS_WINDOWS
969+
// bpo-42296: On Windows, _PyEval_SignalReceived() can be called in a
970+
// different thread than the Python thread, in which case
971+
// _Py_ThreadCanHandleSignals() is wrong. Recompute eval_breaker in the
972+
// current Python thread with the correct _Py_ThreadCanHandleSignals()
973+
// value. It prevents to interrupt the eval loop at every instruction if
974+
// the current Python thread cannot handle signals (if
975+
// _Py_ThreadCanHandleSignals() is false).
976+
COMPUTE_EVAL_BREAKER(tstate->interp, ceval, ceval2);
977+
#endif
978+
951979
return 0;
952980
}
953981

0 commit comments

Comments
 (0)
0