diff mercurial/lock.py @ 52739:0f2268783c11

clone: explicitly steal lock instead of assigning previous lock The issue with reusing the lock from another repository is that various internal state are no longer correct, the main example of that is the dirstate, captured in the wlock to make sure it is written (when needed) on lock release. So instead, we create a proper lock from the repository, but "stealing" the on-disk lock from the previous object. This is a bit weird, but less than the previous situation.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Thu, 30 Jan 2025 09:23:16 +0100
parents 4cb75772818d
children
line wrap: on
line diff
--- a/mercurial/lock.py	Fri Dec 20 16:11:19 2024 +0100
+++ b/mercurial/lock.py	Thu Jan 30 09:23:16 2025 +0100
@@ -111,6 +111,24 @@
         raiseinterrupt(assertedsigs[0])
 
 
+def steal_lock(ui, vfs, lockname, stolen_lock, *args, **kwargs) -> lock:
+    """return a new lock that "steal" the locking made by a source lock
+
+    This is used during local clone when reloading a repository. If we could
+    remove the need for this during copy clone, we could remove this function.
+    """
+    new_lock = lock(vfs, lockname, 0, *args, dolock=False, **kwargs)
+
+    assert stolen_lock.f == new_lock.f
+    assert stolen_lock.held > 0
+    assert new_lock.held == 0
+    new_lock.held += 1
+    stolen_lock.held = None
+    if new_lock.acquirefn is not None:
+        new_lock.acquirefn()
+    return new_lock
+
+
 def trylock(ui, vfs, lockname, timeout, warntimeout, *args, **kwargs) -> lock:
     """return an acquired lock or raise an a LockHeld exception
 
@@ -240,7 +258,11 @@
         self.release(success=success)
 
     def __del__(self):
-        if self.held:
+        if self.held is None:
+            # lock has been stolen (during a local clone) and should never be
+            # touched again.
+            return
+        if self.held > 0:
             warnings.warn(
                 "use lock.release instead of del lock",
                 category=DeprecationWarning,
@@ -274,7 +296,10 @@
                 )
 
     def _trylock(self) -> None:
-        if self.held:
+        if self.held is None:
+            msg = "cannot acquire a lock after it was stolen"
+            raise error.ProgrammingError(msg)
+        if self.held > 0:
             self.held += 1
             return
         if lock._host is None:
@@ -380,6 +405,9 @@
 
         If the lock has been acquired multiple times, the actual release is
         delayed to the last release call."""
+        if self.held is None:
+            msg = "cannot release a lock after it was stolen"
+            raise error.ProgrammingError(msg)
         if self.held > 1:
             self.held -= 1
         elif self.held == 1: