localrepo: do not cache auditor/nofsauditor which would make reference cycle
Before, self.auditor and self.nofsauditor held self through self._checknested,
and the following code couldn't free a repo by ref-counting.
def main():
repo = localrepo.localrepository(uimod.ui(), '../testrepos/hello')
main()
With this change, the cache of the nofsauditor is limited to a single match
session. I think that's okay as the nofsauditor doesn't do any filesystem
access. Alternatively, maybe we can remove the callback from nofsauditor
since it isn't used unless realfs=True, but I have no idea whether it is
a bug or not.
--- a/mercurial/localrepo.py Sun Aug 05 13:13:06 2018 +0900
+++ b/mercurial/localrepo.py Wed Aug 22 20:52:36 2018 +0900
@@ -436,14 +436,6 @@
self.root = self.wvfs.base
self.path = self.wvfs.join(".hg")
self.origroot = path
- # This is only used by context.workingctx.match in order to
- # detect files in subrepos.
- self.auditor = pathutil.pathauditor(
- self.root, callback=self._checknested)
- # This is only used by context.basectx.match in order to detect
- # files in subrepos.
- self.nofsauditor = pathutil.pathauditor(
- self.root, callback=self._checknested, realfs=False, cached=True)
self.baseui = baseui
self.ui = baseui.copy()
self.ui.copy = baseui.copy # prevent copying repo configuration
@@ -709,6 +701,22 @@
def _writerequirements(self):
scmutil.writerequires(self.vfs, self.requirements)
+ # Don't cache auditor/nofsauditor, or you'll end up with reference cycle:
+ # self -> auditor -> self._checknested -> self
+
+ @property
+ def auditor(self):
+ # This is only used by context.workingctx.match in order to
+ # detect files in subrepos.
+ return pathutil.pathauditor(self.root, callback=self._checknested)
+
+ @property
+ def nofsauditor(self):
+ # This is only used by context.basectx.match in order to detect
+ # files in subrepos.
+ return pathutil.pathauditor(self.root, callback=self._checknested,
+ realfs=False, cached=True)
+
def _checknested(self, path):
"""Determine if path is a legal nested repository."""
if not path.startswith(self.root):