bpo-33613, test_semaphore_tracker_sigint: fix race condition (#7850) · python/cpython@ec74d18
@@ -23,6 +23,9 @@
23232424__all__ = ['ensure_running', 'register', 'unregister']
252526+_HAVE_SIGMASK = hasattr(signal, 'pthread_sigmask')
27+_IGNORED_SIGNALS = (signal.SIGINT, signal.SIGTERM)
28+26292730class SemaphoreTracker(object):
2831@@ -43,10 +46,16 @@ def ensure_running(self):
4346with self._lock:
4447if self._pid is not None:
4548# semaphore tracker was launched before, is it still running?
46-pid, status = os.waitpid(self._pid, os.WNOHANG)
47-if not pid:
48-# => still alive
49-return
49+try:
50+pid, _ = os.waitpid(self._pid, os.WNOHANG)
51+except ChildProcessError:
52+# The process terminated
53+pass
54+else:
55+if not pid:
56+# => still alive
57+return
58+5059# => dead, launch it again
5160os.close(self._fd)
5261self._fd = None
@@ -68,7 +77,19 @@ def ensure_running(self):
6877exe = spawn.get_executable()
6978args = [exe] + util._args_from_interpreter_flags()
7079args += ['-c', cmd % r]
71-pid = util.spawnv_passfds(exe, args, fds_to_pass)
80+# bpo-33613: Register a signal mask that will block the signals.
81+# This signal mask will be inherited by the child that is going
82+# to be spawned and will protect the child from a race condition
83+# that can make the child die before it registers signal handlers
84+# for SIGINT and SIGTERM. The mask is unregistered after spawning
85+# the child.
86+try:
87+if _HAVE_SIGMASK:
88+signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
89+pid = util.spawnv_passfds(exe, args, fds_to_pass)
90+finally:
91+if _HAVE_SIGMASK:
92+signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS)
7293except:
7394os.close(w)
7495raise
@@ -104,12 +125,13 @@ def _send(self, cmd, name):
104125unregister = _semaphore_tracker.unregister
105126getfd = _semaphore_tracker.getfd
106127107-108128def main(fd):
109129'''Run semaphore tracker.'''
110130# protect the process from ^C and "killall python" etc
111131signal.signal(signal.SIGINT, signal.SIG_IGN)
112132signal.signal(signal.SIGTERM, signal.SIG_IGN)
133+if _HAVE_SIGMASK:
134+signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS)
113135114136for f in (sys.stdin, sys.stdout):
115137try: