[PATCH 5 of 5 rfc] posix: give checklink a fast path that cache the check file and is read only

Mads Kiilerich mads at kiilerich.com
Sat Oct 24 11:32:03 CDT 2015


# HG changeset patch
# User Mads Kiilerich <madski at unity3d.com>
# Date 1421194526 -3600
#      Wed Jan 14 01:15:26 2015 +0100
# Branch stable
# Node ID 77319eeabd529382868e4e48e695c4b5ea909124
# Parent  3a6d970e2d2f86b51b6ad0223a4da1a3a3e19bc9
posix: give checklink a fast path that cache the check file and is read only

util.checklink would create a symlink and remove it again. That would sometimes
happen multiple times. Write operations are relatively expensive and give disk
tear and noise for applications monitoring file system activity.

Instead of creating a symlink and deleting it again, just create it once and
leave it in .hg/cache/check-link . If the file exists, just verify that
os.islink reports true.

The symlink left in .hg/cache has to resolve to a file - otherwise 'make dist'
will fail ...

test-symlink-os-yes-fs-no.py does some monkey patching to simulate a platform
without symlink support. The slightly different testing method requires
additional monkeying.

diff --git a/mercurial/posix.py b/mercurial/posix.py
--- a/mercurial/posix.py
+++ b/mercurial/posix.py
@@ -215,6 +215,10 @@ def checklink(path):
     # mktemp is not racy because symlink creation will fail if the
     # file already exists
     cachedir = os.path.join(path, '.hg', 'cache')
+    checklink = os.path.join(cachedir, 'checklink')
+    # try fast path, read only
+    if os.path.islink(checklink):
+        return True
     if os.path.isdir(cachedir):
         checkdir = cachedir
     else:
@@ -225,7 +229,13 @@ def checklink(path):
         fd = tempfile.NamedTemporaryFile(dir=checkdir, prefix='hg-checklink-')
         try:
             os.symlink(os.path.basename(fd.name), name)
-            os.unlink(name)
+            if cachedir is None:
+                os.unlink(name)
+            else:
+                try:
+                    os.rename(name, checklink)
+                except OSError:
+                    os.unlink(name)
             return True
         finally:
             fd.close()
diff --git a/tests/test-clone.t b/tests/test-clone.t
--- a/tests/test-clone.t
+++ b/tests/test-clone.t
@@ -32,6 +32,7 @@ Trigger branchcache creation:
   $ ls .hg/cache
   branch2-served
   checkisexec
+  checklink
   checknoexec
   rbc-names-v1
   rbc-revs-v1
@@ -48,6 +49,7 @@ Ensure branchcache got copied over:
   $ ls .hg/cache
   branch2-served
   checkisexec
+  checklink
 
   $ cat a
   a
diff --git a/tests/test-symlink-os-yes-fs-no.py b/tests/test-symlink-os-yes-fs-no.py
--- a/tests/test-symlink-os-yes-fs-no.py
+++ b/tests/test-symlink-os-yes-fs-no.py
@@ -26,6 +26,9 @@ commands.status(u, repo)
 def symlink_failure(src, dst):
     raise OSError(1, "Operation not permitted")
 os.symlink = symlink_failure
+def islink_failure(path):
+    return False
+os.path.islink = islink_failure
 
 # dereference links as if a Samba server has exported this to a
 # Windows client
diff --git a/tests/test-tags.t b/tests/test-tags.t
--- a/tests/test-tags.t
+++ b/tests/test-tags.t
@@ -666,6 +666,7 @@ Missing tags2* files means the cache was
   $ ls tagsclient/.hg/cache
   branch2-served
   checkisexec
+  checklink
   hgtagsfnodes1
   rbc-names-v1
   rbc-revs-v1
@@ -691,6 +692,7 @@ Running hg tags should produce tags2* fi
   $ ls tagsclient/.hg/cache
   branch2-served
   checkisexec
+  checklink
   hgtagsfnodes1
   rbc-names-v1
   rbc-revs-v1


More information about the Mercurial-devel mailing list