comparison mercurial/lock.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents ead71b15efd5
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
22 error, 22 error,
23 pycompat, 23 pycompat,
24 util, 24 util,
25 ) 25 )
26 26
27 from .utils import ( 27 from .utils import procutil
28 procutil, 28
29 )
30 29
31 def _getlockprefix(): 30 def _getlockprefix():
32 """Return a string which is used to differentiate pid namespaces 31 """Return a string which is used to differentiate pid namespaces
33 32
34 It's useful to detect "dead" processes and remove stale locks with 33 It's useful to detect "dead" processes and remove stale locks with
41 result += '/%x' % os.stat('/proc/self/ns/pid').st_ino 40 result += '/%x' % os.stat('/proc/self/ns/pid').st_ino
42 except OSError as ex: 41 except OSError as ex:
43 if ex.errno not in (errno.ENOENT, errno.EACCES, errno.ENOTDIR): 42 if ex.errno not in (errno.ENOENT, errno.EACCES, errno.ENOTDIR):
44 raise 43 raise
45 return result 44 return result
45
46 46
47 @contextlib.contextmanager 47 @contextlib.contextmanager
48 def _delayedinterrupt(): 48 def _delayedinterrupt():
49 """Block signal interrupt while doing something critical 49 """Block signal interrupt while doing something critical
50 50
58 assertedsigs = [] 58 assertedsigs = []
59 blocked = False 59 blocked = False
60 orighandlers = {} 60 orighandlers = {}
61 61
62 def raiseinterrupt(num): 62 def raiseinterrupt(num):
63 if (num == getattr(signal, 'SIGINT', None) or 63 if num == getattr(signal, 'SIGINT', None) or num == getattr(
64 num == getattr(signal, 'CTRL_C_EVENT', None)): 64 signal, 'CTRL_C_EVENT', None
65 ):
65 raise KeyboardInterrupt 66 raise KeyboardInterrupt
66 else: 67 else:
67 raise error.SignalInterrupt 68 raise error.SignalInterrupt
69
68 def catchterm(num, frame): 70 def catchterm(num, frame):
69 if blocked: 71 if blocked:
70 assertedsigs.append(num) 72 assertedsigs.append(num)
71 else: 73 else:
72 raiseinterrupt(num) 74 raiseinterrupt(num)
80 orighandlers[num] = signal.getsignal(num) 82 orighandlers[num] = signal.getsignal(num)
81 try: 83 try:
82 for num in orighandlers: 84 for num in orighandlers:
83 signal.signal(num, catchterm) 85 signal.signal(num, catchterm)
84 except ValueError: 86 except ValueError:
85 pass # in a thread? no luck 87 pass # in a thread? no luck
86 88
87 blocked = True 89 blocked = True
88 yield 90 yield
89 finally: 91 finally:
90 # no simple way to reliably restore all signal handlers because 92 # no simple way to reliably restore all signal handlers because
93 blocked = False 95 blocked = False
94 try: 96 try:
95 for num, handler in orighandlers.items(): 97 for num, handler in orighandlers.items():
96 signal.signal(num, handler) 98 signal.signal(num, handler)
97 except ValueError: 99 except ValueError:
98 pass # in a thread? 100 pass # in a thread?
99 101
100 # re-raise interrupt exception if any, which may be shadowed by a new 102 # re-raise interrupt exception if any, which may be shadowed by a new
101 # interrupt occurred while re-raising the first one 103 # interrupt occurred while re-raising the first one
102 if assertedsigs: 104 if assertedsigs:
103 raiseinterrupt(assertedsigs[0]) 105 raiseinterrupt(assertedsigs[0])
106
104 107
105 def trylock(ui, vfs, lockname, timeout, warntimeout, *args, **kwargs): 108 def trylock(ui, vfs, lockname, timeout, warntimeout, *args, **kwargs):
106 """return an acquired lock or raise an a LockHeld exception 109 """return an acquired lock or raise an a LockHeld exception
107 110
108 This function is responsible to issue warnings and or debug messages about 111 This function is responsible to issue warnings and or debug messages about
111 def printwarning(printer, locker): 114 def printwarning(printer, locker):
112 """issue the usual "waiting on lock" message through any channel""" 115 """issue the usual "waiting on lock" message through any channel"""
113 # show more details for new-style locks 116 # show more details for new-style locks
114 if ':' in locker: 117 if ':' in locker:
115 host, pid = locker.split(":", 1) 118 host, pid = locker.split(":", 1)
116 msg = (_("waiting for lock on %s held by process %r on host %r\n") 119 msg = _(
117 % (pycompat.bytestr(l.desc), pycompat.bytestr(pid), 120 "waiting for lock on %s held by process %r on host %r\n"
118 pycompat.bytestr(host))) 121 ) % (
119 else: 122 pycompat.bytestr(l.desc),
120 msg = (_("waiting for lock on %s held by %r\n") 123 pycompat.bytestr(pid),
121 % (l.desc, pycompat.bytestr(locker))) 124 pycompat.bytestr(host),
125 )
126 else:
127 msg = _("waiting for lock on %s held by %r\n") % (
128 l.desc,
129 pycompat.bytestr(locker),
130 )
122 printer(msg) 131 printer(msg)
123 132
124 l = lock(vfs, lockname, 0, *args, dolock=False, **kwargs) 133 l = lock(vfs, lockname, 0, *args, dolock=False, **kwargs)
125 134
126 debugidx = 0 if (warntimeout and timeout) else -1 135 debugidx = 0 if (warntimeout and timeout) else -1
139 if delay == debugidx: 148 if delay == debugidx:
140 printwarning(ui.debug, inst.locker) 149 printwarning(ui.debug, inst.locker)
141 if delay == warningidx: 150 if delay == warningidx:
142 printwarning(ui.warn, inst.locker) 151 printwarning(ui.warn, inst.locker)
143 if timeout <= delay: 152 if timeout <= delay:
144 raise error.LockHeld(errno.ETIMEDOUT, inst.filename, 153 raise error.LockHeld(
145 l.desc, inst.locker) 154 errno.ETIMEDOUT, inst.filename, l.desc, inst.locker
155 )
146 time.sleep(1) 156 time.sleep(1)
147 delay += 1 157 delay += 1
148 158
149 l.delay = delay 159 l.delay = delay
150 if l.delay: 160 if l.delay:
153 else: 163 else:
154 ui.debug("got lock after %d seconds\n" % l.delay) 164 ui.debug("got lock after %d seconds\n" % l.delay)
155 if l.acquirefn: 165 if l.acquirefn:
156 l.acquirefn() 166 l.acquirefn()
157 return l 167 return l
168
158 169
159 class lock(object): 170 class lock(object):
160 '''An advisory lock held by one process to control access to a set 171 '''An advisory lock held by one process to control access to a set
161 of files. Non-cooperating processes or incorrectly written scripts 172 of files. Non-cooperating processes or incorrectly written scripts
162 can ignore Mercurial's locking scheme and stomp all over the 173 can ignore Mercurial's locking scheme and stomp all over the
174 # old-style lock: symlink to pid 185 # old-style lock: symlink to pid
175 # new-style lock: symlink to hostname:pid 186 # new-style lock: symlink to hostname:pid
176 187
177 _host = None 188 _host = None
178 189
179 def __init__(self, vfs, fname, timeout=-1, releasefn=None, acquirefn=None, 190 def __init__(
180 desc=None, inheritchecker=None, parentlock=None, 191 self,
181 signalsafe=True, dolock=True): 192 vfs,
193 fname,
194 timeout=-1,
195 releasefn=None,
196 acquirefn=None,
197 desc=None,
198 inheritchecker=None,
199 parentlock=None,
200 signalsafe=True,
201 dolock=True,
202 ):
182 self.vfs = vfs 203 self.vfs = vfs
183 self.f = fname 204 self.f = fname
184 self.held = 0 205 self.held = 0
185 self.timeout = timeout 206 self.timeout = timeout
186 self.releasefn = releasefn 207 self.releasefn = releasefn
192 self._inherited = False 213 self._inherited = False
193 if signalsafe: 214 if signalsafe:
194 self._maybedelayedinterrupt = _delayedinterrupt 215 self._maybedelayedinterrupt = _delayedinterrupt
195 else: 216 else:
196 self._maybedelayedinterrupt = util.nullcontextmanager 217 self._maybedelayedinterrupt = util.nullcontextmanager
197 self.postrelease = [] 218 self.postrelease = []
198 self.pid = self._getpid() 219 self.pid = self._getpid()
199 if dolock: 220 if dolock:
200 self.delay = self.lock() 221 self.delay = self.lock()
201 if self.acquirefn: 222 if self.acquirefn:
202 self.acquirefn() 223 self.acquirefn()
207 def __exit__(self, exc_type, exc_value, exc_tb): 228 def __exit__(self, exc_type, exc_value, exc_tb):
208 self.release() 229 self.release()
209 230
210 def __del__(self): 231 def __del__(self):
211 if self.held: 232 if self.held:
212 warnings.warn(r"use lock.release instead of del lock", 233 warnings.warn(
213 category=DeprecationWarning, 234 r"use lock.release instead of del lock",
214 stacklevel=2) 235 category=DeprecationWarning,
236 stacklevel=2,
237 )
215 238
216 # ensure the lock will be removed 239 # ensure the lock will be removed
217 # even if recursive locking did occur 240 # even if recursive locking did occur
218 self.held = 1 241 self.held = 1
219 242
233 if timeout != 0: 256 if timeout != 0:
234 time.sleep(1) 257 time.sleep(1)
235 if timeout > 0: 258 if timeout > 0:
236 timeout -= 1 259 timeout -= 1
237 continue 260 continue
238 raise error.LockHeld(errno.ETIMEDOUT, inst.filename, self.desc, 261 raise error.LockHeld(
239 inst.locker) 262 errno.ETIMEDOUT, inst.filename, self.desc, inst.locker
263 )
240 264
241 def _trylock(self): 265 def _trylock(self):
242 if self.held: 266 if self.held:
243 self.held += 1 267 self.held += 1
244 return 268 return
266 self._parentheld = True 290 self._parentheld = True
267 self.held = 1 291 self.held = 1
268 return 292 return
269 locker = self._testlock(locker) 293 locker = self._testlock(locker)
270 if locker is not None: 294 if locker is not None:
271 raise error.LockHeld(errno.EAGAIN, 295 raise error.LockHeld(
272 self.vfs.join(self.f), self.desc, 296 errno.EAGAIN,
273 locker) 297 self.vfs.join(self.f),
298 self.desc,
299 locker,
300 )
274 else: 301 else:
275 raise error.LockUnavailable(why.errno, why.strerror, 302 raise error.LockUnavailable(
276 why.filename, self.desc) 303 why.errno, why.strerror, why.filename, self.desc
304 )
277 305
278 if not self.held: 306 if not self.held:
279 # use empty locker to mean "busy for frequent lock/unlock 307 # use empty locker to mean "busy for frequent lock/unlock
280 # by many processes" 308 # by many processes"
281 raise error.LockHeld(errno.EAGAIN, 309 raise error.LockHeld(
282 self.vfs.join(self.f), self.desc, "") 310 errno.EAGAIN, self.vfs.join(self.f), self.desc, ""
311 )
283 312
284 def _readlock(self): 313 def _readlock(self):
285 """read lock and return its value 314 """read lock and return its value
286 315
287 Returns None if no lock exists, pid for old-style locks, and host:pid 316 Returns None if no lock exists, pid for old-style locks, and host:pid
340 Communicating this string to the subprocess needs to be done separately 369 Communicating this string to the subprocess needs to be done separately
341 -- typically by an environment variable. 370 -- typically by an environment variable.
342 """ 371 """
343 if not self.held: 372 if not self.held:
344 raise error.LockInheritanceContractViolation( 373 raise error.LockInheritanceContractViolation(
345 'inherit can only be called while lock is held') 374 'inherit can only be called while lock is held'
375 )
346 if self._inherited: 376 if self._inherited:
347 raise error.LockInheritanceContractViolation( 377 raise error.LockInheritanceContractViolation(
348 'inherit cannot be called while lock is already inherited') 378 'inherit cannot be called while lock is already inherited'
379 )
349 if self._inheritchecker is not None: 380 if self._inheritchecker is not None:
350 self._inheritchecker() 381 self._inheritchecker()
351 if self.releasefn: 382 if self.releasefn:
352 self.releasefn() 383 self.releasefn()
353 if self._parentheld: 384 if self._parentheld:
389 for callback in self.postrelease: 420 for callback in self.postrelease:
390 callback() 421 callback()
391 # Prevent double usage and help clear cycles. 422 # Prevent double usage and help clear cycles.
392 self.postrelease = None 423 self.postrelease = None
393 424
425
394 def release(*locks): 426 def release(*locks):
395 for lock in locks: 427 for lock in locks:
396 if lock is not None: 428 if lock is not None:
397 lock.release() 429 lock.release()