Fix Process.WaitForExit hang when grandchild process keeps redirected pipe open#125732
Fix Process.WaitForExit hang when grandchild process keeps redirected pipe open#125732
Conversation
Add CancelDueToProcessExit and CancelDueToProcessExitAsync methods to AsyncStreamReader that wait a timeout for the read task to finish and cancel it if it doesn't. Replace all EOF.GetAwaiter().GetResult() and EOF.WaitAsync() calls in WaitForExitCore (Unix/Windows) and WaitForExitAsync with the new timeout-based methods. Use 300ms default when no timeout is provided. Add Theory test reproducing the hang scenario with a grandchild process that sleeps for 8 hours. Co-authored-by: adamsitnik <[email protected]>
… grandchild process dispatch Co-authored-by: adamsitnik <[email protected]>
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot please address my feedback
src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs
Outdated
Show resolved
Hide resolved
|
Tagging subscribers to this area: @dotnet/area-system-diagnostics-process |
Co-authored-by: adamsitnik <[email protected]>
Co-authored-by: adamsitnik <[email protected]>
|
@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR |
🤖 Copilot Code Review — PR #125790Holistic AssessmentMotivation: The problem is real and well-documented — Approach: The fix introduces a timeout-based cancellation mechanism in Summary: Detailed Findings
|
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot address my feedback
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs
Show resolved
Hide resolved
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs
Outdated
Show resolved
Hide resolved
…thods parameterless, remove EOF Co-authored-by: adamsitnik <[email protected]>
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot please address my feedback
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/AsyncStreamReader.cs
Outdated
Show resolved
Hide resolved
…ss from callers Co-authored-by: adamsitnik <[email protected]>
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs
Outdated
Show resolved
Hide resolved
…xitAsync local function Co-authored-by: adamsitnik <[email protected]>
|
@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR |
🤖 Copilot Code Review — PR #125790Note This review was AI/Copilot-generated. Models consulted: Claude Opus 4.6 (primary), Gemini 2.5 Pro, GPT-4.1. Holistic AssessmentMotivation: The problem is real and well-documented — Approach: The fix replaces the unbounded Summary: ✅ LGTM. The code is correct, thread-safe, and addresses the root cause. All prior review feedback has been addressed. No blocking issues remain. Detailed Findings✅ Correctness —
|
Description
Process.WaitForExit()hangs indefinitely when stdout/stderr is redirected and a grandchild process inherits the pipe handle. The root cause: after the child process exits,AsyncStreamReaderblocks forever onEOF.GetAwaiter().GetResult()because the grandchild still holds the write end of the pipe open.Changes
AsyncStreamReader.cs— Added parameterlessCancelDueToProcessExit()andCancelDueToProcessExitAsync(CancellationToken)methods that use a privateStreamDrainDefaultTimeoutMsconstant (300ms) internally. These wait up to the default timeout for_readToBufferTaskto complete, then cancel via_cts.Cancel()if it hasn't. The async variant accepts aCancellationTokento allow callers to cancel the stream drain phase. Removed the now-unusedEOFproperty.Process.Unix.cs/Process.Windows.cs— Replaced_output?.EOF.GetAwaiter().GetResult()withCancelDueToProcessExit(). Now drains streams for all exited processes (not justTimeout.Infinite).Process.cs— UpdatedWaitForExitAsyncto useCancelDueToProcessExitAsync(cancellationToken)instead ofEOF.WaitAsync(cancellationToken), forwarding the caller's cancellation token to the stream drain phase.ProcessWaitingTests.cs— Added[Theory]covering all fourWaitForExitoverloads: spawns a child with redirected output that usesCreateProcessto create a sleeping grandchild, asserts WaitForExit completes in <2s.💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.