[PATCH 1 of 6 RFC] manifest: introduce an accessor class for manifests

Durham Goode durham at fb.com
Thu Nov 3 22:27:37 UTC 2016


# HG changeset patch
# User Durham Goode <durham at fb.com>
# Date 1478208817 25200
#      Thu Nov 03 14:33:37 2016 -0700
# Branch stable
# Node ID 1788ee9e1df92ac94b9be84eac6d16e3bad903a9
# Parent  b9f7b0c10027764cee77f9c6d61877fcffea837f
manifest: introduce an accessor class for manifests

This introduces a revlogaccessor class which can be used to allow multiple
objects hold an auto-invalidating reference to a revlog, without having to hold
a reference to the actual repo object. Future patches will switch repo.manifest
and repo.manifestlog to access the manifest through this accessor. This will fix
the circular reference caused by manifestlog and manifestctx holding a reference
to the repo

diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -514,6 +514,11 @@ class localrepository(object):
         # manifest creation.
         return manifest.manifest(self.svfs)
 
+    @unfilteredpropertycache
+    def manifestaccessor(self):
+        return revlogaccessor('00manifest.i', self.svfs,
+                              self._constructmanifest)
+
     @storecache('00manifest.i')
     def manifestlog(self):
         return manifest.manifestlog(self.svfs, self)
@@ -1275,6 +1280,8 @@ class localrepository(object):
                 delattr(unfiltered, k)
             except AttributeError:
                 pass
+        self.manifestaccessor.clear(clearfilecache)
+
         self.invalidatecaches()
         if not self.currenttransaction():
             # TODO: Changing contents of store outside transaction
@@ -1296,6 +1303,7 @@ class localrepository(object):
             if k == 'dirstate' or k not in self.__dict__:
                 continue
             ce.refresh()
+        self.manifestaccessor.refresh()
 
     def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc,
               inheritchecker=None, parentenvvar=None):
@@ -2004,3 +2012,40 @@ def newreporequirements(repo):
         requirements.add('manifestv2')
 
     return requirements
+
+def revlogaccessor(filename, opener, constructor):
+    """Creates an accessor that provides cached and invalidated access to a
+    revlog, via instance.revlog. This is useful for letting multiple objects
+    hold a reference to the revlog, without having to hold a possibly-circular
+    reference to the actual repository.  """
+
+    # We have to use a runtime type here, because the only way to create a
+    # property is to put it on a class itself, and the property is dynamically
+    # defined by the filename parameter.
+    class accessor(object):
+        def __init__(self):
+            self._filecache = {}
+
+        @scmutil.filecache(filename)
+        def revlog(self):
+            return constructor()
+
+        def join(self, name):
+            return opener.join(name)
+
+        def clear(self, clearfilecache):
+            for k in self._filecache.keys():
+                if clearfilecache:
+                    del self._filecache[k]
+                try:
+                    delattr(self, k)
+                except AttributeError:
+                    pass
+
+        def refresh(self):
+            for k, ce in self._filecache.items():
+                if k not in self.__dict__:
+                    continue
+                ce.refresh()
+
+    return accessor()
diff --git a/mercurial/manifest.py b/mercurial/manifest.py
--- a/mercurial/manifest.py
+++ b/mercurial/manifest.py
@@ -1254,7 +1254,7 @@ class manifestlog(object):
             usetreemanifest = opts.get('treemanifest', usetreemanifest)
         self._treeinmem = usetreemanifest
 
-        self._oldmanifest = repo._constructmanifest()
+        self._oldmanifest = repo.manifestaccessor.revlog
         self._revlog = self._oldmanifest
 
         # We'll separate this into it's own cache once oldmanifest is no longer


More information about the Mercurial-devel mailing list