##// END OF EJS Templates
procutil: avoid using os.fork() to implement runbgcommand...
Valentin Gatien-Baron -
r47651:8759e22f default
parent child Browse files
Show More
@@ -701,7 +701,88 b' if pycompat.iswindows:'
701
701
702 else:
702 else:
703
703
704 def runbgcommand(
704 def runbgcommandpy3(
705 cmd,
706 env,
707 shell=False,
708 stdout=None,
709 stderr=None,
710 ensurestart=True,
711 record_wait=None,
712 stdin_bytes=None,
713 ):
714 """Spawn a command without waiting for it to finish.
715
716
717 When `record_wait` is not None, the spawned process will not be fully
718 detached and the `record_wait` argument will be called with a the
719 `Subprocess.wait` function for the spawned process. This is mostly
720 useful for developers that need to make sure the spawned process
721 finished before a certain point. (eg: writing test)"""
722 if pycompat.isdarwin:
723 # avoid crash in CoreFoundation in case another thread
724 # calls gui() while we're calling fork().
725 gui()
726
727 if shell:
728 script = cmd
729 else:
730 if isinstance(cmd, bytes):
731 cmd = [cmd]
732 script = b' '.join(shellquote(x) for x in cmd)
733 if record_wait is None:
734 # double-fork to completely detach from the parent process
735 script = b'( %s ) &' % script
736 start_new_session = True
737 else:
738 start_new_session = False
739 ensurestart = True
740
741 try:
742 if stdin_bytes is None:
743 stdin = subprocess.DEVNULL
744 else:
745 stdin = pycompat.unnamedtempfile()
746 stdin.write(stdin_bytes)
747 stdin.flush()
748 stdin.seek(0)
749 if stdout is None:
750 stdout = subprocess.DEVNULL
751 if stderr is None:
752 stderr = subprocess.DEVNULL
753
754 p = subprocess.Popen(
755 script,
756 shell=True,
757 env=env,
758 close_fds=True,
759 stdin=stdin,
760 stdout=stdout,
761 stderr=stderr,
762 start_new_session=start_new_session,
763 )
764 except Exception:
765 if record_wait is not None:
766 record_wait(255)
767 raise
768 finally:
769 if stdin_bytes is not None:
770 stdin.close()
771 if not ensurestart:
772 # Even though we're not waiting on the child process,
773 # we still must call waitpid() on it at some point so
774 # it's not a zombie/defunct. This is especially relevant for
775 # chg since the parent process won't die anytime soon.
776 # We use a thread to make the overhead tiny.
777 t = threading.Thread(target=lambda: p.wait)
778 t.daemon = True
779 t.start()
780 else:
781 returncode = p.wait
782 if record_wait is not None:
783 record_wait(returncode)
784
785 def runbgcommandpy2(
705 cmd,
786 cmd,
706 env,
787 env,
707 shell=False,
788 shell=False,
@@ -811,3 +892,14 b' else:'
811 stdin.close()
892 stdin.close()
812 if record_wait is None:
893 if record_wait is None:
813 os._exit(returncode)
894 os._exit(returncode)
895
896 if pycompat.ispy3:
897 # This branch is more robust, because it avoids running python
898 # code (hence gc finalizers, like sshpeer.__del__, which
899 # blocks). But we can't easily do the equivalent in py2,
900 # because of the lack of start_new_session=True flag. Given
901 # that the py2 branch should die soon, the short-lived
902 # duplication seems acceptable.
903 runbgcommand = runbgcommandpy3
904 else:
905 runbgcommand = runbgcommandpy2
General Comments 0
You need to be logged in to leave comments. Login now