Patchwork [2,of,3,V2] lock: add a trylock method handling the timeout and messaging logic

login
register
mail settings
Submitter Boris Feld
Date Dec. 4, 2017, 10:28 a.m.
Message ID <25564a60ab9e4446256a.1512383323@FB>
Download mbox | patch
Permalink /patch/25915/
State Accepted
Headers show

Comments

Boris Feld - Dec. 4, 2017, 10:28 a.m.
# HG changeset patch
# User Boris Feld <boris.feld@octobus.net>
# Date 1512005789 18000
#      Wed Nov 29 20:36:29 2017 -0500
# Node ID 25564a60ab9e4446256a2bde0af18186f6094755
# Parent  144214ed28417180a25a0f6311de1f0ac0b02b8e
# EXP-Topic lock-message
# Available At https://bitbucket.org/octobus/mercurial-devel/
#              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 25564a60ab9e
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.

Patch

diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1590,29 +1590,16 @@  class localrepository(object):
         # 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):
diff --git a/mercurial/lock.py b/mercurial/lock.py
--- a/mercurial/lock.py
+++ b/mercurial/lock.py
@@ -14,6 +14,8 @@  import socket
 import time
 import warnings
 
+from .i18n import _
+
 from . import (
     encoding,
     error,
@@ -39,6 +41,50 @@  def _getlockprefix():
                 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 @@  class lock(object):
     _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 @@  class lock(object):
         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