Skip to content

Commit 2b5cc5e

Browse files
authored
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
1 parent bd4e9e0 commit 2b5cc5e

File tree

1 file changed

+8
-4
lines changed

1 file changed

+8
-4
lines changed

Lib/multiprocessing/forkserver.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,22 +150,25 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None):
150150
util._close_stdin()
151151

152152
sig_r, sig_w = os.pipe()
153+
os.set_blocking(sig_r, False)
153154
os.set_blocking(sig_w, False)
154155

155156
def sigchld_handler(*_unused):
156-
try:
157-
os.write(sig_w, b'.')
158-
except BlockingIOError:
159-
pass
157+
# Dummy signal handler, doesn't do anything
158+
pass
160159

161160
# letting SIGINT through avoids KeyboardInterrupt tracebacks
161+
# unblocking SIGCHLD allows the wakeup fd to notify our event loop
162162
handlers = {
163163
signal.SIGCHLD: sigchld_handler,
164164
signal.SIGINT: signal.SIG_DFL,
165165
}
166166
old_handlers = {sig: signal.signal(sig, val)
167167
for (sig, val) in handlers.items()}
168168

169+
# calling os.write() in the Python signal handler is racy
170+
signal.set_wakeup_fd(sig_w)
171+
169172
# map child pids to client fds
170173
pid_to_fd = {}
171174

@@ -252,6 +255,7 @@ def sigchld_handler(*_unused):
252255

253256
def _serve_one(child_r, fds, unused_fds, handlers):
254257
# close unnecessary stuff and reset signal handlers
258+
signal.set_wakeup_fd(-1)
255259
for sig, val in handlers.items():
256260
signal.signal(sig, val)
257261
for fd in unused_fds:

0 commit comments

Comments
 (0)