Repository navigation
Never run the standard output and standard error delegates at once [patch] - #123
Merged
Merged
Conversation
… once The two pipes are read concurrently, and each read loop called its delegate from its own continuation, so the two delegates ran inside each other on different thread-pool threads. A caller sharing one List<T> or StringBuilder between them lost lines with no exception. Delivery now takes one lock per run, so the reads stay concurrent while the delegates run one at a time, and each stream keeps its own order. Fixes #95 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017UuQscGFrjteUf9Ui3jWdd
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #95
What was wrong
AsyncProcessStreamReaderreads stdout and stderr in two independent loops, and each loop called its delegate from its own read continuation. SoonStandardOutputandonStandardErrorcould run inside each other on different thread-pool threads. If both delegates added to oneList<string>(the most natural way to capture a command's whole output), lines disappeared with no exception.Change
This is option 1 from the triage: serialise delivery.
AsyncProcessStreamReaderholds oneLockper run and takes it around each delegate call. The two pipe reads still run concurrently. Only the hand-off to the caller is serialised, and each stream's chunks still arrive in the order they were written.OutputHandlerXML docs now state the contract: the two actions never run at the same time, so they can share state without locking.A caller whose delegates already take their own lock is unaffected, apart from a lock that is now redundant.
Test
ExecuteAsyncShouldNeverRunTheStandardOutputAndStandardErrorDelegatesAtOnceinterleaves 5,000 lines on each stream into a single non-thread-safeList<string>through both delegates, five times over. On every attempt it asserts that:It is skipped (
Assert.Inconclusive) on Windows, like the neighbouring shell-based test, because the code it covers is platform independent.Assert.AreEqual(0, overlaps).Generated by Claude Code