[PATCH 19 of 19] localrepo: introduce lock validation
Mads Kiilerich
mads at kiilerich.com
Thu Jan 12 19:32:53 CST 2012
# HG changeset patch
# User Mads Kiilerich <mads at kiilerich.com>
# Date 1326326392 -3600
# Node ID a624c5a3c226f20ab0585a24e37061bba28b7183
# Parent 87bc847a7ede54c4c1637e07d3ac31da08adb944
localrepo: introduce lock validation
Instrumenting critical places with these checks can help verifying compliance
with the locking strategy described on
http://mercurial.selenic.com/wiki/LockingDesign.
Occasionally running the test suite with this patch might catch some locking
errors, but the checking as it is is probably too intrusive for inclusion in
core Mercurial.
diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -2283,6 +2283,7 @@
# We don't want to lose the patch message if qrefresh fails (issue2062)
repo.savecommitmessage(message)
setupheaderopts(ui, opts)
+ repo.checkwlock()
return q.refresh(repo, pats, msg=message, **opts)
@command("^qdiff",
@@ -2371,6 +2372,7 @@
message = ui.edit(message, user or ui.username())
diffopts = q.patchopts(q.diffopts(), *patches)
+ repo.checkwlock()
q.refresh(repo, msg=message, git=diffopts.git)
q.delete(repo, patches, opts)
@@ -2761,6 +2763,7 @@
revs = list(rootnodes)
if update and opts.get('keep'):
+ repo.checkwlock()
urev = repo.mq.qparents(repo, revs[0])
repo.dirstate.rebuild(urev, repo[urev].manifest())
repo.dirstate.write()
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -170,7 +170,7 @@
return bookmarks.readcurrent(self)
def _writebookmarks(self, marks):
- bookmarks.write(self)
+ bookmarks.write(self)
@filecache('phaseroots')
def _phaseroots(self):
@@ -264,6 +264,7 @@
tag_disallowed = ':\r\n'
def _tag(self, names, node, message, local, user, date, extra={}):
+ self.checklock()
if isinstance(names, str):
allchars = names
names = (names,)
@@ -725,6 +726,7 @@
return self._filter(self._decodefilterpats, filename, data)
def transaction(self, desc):
+ self.checklock()
tr = self._transref and self._transref() or None
if tr and tr.running():
return tr.nest()
@@ -745,6 +747,7 @@
return tr
def _writejournal(self, desc):
+ self.checklock()
# save dirstate for rollback
try:
ds = self.opener.read("dirstate")
@@ -904,8 +907,8 @@
except error.LockHeld, inst:
if not wait:
raise
- self.ui.warn(_("waiting for lock on %s held by %r\n") %
- (desc, inst.locker))
+ self.ui.warnstack(_("%s waiting for %s on %s held by %r\n") %
+ (os.getpid(), lockname, desc, inst.locker), skip=2)
# default to 600 seconds timeout
l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
releasefn, desc=desc)
@@ -944,6 +947,11 @@
self._lockref = weakref.ref(l)
return l
+ def checklock(self):
+ l = self._lockref and self._lockref()
+ if l is None or not l.held:
+ self.ui.warnstack('no lock', skip=2)
+
def wlock(self, wait=True):
'''Lock the non-store parts of the repository (everything under
.hg except .hg/store) and return a weak reference to the lock.
@@ -953,6 +961,10 @@
l.lock()
return l
+ s = self._lockref and self._lockref()
+ if s is not None and s.held:
+ self.ui.warnstack('lock taken when trying to take wlock', skip=2)
+
def unlock():
self.dirstate.write()
ce = self._filecache.get('dirstate')
@@ -965,6 +977,11 @@
self._wlockref = weakref.ref(l)
return l
+ def checkwlock(self):
+ l = self._wlockref and self._wlockref()
+ if l is None or not l.held:
+ self.ui.warnstack('no wlock', skip=2)
+
def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
"""
commit an individual file as part of a larger transaction
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -673,6 +673,15 @@
traceback.print_exc(file=self.ferr)
return self.tracebackflag
+ def warnstack(self, msg, skip=1):
+ '''issue warning with the message and the current stack, skipping the
+ skip last entries'''
+ self.warn('%s at:\n' % msg)
+ entries = traceback.extract_stack()[:-skip]
+ fnmax = max(len(entry[0]) for entry in entries)
+ for fn, ln, func, _text in entries:
+ self.warn(' %*s:%-4s in %s\n' % (fnmax, fn, ln, func))
+
def geteditor(self):
'''return editor to use'''
return (os.environ.get("HGEDITOR") or
diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -826,6 +826,7 @@
hgrc = open(HGRCPATH, 'w+')
hgrc.write('[ui]\n')
hgrc.write('slash = True\n')
+ hgrc.write('timeout = 5\n')
hgrc.write('[defaults]\n')
hgrc.write('backout = -d "0 0"\n')
hgrc.write('commit = -d "0 0"\n')
diff --git a/tests/test-commandserver.py.out b/tests/test-commandserver.py.out
--- a/tests/test-commandserver.py.out
+++ b/tests/test-commandserver.py.out
@@ -76,6 +76,7 @@
defaults.commit=-d "0 0"
defaults.tag=-d "0 0"
ui.slash=True
+ui.timeout=5
ui.foo=bar
runcommand init foo
runcommand -R foo showconfig ui defaults
@@ -83,6 +84,7 @@
defaults.commit=-d "0 0"
defaults.tag=-d "0 0"
ui.slash=True
+ui.timeout=5
testing hookoutput:
More information about the Mercurial-devel
mailing list