From 2b5cc5ebaff41445200753f1a69fd4e6a9475a1e Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Tue, 13 Jun 2017 09:46:06 +0200 Subject: [PATCH] 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 --- Lib/multiprocessing/forkserver.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index ddbd0c25739..70105158e55 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: