changeset 27906:c183f7b79541

context: don't use util.cachefunc due to cycle creation (issue5043) util.cachefunc stores all arguments as the cache key. For filectxfn functions, the arguments include the memctx instance. This creates a cycle where memctx._filectxfn references self. This causes a memory leak. We break the cycle by implementing our own memoizing function that only uses the path as the cache key. Since each memctx has its own cache instance, there is no concern about invalid cache hits.
author Gregory Szorc <gregory.szorc@gmail.com>
date Sun, 17 Jan 2016 12:10:30 -0800
parents 27f2f5c1d499
children e219dbfd0342
files mercurial/context.py
diffstat 1 files changed, 18 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/context.py	Sun Jan 17 19:29:27 2016 +0100
+++ b/mercurial/context.py	Sun Jan 17 12:10:30 2016 -0800
@@ -1779,6 +1779,22 @@
         changed.update(self._status.removed)
         return changed
 
+def makecachingfilectxfn(func):
+    """Create a filectxfn that caches based on the path.
+
+    We can't use util.cachefunc because it uses all arguments as the cache
+    key and this creates a cycle since the arguments include the repo and
+    memctx.
+    """
+    cache = {}
+
+    def getfilectx(repo, memctx, path):
+        if path not in cache:
+            cache[path] = func(repo, memctx, path)
+        return cache[path]
+
+    return getfilectx
+
 class memctx(committablectx):
     """Use memctx to perform in-memory commits via localrepo.commitctx().
 
@@ -1838,9 +1854,8 @@
                                   copied=copied, memctx=memctx)
             self._filectxfn = getfilectx
         else:
-            # "util.cachefunc" reduces invocation of possibly expensive
-            # "filectxfn" for performance (e.g. converting from another VCS)
-            self._filectxfn = util.cachefunc(filectxfn)
+            # memoizing increases performance for e.g. vcs convert scenarios.
+            self._filectxfn = makecachingfilectxfn(filectxfn)
 
         if extra:
             self._extra = extra.copy()