@@ -140,6 +140,15 @@ static long dxp[256];
140140 do { pending_async_exc = 0; COMPUTE_EVAL_BREAKER(); } while (0)
141141
142142
143+ /* This single variable consolidates all requests to break out of the fast path
144+ in the eval loop. */
145+ static _Py_atomic_int eval_breaker = {0 };
146+ /* Request for running pending calls. */
147+ static _Py_atomic_int pendingcalls_to_do = {0 };
148+ /* Request for looking at the `async_exc` field of the current thread state.
149+ Guarded by the GIL. */
150+ static int pending_async_exc = 0 ;
151+
143152#ifdef WITH_THREAD
144153
145154#ifdef HAVE_ERRNO_H
@@ -149,16 +158,8 @@ static long dxp[256];
149158
150159static PyThread_type_lock pending_lock = 0 ; /* for pending calls */
151160static unsigned long main_thread = 0 ;
152- /* This single variable consolidates all requests to break out of the fast path
153- in the eval loop. */
154- static _Py_atomic_int eval_breaker = {0 };
155161/* Request for dropping the GIL */
156162static _Py_atomic_int gil_drop_request = {0 };
157- /* Request for running pending calls. */
158- static _Py_atomic_int pendingcalls_to_do = {0 };
159- /* Request for looking at the `async_exc` field of the current thread state.
160- Guarded by the GIL. */
161- static int pending_async_exc = 0 ;
162163
163164#include "ceval_gil.h"
164165
@@ -253,9 +254,6 @@ PyEval_ReInitThreads(void)
253254 _PyThreadState_DeleteExcept (current_tstate );
254255}
255256
256- #else
257- static _Py_atomic_int eval_breaker = {0 };
258- static int pending_async_exc = 0 ;
259257#endif /* WITH_THREAD */
260258
261259/* This function is used to signal that async exceptions are waiting to be
@@ -330,6 +328,15 @@ PyEval_RestoreThread(PyThreadState *tstate)
330328#endif
331329*/
332330
331+ void
332+ _PyEval_SignalReceived (void )
333+ {
334+ /* bpo-30703: Function called when the C signal handler of Python gets a
335+ signal. We cannot queue a callback using Py_AddPendingCall() since
336+ that function is not async-signal-safe. */
337+ SIGNAL_PENDING_CALLS ();
338+ }
339+
333340#ifdef WITH_THREAD
334341
335342/* The WITH_THREAD implementation is thread-safe. It allows
@@ -394,6 +401,8 @@ Py_MakePendingCalls(void)
394401 int i ;
395402 int r = 0 ;
396403
404+ assert (PyGILState_Check ());
405+
397406 if (!pending_lock ) {
398407 /* initial allocation of the lock */
399408 pending_lock = PyThread_allocate_lock ();
@@ -408,6 +417,16 @@ Py_MakePendingCalls(void)
408417 if (busy )
409418 return 0 ;
410419 busy = 1 ;
420+ /* unsignal before starting to call callbacks, so that any callback
421+ added in-between re-signals */
422+ UNSIGNAL_PENDING_CALLS ();
423+
424+ /* Python signal handler doesn't really queue a callback: it only signals
425+ that a signal was received, see _PyEval_SignalReceived(). */
426+ if (PyErr_CheckSignals () < 0 ) {
427+ goto error ;
428+ }
429+
411430 /* perform a bounded number of calls, in case of recursion */
412431 for (i = 0 ; i < NPENDINGCALLS ; i ++ ) {
413432 int j ;
@@ -424,20 +443,23 @@ Py_MakePendingCalls(void)
424443 arg = pendingcalls [j ].arg ;
425444 pendingfirst = (j + 1 ) % NPENDINGCALLS ;
426445 }
427- if (pendingfirst != pendinglast )
428- SIGNAL_PENDING_CALLS ();
429- else
430- UNSIGNAL_PENDING_CALLS ();
431446 PyThread_release_lock (pending_lock );
432447 /* having released the lock, perform the callback */
433448 if (func == NULL )
434449 break ;
435450 r = func (arg );
436- if (r )
437- break ;
451+ if (r ) {
452+ goto error ;
453+ }
438454 }
455+
439456 busy = 0 ;
440457 return r ;
458+
459+ error :
460+ busy = 0 ;
461+ SIGNAL_PENDING_CALLS (); /* We're not done yet */
462+ return -1 ;
441463}
442464
443465#else /* if ! defined WITH_THREAD */
@@ -472,7 +494,6 @@ static struct {
472494} pendingcalls [NPENDINGCALLS ];
473495static volatile int pendingfirst = 0 ;
474496static volatile int pendinglast = 0 ;
475- static _Py_atomic_int pendingcalls_to_do = {0 };
476497
477498int
478499Py_AddPendingCall (int (* func )(void * ), void * arg )
@@ -506,7 +527,16 @@ Py_MakePendingCalls(void)
506527 if (busy )
507528 return 0 ;
508529 busy = 1 ;
530+
531+ /* unsignal before starting to call callbacks, so that any callback
532+ added in-between re-signals */
509533 UNSIGNAL_PENDING_CALLS ();
534+ /* Python signal handler doesn't really queue a callback: it only signals
535+ that a signal was received, see _PyEval_SignalReceived(). */
536+ if (PyErr_CheckSignals () < 0 ) {
537+ goto error ;
538+ }
539+
510540 for (;;) {
511541 int i ;
512542 int (* func )(void * );
@@ -518,13 +548,16 @@ Py_MakePendingCalls(void)
518548 arg = pendingcalls [i ].arg ;
519549 pendingfirst = (i + 1 ) % NPENDINGCALLS ;
520550 if (func (arg ) < 0 ) {
521- busy = 0 ;
522- SIGNAL_PENDING_CALLS (); /* We're not done yet */
523- return -1 ;
551+ goto error :
524552 }
525553 }
526554 busy = 0 ;
527555 return 0 ;
556+
557+ error :
558+ busy = 0 ;
559+ SIGNAL_PENDING_CALLS (); /* We're not done yet */
560+ return -1 ;
528561}
529562
530563#endif /* WITH_THREAD */
0 commit comments