Conversation
…mentations and tests Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bb3ce72c-ce34-45fe-b196-16d776008a31 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…ary await in test helper Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/bb3ce72c-ce34-45fe-b196-16d776008a31 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-system-diagnostics-process |
There was a problem hiding this comment.
Pull request overview
Adds a new synchronous Process.ReadAllLines(TimeSpan? timeout = default) API that multiplexes stdout/stderr and yields interleaved ProcessOutputLine instances without deadlocks, plus updates tests and the ref assembly.
Changes:
- Introduces
Process.ReadAllLines(...)and shared buffer-to-line parsing helpers. - Implements platform-specific synchronous multiplexing iterators for Windows (overlapped I/O +
WaitAny) and Unix (poll(2)+ non-blocking reads). - Extends existing streaming tests to validate both sync and async line-reading paths.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Diagnostics.Process/tests/ProcessStreamingTests.cs | Expands ReadAllLinesAsync test coverage to also exercise the new sync API. |
| src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Multiplexing.cs | Adds ReadAllLines(...) entrypoint and shared helpers for parsing/compacting line buffers. |
| src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Multiplexing.Windows.cs | Adds Windows-specific synchronous line multiplexing iterator using overlapped I/O. |
| src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Multiplexing.Unix.cs | Adds Unix-specific synchronous line multiplexing iterator using poll and non-blocking reads. |
| src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs | Adds the ReadAllLines(...) signature to the reference assembly. |
…UTF-16/UTF-32 support; fix Windows use-after-free by canceling pending IO in finally Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/a9eadcb8-dd77-4933-9877-920b21513e25 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
…plexing.Unix.cs Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/a9eadcb8-dd77-4933-9877-920b21513e25 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/libraries/System.Diagnostics.Process/tests/ProcessStreamingTests.cs:213
- The new sync path adds incremental decoding/line-splitting logic that’s meant to support multi-byte encodings (UTF-16/UTF-32, partial sequences across reads, BOM). The updated tests don’t currently exercise
StandardOutputEncoding/StandardErrorEncodingvariations; adding a theory that runs both sync and async with non-default encodings would help prevent regressions in the decoder/line parsing behavior.
public async Task ReadAllLines_ReadsBothOutputAndError(string standardOutput, string standardError, bool useAsync)
{
using Process process = StartLinePrintingProcess(
string.IsNullOrEmpty(standardOutput) ? DontPrintAnything : standardOutput,
string.IsNullOrEmpty(standardError) ? DontPrintAnything : standardError);
…UTF-16/UTF-32 Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/4a5f20d7-dcd7-415c-b5c8-e15806a7e5c0 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/4a5f20d7-dcd7-415c-b5c8-e15806a7e5c0 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
🤖 Copilot Code Review — PR #127106Note This review was AI-generated using the code-review skill with multi-model analysis (Claude Sonnet 4, GPT-5.3-Codex, Gemini 2.5 Pro). Holistic AssessmentMotivation: The PR implements the synchronous counterpart ( Approach: Platform-specific multiplexing (overlapped I/O + Summary: Detailed Findings✅ API Approval — Matches approved shape exactlyThe ref assembly signature ✅ Resource Management — Correct on both platforms
✅ Encoding & Line Parsing — Correct char-level operation
✅
|
This comment was marked as resolved.
This comment was marked as resolved.
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/869a5549-5949-464f-87b5-fdec953f9edd Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
@copilot address this feedback
@copilot address this feedback @copilot make sure you build the process solution and run all tests before pushing any changes |
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/fb89045e-df31-40c7-84fc-9f8df3a0e2da Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Both items addressed in d19dd3e:
|
tarekgh
left a comment
There was a problem hiding this comment.
Added a couple of comments, LGTM otherwise.
…ally; add test Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/8ed9ed48-a800-4d97-ac90-68590832e24e Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
|
@adamsitnik @tarekgh, @copilot struggles to understand the BOM/preamble handling which causes extra review cycles. |
I agree, I will fix it myself soon |
…e in finally; add test" This reverts commit c8bda3e.
…it unconsumed bytes at the end
|
@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
|
@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR |
🤖 Copilot Code Review — PR #127106Note This review was generated by Copilot using multi-model analysis (Claude Opus 4.6 primary, with GPT-5.3-Codex and Claude Opus 4.5 sub-agents). Holistic AssessmentMotivation: The PR is well-justified — it implements the synchronous Approach: The implementation mirrors the platform-specific multiplexing strategy already used by Summary: ✅ LGTM. The implementation is thorough, correctly handles resource cleanup in iterators, properly detects BOM/encoding across partial reads, handles all line ending styles (\r, \n, \r\n), and the test coverage is comprehensive. The API shape matches the approved proposal exactly. No blocking issues found. A few minor suggestions below. Detailed Findings✅ API Approval — Matches approved shapeThe ✅ Resource Safety — Correct cleanup in both iterator implementationsBoth the Unix (
This was confirmed by both sub-agent reviews (Claude Opus 4.5 and GPT-5.3-Codex). ✅ BOM/Preamble Detection — Correctly handles partial readsThe accumulation logic ( ✅ Line Parsing — Correct \r/\n/\r\n handling
💡 Eager vs. Lazy Validation — Minor behavioral difference (not blocking)
💡 DecodeAndAppendChars double-scans bytes — Acceptable trade-off
💡 Test Coverage — Consider adding in follow-upA few edge cases that could be tested in a follow-up:
These are minor and do not block merge.
|
adamsitnik
left a comment
There was a problem hiding this comment.
LGTM, thank you for all the code reviews @tmds @tarekgh and @JeremyKuhne !
Description
Implements
Process.ReadAllLines(TimeSpan? timeout = default)— a synchronous, single-threaded multiplexing API that reads interleaved lines from both stdout and stderr without deadlocks.API approved in #125838 (comment).
Cross-platform core (
Process.Multiplexing.cs)ReadAllLines()validates state eagerly viaValidateReadAllState, passesEncodinginstances per stream to the platform-specificReadPipesToLinesiterator (which createsDecoderinstances locally so they can be replaced on encoding detection)DecodeAndAppendChars(incremental byte→char decoding viaDecoder),ParseLinesFromCharBuffer(scans for'\r'or'\n'viaIndexOfAny, handles'\r\n'as a single terminator),EmitRemainingCharsAsLine(EOF partial line)DecodeAndAppendCharsacceptsref int charStartIndexand always compacts the char buffer (moving unconsumed data to the front) before considering growth whencharStartIndex > 0, ensuringRentLargerBufferonly copies the unconsumed tail rather than the entire buffer including the consumed prefixDecodeAndAppendCharshas no early return guard — thewhile (charEndIndex + charCount > charBuffer.Length)buffer growth loop is trivially skipped whencharCount == 0, andGetCharsis still called so partial bytes buffered internally by theDecoderare preservedDecodeBytesAndParseLinesaccumulates the initial bytes of each stream into a small 4-bytebomAccumbuffer before callingSkipPreambleOrDetectEncoding, ensuring that a UTF-32 LE BOM (FF FE 00 00) is never misclassified as a UTF-16 LE BOM (FF FE) when the first OS read delivers fewer than 4 bytes — a newFlushBomAccumulationhelper resolves any accumulated bytes at EOFSkipPreambleOrDetectEncodingoperates at the byte level and is always called with at least 4 accumulated bytes (or all available bytes at EOF), mimickingStreamReader's two-phase approach: first checks whether the bytes match the current encoding's preamble (likeStreamReader.IsPreamble), then falls back to BOM-based encoding detection (likeStreamReader.DetectEncoding) supporting UTF-8, UTF-16 LE/BE, and UTF-32 LE/BE — if a different encoding is detected, theDecoderis replaced accordinglychar[]buffers usingEncoding.GetDecoder()per stream, correctly handling multi-byte encodings (UTF-16, UTF-32) and partial sequences across reads — byte-level0x0Ascanning is not usedRentLargerBuffer<T>is a single generic helper used for bothbyte[]andchar[]buffer growth (no duplication)CompactOrGrowCharBufferwas removed —DecodeAndAppendCharsalready handles compacting and growing lazily on the next decode callWindows (
Process.Multiplexing.Windows.cs)ReadPipesToLinesiterator using overlapped I/O withWaitAnymultiplexingNativeOverlapped*asnintto avoid pointer fields in iterator state machine; casts back inside scopedunsafeblocks (C# 13 allows unsafe in iterators, butyield returnmust be in safe context)DangerousAddRef/DangerousReleaseneeded — all Windows syscalls acceptSafeHandledirectlyUnix (
Process.Multiplexing.Unix.cs)ReadPipesToLinesiterator usingpoll(2)with non-blocking readsPollForPipeActivityhelper acceptsSpan<Interop.PollEvent>and encapsulatesPreparePollFds+ timeout check + inlinepollcall (singletriggeredvariable) + error/EINTR handling, shared by bothReadPipesToLinesandReadPipesto eliminate code duplicationReadPipes(non-iterator) usesstackallocfor the poll event buffer to avoid heap allocation, while the iterator-basedReadPipesToLinespasses a heap-allocated array (implicitly converted toSpan)HandlePipeLineReadhelper encapsulates the read → decode → preamble/encoding check → parse lines flowfinallyblock restores each file descriptor to blocking mode (DangerousSetIsNonBlocking(fd, 0)) before callingDangerousRelease(), ensuring the handles are cleaned up in the correct stateTests
ReadAllLines_WorksWithNonDefaultEncodingstheory tests UTF-8, UTF-16, and UTF-32 encodings for both sync and async paths, asserting identical output — test logic is inlined into the test method usingCreateProcesswith static lambdas (no separateRemotelyInvokablehelper methods)ReadAllLines_WorksWithMultiByteCharacterstheory tests multi-byte character handling with UTF-8, UTF-16, and UTF-32, writing raw encoded bytes split at the byte-array midpoint (which lands inside a multi-byte character for all three encodings) with an explicitFlush()between the two halves — this deterministically exercises decoder state across reads and would fail if partial-sequence handling were brokenReadAllLines_HandlesMixedLineEndingstheory tests all three line-terminator styles (\r\n, bare\n, bare\r) and an unterminated final chunk in the same stream, asserting thatParseLinesFromCharBufferand EOF flushing produce the correct four linesReadAllLines_HandlesPartialBomAcrossReadstheory tests that a UTF-32 LE BOM split across two separately-flushed writes (FF FEthen00 00+ content) is correctly accumulated and detected rather than misclassified as a UTF-16 LE BOM — exercises thebomAccumdeferred-detection pathReadAllLines_ReadsVeryLongLinestheory tests buffer growth by producing lines over 8192 characters on both stdout and stderrEnumerateLinestest helper populatesList<string> capturedOutput, List<string> capturedErrordirectlyusing System.IO;toProcessStreamingTests.csto resolve a build error caused byStreamWriterandStreamreferences in the inlined test helpersBuild fix
ToTimeoutMillisecondscall toProcessUtils.ToTimeoutMillisecondsto match upstream refactor onmainthat moved this method fromProcesstoProcessUtils