Skip to content

Commit 2c8a5e4

Browse files
authored
bpo-30808: Use _Py_atomic API for concurrency-sensitive signal state (#2417)
* Improve signal delivery Avoid using Py_AddPendingCall from signal handler, to avoid calling signal-unsafe functions. * Remove unused function * Improve comments * Use _Py_atomic API for concurrency-sensitive signal state * Add blurb
1 parent 68d663c commit 2c8a5e4

File tree

2 files changed

+21
-18
lines changed

2 files changed

+21
-18
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Use _Py_atomic API for concurrency-sensitive signal state.

Modules/signalmodule.c

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ static pid_t main_pid;
9393
#endif
9494

9595
static volatile struct {
96-
sig_atomic_t tripped;
96+
_Py_atomic_int tripped;
9797
PyObject *func;
9898
} Handlers[NSIG];
9999

@@ -113,7 +113,7 @@ static volatile sig_atomic_t wakeup_fd = -1;
113113
#endif
114114

115115
/* Speed up sigcheck() when none tripped */
116-
static volatile sig_atomic_t is_tripped = 0;
116+
static _Py_atomic_int is_tripped;
117117

118118
static PyObject *DefaultHandler;
119119
static PyObject *IgnoreHandler;
@@ -240,11 +240,13 @@ trip_signal(int sig_num)
240240
int fd;
241241
Py_ssize_t rc;
242242

243-
Handlers[sig_num].tripped = 1;
243+
_Py_atomic_store_relaxed(&Handlers[sig_num].tripped, 1);
244244

245245
/* Set is_tripped after setting .tripped, as it gets
246246
cleared in PyErr_CheckSignals() before .tripped. */
247-
is_tripped = 1;
247+
_Py_atomic_store(&is_tripped, 1);
248+
249+
/* Notify ceval.c */
248250
_PyEval_SignalReceived();
249251

250252
/* And then write to the wakeup fd *after* setting all the globals and
@@ -465,7 +467,7 @@ signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)
465467
return NULL;
466468
}
467469
old_handler = Handlers[signalnum].func;
468-
Handlers[signalnum].tripped = 0;
470+
_Py_atomic_store_relaxed(&Handlers[signalnum].tripped, 0);
469471
Py_INCREF(handler);
470472
Handlers[signalnum].func = handler;
471473
if (old_handler != NULL)
@@ -1269,11 +1271,11 @@ PyInit__signal(void)
12691271
goto finally;
12701272
Py_INCREF(IntHandler);
12711273

1272-
Handlers[0].tripped = 0;
1274+
_Py_atomic_store_relaxed(&Handlers[0].tripped, 0);
12731275
for (i = 1; i < NSIG; i++) {
12741276
void (*t)(int);
12751277
t = PyOS_getsig(i);
1276-
Handlers[i].tripped = 0;
1278+
_Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
12771279
if (t == SIG_DFL)
12781280
Handlers[i].func = DefaultHandler;
12791281
else if (t == SIG_IGN)
@@ -1497,7 +1499,7 @@ finisignal(void)
14971499

14981500
for (i = 1; i < NSIG; i++) {
14991501
func = Handlers[i].func;
1500-
Handlers[i].tripped = 0;
1502+
_Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
15011503
Handlers[i].func = NULL;
15021504
if (i != SIGINT && func != NULL && func != Py_None &&
15031505
func != DefaultHandler && func != IgnoreHandler)
@@ -1518,7 +1520,7 @@ PyErr_CheckSignals(void)
15181520
int i;
15191521
PyObject *f;
15201522

1521-
if (!is_tripped)
1523+
if (!_Py_atomic_load(&is_tripped))
15221524
return 0;
15231525

15241526
#ifdef WITH_THREAD
@@ -1540,24 +1542,24 @@ PyErr_CheckSignals(void)
15401542
* we receive a signal i after we zero is_tripped and before we
15411543
* check Handlers[i].tripped.
15421544
*/
1543-
is_tripped = 0;
1545+
_Py_atomic_store(&is_tripped, 0);
15441546

15451547
if (!(f = (PyObject *)PyEval_GetFrame()))
15461548
f = Py_None;
15471549

15481550
for (i = 1; i < NSIG; i++) {
1549-
if (Handlers[i].tripped) {
1551+
if (_Py_atomic_load_relaxed(&Handlers[i].tripped)) {
15501552
PyObject *result = NULL;
15511553
PyObject *arglist = Py_BuildValue("(iO)", i, f);
1552-
Handlers[i].tripped = 0;
1554+
_Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
15531555

15541556
if (arglist) {
15551557
result = PyEval_CallObject(Handlers[i].func,
15561558
arglist);
15571559
Py_DECREF(arglist);
15581560
}
15591561
if (!result) {
1560-
is_tripped = 1;
1562+
_Py_atomic_store(&is_tripped, 1);
15611563
return -1;
15621564
}
15631565

@@ -1596,12 +1598,12 @@ PyOS_FiniInterrupts(void)
15961598
int
15971599
PyOS_InterruptOccurred(void)
15981600
{
1599-
if (Handlers[SIGINT].tripped) {
1601+
if (_Py_atomic_load_relaxed(&Handlers[SIGINT].tripped)) {
16001602
#ifdef WITH_THREAD
16011603
if (PyThread_get_thread_ident() != main_thread)
16021604
return 0;
16031605
#endif
1604-
Handlers[SIGINT].tripped = 0;
1606+
_Py_atomic_store_relaxed(&Handlers[SIGINT].tripped, 0);
16051607
return 1;
16061608
}
16071609
return 0;
@@ -1611,11 +1613,11 @@ static void
16111613
_clear_pending_signals(void)
16121614
{
16131615
int i;
1614-
if (!is_tripped)
1616+
if (!_Py_atomic_load(&is_tripped))
16151617
return;
1616-
is_tripped = 0;
1618+
_Py_atomic_store(&is_tripped, 0);
16171619
for (i = 1; i < NSIG; ++i) {
1618-
Handlers[i].tripped = 0;
1620+
_Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
16191621
}
16201622
}
16211623

0 commit comments

Comments
 (0)