Skip to content

Bound the stderr drain when an inhibitor helper exits at startup [patch] - #45

Open
matt-edmondson wants to merge 1 commit into
mainfrom
fix/bounded-startup-drain-43
Open

matt-edmondson wants to merge 1 commit into
mainfrom
fix/bounded-startup-drain-43

Conversation

@matt-edmondson

Copy link
Copy Markdown
Collaborator

Fixes #43

What was wrong

When the helper exited within StartupGrace, ChildProcessHold.Start called the untimed process.WaitForExit() to collect the last stderr lines. With redirected output, that call also waits for both pipes to reach EOF. A background child that inherited the helper's stderr keeps the pipe open for as long as it runs, so Start hung for that long. It hung inside LinuxSleepBlocker's and KeepAwakeController's locks, so IsActive, toggles and Dispose queued behind it.

Change

  • StartupErrorOutput now records the null line that ErrorDataReceived delivers at end of input, using a TaskCompletionSource (nothing to dispose, so a late callback can't hit a disposed handle).
  • After the helper exits, Start waits for that end of input for at most ExitDrainTimeout (1 s), then reads whatever stderr has arrived and throws the same SleepBlockException as before.
  • Stdout is still drained and dropped. Nothing now waits on its EOF.
  • I didn't kill the leftover child. Once the helper has exited, the child has been reparented, and Kill(entireProcessTree) on the dead helper can't reach it.

Tests

  • New: Start_WhenTheHelperExitsLeavingAChildOnItsStandardError_ThrowsWithoutWaitingForTheChild. The helper writes a reason to stderr, starts sleep 20 &, and exits 1. The test checks that Start throws within 5 s and that the message includes the stderr line. It is Inconclusive off Unix, like the other helper tests.
  • With the fix reverted, this test fails after about 20 s.
  • Full suite on Linux / .NET 10: 47/47 passed. The existing stderr-capture tests from Include an inhibitor helper's stderr when it exits immediately [patch] #40 still pass.

Generated by Claude Code

ChildProcessHold.Start waited for the helper's redirected streams with an
untimed WaitForExit after the helper had exited. A background child that
inherited the helper's stderr kept the pipe open for as long as it ran, so
Start (inside the blocker's and controller's locks) hung with it.

Wait for stderr's end of input for at most one second instead, then report
whatever stderr arrived by then.

Fixes #43

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW5nyEDqgqjU6AtLLLi
@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable/Acquire hangs, holding both locks, when a failed helper leaves a background child on its stderr: the untimed WaitForExit waits for that child

2 participants