git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jeff King <peff@peff.net>
To: Johannes Sixt <j6t@kdbg.org>
Cc: "Clemens Buchacher" <drizzd@aon.at>,
	"Zbigniew Jędrzejewski-Szmek" <zbyszek@in.waw.pl>,
	git@vger.kernel.org, gitster@pobox.com
Subject: Re: [PATCH] t5570: forward git-daemon messages in a different way
Date: Fri, 27 Apr 2012 03:55:51 -0400	[thread overview]
Message-ID: <20120427075551.GA12092@sigill.intra.peff.net> (raw)
In-Reply-To: <4F999105.200@kdbg.org>

On Thu, Apr 26, 2012 at 08:16:37PM +0200, Johannes Sixt wrote:

> > Thanks for the pointer; I looked in POSIX but couldn't find that
> > passage. It does say "In all cases, explicit redirection of standard
> > input shall override this activity". It looks like dash interprets that
> > as "open /dev/null, then open the redirected stdin". Which leaves a race
> > condition.
> 
> I don't see a race condition.

One of the proposed solutions was:

  {
    read line
    cat <fifo &
  } <fifo

which I think can end up with this race:

  1. shell opens pipe, reads line

  2. shell forks for 'cat' process

  3. parent shell sees that cat is to be backgrounded, so it does not
     wait for cat to finish, ends {} block, and closes pipe

  4. forked shell process re-opens stdin from /dev/null

  5. nobody has the fifo open, so a writer may get SIGPIPE

  6. forked shell process re-opens stdin from the fifo

> The specs are clear: First redirect stdin
> to /dev/null, and if there are other redirections, apply them later.
> But in our code we have only:
> 
> 	cat >&4 &

Yes. But it also fails sometimes with the solution above, in which we
explicitly redirect from the fifo. The issue is not that we redirect
from /dev/null in the long term, but that there is a moment where we
have closed the old stdin and not yet opened the new one.

> I don't think so. How about this?
> 
> diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
> index ef2d01f..7245ab3 100644
> --- a/t/lib-git-daemon.sh
> +++ b/t/lib-git-daemon.sh
> @@ -30,10 +30,10 @@ start_git_daemon() {
>  		"$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
>  		>&3 2>git_daemon_output &
>  	GIT_DAEMON_PID=$!
> +	exec 7<git_daemon_output &&
>  	{
> -		read line
> +		read line <&7
>  		echo >&4 "$line"
> -		cat >&4 &
>  
>  		# Check expected output
>  		if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble"
> @@ -43,7 +43,9 @@ start_git_daemon() {
>  			trap 'die' EXIT
>  			error "git daemon failed to start"
>  		fi
> -	} <git_daemon_output
> +		cat <&7 >&4 &
> +		exec 7<&-
> +	}
>  }
>  
>  stop_git_daemon() {
> 
> 
> i.e., we open the readable end of the pipe in the shell, and dup
> it from there to 'read' and later to 'cat'. Finally, we can
> close it, because 'cat' has it still open in the background.

Yes, I believe this will work reliably. Though there is one subtle thing
happening.  At first glance, I thought you might have the same race I
mentioned above; there's no guarantee that the shell subprocess has
actually set up its stdin before your "exec 7<&-" runs. But because
the subprocess has inherited descriptor 7, and because it never
explicitly closes descriptor 7 (as it does for descriptor 0 while
setting up the command's redirections), the pipe is always open. As fd 7
before exec'ing cat, and as both 7 and 0 afterwards.

So I think you could even get rid of your "exec" lines entirely, and
just do:

  {
    read line <&7
    cat <&7 &
  } 7<git_daemon_output

That works reliably for me with this test:

  mkfifo fd
  yes >fd &
  pid=$!
  {
    read line
    echo $line
    wc <fd &
  } <fd
  sleep 1
  kill $pid
  wait $pid
  rm -f fd

which fails for me about one-third of the time in the form above, but
works if you replace the middle part with:

  {
    read line <&7
    echo $line
    wc <&7 &
  } 7<fd

(This is the same thing the test script is doing, but it exercises the race
much better because "yes" is constantly writing output).

Thanks for a clever solution. This is much better than doing something
custom in C.

-Peff

      parent reply	other threads:[~2012-04-27  7:56 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-04-14  8:44 [PATCH] t5570: forward git-daemon messages in a different way Zbigniew Jędrzejewski-Szmek
2012-04-14 12:13 ` Clemens Buchacher
2012-04-14 12:21   ` Clemens Buchacher
2012-04-16 15:43     ` Zbigniew Jędrzejewski-Szmek
2012-04-16 17:09       ` Junio C Hamano
2012-04-16 21:22         ` Clemens Buchacher
2012-04-16 22:06           ` Zbigniew Jędrzejewski-Szmek
2012-04-17 15:43             ` Junio C Hamano
2012-04-16 17:42       ` Jeff King
2012-04-16 22:44         ` Clemens Buchacher
2012-04-19  6:03           ` Jeff King
2012-04-19  6:58             ` Johannes Sixt
2012-04-26 13:01               ` Jeff King
2012-04-26 18:16                 ` Johannes Sixt
2012-04-26 19:55                   ` Clemens Buchacher
2012-04-26 21:00                     ` [PATCH] t5570: fix forwarding of git-daemon messages via cat Johannes Sixt
2012-04-26 21:10                       ` Zbigniew Jędrzejewski-Szmek
2012-04-27  7:59                       ` Jeff King
2012-04-27 15:02                       ` Junio C Hamano
2012-04-27  7:55                   ` Jeff King [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120427075551.GA12092@sigill.intra.peff.net \
    --to=peff@peff.net \
    --cc=drizzd@aon.at \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=j6t@kdbg.org \
    --cc=zbyszek@in.waw.pl \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).