summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Pitrou <pitrou@free.fr>2017-06-13 07:46:06 (GMT)
committerGitHub <noreply@github.com>2017-06-13 07:46:06 (GMT)
commit2b5cc5ebaff41445200753f1a69fd4e6a9475a1e (patch)
treee5d70f0ba342a9f6cfe79329abfbb705e17a89be
parentbd4e9e0ca96dabf33605d9b1fd1e0562ece8ae18 (diff)
downloadcpython-2b5cc5ebaff41445200753f1a69fd4e6a9475a1e.zip
cpython-2b5cc5ebaff41445200753f1a69fd4e6a9475a1e.tar.gz
cpython-2b5cc5ebaff41445200753f1a69fd4e6a9475a1e.tar.bz2
bpo-30643: Fix race condition in signal wakeup in forkserver (followup to PR #1989) (#2139)
* Fix race condition in signal wakeup in forkserver (followup to PR #1989) There's an admittedly well-known race condition where ECHILD can arrive just before the C function epoll_wait() and the latter wouldn't therefore return EINTR. The solution is to use set_wakeup_fd(), which was designed to avoid such race conditions. * Reset wakeup fd in child
-rw-r--r--Lib/multiprocessing/forkserver.py12
1 files changed, 8 insertions, 4 deletions
diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py
index ddbd0c2..7010515 100644
--- a/Lib/multiprocessing/forkserver.py
+++ b/Lib/multiprocessing/forkserver.py
@@ -150,15 +150,15 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None):
util._close_stdin()
sig_r, sig_w = os.pipe()
+ os.set_blocking(sig_r, False)
os.set_blocking(sig_w, False)
def sigchld_handler(*_unused):
- try:
- os.write(sig_w, b'.')
- except BlockingIOError:
- pass
+ # Dummy signal handler, doesn't do anything
+ pass
# letting SIGINT through avoids KeyboardInterrupt tracebacks
+ # unblocking SIGCHLD allows the wakeup fd to notify our event loop
handlers = {
signal.SIGCHLD: sigchld_handler,
signal.SIGINT: signal.SIG_DFL,
@@ -166,6 +166,9 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None):
old_handlers = {sig: signal.signal(sig, val)
for (sig, val) in handlers.items()}
+ # calling os.write() in the Python signal handler is racy
+ signal.set_wakeup_fd(sig_w)
+
# map child pids to client fds
pid_to_fd = {}
@@ -252,6 +255,7 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None):
def _serve_one(child_r, fds, unused_fds, handlers):
# close unnecessary stuff and reset signal handlers
+ signal.set_wakeup_fd(-1)
for sig, val in handlers.items():
signal.signal(sig, val)
for fd in unused_fds: