From fb11664cb710841df0fe2ff06db745f9566b6010 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 26 Jun 2017 15:41:42 +0200 Subject: [PATCH] bpo-30703: More reentrant signal handler Modify the signal handler to not call Py_AddPendingCall() function since this function uses a lock and a list, and so is unlikely to be reentrant. Add a new _PyEval_SignalReceived() function which only writes into an atomic variable and so is reentrant. --- Include/ceval.h | 1 + Modules/signalmodule.c | 2 +- Python/ceval.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Include/ceval.h b/Include/ceval.h index e5cb4112274794..b2d57cbd6f7425 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -54,6 +54,7 @@ PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf); #endif PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg); +PyAPI_FUNC(void) _PyEval_SignalReceived(void); PyAPI_FUNC(int) Py_MakePendingCalls(void); /* Protection against deeply nested recursive calls diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 75abc98bc4b593..957ab54b652f13 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -248,7 +248,7 @@ trip_signal(int sig_num) /* Set is_tripped after setting .tripped, as it gets cleared in PyErr_CheckSignals() before .tripped. */ is_tripped = 1; - Py_AddPendingCall(checksignals_witharg, NULL); + _PyEval_SignalReceived(); } /* And then write to the wakeup fd *after* setting all the globals and diff --git a/Python/ceval.c b/Python/ceval.c index 3243a4f8a01d9c..317b088eb1d853 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -387,6 +387,15 @@ Py_AddPendingCall(int (*func)(void *), void *arg) return result; } +void +_PyEval_SignalReceived(void) +{ + /* bpo-30703: Function called when the C signal handler of Python gets a + signal. We cannot queue a callback using Py_AddPendingCall() since this + function is not reentrant (use a lock and a list). */ + SIGNAL_PENDING_CALLS(); +} + int Py_MakePendingCalls(void) { @@ -394,6 +403,12 @@ Py_MakePendingCalls(void) int i; int r = 0; + /* Python signal handler doesn't really queue a callback: it only signals + that an UNIX signal was received, see _PyEval_SignalReceived(). */ + if (PyErr_CheckSignals() < 0) { + return -1; + } + if (!pending_lock) { /* initial allocation of the lock */ pending_lock = PyThread_allocate_lock();