git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 02/10] t7422: fix flaky test caused by buffered stdout

From
Jeff King <peff@peff.net>
Date
Jan 3, 2025, 18:17 UTC
Message-ID
<20250103181739.GA2527684@coredump.intra.peff.net>
In-Reply-To
<20250103-b4-pks-ci-fixes-v1-2-a9bb95dff833@pks.im>
On Fri, Jan 03, 2025 at 03:46:39PM +0100, Patrick Steinhardt wrote:
Show 9 quoted lines
> 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.

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

  - 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 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).

Anyway, hopefully that gives you enough to play around with.

-Peff
Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 4 of 57 in “A couple of CI improvements”
  1. 00/10 A couple of CI improvementsPatrick Steinhardt, Jan 3, 2025
  2. 01/10 t0060: fix EBUSY in MinGW when setting up runtime prefixPatrick Steinhardt, Jan 3, 2025
  3. 02/10 t7422: fix flaky test caused by buffered stdoutPatrick Steinhardt, Jan 3, 2025
  4. Jeff KingJan 3, 2025
  5. Patrick SteinhardtJan 6, 2025
  6. Jeff KingJan 7, 2025
  7. Patrick SteinhardtJan 7, 2025
  8. Patrick SteinhardtJan 7, 2025
  9. Jeff KingJan 9, 2025
  10. Junio C HamanoJan 9, 2025
  11. Jeff KingJan 7, 2025
  12. Junio C HamanoJan 7, 2025
  13. 03/10 github: adapt containerized jobs to be rootlessPatrick Steinhardt, Jan 3, 2025
  14. 05/10 github: simplify computation of the job's distroPatrick Steinhardt, Jan 3, 2025
  15. Junio C HamanoJan 3, 2025
  16. 06/10 gitlab-ci: remove the "linux-old" jobPatrick Steinhardt, Jan 3, 2025
  17. Junio C HamanoJan 3, 2025
  18. 04/10 github: convert all Linux jobs to be containerizedPatrick Steinhardt, Jan 3, 2025
  19. Jeff KingJan 3, 2025
  20. Jeff KingJan 3, 2025
  21. Patrick SteinhardtJan 6, 2025
  22. Junio C HamanoJan 3, 2025
  23. 07/10 gitlab-ci: add linux32 job testing against i386Patrick Steinhardt, Jan 3, 2025
  24. 08/10 ci: stop special-casing for Ubuntu 16.04Patrick Steinhardt, Jan 3, 2025
  25. 09/10 ci: use latest Ubuntu releasePatrick Steinhardt, Jan 3, 2025
  26. 10/10 ci: remove stale code for Azure PipelinesPatrick Steinhardt, Jan 3, 2025
  27. Jeff KingJan 3, 2025
  28. 00/10 A couple of CI improvementsPatrick Steinhardt, Jan 6, 2025
  29. 01/10 t0060: fix EBUSY in MinGW when setting up runtime prefixPatrick Steinhardt, Jan 6, 2025
  30. 03/10 github: adapt containerized jobs to be rootlessPatrick Steinhardt, Jan 6, 2025
  31. 02/10 t7422: fix flaky test caused by buffered stdoutPatrick Steinhardt, Jan 6, 2025
  32. Jeff KingJan 7, 2025
  33. 04/10 github: convert all Linux jobs to be containerizedPatrick Steinhardt, Jan 6, 2025
  34. 06/10 gitlab-ci: remove the "linux-old" jobPatrick Steinhardt, Jan 6, 2025
  35. 05/10 github: simplify computation of the job's distroPatrick Steinhardt, Jan 6, 2025
  36. 07/10 gitlab-ci: add linux32 job testing against i386Patrick Steinhardt, Jan 6, 2025
  37. 09/10 ci: use latest Ubuntu releasePatrick Steinhardt, Jan 6, 2025
  38. 08/10 ci: stop special-casing for Ubuntu 16.04Patrick Steinhardt, Jan 6, 2025
  39. 10/10 ci: remove stale code for Azure PipelinesPatrick Steinhardt, Jan 6, 2025
  40. 00/10 A couple of CI improvementsPatrick Steinhardt, Jan 10, 2025
  41. 01/10 t0060: fix EBUSY in MinGW when setting up runtime prefixPatrick Steinhardt, Jan 10, 2025
  42. 02/10 t7422: fix flaky test caused by buffered stdoutPatrick Steinhardt, Jan 10, 2025
  43. Christian CouderJan 24, 2025
  44. 04/10 github: convert all Linux jobs to be containerizedPatrick Steinhardt, Jan 10, 2025
  45. 03/10 github: adapt containerized jobs to be rootlessPatrick Steinhardt, Jan 10, 2025
  46. Christian CouderJan 24, 2025
  47. Johannes SchindelinAug 28, 2025
  48. Johannes SchindelinNov 17, 2025
  49. 05/10 github: simplify computation of the job's distroPatrick Steinhardt, Jan 10, 2025
  50. 06/10 gitlab-ci: remove the "linux-old" jobPatrick Steinhardt, Jan 10, 2025
  51. 07/10 gitlab-ci: add linux32 job testing against i386Patrick Steinhardt, Jan 10, 2025
  52. 08/10 ci: stop special-casing for Ubuntu 16.04Patrick Steinhardt, Jan 10, 2025
  53. 09/10 ci: use latest Ubuntu releasePatrick Steinhardt, Jan 10, 2025
  54. 10/10 ci: remove stale code for Azure PipelinesPatrick Steinhardt, Jan 10, 2025
  55. Jeff KingJan 10, 2025
  56. Christian CouderJan 24, 2025
  57. Patrick SteinhardtJan 27, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.