@@ -100,6 +100,7 @@ static long dxp[256];
100100 _Py_atomic_store_relaxed( \
101101 &_PyRuntime.ceval.eval_breaker, \
102102 GIL_REQUEST | \
103+ _Py_atomic_load_relaxed(&_PyRuntime.ceval.signals_pending) | \
103104 _Py_atomic_load_relaxed(&_PyRuntime.ceval.pending.calls_to_do) | \
104105 _PyRuntime.ceval.pending.async_exc)
105106
@@ -128,6 +129,18 @@ static long dxp[256];
128129 COMPUTE_EVAL_BREAKER(); \
129130 } while (0)
130131
132+ #define SIGNAL_PENDING_SIGNALS () \
133+ do { \
134+ _Py_atomic_store_relaxed(&_PyRuntime.ceval.signals_pending, 1); \
135+ _Py_atomic_store_relaxed(&_PyRuntime.ceval.eval_breaker, 1); \
136+ } while (0)
137+
138+ #define UNSIGNAL_PENDING_SIGNALS () \
139+ do { \
140+ _Py_atomic_store_relaxed(&_PyRuntime.ceval.signals_pending, 0); \
141+ COMPUTE_EVAL_BREAKER(); \
142+ } while (0)
143+
131144#define SIGNAL_ASYNC_EXC () \
132145 do { \
133146 _PyRuntime.ceval.pending.async_exc = 1; \
@@ -306,7 +319,7 @@ _PyEval_SignalReceived(void)
306319 /* bpo-30703: Function called when the C signal handler of Python gets a
307320 signal. We cannot queue a callback using Py_AddPendingCall() since
308321 that function is not async-signal-safe. */
309- SIGNAL_PENDING_CALLS ();
322+ SIGNAL_PENDING_SIGNALS ();
310323}
311324
312325/* This implementation is thread-safe. It allows
@@ -356,44 +369,57 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
356369 return result ;
357370}
358371
359- int
360- Py_MakePendingCalls (void )
372+ static int
373+ handle_signals (void )
361374{
362- static int busy = 0 ;
363- int i ;
364- int r = 0 ;
365-
366- assert (PyGILState_Check ());
375+ /* Only handle signals on main thread. */
376+ if (_PyRuntime .ceval .pending .main_thread &&
377+ PyThread_get_thread_ident () != _PyRuntime .ceval .pending .main_thread )
378+ {
379+ return 0 ;
380+ }
367381
368- if (!_PyRuntime .ceval .pending .lock ) {
369- /* initial allocation of the lock */
370- _PyRuntime .ceval .pending .lock = PyThread_allocate_lock ();
371- if (_PyRuntime .ceval .pending .lock == NULL )
372- return -1 ;
382+ UNSIGNAL_PENDING_SIGNALS ();
383+ if (PyErr_CheckSignals () < 0 ) {
384+ SIGNAL_PENDING_SIGNALS (); /* We're not done yet */
385+ return -1 ;
373386 }
387+ return 0 ;
388+ }
389+
390+ static int
391+ make_pending_calls (void )
392+ {
393+ static int busy = 0 ;
374394
375395 /* only service pending calls on main thread */
376396 if (_PyRuntime .ceval .pending .main_thread &&
377397 PyThread_get_thread_ident () != _PyRuntime .ceval .pending .main_thread )
378398 {
379399 return 0 ;
380400 }
401+
381402 /* don't perform recursive pending calls */
382- if (busy )
403+ if (busy ) {
383404 return 0 ;
405+ }
384406 busy = 1 ;
385407 /* unsignal before starting to call callbacks, so that any callback
386408 added in-between re-signals */
387409 UNSIGNAL_PENDING_CALLS ();
410+ int res = 0 ;
388411
389- /* Python signal handler doesn't really queue a callback: it only signals
390- that a signal was received, see _PyEval_SignalReceived(). */
391- if (PyErr_CheckSignals () < 0 ) {
392- goto error ;
412+ if (!_PyRuntime .ceval .pending .lock ) {
413+ /* initial allocation of the lock */
414+ _PyRuntime .ceval .pending .lock = PyThread_allocate_lock ();
415+ if (_PyRuntime .ceval .pending .lock == NULL ) {
416+ res = -1 ;
417+ goto error ;
418+ }
393419 }
394420
395421 /* perform a bounded number of calls, in case of recursion */
396- for (i = 0 ; i < NPENDINGCALLS ; i ++ ) {
422+ for (int i = 0 ; i < NPENDINGCALLS ; i ++ ) {
397423 int j ;
398424 int (* func )(void * );
399425 void * arg = NULL ;
@@ -412,19 +438,41 @@ Py_MakePendingCalls(void)
412438 /* having released the lock, perform the callback */
413439 if (func == NULL )
414440 break ;
415- r = func (arg );
416- if (r ) {
441+ res = func (arg );
442+ if (res ) {
417443 goto error ;
418444 }
419445 }
420446
421447 busy = 0 ;
422- return r ;
448+ return res ;
423449
424450error :
425451 busy = 0 ;
426- SIGNAL_PENDING_CALLS (); /* We're not done yet */
427- return -1 ;
452+ SIGNAL_PENDING_CALLS ();
453+ return res ;
454+ }
455+
456+ /* Py_MakePendingCalls() is a simple wrapper for the sake
457+ of backward-compatibility. */
458+ int
459+ Py_MakePendingCalls (void )
460+ {
461+ assert (PyGILState_Check ());
462+
463+ /* Python signal handler doesn't really queue a callback: it only signals
464+ that a signal was received, see _PyEval_SignalReceived(). */
465+ int res = handle_signals ();
466+ if (res != 0 ) {
467+ return res ;
468+ }
469+
470+ res = make_pending_calls ();
471+ if (res != 0 ) {
472+ return res ;
473+ }
474+
475+ return 0 ;
428476}
429477
430478/* The interpreter's recursion limit */
@@ -957,12 +1005,22 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
9571005 */
9581006 goto fast_next_opcode ;
9591007 }
1008+
1009+ if (_Py_atomic_load_relaxed (
1010+ & _PyRuntime .ceval .signals_pending ))
1011+ {
1012+ if (handle_signals () != 0 ) {
1013+ goto error ;
1014+ }
1015+ }
9601016 if (_Py_atomic_load_relaxed (
9611017 & _PyRuntime .ceval .pending .calls_to_do ))
9621018 {
963- if (Py_MakePendingCalls () < 0 )
1019+ if (make_pending_calls () != 0 ) {
9641020 goto error ;
1021+ }
9651022 }
1023+
9661024 if (_Py_atomic_load_relaxed (
9671025 & _PyRuntime .ceval .gil_drop_request ))
9681026 {
0 commit comments