Clear pending signals from child process after fork() In existing Python, pending signals are inherited by children of a process, as demonstrated by pressing C-c in the middle of the following program: """ import os, sys, time, threading def do_fork(): while True: if not os.fork(): print 'hello from child' sys.exit(0) time.sleep(0.5) t = threading.Thread(target=do_fork) t.start() t.join() """ diff --git a/Include/pythonrun.h b/Include/pythonrun.h index f8ed718..3b8ddf3 100644 --- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -145,6 +145,7 @@ PyAPI_FUNC(void) PyByteArray_Fini(void); /* Stuff with no proper home (yet) */ PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *); +PyAPI_FUNC(void) PyOS_ClearPendingSignals(void); PyAPI_DATA(int) (*PyOS_InputHook)(void); PyAPI_DATA(char) *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, char *); PyAPI_DATA(PyThreadState*) _PyOS_ReadlineTState; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 8a23e65..6554326 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3614,6 +3614,13 @@ posix_fork1(PyObject *self, PyObject *noargs) _PyImport_AcquireLock(); pid = fork1(); if (pid == 0) { + /* + If a signal is received by the child after the fork but + before clearing pending signals, it will be lost. Since + there is no cross-platform way of disabling signals, however, + there's not much we can do except hope. + */ + PyOS_ClearPendingSignals(); /* child: this clobbers and resets the import lock. */ PyOS_AfterFork(); } else { @@ -3647,6 +3654,7 @@ posix_fork(PyObject *self, PyObject *noargs) _PyImport_AcquireLock(); pid = fork(); if (pid == 0) { + PyOS_ClearPendingSignals(); /* child: this clobbers and resets the import lock. */ PyOS_AfterFork(); } else { diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 9dc781d..a822d47 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -927,6 +927,18 @@ PyOS_FiniInterrupts(void) finisignal(); } +void +PyOS_ClearPendingSignals(void) +{ + int i; + if (!is_tripped) + return; + is_tripped = 0; + for (i = 1; i < NSIG; i++) { + Handlers[i].tripped = 0; + } +} + int PyOS_InterruptOccurred(void) {