lock: add a trylock method handling the timeout and messaging logic
We are about to make the messages around lock more flexible. We move all the
currently logic into a function in the lock module. We'll update the message
scheme in the next changeset.
--- a/mercurial/localrepo.py Wed Nov 29 21:00:02 2017 -0500
+++ b/mercurial/localrepo.py Wed Nov 29 20:36:29 2017 -0500
@@ -1592,29 +1592,16 @@
# determine whether it can be inherited
if parentenvvar is not None:
parentlock = encoding.environ.get(parentenvvar)
- try:
- l = lockmod.lock(vfs, lockname, 0, releasefn=releasefn,
- acquirefn=acquirefn, desc=desc,
- inheritchecker=inheritchecker,
- parentlock=parentlock)
- except error.LockHeld as inst:
- if not wait:
- raise
- # show more details for new-style locks
- if ':' in inst.locker:
- host, pid = inst.locker.split(":", 1)
- self.ui.warn(
- _("waiting for lock on %s held by process %r "
- "on host %r\n") % (desc, pid, host))
- else:
- self.ui.warn(_("waiting for lock on %s held by %r\n") %
- (desc, inst.locker))
- # default to 600 seconds timeout
- l = lockmod.lock(vfs, lockname,
- self.ui.configint("ui", "timeout"),
- releasefn=releasefn, acquirefn=acquirefn,
- desc=desc)
- self.ui.warn(_("got lock after %s seconds\n") % l.delay)
+
+ timeout = 0
+ if wait:
+ timeout = self.ui.configint("ui", "timeout")
+
+ l = lockmod.trylock(self.ui, vfs, lockname, timeout,
+ releasefn=releasefn,
+ acquirefn=acquirefn, desc=desc,
+ inheritchecker=inheritchecker,
+ parentlock=parentlock)
return l
def _afterlock(self, callback):
--- a/mercurial/lock.py Wed Nov 29 21:00:02 2017 -0500
+++ b/mercurial/lock.py Wed Nov 29 20:36:29 2017 -0500
@@ -14,6 +14,8 @@
import time
import warnings
+from .i18n import _
+
from . import (
encoding,
error,
@@ -39,6 +41,50 @@
raise
return result
+def trylock(ui, vfs, lockname, timeout, *args, **kwargs):
+ """return an acquired lock or raise an a LockHeld exception
+
+ This function is responsible to issue warnings about the held lock while
+ trying to acquires it."""
+
+ def printwarning(printer, locker):
+ """issue the usual "waiting on lock" message through any channel"""
+ # show more details for new-style locks
+ if ':' in locker:
+ host, pid = locker.split(":", 1)
+ msg = _("waiting for lock on %s held by process %r "
+ "on host %r\n") % (l.desc, pid, host)
+ else:
+ msg = _("waiting for lock on %s held by %r\n") % (l.desc, locker)
+ printer(msg)
+
+ l = lock(vfs, lockname, 0, *args, dolock=False, **kwargs)
+
+ warningidx = 0
+ if not timeout:
+ warningidx = -1
+
+ delay = 0
+ while True:
+ try:
+ l._trylock()
+ break
+ except error.LockHeld as inst:
+ if delay == warningidx:
+ printwarning(ui.warn, inst.locker)
+ if timeout <= delay:
+ raise error.LockHeld(errno.ETIMEDOUT, inst.filename,
+ l.desc, inst.locker)
+ time.sleep(1)
+ delay += 1
+
+ l.delay = delay
+ if l.delay:
+ ui.warn(_("got lock after %s seconds\n") % l.delay)
+ if l.acquirefn:
+ l.acquirefn()
+ return l
+
class lock(object):
'''An advisory lock held by one process to control access to a set
of files. Non-cooperating processes or incorrectly written scripts
@@ -60,7 +106,8 @@
_host = None
def __init__(self, vfs, file, timeout=-1, releasefn=None, acquirefn=None,
- desc=None, inheritchecker=None, parentlock=None):
+ desc=None, inheritchecker=None, parentlock=None,
+ dolock=True):
self.vfs = vfs
self.f = file
self.held = 0
@@ -74,9 +121,10 @@
self._inherited = False
self.postrelease = []
self.pid = self._getpid()
- self.delay = self.lock()
- if self.acquirefn:
- self.acquirefn()
+ if dolock:
+ self.delay = self.lock()
+ if self.acquirefn:
+ self.acquirefn()
def __enter__(self):
return self