Mercurial > hg-stable
diff mercurial/util.py @ 10344:9501cde4c034
util: make spawndetached() handle subprocess early terminations
The file-based synchronization introduced by e22695b4472f hangs when the child
process fails before terminating the handshake, which the previous pipe-based
version handled correctly. To fix this, the parent polling loop was fixed to
detect premature terminations of the child process.
author | Patrick Mezard <pmezard@gmail.com> |
---|---|
date | Sat, 06 Feb 2010 16:50:00 +0100 |
parents | 08a0f04b56bd |
children | 600142e7a028 |
line wrap: on
line diff
--- a/mercurial/util.py Sat Feb 06 17:31:54 2010 +0100 +++ b/mercurial/util.py Sat Feb 06 16:50:00 2010 +0100 @@ -16,7 +16,7 @@ from i18n import _ import error, osutil, encoding import cStringIO, errno, re, shutil, sys, tempfile, traceback -import os, stat, time, calendar, textwrap +import os, stat, time, calendar, textwrap, signal import imp # Python compatibility @@ -1308,3 +1308,37 @@ if main_is_frozen(): return [sys.executable] return gethgcmd() + +def rundetached(args, condfn): + """Execute the argument list in a detached process. + + condfn is a callable which is called repeatedly and should return + True once the child process is known to have started successfully. + At this point, the child process PID is returned. If the child + process fails to start or finishes before condfn() evaluates to + True, return -1. + """ + # Windows case is easier because the child process is either + # successfully starting and validating the condition or exiting + # on failure. We just poll on its PID. On Unix, if the child + # process fails to start, it will be left in a zombie state until + # the parent wait on it, which we cannot do since we expect a long + # running process on success. Instead we listen for SIGCHLD telling + # us our child process terminated. + terminated = set() + def handler(signum, frame): + terminated.add(os.wait()) + prevhandler = None + if hasattr(signal, 'SIGCHLD'): + prevhandler = signal.signal(signal.SIGCHLD, handler) + try: + pid = spawndetached(args) + while not condfn(): + if ((pid in terminated or not testpid(pid)) + and not condfn()): + return -1 + time.sleep(0.1) + return pid + finally: + if prevhandler is not None: + signal.signal(signal.SIGCHLD, prevhandler)