Patchwork D7584: procutil: add a option to not fully detach background process

login
register
mail settings
Submitter phabricator
Date Dec. 9, 2019, 3:51 p.m.
Message ID <fdb1e2560df9f6b195ee504b726a803d@localhost.localdomain>
Download mbox | patch
Permalink /patch/43656/
State Not Applicable
Headers show

Comments

phabricator - Dec. 9, 2019, 3:51 p.m.
marmoute updated this revision to Diff 18556.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7584?vs=18549&id=18556

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7584/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7584

AFFECTED FILES
  mercurial/utils/procutil.py

CHANGE DETAILS




To: marmoute, #hg-reviewers, durin42
Cc: durin42, mercurial-devel

Patch

diff --git a/mercurial/utils/procutil.py b/mercurial/utils/procutil.py
--- a/mercurial/utils/procutil.py
+++ b/mercurial/utils/procutil.py
@@ -548,12 +548,18 @@ 
     _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP
 
     def runbgcommand(
-        script, env, shell=False, stdout=None, stderr=None, ensurestart=True
+        script,
+        env,
+        shell=False,
+        stdout=None,
+        stderr=None,
+        ensurestart=True,
+        record_wait=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(
+        p = subprocess.Popen(
             tonativestr(script),
             shell=shell,
             env=tonativeenv(env),
@@ -562,46 +568,64 @@ 
             stdout=stdout,
             stderr=stderr,
         )
+        if record_wait is not None:
+            record_wait(p.wait)
 
 
 else:
 
     def runbgcommand(
-        cmd, env, shell=False, stdout=None, stderr=None, ensurestart=True
+        cmd,
+        env,
+        shell=False,
+        stdout=None,
+        stderr=None,
+        ensurestart=True,
+        record_wait=None,
     ):
-        '''Spawn a command without waiting for it to finish.'''
+        '''Spawn a command without waiting for it to finish.
+
+
+        When `record_wait` is not None, the spawned process will not be fully
+        detached and the `record_wait` argument will be called with a the
+        `Subprocess.wait` function for the spawned process.  This is mostly
+        useful for developers that need to make sure the spawned process
+        finished before a certain point. (eg: writing test)'''
         # double-fork to completely detach from the parent process
         # based on http://code.activestate.com/recipes/278731
-        pid = os.fork()
-        if pid:
-            if not ensurestart:
+        if record_wait is None:
+            pid = os.fork()
+            if pid:
+                if not ensurestart:
+                    return
+                # 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,
+                        b'error running %r: %s'
+                        % (cmd, os.strerror(returncode)),
+                    )
                 return
-            # 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,
-                    b'error running %r: %s' % (cmd, os.strerror(returncode)),
-                )
-            return
 
         returncode = 255
         try:
-            # Start a new session
-            os.setsid()
+            if record_wait is None:
+                # Start a new session
+                os.setsid()
 
             stdin = open(os.devnull, b'r')
             if stdout is None:
@@ -611,7 +635,7 @@ 
 
             # connect stdin to devnull to make sure the subprocess can't
             # muck up that stream for mercurial.
-            subprocess.Popen(
+            p = subprocess.Popen(
                 cmd,
                 shell=shell,
                 env=env,
@@ -620,6 +644,8 @@ 
                 stdout=stdout,
                 stderr=stderr,
             )
+            if record_wait is not None:
+                record_wait(p.wait)
             returncode = 0
         except EnvironmentError as ex:
             returncode = ex.errno & 0xFF
@@ -632,4 +658,5 @@ 
         finally:
             # mission accomplished, this child needs to exit and not
             # continue the hg process here.
-            os._exit(returncode)
+            if record_wait is None:
+                os._exit(returncode)