Show More
@@ -10,6 +10,7 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 signal | |||
13 | import socket |
|
14 | import socket | |
14 | import time |
|
15 | import time | |
15 | import warnings |
|
16 | import warnings | |
@@ -39,6 +40,64 b' def _getlockprefix():' | |||||
39 | raise |
|
40 | raise | |
40 | return result |
|
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 | def trylock(ui, vfs, lockname, timeout, warntimeout, *args, **kwargs): |
|
101 | def trylock(ui, vfs, lockname, timeout, warntimeout, *args, **kwargs): | |
43 | """return an acquired lock or raise an a LockHeld exception |
|
102 | """return an acquired lock or raise an a LockHeld exception | |
44 |
|
103 | |||
@@ -182,8 +241,9 b' class lock(object):' | |||||
182 | while not self.held and retry: |
|
241 | while not self.held and retry: | |
183 | retry -= 1 |
|
242 | retry -= 1 | |
184 | try: |
|
243 | try: | |
185 | self.vfs.makelock(lockname, self.f) |
|
244 | with _delayedinterrupt(): | |
186 | self.held = 1 |
|
245 | self.vfs.makelock(lockname, self.f) | |
|
246 | self.held = 1 | |||
187 | except (OSError, IOError) as why: |
|
247 | except (OSError, IOError) as why: | |
188 | if why.errno == errno.EEXIST: |
|
248 | if why.errno == errno.EEXIST: | |
189 | locker = self._readlock() |
|
249 | locker = self._readlock() |
@@ -1676,6 +1676,11 b' if safehasattr(time, "perf_counter"):' | |||||
1676 | timer = time.perf_counter |
|
1676 | timer = time.perf_counter | |
1677 |
|
1677 | |||
1678 | def makelock(info, pathname): |
|
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 | try: |
|
1684 | try: | |
1680 | return os.symlink(info, pathname) |
|
1685 | return os.symlink(info, pathname) | |
1681 | except OSError as why: |
|
1686 | except OSError as why: |
General Comments 0
You need to be logged in to leave comments.
Login now