git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Patrick Steinhardt <ps@pks.im>
To: Jeff King <peff@peff.net>
Cc: git@vger.kernel.org
Subject: Re: [PATCH 02/10] t7422: fix flaky test caused by buffered stdout
Date: Mon, 6 Jan 2025 12:12:22 +0100	[thread overview]
Message-ID: <Z3u6lj_bpM7N93Fd@pks.im> (raw)
In-Reply-To: <20250103181739.GA2527684@coredump.intra.peff.net>

On Fri, Jan 03, 2025 at 01:17:39PM -0500, Jeff King wrote:
> On Fri, Jan 03, 2025 at 03:46:39PM +0100, Patrick Steinhardt wrote:
> > One test in t7422 asserts that `git submodule status --recursive`
> > properly handles SIGPIPE. This test is flaky though and may sometimes
> > not see a SIGPIPE at all:
> > 
> >     expecting success of 7422.18 'git submodule status --recursive propagates SIGPIPE':
> >             { git submodule status --recursive 2>err; echo $?>status; } |
> >                     grep -q X/S &&
> >             test_must_be_empty err &&
> >             test_match_signal 13 "$(cat status)"
> 
> I couldn't reproduce with --stress, but you can trigger it all the time
> with:
> 
> diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh
> index f21e920367..9338c75626 100755
> --- a/t/t7422-submodule-output.sh
> +++ b/t/t7422-submodule-output.sh
> @@ -168,7 +168,7 @@ done
>  
>  test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE' '
>  	{ git submodule status --recursive 2>err; echo $?>status; } |
> -		grep -q X/S &&
> +		{ sleep 1 && grep -q X/S; } &&
>  	test_must_be_empty err &&
>  	test_match_signal 13 "$(cat status)"
>  '
> 
> The problem is that git-submodule may write all of its output before
> grep exits, and it gets stored in the pipe buffer. And then even if grep
> exits before reading all of it, it is too late for SIGPIPE, and the data
> in the pipe is just discarded by the OS.
> 
> So this:
> 
> > The issue is caused by us using grep(1) to terminate the pipe on the
> > first matching line in the recursing git-submodule(1) process. Standard
> > streams are typically buffered though, so this condition is racy and may
> > cause us to terminate the pipe after git-submodule(1) has already
> > exited, and in that case we wouldn't see the expected signal.
> > 
> > Fix the issue by converting standard streams to be unbuffered. I have
> > only been able to reproduce this issue a single time after running t7422
> > with `--stress` after an extended amount of time, so I cannot claim to
> > be fully certain that this fix is sufficient.
> 
> isn't quite right. Even without input buffering on grep's part, it may
> be too slow to read the data. And adding a sleep as above shows that it
> still fails with your patch.

Great. I was hoping to nerd-snipe somebody into helping me out with the
last sentence in my above paragraph :) Happy to see that you bit.

> The usual way to reliably get SIGPIPE is to make sure the writer
> produces enough data to fill the pipe buffer. But it's tricky to get
> "submodule status" to produce a lot of data without having a ton of
> submodules, which is expensive to set up.
> 
> But we can hack around it by stuffing the pipe full with a separate
> process. Like this:
> 
> diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh
> index f21e920367..c4df2629e8 100755
> --- a/t/t7422-submodule-output.sh
> +++ b/t/t7422-submodule-output.sh
> @@ -167,8 +167,15 @@ do
>  done
>  
>  test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE' '
> -	{ git submodule status --recursive 2>err; echo $?>status; } |
> -		grep -q X/S &&
> +	{
> +		# stuff pipe buffer full of input so that submodule status
> +		# will require blocking on write; this script will write over
> +		# 128kb. It might itself get SIGPIPE, so we must not &&-chain
> +		# it directly.
> +		{ perl -le "print q{foo} for (1..33000)" || true; } &&
> +		git submodule status --recursive 2>err
> +		echo $? >status
> +	} | { sleep 1 && head -n 1 >/dev/null; } &&
>  	test_must_be_empty err &&
>  	test_match_signal 13 "$(cat status)"
>  '
> A few notes:
> 
>   - the sleep is still there to demonstrate that it always works, but
>     obviously we'd want to remove that

Nice, this indeed lets me reproduce the issue reliably.

>   - I swapped out "grep" for "head". What we are matching is not
>     relevant; the important thing is that the reader closes the pipe
>     immediately. So I guess in that sense we could probably even just
>     pipe to "true" or similar.

I think the grep(1) is relevant though. The test explicitly verifies
that `--recursive` propagates SIGPIPE, so we must make sure that we
trigger the SIGPIPE when the child process produces output, not when the
parent process produces it. That's why we grep for "X/S", where "X" is a
submodule -- it means that we know that it is currently the subprocess
doing its thing.

It also simplifies the code a bit given that the call to Perl doesn't
need `|| true` anymore.

>   - I tried using test_seq to avoid the inline perl, but it doesn't
>     work! The problem is that it's implemented as a shell function. So
>     when it gets SIGPIPE, the whole subshell is killed, and we never
>     even run git-submodule at all. So it has to be a separate process
>     (though I guess it could be test_seq in a subshell).

And that one should also work if we retain the grep. I wonder though
whether we shouldn't prefer to use Perl regardless as it's likely to be
faster when generating all that gibberish. Perl is basically a hard
prerequisite for our tests anyway, so it doesn't really hurt to call it
here.

Patrick

  reply	other threads:[~2025-01-06 11:12 UTC|newest]

Thread overview: 69+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-03 14:46 [PATCH 00/10] A couple of CI improvements Patrick Steinhardt
2025-01-03 14:46 ` [PATCH 01/10] t0060: fix EBUSY in MinGW when setting up runtime prefix Patrick Steinhardt
2025-01-03 14:46 ` [PATCH 02/10] t7422: fix flaky test caused by buffered stdout Patrick Steinhardt
2025-01-03 18:17   ` Jeff King
2025-01-06 11:12     ` Patrick Steinhardt [this message]
2025-01-07  2:39       ` Jeff King
2025-01-07  8:47         ` Patrick Steinhardt
2025-01-07  8:50           ` Patrick Steinhardt
2025-01-09  7:17           ` Jeff King
2025-01-09 16:16             ` Junio C Hamano
2025-01-07  2:48       ` Jeff King
2025-01-07 16:02         ` Junio C Hamano
2025-01-03 14:46 ` [PATCH 03/10] github: adapt containerized jobs to be rootless Patrick Steinhardt
2025-01-03 14:46 ` [PATCH 04/10] github: convert all Linux jobs to be containerized Patrick Steinhardt
2025-01-03 18:56   ` Jeff King
2025-01-03 19:06     ` Jeff King
2025-01-06 11:12       ` Patrick Steinhardt
2025-01-03 19:16   ` Junio C Hamano
2025-01-03 14:46 ` [PATCH 05/10] github: simplify computation of the job's distro Patrick Steinhardt
2025-01-03 19:09   ` Junio C Hamano
2025-01-03 14:46 ` [PATCH 06/10] gitlab-ci: remove the "linux-old" job Patrick Steinhardt
2025-01-03 19:12   ` Junio C Hamano
2025-01-03 14:46 ` [PATCH 07/10] gitlab-ci: add linux32 job testing against i386 Patrick Steinhardt
2025-01-03 14:46 ` [PATCH 08/10] ci: stop special-casing for Ubuntu 16.04 Patrick Steinhardt
2025-01-03 14:46 ` [PATCH 09/10] ci: use latest Ubuntu release Patrick Steinhardt
2025-01-03 14:46 ` [PATCH 10/10] ci: remove stale code for Azure Pipelines Patrick Steinhardt
2025-01-03 18:57 ` [PATCH 00/10] A couple of CI improvements Jeff King
2025-01-06 11:16 ` [PATCH v2 " Patrick Steinhardt
2025-01-06 11:16   ` [PATCH v2 01/10] t0060: fix EBUSY in MinGW when setting up runtime prefix Patrick Steinhardt
2025-01-06 11:16   ` [PATCH v2 02/10] t7422: fix flaky test caused by buffered stdout Patrick Steinhardt
2025-01-07  2:49     ` Jeff King
2025-01-06 11:16   ` [PATCH v2 03/10] github: adapt containerized jobs to be rootless Patrick Steinhardt
2025-01-06 11:16   ` [PATCH v2 04/10] github: convert all Linux jobs to be containerized Patrick Steinhardt
2025-01-06 11:16   ` [PATCH v2 05/10] github: simplify computation of the job's distro Patrick Steinhardt
2025-01-06 11:16   ` [PATCH v2 06/10] gitlab-ci: remove the "linux-old" job Patrick Steinhardt
2025-01-06 11:16   ` [PATCH v2 07/10] gitlab-ci: add linux32 job testing against i386 Patrick Steinhardt
2025-01-06 11:16   ` [PATCH v2 08/10] ci: stop special-casing for Ubuntu 16.04 Patrick Steinhardt
2025-01-06 11:16   ` [PATCH v2 09/10] ci: use latest Ubuntu release Patrick Steinhardt
2025-01-06 11:16   ` [PATCH v2 10/10] ci: remove stale code for Azure Pipelines Patrick Steinhardt
2025-01-07 12:30 ` [PATCH v3 00/10] A couple of CI improvements Patrick Steinhardt
2025-01-07 12:30   ` [PATCH v3 01/10] t0060: fix EBUSY in MinGW when setting up runtime prefix Patrick Steinhardt
2025-01-07 12:30   ` [PATCH v3 02/10] t7422: fix flaky test caused by buffered stdout Patrick Steinhardt
2025-01-09  7:33     ` Jeff King
2025-01-07 12:30   ` [PATCH v3 03/10] github: adapt containerized jobs to be rootless Patrick Steinhardt
2025-01-07 12:30   ` [PATCH v3 04/10] github: convert all Linux jobs to be containerized Patrick Steinhardt
2025-01-07 12:30   ` [PATCH v3 05/10] github: simplify computation of the job's distro Patrick Steinhardt
2025-01-07 12:30   ` [PATCH v3 06/10] gitlab-ci: remove the "linux-old" job Patrick Steinhardt
2025-01-07 12:30   ` [PATCH v3 07/10] gitlab-ci: add linux32 job testing against i386 Patrick Steinhardt
2025-01-07 12:30   ` [PATCH v3 08/10] ci: stop special-casing for Ubuntu 16.04 Patrick Steinhardt
2025-01-07 12:30   ` [PATCH v3 09/10] ci: use latest Ubuntu release Patrick Steinhardt
2025-01-07 12:30   ` [PATCH v3 10/10] ci: remove stale code for Azure Pipelines Patrick Steinhardt
2025-01-10 11:31 ` [PATCH v4 00/10] A couple of CI improvements Patrick Steinhardt
2025-01-10 11:31   ` [PATCH v4 01/10] t0060: fix EBUSY in MinGW when setting up runtime prefix Patrick Steinhardt
2025-01-10 11:31   ` [PATCH v4 02/10] t7422: fix flaky test caused by buffered stdout Patrick Steinhardt
2025-01-24  9:16     ` Christian Couder
2025-01-10 11:31   ` [PATCH v4 03/10] github: adapt containerized jobs to be rootless Patrick Steinhardt
2025-01-24  9:56     ` Christian Couder
2025-08-28  9:58     ` Johannes Schindelin
2025-11-17 17:30       ` Johannes Schindelin
2025-01-10 11:32   ` [PATCH v4 04/10] github: convert all Linux jobs to be containerized Patrick Steinhardt
2025-01-10 11:32   ` [PATCH v4 05/10] github: simplify computation of the job's distro Patrick Steinhardt
2025-01-10 11:32   ` [PATCH v4 06/10] gitlab-ci: remove the "linux-old" job Patrick Steinhardt
2025-01-10 11:32   ` [PATCH v4 07/10] gitlab-ci: add linux32 job testing against i386 Patrick Steinhardt
2025-01-10 11:32   ` [PATCH v4 08/10] ci: stop special-casing for Ubuntu 16.04 Patrick Steinhardt
2025-01-10 11:32   ` [PATCH v4 09/10] ci: use latest Ubuntu release Patrick Steinhardt
2025-01-10 11:32   ` [PATCH v4 10/10] ci: remove stale code for Azure Pipelines Patrick Steinhardt
2025-01-10 12:03   ` [PATCH v4 00/10] A couple of CI improvements Jeff King
2025-01-24  9:59   ` Christian Couder
2025-01-27  7:00     ` Patrick Steinhardt

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=Z3u6lj_bpM7N93Fd@pks.im \
    --to=ps@pks.im \
    --cc=git@vger.kernel.org \
    --cc=peff@peff.net \
    /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).