##// END OF EJS Templates
lock: fix race in lock-breaking code...
lock: fix race in lock-breaking code With low frequency, I see hg pulls fail with output like: abort: no such file or directory: .hg/store/lock I think what happens is, in lock.py, in: def _testlock(self, locker): if not self._lockshouldbebroken(locker): return locker # if locker dead, break lock. must do this with another lock # held, or can race and break valid lock. try: with lock(self.vfs, self.f + b'.break', timeout=0): self.vfs.unlink(self.f) except error.LockError: return locker if a lock is breakable on disk, and two hg processes concurrently get to the "if locker dead" comment, a possible interleaving is: process1 finishes executing the function and then process2 finishes executing the function. If that happens, process2 will either get ENOENT in self.vfs.unlink (resulting in the spurious failure above), or break a valid lock and potentially cause repository corruption. The fix is simple enough: make sure the lock is breakable _inside_ the critical section, because only then can we know that no other process can invalidate our knowledge on the lock on disk. I don't think there are tests for this. I've tested this manually with: diff --git a/mercurial/lock.py b/mercurial/lock.py --- a/mercurial/lock.py +++ b/mercurial/lock.py @@ -351,6 +351,8 @@ class lock(object): if not self._lockshouldbebroken(locker): return locker + import random + time.sleep(1. + random.random()) # if locker dead, break lock. must do this with another lock # held, or can race and break valid lock. try: @@ -358,6 +360,7 @@ class lock(object): self.vfs.unlink(self.f) except error.LockError: return locker + time.sleep(1) def testlock(self): """return id of locker if lock is valid, else None. and I see this change of behavior before/after this commit: $ $hg init repo $ cd repo $ ln -s $HOSTNAME/effffffc:987654321 .hg/wlock $ touch a $ $hg commit -Am_ & $hg commit -Am _; wait -abort: No such file or directory: '/tmp/repo/.hg/wlock' adding a +warning: ignoring unknown working parent 679a8959a8ca! +nothing changed Differential Revision: https://phab.mercurial-scm.org/D7199

File last commit:

r43347:687b865b default
r44108:039fbd14 default
Show More
lsprofcalltree.py
96 lines | 2.7 KiB | text/x-python | PythonLexer
"""
lsprofcalltree.py - lsprof output which is readable by kcachegrind
Authors:
* David Allouche <david <at> allouche.net>
* Jp Calderone & Itamar Shtull-Trauring
* Johan Dahlin
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
"""
from __future__ import absolute_import
from . import pycompat
def label(code):
if isinstance(code, str):
# built-in functions ('~' sorts at the end)
return b'~' + pycompat.sysbytes(code)
else:
return b'%s %s:%d' % (
pycompat.sysbytes(code.co_name),
pycompat.sysbytes(code.co_filename),
code.co_firstlineno,
)
class KCacheGrind(object):
def __init__(self, profiler):
self.data = profiler.getstats()
self.out_file = None
def output(self, out_file):
self.out_file = out_file
out_file.write(b'events: Ticks\n')
self._print_summary()
for entry in self.data:
self._entry(entry)
def _print_summary(self):
max_cost = 0
for entry in self.data:
totaltime = int(entry.totaltime * 1000)
max_cost = max(max_cost, totaltime)
self.out_file.write(b'summary: %d\n' % max_cost)
def _entry(self, entry):
out_file = self.out_file
code = entry.code
if isinstance(code, str):
out_file.write(b'fi=~\n')
else:
out_file.write(b'fi=%s\n' % pycompat.sysbytes(code.co_filename))
out_file.write(b'fn=%s\n' % label(code))
inlinetime = int(entry.inlinetime * 1000)
if isinstance(code, str):
out_file.write(b'0 %d\n' % inlinetime)
else:
out_file.write(b'%d %d\n' % (code.co_firstlineno, inlinetime))
# recursive calls are counted in entry.calls
if entry.calls:
calls = entry.calls
else:
calls = []
if isinstance(code, str):
lineno = 0
else:
lineno = code.co_firstlineno
for subentry in calls:
self._subentry(lineno, subentry)
out_file.write(b'\n')
def _subentry(self, lineno, subentry):
out_file = self.out_file
code = subentry.code
out_file.write(b'cfn=%s\n' % label(code))
if isinstance(code, str):
out_file.write(b'cfi=~\n')
out_file.write(b'calls=%d 0\n' % subentry.callcount)
else:
out_file.write(b'cfi=%s\n' % pycompat.sysbytes(code.co_filename))
out_file.write(
b'calls=%d %d\n' % (subentry.callcount, code.co_firstlineno)
)
totaltime = int(subentry.totaltime * 1000)
out_file.write(b'%d %d\n' % (lineno, totaltime))