Show More
@@ -10,6 +10,7 b' from __future__ import absolute_import' | |||
|
10 | 10 | import contextlib |
|
11 | 11 | import errno |
|
12 | 12 | import os |
|
13 | import signal | |
|
13 | 14 | import socket |
|
14 | 15 | import time |
|
15 | 16 | import warnings |
@@ -39,6 +40,64 b' def _getlockprefix():' | |||
|
39 | 40 | raise |
|
40 | 41 | return result |
|
41 | 42 | |
|
43 | @contextlib.contextmanager | |
|
44 | def _delayedinterrupt(): | |
|
45 | """Block signal interrupt while doing something critical | |
|
46 | ||
|
47 | This makes sure that the code block wrapped by this context manager won't | |
|
48 | be interrupted. | |
|
49 | ||
|
50 | For Windows developers: It appears not possible to guard time.sleep() | |
|
51 | from CTRL_C_EVENT, so please don't use time.sleep() to test if this is | |
|
52 | working. | |
|
53 | """ | |
|
54 | assertedsigs = [] | |
|
55 | blocked = False | |
|
56 | orighandlers = {} | |
|
57 | ||
|
58 | def raiseinterrupt(num): | |
|
59 | if (num == getattr(signal, 'SIGINT', None) or | |
|
60 | num == getattr(signal, 'CTRL_C_EVENT', None)): | |
|
61 | raise KeyboardInterrupt | |
|
62 | else: | |
|
63 | raise error.SignalInterrupt | |
|
64 | def catchterm(num, frame): | |
|
65 | if blocked: | |
|
66 | assertedsigs.append(num) | |
|
67 | else: | |
|
68 | raiseinterrupt(num) | |
|
69 | ||
|
70 | try: | |
|
71 | # save handlers first so they can be restored even if a setup is | |
|
72 | # interrupted between signal.signal() and orighandlers[] =. | |
|
73 | for name in ['CTRL_C_EVENT', 'SIGINT', 'SIGBREAK', 'SIGHUP', 'SIGTERM']: | |
|
74 | num = getattr(signal, name, None) | |
|
75 | if num and num not in orighandlers: | |
|
76 | orighandlers[num] = signal.getsignal(num) | |
|
77 | try: | |
|
78 | for num in orighandlers: | |
|
79 | signal.signal(num, catchterm) | |
|
80 | except ValueError: | |
|
81 | pass # in a thread? no luck | |
|
82 | ||
|
83 | blocked = True | |
|
84 | yield | |
|
85 | finally: | |
|
86 | # no simple way to reliably restore all signal handlers because | |
|
87 | # any loops, recursive function calls, except blocks, etc. can be | |
|
88 | # interrupted. so instead, make catchterm() raise interrupt. | |
|
89 | blocked = False | |
|
90 | try: | |
|
91 | for num, handler in orighandlers.items(): | |
|
92 | signal.signal(num, handler) | |
|
93 | except ValueError: | |
|
94 | pass # in a thread? | |
|
95 | ||
|
96 | # re-raise interrupt exception if any, which may be shadowed by a new | |
|
97 | # interrupt occurred while re-raising the first one | |
|
98 | if assertedsigs: | |
|
99 | raiseinterrupt(assertedsigs[0]) | |
|
100 | ||
|
42 | 101 | def trylock(ui, vfs, lockname, timeout, warntimeout, *args, **kwargs): |
|
43 | 102 | """return an acquired lock or raise an a LockHeld exception |
|
44 | 103 | |
@@ -182,8 +241,9 b' class lock(object):' | |||
|
182 | 241 | while not self.held and retry: |
|
183 | 242 | retry -= 1 |
|
184 | 243 | try: |
|
185 | self.vfs.makelock(lockname, self.f) | |
|
186 | self.held = 1 | |
|
244 | with _delayedinterrupt(): | |
|
245 | self.vfs.makelock(lockname, self.f) | |
|
246 | self.held = 1 | |
|
187 | 247 | except (OSError, IOError) as why: |
|
188 | 248 | if why.errno == errno.EEXIST: |
|
189 | 249 | locker = self._readlock() |
@@ -1676,6 +1676,11 b' if safehasattr(time, "perf_counter"):' | |||
|
1676 | 1676 | timer = time.perf_counter |
|
1677 | 1677 | |
|
1678 | 1678 | def makelock(info, pathname): |
|
1679 | """Create a lock file atomically if possible | |
|
1680 | ||
|
1681 | This may leave a stale lock file if symlink isn't supported and signal | |
|
1682 | interrupt is enabled. | |
|
1683 | """ | |
|
1679 | 1684 | try: |
|
1680 | 1685 | return os.symlink(info, pathname) |
|
1681 | 1686 | except OSError as why: |
General Comments 0
You need to be logged in to leave comments.
Login now