From: Antoine Pitrou Date: Tue, 13 Jun 2017 07:46:06 +0000 (+0200) Subject: bpo-30643: Fix race condition in signal wakeup in forkserver (followup to PR #1989... X-Git-Tag: v3.7.0a1~612 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2b5cc5ebaff41445200753f1a69fd4e6a9475a1e;p=thirdparty%2FPython%2Fcpython.git 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 --- diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index ddbd0c257399..70105158e559 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: