##// END OF EJS Templates
lock: block signal interrupt while making a lock file...
Yuya Nishihara -
r36840:d77c3b02 default
parent child Browse files
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