Show More
@@ -10,88 +10,15 b' from __future__ import absolute_import' | |||||
10 | import contextlib |
|
10 | import contextlib | |
11 | import errno |
|
11 | import errno | |
12 | import os |
|
12 | import os | |
13 | import subprocess |
|
|||
14 | import time |
|
13 | import time | |
15 |
|
14 | |||
16 | from mercurial import ( |
|
15 | from mercurial import ( | |
17 | error, |
|
16 | error, | |
18 | lock as lockmod, |
|
17 | lock as lockmod, | |
19 | pycompat, |
|
|||
20 | util, |
|
18 | util, | |
21 | vfs as vfsmod, |
|
19 | vfs as vfsmod, | |
22 | ) |
|
20 | ) | |
23 |
|
21 | |||
24 | if pycompat.iswindows: |
|
|||
25 | # no fork on Windows, but we can create a detached process |
|
|||
26 | # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx |
|
|||
27 | # No stdlib constant exists for this value |
|
|||
28 | DETACHED_PROCESS = 0x00000008 |
|
|||
29 | _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP |
|
|||
30 |
|
||||
31 | def runbgcommand(script, env, shell=False, stdout=None, stderr=None): |
|
|||
32 | '''Spawn a command without waiting for it to finish.''' |
|
|||
33 | # we can't use close_fds *and* redirect stdin. I'm not sure that we |
|
|||
34 | # need to because the detached process has no console connection. |
|
|||
35 | subprocess.Popen( |
|
|||
36 | script, shell=shell, env=env, close_fds=True, |
|
|||
37 | creationflags=_creationflags, stdout=stdout, stderr=stderr) |
|
|||
38 | else: |
|
|||
39 | def runbgcommand(cmd, env, shell=False, stdout=None, stderr=None): |
|
|||
40 | '''Spawn a command without waiting for it to finish.''' |
|
|||
41 | # double-fork to completely detach from the parent process |
|
|||
42 | # based on http://code.activestate.com/recipes/278731 |
|
|||
43 | pid = os.fork() |
|
|||
44 | if pid: |
|
|||
45 | # Parent process |
|
|||
46 | (_pid, status) = os.waitpid(pid, 0) |
|
|||
47 | if os.WIFEXITED(status): |
|
|||
48 | returncode = os.WEXITSTATUS(status) |
|
|||
49 | else: |
|
|||
50 | returncode = -os.WTERMSIG(status) |
|
|||
51 | if returncode != 0: |
|
|||
52 | # The child process's return code is 0 on success, an errno |
|
|||
53 | # value on failure, or 255 if we don't have a valid errno |
|
|||
54 | # value. |
|
|||
55 | # |
|
|||
56 | # (It would be slightly nicer to return the full exception info |
|
|||
57 | # over a pipe as the subprocess module does. For now it |
|
|||
58 | # doesn't seem worth adding that complexity here, though.) |
|
|||
59 | if returncode == 255: |
|
|||
60 | returncode = errno.EINVAL |
|
|||
61 | raise OSError(returncode, 'error running %r: %s' % |
|
|||
62 | (cmd, os.strerror(returncode))) |
|
|||
63 | return |
|
|||
64 |
|
||||
65 | returncode = 255 |
|
|||
66 | try: |
|
|||
67 | # Start a new session |
|
|||
68 | os.setsid() |
|
|||
69 |
|
||||
70 | stdin = open(os.devnull, 'r') |
|
|||
71 | if stdout is None: |
|
|||
72 | stdout = open(os.devnull, 'w') |
|
|||
73 | if stderr is None: |
|
|||
74 | stderr = open(os.devnull, 'w') |
|
|||
75 |
|
||||
76 | # connect stdin to devnull to make sure the subprocess can't |
|
|||
77 | # muck up that stream for mercurial. |
|
|||
78 | subprocess.Popen( |
|
|||
79 | cmd, shell=shell, env=env, close_fds=True, |
|
|||
80 | stdin=stdin, stdout=stdout, stderr=stderr) |
|
|||
81 | returncode = 0 |
|
|||
82 | except EnvironmentError as ex: |
|
|||
83 | returncode = (ex.errno & 0xff) |
|
|||
84 | if returncode == 0: |
|
|||
85 | # This shouldn't happen, but just in case make sure the |
|
|||
86 | # return code is never 0 here. |
|
|||
87 | returncode = 255 |
|
|||
88 | except Exception: |
|
|||
89 | returncode = 255 |
|
|||
90 | finally: |
|
|||
91 | # mission accomplished, this child needs to exit and not |
|
|||
92 | # continue the hg process here. |
|
|||
93 | os._exit(returncode) |
|
|||
94 |
|
||||
95 | @contextlib.contextmanager |
|
22 | @contextlib.contextmanager | |
96 | def flock(lockpath, description, timeout=-1): |
|
23 | def flock(lockpath, description, timeout=-1): | |
97 | """A flock based lock object. Currently it is always non-blocking. |
|
24 | """A flock based lock object. Currently it is always non-blocking. |
@@ -50,7 +50,7 b' def backgroundrepack(repo, incremental=T' | |||||
50 | if packsonly: |
|
50 | if packsonly: | |
51 | cmd.append('--packsonly') |
|
51 | cmd.append('--packsonly') | |
52 | repo.ui.warn(msg) |
|
52 | repo.ui.warn(msg) | |
53 |
|
|
53 | procutil.runbgcommand(cmd, encoding.environ) | |
54 |
|
54 | |||
55 | def fullrepack(repo, options=None): |
|
55 | def fullrepack(repo, options=None): | |
56 | """If ``packsonly`` is True, stores creating only loose objects are skipped. |
|
56 | """If ``packsonly`` is True, stores creating only loose objects are skipped. |
@@ -25,7 +25,6 b' from . import (' | |||||
25 | constants, |
|
25 | constants, | |
26 | contentstore, |
|
26 | contentstore, | |
27 | datapack, |
|
27 | datapack, | |
28 | extutil, |
|
|||
29 | fileserverclient, |
|
28 | fileserverclient, | |
30 | historypack, |
|
29 | historypack, | |
31 | metadatastore, |
|
30 | metadatastore, | |
@@ -199,7 +198,7 b' def wraprepo(repo):' | |||||
199 | cmd.append('--repack') |
|
198 | cmd.append('--repack') | |
200 | if revs: |
|
199 | if revs: | |
201 | cmd += ['-r', revs] |
|
200 | cmd += ['-r', revs] | |
202 |
|
|
201 | procutil.runbgcommand(cmd, encoding.environ) | |
203 |
|
202 | |||
204 | def prefetch(self, revs, base=None, pats=None, opts=None): |
|
203 | def prefetch(self, revs, base=None, pats=None, opts=None): | |
205 | """Prefetches all the necessary file revisions for the given revs |
|
204 | """Prefetches all the necessary file revisions for the given revs |
@@ -10,6 +10,7 b'' | |||||
10 | from __future__ import absolute_import |
|
10 | from __future__ import absolute_import | |
11 |
|
11 | |||
12 | import contextlib |
|
12 | import contextlib | |
|
13 | import errno | |||
13 | import imp |
|
14 | import imp | |
14 | import io |
|
15 | import io | |
15 | import os |
|
16 | import os | |
@@ -467,3 +468,74 b' def uninterruptable(warn):' | |||||
467 | signal.signal(signal.SIGINT, oldsiginthandler[0]) |
|
468 | signal.signal(signal.SIGINT, oldsiginthandler[0]) | |
468 | if shouldbail: |
|
469 | if shouldbail: | |
469 | raise KeyboardInterrupt |
|
470 | raise KeyboardInterrupt | |
|
471 | ||||
|
472 | if pycompat.iswindows: | |||
|
473 | # no fork on Windows, but we can create a detached process | |||
|
474 | # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx | |||
|
475 | # No stdlib constant exists for this value | |||
|
476 | DETACHED_PROCESS = 0x00000008 | |||
|
477 | _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP | |||
|
478 | ||||
|
479 | def runbgcommand(script, env, shell=False, stdout=None, stderr=None): | |||
|
480 | '''Spawn a command without waiting for it to finish.''' | |||
|
481 | # we can't use close_fds *and* redirect stdin. I'm not sure that we | |||
|
482 | # need to because the detached process has no console connection. | |||
|
483 | subprocess.Popen( | |||
|
484 | script, shell=shell, env=env, close_fds=True, | |||
|
485 | creationflags=_creationflags, stdout=stdout, stderr=stderr) | |||
|
486 | else: | |||
|
487 | def runbgcommand(cmd, env, shell=False, stdout=None, stderr=None): | |||
|
488 | '''Spawn a command without waiting for it to finish.''' | |||
|
489 | # double-fork to completely detach from the parent process | |||
|
490 | # based on http://code.activestate.com/recipes/278731 | |||
|
491 | pid = os.fork() | |||
|
492 | if pid: | |||
|
493 | # Parent process | |||
|
494 | (_pid, status) = os.waitpid(pid, 0) | |||
|
495 | if os.WIFEXITED(status): | |||
|
496 | returncode = os.WEXITSTATUS(status) | |||
|
497 | else: | |||
|
498 | returncode = -os.WTERMSIG(status) | |||
|
499 | if returncode != 0: | |||
|
500 | # The child process's return code is 0 on success, an errno | |||
|
501 | # value on failure, or 255 if we don't have a valid errno | |||
|
502 | # value. | |||
|
503 | # | |||
|
504 | # (It would be slightly nicer to return the full exception info | |||
|
505 | # over a pipe as the subprocess module does. For now it | |||
|
506 | # doesn't seem worth adding that complexity here, though.) | |||
|
507 | if returncode == 255: | |||
|
508 | returncode = errno.EINVAL | |||
|
509 | raise OSError(returncode, 'error running %r: %s' % | |||
|
510 | (cmd, os.strerror(returncode))) | |||
|
511 | return | |||
|
512 | ||||
|
513 | returncode = 255 | |||
|
514 | try: | |||
|
515 | # Start a new session | |||
|
516 | os.setsid() | |||
|
517 | ||||
|
518 | stdin = open(os.devnull, 'r') | |||
|
519 | if stdout is None: | |||
|
520 | stdout = open(os.devnull, 'w') | |||
|
521 | if stderr is None: | |||
|
522 | stderr = open(os.devnull, 'w') | |||
|
523 | ||||
|
524 | # connect stdin to devnull to make sure the subprocess can't | |||
|
525 | # muck up that stream for mercurial. | |||
|
526 | subprocess.Popen( | |||
|
527 | cmd, shell=shell, env=env, close_fds=True, | |||
|
528 | stdin=stdin, stdout=stdout, stderr=stderr) | |||
|
529 | returncode = 0 | |||
|
530 | except EnvironmentError as ex: | |||
|
531 | returncode = (ex.errno & 0xff) | |||
|
532 | if returncode == 0: | |||
|
533 | # This shouldn't happen, but just in case make sure the | |||
|
534 | # return code is never 0 here. | |||
|
535 | returncode = 255 | |||
|
536 | except Exception: | |||
|
537 | returncode = 255 | |||
|
538 | finally: | |||
|
539 | # mission accomplished, this child needs to exit and not | |||
|
540 | # continue the hg process here. | |||
|
541 | os._exit(returncode) |
General Comments 0
You need to be logged in to leave comments.
Login now