Mercurial > hg
changeset 30414:5069a8a40b1b
worker: make waitforworkers reentrant
We are going to use it in the SIGCHLD handler. The handler will be executed
in the main thread with the non-blocking version of waitpid, while the
waitforworkers thread runs the blocking version. It's possible that one of
them collects a worker and makes the other error out (no child to wait).
This patch handles these errors: ECHILD is ignored. EINTR needs a retry.
The "pids" set is designed to be only modifiable by "waitforworkers". And we
only remove items after a successful waitpid. Since a child process can only
be "waitpid"-ed once. It's guaranteed that "pids.remove(p)" won't be called
with duplicated "p"s. And once a "p" is removed from "pids", that "p" does
not need to be killed or waited any more.
author | Jun Wu <quark@fb.com> |
---|---|
date | Tue, 15 Nov 2016 02:12:16 +0000 |
parents | 9c25a1a8c685 |
children | e8fb03cfbbde |
files | mercurial/worker.py |
diffstat | 1 files changed, 13 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/worker.py Tue Nov 15 02:10:40 2016 +0000 +++ b/mercurial/worker.py Tue Nov 15 02:12:16 2016 +0000 @@ -98,9 +98,20 @@ if err.errno != errno.ESRCH: raise def waitforworkers(blocking=True): - for pid in pids: - p, st = os.waitpid(pid, 0 if blocking else os.WNOHANG) + for pid in pids.copy(): + p = st = 0 + while True: + try: + p, st = os.waitpid(pid, (0 if blocking else os.WNOHANG)) + except OSError as e: + if e.errno == errno.EINTR: + continue + elif e.errno == errno.ECHILD: + break # ignore ECHILD + else: + raise if p: + pids.remove(p) st = _exitstatus(st) if st and not problem[0]: problem[0] = st