summaryrefslogtreecommitdiffstats
path: root/Lib/multiprocessing
diff options
context:
space:
mode:
authorPablo Galindo <Pablogsal@gmail.com>2018-09-04 08:53:54 (GMT)
committerAntoine Pitrou <pitrou@free.fr>2018-09-04 08:53:54 (GMT)
commitec74d187f50a8a48f94eb37023300583fbd644cc (patch)
tree718f782542818ce6062448a35f99388511fb1e56 /Lib/multiprocessing
parente9ba3705de656215d52b8f8f4a2e7ad60190e944 (diff)
downloadcpython-ec74d187f50a8a48f94eb37023300583fbd644cc.zip
cpython-ec74d187f50a8a48f94eb37023300583fbd644cc.tar.gz
cpython-ec74d187f50a8a48f94eb37023300583fbd644cc.tar.bz2
bpo-33613, test_semaphore_tracker_sigint: fix race condition (#7850)
Fail `test_semaphore_tracker_sigint` if no warnings are expected and one is received. Fix race condition when the child receives SIGINT before it can register signal handlers for it. The race condition occurs when the parent calls `_semaphore_tracker.ensure_running()` (which in turn spawns the semaphore_tracker using `_posixsubprocess.fork_exec`), the child registers the signal handlers and the parent tries to kill the child. What seem to happen is that in some slow systems, the parent sends the signal to kill the child before the child protects against the signal.
Diffstat (limited to 'Lib/multiprocessing')
-rw-r--r--Lib/multiprocessing/semaphore_tracker.py34
1 files changed, 28 insertions, 6 deletions
diff --git a/Lib/multiprocessing/semaphore_tracker.py b/Lib/multiprocessing/semaphore_tracker.py
index 3e31bf8..82833bc 100644
--- a/Lib/multiprocessing/semaphore_tracker.py
+++ b/Lib/multiprocessing/semaphore_tracker.py
@@ -23,6 +23,9 @@ from . import util
__all__ = ['ensure_running', 'register', 'unregister']
+_HAVE_SIGMASK = hasattr(signal, 'pthread_sigmask')
+_IGNORED_SIGNALS = (signal.SIGINT, signal.SIGTERM)
+
class SemaphoreTracker(object):
@@ -43,10 +46,16 @@ class SemaphoreTracker(object):
with self._lock:
if self._pid is not None:
# semaphore tracker was launched before, is it still running?
- pid, status = os.waitpid(self._pid, os.WNOHANG)
- if not pid:
- # => still alive
- return
+ try:
+ pid, _ = os.waitpid(self._pid, os.WNOHANG)
+ except ChildProcessError:
+ # The process terminated
+ pass
+ else:
+ if not pid:
+ # => still alive
+ return
+
# => dead, launch it again
os.close(self._fd)
self._fd = None
@@ -68,7 +77,19 @@ class SemaphoreTracker(object):
exe = spawn.get_executable()
args = [exe] + util._args_from_interpreter_flags()
args += ['-c', cmd % r]
- pid = util.spawnv_passfds(exe, args, fds_to_pass)
+ # bpo-33613: Register a signal mask that will block the signals.
+ # This signal mask will be inherited by the child that is going
+ # to be spawned and will protect the child from a race condition
+ # that can make the child die before it registers signal handlers
+ # for SIGINT and SIGTERM. The mask is unregistered after spawning
+ # the child.
+ try:
+ if _HAVE_SIGMASK:
+ signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
+ pid = util.spawnv_passfds(exe, args, fds_to_pass)
+ finally:
+ if _HAVE_SIGMASK:
+ signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS)
except:
os.close(w)
raise
@@ -104,12 +125,13 @@ register = _semaphore_tracker.register
unregister = _semaphore_tracker.unregister
getfd = _semaphore_tracker.getfd
-
def main(fd):
'''Run semaphore tracker.'''
# protect the process from ^C and "killall python" etc
signal.signal(signal.SIGINT, signal.SIG_IGN)
signal.signal(signal.SIGTERM, signal.SIG_IGN)
+ if _HAVE_SIGMASK:
+ signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS)
for f in (sys.stdin, sys.stdout):
try: