util: improve iterfile so it chooses code path wisely
We have performance concerns on "iterfile" as it is 4X slower on normal
files. While modern systems have the nice property that reading a "fast"
(on-disk) file cannot be interrupted and should be made use of.
This patch dumps the related knowledge in comments. And "iterfile" chooses
code paths wisely:
1. If it's CPython 3, or PyPY, use the fast path.
2. If fp is a normal file, use the fast path.
3. If fp is not a normal file and CPython version >= 2.7.4, use the same
workaround (4x slower) as before.
4. If fp is not a normal file and CPython version < 2.7.4, use another
workaround (2x slower but may block longer then necessary) which
basically re-invents the buffer + readline logic in Python.
This will give us good confidence on both correctness and performance
dealing with EINTR in iterfile(fp) for all known supported Python versions.
from __future__ import absolute_import
import os
import sys
import time
from mercurial import (
commands,
hg,
ui as uimod,
util,
)
TESTDIR = os.environ["TESTDIR"]
BUNDLEPATH = os.path.join(TESTDIR, 'bundles', 'test-no-symlinks.hg')
# only makes sense to test on os which supports symlinks
if not getattr(os, "symlink", False):
sys.exit(80) # SKIPPED_STATUS defined in run-tests.py
u = uimod.ui()
# hide outer repo
hg.peer(u, {}, '.', create=True)
# clone with symlink support
hg.clone(u, {}, BUNDLEPATH, 'test0')
repo = hg.repository(u, 'test0')
# wait a bit, or the status call wont update the dirstate
time.sleep(1)
commands.status(u, repo)
# now disable symlink support -- this is what os.symlink would do on a
# non-symlink file system
def symlink_failure(src, dst):
raise OSError(1, "Operation not permitted")
os.symlink = symlink_failure
# dereference links as if a Samba server has exported this to a
# Windows client
for f in 'test0/a.lnk', 'test0/d/b.lnk':
os.unlink(f)
fp = open(f, 'wb')
fp.write(util.readfile(f[:-4]))
fp.close()
# reload repository
u = uimod.ui()
repo = hg.repository(u, 'test0')
commands.status(u, repo)
# try cloning a repo which contains symlinks
u = uimod.ui()
hg.clone(u, {}, BUNDLEPATH, 'test1')