git-remote-testgit: fix race when spawning fast-import

Test "pushing to local repo" in t5800-remote-helpers can hang
due to a race condition in git-remote-testgit.  Fix it by
setting stdin to unbuffered.

On the writer side, "git push" invokes push_refs_with_export(),
which sends to stdout the command "export\n" and immediately
starts up "git fast-export".  The latter writes its output stream
to the same stdout.

On the reader side, remote helper "git-remote-testgit" reads from
stdin to get its next command.  It uses getc() to read characters
from libc up until \n.  Libc has buffered a potentially much
larger chunk of stdin.  When it sees the "export\n" command, it
forks "git fast-import" to read the stream.

If fast-export finishes before git fast-import starts, the
fast-export output can end up in libc's buffer in
git-remote-testgit, rather than in git fast-import.  The latter
hangs indefinitely on a now-empty stdin.

Signed-off-by: Pete Wyckoff <pw@padd.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Pete Wyckoff 2012-04-22 16:30:58 -04:00 committed by Junio C Hamano
parent fdec2eb8eb
commit 7fb8e163bd
2 changed files with 20 additions and 0 deletions

View File

@ -22,6 +22,7 @@ except ImportError:
_digest = sha.new _digest = sha.new
import sys import sys
import os import os
import time
sys.path.insert(0, os.getenv("GITPYTHONLIB",".")) sys.path.insert(0, os.getenv("GITPYTHONLIB","."))
from git_remote_helpers.util import die, debug, warn from git_remote_helpers.util import die, debug, warn
@ -204,6 +205,11 @@ def read_one_line(repo):
"""Reads and processes one command. """Reads and processes one command.
""" """
sleepy = os.environ.get("GIT_REMOTE_TESTGIT_SLEEPY")
if sleepy:
debug("Sleeping %d sec before readline" % int(sleepy))
time.sleep(int(sleepy))
line = sys.stdin.readline() line = sys.stdin.readline()
cmdline = line cmdline = line
@ -258,6 +264,7 @@ def main(args):
more = True more = True
sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0)
while (more): while (more):
more = read_one_line(repo) more = read_one_line(repo)

View File

@ -72,6 +72,19 @@ test_expect_success 'pushing to local repo' '
compare_refs localclone HEAD server HEAD compare_refs localclone HEAD server HEAD
' '
# Generally, skip this test. It demonstrates a now-fixed race in
# git-remote-testgit, but is too slow to leave in for general use.
: test_expect_success 'racily pushing to local repo' '
test_when_finished "rm -rf server2 localclone2" &&
cp -a server server2 &&
git clone "testgit::${PWD}/server2" localclone2 &&
(cd localclone2 &&
echo content >>file &&
git commit -a -m three &&
GIT_REMOTE_TESTGIT_SLEEPY=2 git push) &&
compare_refs localclone2 HEAD server2 HEAD
'
test_expect_success 'synch with changes from localclone' ' test_expect_success 'synch with changes from localclone' '
(cd clone && (cd clone &&
git pull) git pull)