Skip to content

fix(runner): stop the timeout watchdog pinning the caller's output - #1273

Merged
Chemaclass merged 2 commits into
mainfrom
fix/1137-suites-acceptance-test-flaky-on-bash-3
Aug 16, 2026
Merged

fix(runner): stop the timeout watchdog pinning the caller's output#1273
Chemaclass merged 2 commits into
mainfrom
fix/1137-suites-acceptance-test-flaky-on-bash-3

Conversation

@Chemaclass

Copy link
Copy Markdown
Member

🤔 Background

Related #1137

[suite:acceptance] in that test's fixture carries test-timeout = 60, so the union case is the only test in the file whose nested run arms a timeout watchdog — the real difference between it and its neighbours, not the parallel/no-parallel conflict the issue guessed at (suite options resolve in argv order, deterministically).

That watchdog was detached on stdin/stdout/stderr only, so it still inherited both descriptors the runner dups from stdout, plus the outer run's pair when a test spawns a nested bashunit. One that outlived its parent held the pipe its caller was reading with no visible writer, and the caller waited out the whole timeout budget for an EOF that could not arrive. Verified on a real Bash 3.0 build: a killed run blocked its capture for 20 of 20 seconds, 5 after.

💡 Changes

  • The watchdog closes both inherited descriptors with exec; a per-command 3>&- 5>&- would not, since bash implements that by dup'ing each to a fd >= 10 the child inherits anyway
  • It also refuses to fire once the run it was policing is gone — after a full budget the pid it holds may name something else, and the two kills that follow signal a process group
  • Regression test: a killed run must release its captured output at once (20s before, 5s after)

`run_with_timeout` forks a watchdog that sleeps out the timeout budget. It
was detached on 0/1/2 only, so it still inherited both descriptors the
runner dups from stdout -- FD 3, handed to the test body, and FD 5, kept for
the EXIT trap -- plus the outer run's pair when the test spawns a nested
bashunit. A watchdog that outlived its parent therefore held the pipe its
caller was reading with no visible writer, and that caller waited out the
whole budget for an EOF that could not arrive. Verified on a real Bash 3.0
build: a SIGKILLed run blocked its capture for 20 of 20 seconds.

Only a `--test-timeout` run reaches this, which is why the suites acceptance
test is the one that shows it: `[suite:acceptance]` carries `test-timeout =
60`, so the union case is the only test there whose nested run arms a
watchdog at all. The conditions for an orphan -- the group kill that ends it
"intermittently misses", per the comment already in that function -- are
load-dependent, matching a flake seen only on loaded CI.

The watchdog now closes both descriptors with `exec`, which closes for good;
a per-command `3>&- 5>&-` would not, because bash implements that by dup'ing
each descriptor to a fd >= 10 that the child inherits anyway. It also
refuses to fire once the run it was policing is gone: after a full budget
the pid it holds may name something else, and the two kills that follow
signal a process GROUP.

Related #1137
@Chemaclass Chemaclass added the bug Something isn't working label Aug 16, 2026
@Chemaclass Chemaclass self-assigned this Aug 16, 2026
Closing the inherited descriptors keeps an orphaned watchdog from pinning
its caller's output, but it still slept out the whole budget before
checking anything -- so a killed run left a process behind for up to
`--test-timeout` seconds, and CI reported it as an orphan. Wait in
one-second steps instead, giving up as soon as either the run or the test
being watched is gone. That also retires the "intermittently misses" group
kill as a correctness dependency: a watchdog the signal misses now exits on
its own within a second rather than waking up later to signal a process
GROUP by a pid the kernel may have reused.

The regression test now generates its fixture so the body can announce
itself, and the kill lands at that moment rather than after a guessed
delay. A fixed 2s sleep killed the run before it had started a test -- or
printed anything -- on the slower macOS runner, which is what failed the
build-verify job rather than the code under test.

Related #1137
@Chemaclass
Chemaclass merged commit ea50674 into main Aug 16, 2026
37 checks passed
@Chemaclass
Chemaclass deleted the fix/1137-suites-acceptance-test-flaky-on-bash-3 branch August 16, 2026 10:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant