diff --git a/hgext/remotefilelog/extutil.py b/hgext/remotefilelog/extutil.py --- a/hgext/remotefilelog/extutil.py +++ b/hgext/remotefilelog/extutil.py @@ -10,88 +10,15 @@ import contextlib import errno import os -import subprocess import time from mercurial import ( error, lock as lockmod, - pycompat, util, vfs as vfsmod, ) -if pycompat.iswindows: - # no fork on Windows, but we can create a detached process - # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx - # No stdlib constant exists for this value - DETACHED_PROCESS = 0x00000008 - _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP - - def runbgcommand(script, env, shell=False, stdout=None, stderr=None): - '''Spawn a command without waiting for it to finish.''' - # we can't use close_fds *and* redirect stdin. I'm not sure that we - # need to because the detached process has no console connection. - subprocess.Popen( - script, shell=shell, env=env, close_fds=True, - creationflags=_creationflags, stdout=stdout, stderr=stderr) -else: - def runbgcommand(cmd, env, shell=False, stdout=None, stderr=None): - '''Spawn a command without waiting for it to finish.''' - # double-fork to completely detach from the parent process - # based on http://code.activestate.com/recipes/278731 - pid = os.fork() - if pid: - # Parent process - (_pid, status) = os.waitpid(pid, 0) - if os.WIFEXITED(status): - returncode = os.WEXITSTATUS(status) - else: - returncode = -os.WTERMSIG(status) - if returncode != 0: - # The child process's return code is 0 on success, an errno - # value on failure, or 255 if we don't have a valid errno - # value. - # - # (It would be slightly nicer to return the full exception info - # over a pipe as the subprocess module does. For now it - # doesn't seem worth adding that complexity here, though.) - if returncode == 255: - returncode = errno.EINVAL - raise OSError(returncode, 'error running %r: %s' % - (cmd, os.strerror(returncode))) - return - - returncode = 255 - try: - # Start a new session - os.setsid() - - stdin = open(os.devnull, 'r') - if stdout is None: - stdout = open(os.devnull, 'w') - if stderr is None: - stderr = open(os.devnull, 'w') - - # connect stdin to devnull to make sure the subprocess can't - # muck up that stream for mercurial. - subprocess.Popen( - cmd, shell=shell, env=env, close_fds=True, - stdin=stdin, stdout=stdout, stderr=stderr) - returncode = 0 - except EnvironmentError as ex: - returncode = (ex.errno & 0xff) - if returncode == 0: - # This shouldn't happen, but just in case make sure the - # return code is never 0 here. - returncode = 255 - except Exception: - returncode = 255 - finally: - # mission accomplished, this child needs to exit and not - # continue the hg process here. - os._exit(returncode) - @contextlib.contextmanager def flock(lockpath, description, timeout=-1): """A flock based lock object. Currently it is always non-blocking. diff --git a/hgext/remotefilelog/repack.py b/hgext/remotefilelog/repack.py --- a/hgext/remotefilelog/repack.py +++ b/hgext/remotefilelog/repack.py @@ -50,7 +50,7 @@ if packsonly: cmd.append('--packsonly') repo.ui.warn(msg) - extutil.runbgcommand(cmd, encoding.environ) + procutil.runbgcommand(cmd, encoding.environ) def fullrepack(repo, options=None): """If ``packsonly`` is True, stores creating only loose objects are skipped. diff --git a/hgext/remotefilelog/shallowrepo.py b/hgext/remotefilelog/shallowrepo.py --- a/hgext/remotefilelog/shallowrepo.py +++ b/hgext/remotefilelog/shallowrepo.py @@ -25,7 +25,6 @@ constants, contentstore, datapack, - extutil, fileserverclient, historypack, metadatastore, @@ -199,7 +198,7 @@ cmd.append('--repack') if revs: cmd += ['-r', revs] - extutil.runbgcommand(cmd, encoding.environ) + procutil.runbgcommand(cmd, encoding.environ) def prefetch(self, revs, base=None, pats=None, opts=None): """Prefetches all the necessary file revisions for the given revs diff --git a/mercurial/utils/procutil.py b/mercurial/utils/procutil.py --- a/mercurial/utils/procutil.py +++ b/mercurial/utils/procutil.py @@ -10,6 +10,7 @@ from __future__ import absolute_import import contextlib +import errno import imp import io import os @@ -467,3 +468,74 @@ signal.signal(signal.SIGINT, oldsiginthandler[0]) if shouldbail: raise KeyboardInterrupt + +if pycompat.iswindows: + # no fork on Windows, but we can create a detached process + # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx + # No stdlib constant exists for this value + DETACHED_PROCESS = 0x00000008 + _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP + + def runbgcommand(script, env, shell=False, stdout=None, stderr=None): + '''Spawn a command without waiting for it to finish.''' + # we can't use close_fds *and* redirect stdin. I'm not sure that we + # need to because the detached process has no console connection. + subprocess.Popen( + script, shell=shell, env=env, close_fds=True, + creationflags=_creationflags, stdout=stdout, stderr=stderr) +else: + def runbgcommand(cmd, env, shell=False, stdout=None, stderr=None): + '''Spawn a command without waiting for it to finish.''' + # double-fork to completely detach from the parent process + # based on http://code.activestate.com/recipes/278731 + pid = os.fork() + if pid: + # Parent process + (_pid, status) = os.waitpid(pid, 0) + if os.WIFEXITED(status): + returncode = os.WEXITSTATUS(status) + else: + returncode = -os.WTERMSIG(status) + if returncode != 0: + # The child process's return code is 0 on success, an errno + # value on failure, or 255 if we don't have a valid errno + # value. + # + # (It would be slightly nicer to return the full exception info + # over a pipe as the subprocess module does. For now it + # doesn't seem worth adding that complexity here, though.) + if returncode == 255: + returncode = errno.EINVAL + raise OSError(returncode, 'error running %r: %s' % + (cmd, os.strerror(returncode))) + return + + returncode = 255 + try: + # Start a new session + os.setsid() + + stdin = open(os.devnull, 'r') + if stdout is None: + stdout = open(os.devnull, 'w') + if stderr is None: + stderr = open(os.devnull, 'w') + + # connect stdin to devnull to make sure the subprocess can't + # muck up that stream for mercurial. + subprocess.Popen( + cmd, shell=shell, env=env, close_fds=True, + stdin=stdin, stdout=stdout, stderr=stderr) + returncode = 0 + except EnvironmentError as ex: + returncode = (ex.errno & 0xff) + if returncode == 0: + # This shouldn't happen, but just in case make sure the + # return code is never 0 here. + returncode = 255 + except Exception: + returncode = 255 + finally: + # mission accomplished, this child needs to exit and not + # continue the hg process here. + os._exit(returncode)