Show More
@@ -548,12 +548,18 b' if pycompat.iswindows:' | |||||
548 | _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP |
|
548 | _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP | |
549 |
|
549 | |||
550 | def runbgcommand( |
|
550 | def runbgcommand( | |
551 | script, env, shell=False, stdout=None, stderr=None, ensurestart=True |
|
551 | script, | |
|
552 | env, | |||
|
553 | shell=False, | |||
|
554 | stdout=None, | |||
|
555 | stderr=None, | |||
|
556 | ensurestart=True, | |||
|
557 | record_wait=None, | |||
552 | ): |
|
558 | ): | |
553 | '''Spawn a command without waiting for it to finish.''' |
|
559 | '''Spawn a command without waiting for it to finish.''' | |
554 | # we can't use close_fds *and* redirect stdin. I'm not sure that we |
|
560 | # we can't use close_fds *and* redirect stdin. I'm not sure that we | |
555 | # need to because the detached process has no console connection. |
|
561 | # need to because the detached process has no console connection. | |
556 | subprocess.Popen( |
|
562 | p = subprocess.Popen( | |
557 | tonativestr(script), |
|
563 | tonativestr(script), | |
558 | shell=shell, |
|
564 | shell=shell, | |
559 | env=tonativeenv(env), |
|
565 | env=tonativeenv(env), | |
@@ -562,46 +568,64 b' if pycompat.iswindows:' | |||||
562 | stdout=stdout, |
|
568 | stdout=stdout, | |
563 | stderr=stderr, |
|
569 | stderr=stderr, | |
564 | ) |
|
570 | ) | |
|
571 | if record_wait is not None: | |||
|
572 | record_wait(p.wait) | |||
565 |
|
573 | |||
566 |
|
574 | |||
567 | else: |
|
575 | else: | |
568 |
|
576 | |||
569 | def runbgcommand( |
|
577 | def runbgcommand( | |
570 | cmd, env, shell=False, stdout=None, stderr=None, ensurestart=True |
|
578 | cmd, | |
|
579 | env, | |||
|
580 | shell=False, | |||
|
581 | stdout=None, | |||
|
582 | stderr=None, | |||
|
583 | ensurestart=True, | |||
|
584 | record_wait=None, | |||
571 | ): |
|
585 | ): | |
572 |
'''Spawn a command without waiting for it to finish. |
|
586 | '''Spawn a command without waiting for it to finish. | |
|
587 | ||||
|
588 | ||||
|
589 | When `record_wait` is not None, the spawned process will not be fully | |||
|
590 | detached and the `record_wait` argument will be called with a the | |||
|
591 | `Subprocess.wait` function for the spawned process. This is mostly | |||
|
592 | useful for developers that need to make sure the spawned process | |||
|
593 | finished before a certain point. (eg: writing test)''' | |||
573 | # double-fork to completely detach from the parent process |
|
594 | # double-fork to completely detach from the parent process | |
574 | # based on http://code.activestate.com/recipes/278731 |
|
595 | # based on http://code.activestate.com/recipes/278731 | |
575 | pid = os.fork() |
|
596 | if record_wait is None: | |
576 | if pid: |
|
597 | pid = os.fork() | |
577 |
if |
|
598 | if pid: | |
|
599 | if not ensurestart: | |||
|
600 | return | |||
|
601 | # Parent process | |||
|
602 | (_pid, status) = os.waitpid(pid, 0) | |||
|
603 | if os.WIFEXITED(status): | |||
|
604 | returncode = os.WEXITSTATUS(status) | |||
|
605 | else: | |||
|
606 | returncode = -(os.WTERMSIG(status)) | |||
|
607 | if returncode != 0: | |||
|
608 | # The child process's return code is 0 on success, an errno | |||
|
609 | # value on failure, or 255 if we don't have a valid errno | |||
|
610 | # value. | |||
|
611 | # | |||
|
612 | # (It would be slightly nicer to return the full exception info | |||
|
613 | # over a pipe as the subprocess module does. For now it | |||
|
614 | # doesn't seem worth adding that complexity here, though.) | |||
|
615 | if returncode == 255: | |||
|
616 | returncode = errno.EINVAL | |||
|
617 | raise OSError( | |||
|
618 | returncode, | |||
|
619 | b'error running %r: %s' | |||
|
620 | % (cmd, os.strerror(returncode)), | |||
|
621 | ) | |||
578 | return |
|
622 | return | |
579 | # Parent process |
|
|||
580 | (_pid, status) = os.waitpid(pid, 0) |
|
|||
581 | if os.WIFEXITED(status): |
|
|||
582 | returncode = os.WEXITSTATUS(status) |
|
|||
583 | else: |
|
|||
584 | returncode = -(os.WTERMSIG(status)) |
|
|||
585 | if returncode != 0: |
|
|||
586 | # The child process's return code is 0 on success, an errno |
|
|||
587 | # value on failure, or 255 if we don't have a valid errno |
|
|||
588 | # value. |
|
|||
589 | # |
|
|||
590 | # (It would be slightly nicer to return the full exception info |
|
|||
591 | # over a pipe as the subprocess module does. For now it |
|
|||
592 | # doesn't seem worth adding that complexity here, though.) |
|
|||
593 | if returncode == 255: |
|
|||
594 | returncode = errno.EINVAL |
|
|||
595 | raise OSError( |
|
|||
596 | returncode, |
|
|||
597 | b'error running %r: %s' % (cmd, os.strerror(returncode)), |
|
|||
598 | ) |
|
|||
599 | return |
|
|||
600 |
|
623 | |||
601 | returncode = 255 |
|
624 | returncode = 255 | |
602 | try: |
|
625 | try: | |
603 | # Start a new session |
|
626 | if record_wait is None: | |
604 | os.setsid() |
|
627 | # Start a new session | |
|
628 | os.setsid() | |||
605 |
|
629 | |||
606 | stdin = open(os.devnull, b'r') |
|
630 | stdin = open(os.devnull, b'r') | |
607 | if stdout is None: |
|
631 | if stdout is None: | |
@@ -611,7 +635,7 b' else:' | |||||
611 |
|
635 | |||
612 | # connect stdin to devnull to make sure the subprocess can't |
|
636 | # connect stdin to devnull to make sure the subprocess can't | |
613 | # muck up that stream for mercurial. |
|
637 | # muck up that stream for mercurial. | |
614 | subprocess.Popen( |
|
638 | p = subprocess.Popen( | |
615 | cmd, |
|
639 | cmd, | |
616 | shell=shell, |
|
640 | shell=shell, | |
617 | env=env, |
|
641 | env=env, | |
@@ -620,6 +644,8 b' else:' | |||||
620 | stdout=stdout, |
|
644 | stdout=stdout, | |
621 | stderr=stderr, |
|
645 | stderr=stderr, | |
622 | ) |
|
646 | ) | |
|
647 | if record_wait is not None: | |||
|
648 | record_wait(p.wait) | |||
623 | returncode = 0 |
|
649 | returncode = 0 | |
624 | except EnvironmentError as ex: |
|
650 | except EnvironmentError as ex: | |
625 | returncode = ex.errno & 0xFF |
|
651 | returncode = ex.errno & 0xFF | |
@@ -632,4 +658,5 b' else:' | |||||
632 | finally: |
|
658 | finally: | |
633 | # mission accomplished, this child needs to exit and not |
|
659 | # mission accomplished, this child needs to exit and not | |
634 | # continue the hg process here. |
|
660 | # continue the hg process here. | |
635 | os._exit(returncode) |
|
661 | if record_wait is None: | |
|
662 | os._exit(returncode) |
General Comments 0
You need to be logged in to leave comments.
Login now