Add ProcessOutputLine struct and Process.ReadAllLinesAsync#126987
Add ProcessOutputLine struct and Process.ReadAllLinesAsync#126987adamsitnik merged 7 commits intomainfrom
Conversation
- Add ProcessOutputLine readonly struct with Content and StandardError properties - Implement ReadAllLinesAsync as async IAsyncEnumerable in Process.Multiplexing.cs - Add types to reference assembly - Add ProcessStreamingTests.cs with comprehensive tests - Remove unused usings from edited files Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/b28836b3-abff-4763-8aff-2c77ebe0e022 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/b28836b3-abff-4763-8aff-2c77ebe0e022 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-system-diagnostics-process |
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot please address my feedback
…, use Assert.Single in test, fix IDE0059 Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/253bb871-3466-4fd3-bb3d-7f5fb4ece627 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
|
@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR |
There was a problem hiding this comment.
Pull request overview
Adds a new line-oriented, streaming output API to System.Diagnostics.Process, enabling consumers to asynchronously iterate interleaved stdout/stderr lines via a lightweight ProcessOutputLine value type.
Changes:
- Introduces
ProcessOutputLine(Content,StandardError) as a publicreadonly struct. - Implements
Process.ReadAllLinesAsyncas anIAsyncEnumerable<ProcessOutputLine>in the multiplexing implementation. - Adds a dedicated test suite covering validation, interleaving, large output, cancellation, and property semantics; registers new source/test files in csproj/ref.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Diagnostics.Process/tests/System.Diagnostics.Process.Tests.csproj | Registers the new ProcessStreamingTests.cs test file for compilation. |
| src/libraries/System.Diagnostics.Process/tests/ProcessStreamingTests.cs | Adds test coverage for ReadAllLinesAsync behavior and argument/state validation. |
| src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessOutputLine.cs | Adds the new public ProcessOutputLine struct used by the streaming API. |
| src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Multiplexing.cs | Implements Process.ReadAllLinesAsync using concurrent ReadLineAsync + Task.WhenAny. |
| src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj | Registers ProcessOutputLine.cs in the library project. |
| src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs | Updates public API surface to include ProcessOutputLine and ReadAllLinesAsync. |
🤖 Copilot Code Review — PR #126987Holistic AssessmentMotivation: The PR implements Approach: The implementation uses Summary: Detailed Findings✅ API Compliance — Matches approved shape exactlyThe implementation matches the approved API from #125838 (bartonjs's comment). All type names, method signatures, parameter names, and return types are correct. The ✅ Core Algorithm Correctness — Verified through manual traceTraced through 6 scenarios:
The ✅ Drain Logic — Correct direction verifiedWhen
|
adamsitnik
left a comment
There was a problem hiding this comment.
⚠️ isErrorPriority — Semantic subtlety worth documentingisError = completedTask == readError || (readOutput.IsCompleted && readError.IsCompleted);When
WhenAnyreturnsreadOutputbutreadErrorhas also completed by that time,isErrorbecomestrue— overridingWhenAny's choice to prioritize stderr. Stdout's completed task remains unconsumed until the next iteration. The behavior is correct (no data loss), but this priority guarantee is non-obvious. A human reviewer should confirm this is the desired semantic.
@copilot document this in the XML docs (this is on purpose)
… and early-break test Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/4ba7b698-1f25-4f03-9bfc-46024ae09633 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Documented in a79f653 — added a |
adamsitnik
left a comment
There was a problem hiding this comment.
LGTM.
I wish I could make the implementation simpler around cancelling the pending tasks when enumeration is stopped early, but I simple don't have any ideas right now.
adamsitnik
left a comment
There was a problem hiding this comment.
@copilot please address my feedback
…ruct dummy fields Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ad403c98-041e-4789-a64e-27b071d06213 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…Async+TryRead loop Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/921772ae-f4c2-44a3-9f62-2631c619eb62 Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com>
Description
Implements
ProcessOutputLineandProcess.ReadAllLinesAsyncfrom the approved API in #125838.ReadAllLinesAsyncstreams stdout/stderr lines as anIAsyncEnumerable<ProcessOutputLine>, interleaving them as they become available. The implementation uses aChannel<ProcessOutputLine>with rendezvous semantics (capacity 0) to multiplex two concurrentStreamReader.ReadLineAsynctasks into a single async enumerable. The consumer reads via aWaitToReadAsync/TryReadloop for efficient channel consumption. If the consumer stops enumerating early (e.g., breaks out ofawait foreach), in-flight reads are canceled via a linkedCancellationTokenSourcein atry/finallyblock, and both reader tasks are awaited before the CTS is disposed.Changes
ProcessOutputLine.cs— newreadonly structwithContentandStandardErrorproperties; constructor throwsArgumentNullExceptionwhencontentis nullProcess.Multiplexing.cs—ReadAllLinesAsyncimplementation using a Channel-based approach: twoReadToChannelAsynclocal tasks read from stdout/stderr and write to aChannel.CreateBounded<ProcessOutputLine>(0)rendezvous channel, with aWaitToReadAsync/TryReadloop for consumption. Channel completion is tracked viaInterlocked.Exchangeon abool firstCompletedflag — the second writer to finish completes the channel. A linkedCancellationTokenSourcewithtry/finallycancels (synchronously viaCancel()) and awaits both reader tasks on early enumeration stop before disposing the CTS. Removed unusedSystem.Runtime.InteropServicesusing.System.Diagnostics.Process.cs(ref) — public API surface forProcessOutputLine(with_dummy/_dummyPrimitivefields for proper struct layout) andReadAllLinesAsync, sorted alphabetically per ref file conventionsProcessStreamingTests.cs— 11 test methods (17 cases) mirroringProcessMultiplexingTests: dispose/redirect/mode validation, interleaved reads, large output, cancellation, property verification withAssert.Single, and early enumeration stop (ReadAllLinesAsync_StopsCleanlyWhenConsumerBreaksEarly). Structured for futureReadAllLines(sync) extension.System.Threading.Channelsproject reference