diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index ed0368329572ab..149e2f177c4f3b 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -244,6 +244,32 @@ trip_signal(int sig_num) Handlers[sig_num].tripped = 1; + if (!is_tripped) { + /* Set is_tripped after setting .tripped, as it gets + cleared in PyErr_CheckSignals() before .tripped. */ + is_tripped = 1; + Py_AddPendingCall(checksignals_witharg, NULL); + } + + /* And then write to the wakeup fd *after* setting all the globals and + doing the Py_AddPendingCall. We used to write to the wakeup fd and then + set the flag, but this allowed the following sequence of events + (especially on windows, where trip_signal runs in a new thread): + + - main thread blocks on select([wakeup_fd], ...) + - signal arrives + - trip_signal writes to the wakeup fd + - the main thread wakes up + - the main thread checks the signal flags, sees that they're unset + - the main thread empties the wakeup fd + - the main thread goes back to sleep + - trip_signal sets the flags to request the Python-level signal handler + be run + - the main thread doesn't notice, because it's asleep + + See bpo-30038 for more details. + */ + #ifdef MS_WINDOWS fd = Py_SAFE_DOWNCAST(wakeup.fd, SOCKET_T, int); #else @@ -281,13 +307,6 @@ trip_signal(int sig_num) } } } - - if (!is_tripped) { - /* Set is_tripped after setting .tripped, as it gets - cleared in PyErr_CheckSignals() before .tripped. */ - is_tripped = 1; - Py_AddPendingCall(checksignals_witharg, NULL); - } } static void