##// 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 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