Skip to content

Bound the async exchange with the inline queue owner - #779

Merged
SimonCropp merged 1 commit into
mainfrom
fix-viewer-client-async-deadline
Aug 21, 2026
Merged

Bound the async exchange with the inline queue owner#779
SimonCropp merged 1 commit into
mainfrom
fix-viewer-client-async-deadline

Conversation

@SimonCropp

Copy link
Copy Markdown
Member

TrySendAsync had no deadline of any kind. Configure sets SendTimeout and ReceiveTimeout, which apply only to synchronous calls, and every async read and write used the caller's token — which is default from DiffRunner.AddInlineAsync, because Verify passes none, and likewise from AddDeleteAsync and InnerLaunchAsync.

So an owner that accepted the connection and then stopped answering hung the failing test indefinitely. That is not hypothetical: the owner answers on its listener thread, so a connection can sit behind an accept that is itself waiting up to ten seconds on InlineApplier's cross process mutex, and a viewer stopped in a debugger does the same thing for as long as it is stopped. The synchronous TrySend gives up after three seconds; the async path, the one Verify actually takes, waited forever.

Link a CancellationTokenSource with a 30 second deadline — longer than the sync wait to leave room for the applier mutex — and use its token everywhere the caller's was used. The token also closes the socket, because that is the only thing that unblocks every target: pre-net7 ReadToEndAsync takes no token at all and net462 has no cancellable connect or write either.

A timeout is reported as absence, so the caller launches a viewer or stages the patch rather than waiting on a process that has stopped listening, and traced as "present but unresponsive" so the two stay tellable apart. port and wait overrides mirror the synchronous overload so the new test can hold a connection open on its own ephemeral port without touching anything static.

TrySendAsync had no deadline of any kind. Configure sets SendTimeout and
ReceiveTimeout, which apply only to synchronous calls, and every async read and
write used the caller's token — which is default from DiffRunner.AddInlineAsync,
because Verify passes none, and likewise from AddDeleteAsync and
InnerLaunchAsync.

So an owner that accepted the connection and then stopped answering hung the
failing test indefinitely. That is not hypothetical: the owner answers on its
listener thread, so a connection can sit behind an accept that is itself waiting
up to ten seconds on InlineApplier's cross process mutex, and a viewer stopped
in a debugger does the same thing for as long as it is stopped. The synchronous
TrySend gives up after three seconds; the async path, the one Verify actually
takes, waited forever.

Link a CancellationTokenSource with a 30 second deadline — longer than the sync
wait to leave room for the applier mutex — and use its token everywhere the
caller's was used. The token also closes the socket, because that is the only
thing that unblocks every target: pre-net7 ReadToEndAsync takes no token at all
and net462 has no cancellable connect or write either.

A timeout is reported as absence, so the caller launches a viewer or stages the
patch rather than waiting on a process that has stopped listening, and traced as
"present but unresponsive" so the two stay tellable apart. port and wait
overrides mirror the synchronous overload so the new test can hold a connection
open on its own ephemeral port without touching anything static.
@SimonCropp SimonCropp added this to the 20.0.0 milestone Aug 21, 2026
@SimonCropp
SimonCropp merged commit d5b319f into main Aug 21, 2026
8 of 10 checks passed
@SimonCropp
SimonCropp deleted the fix-viewer-client-async-deadline branch August 21, 2026 11:22
@SimonCropp SimonCropp removed this from the 20.0.0 milestone Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant