From 8a3855de27720917f2ba0cd21cf176a248c5ce95 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sat, 22 Aug 2026 14:20:02 +1000 Subject: [PATCH] Drain the listener on the same margin the client uses AnOwnerAnswersAClient failed on Windows with a TimeoutException out of Wait: the listener had not unwound within five seconds of being cancelled. A re-run with no code change went green. The comment on underLoad already describes exactly why: CI starts six test assemblies at once on a two core runner, and an answer arriving on a scheduled task has twice missed a deadline set for an idle machine. Unwinding the accept needs its continuation scheduled on that same pool, so Wait was making the bet the client timeout above it had already stopped making. It now uses the same constant, so there is one number rather than two that disagree. Still throws when it runs out. A listener that never unwinds is a real bug, and this is the only place that would notice - so the margin is widened rather than the failure swallowed. --- src/DiffEngine.Tests/ViewerProtocolTests.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/DiffEngine.Tests/ViewerProtocolTests.cs b/src/DiffEngine.Tests/ViewerProtocolTests.cs index c581eedb..ab1f05a5 100644 --- a/src/DiffEngine.Tests/ViewerProtocolTests.cs +++ b/src/DiffEngine.Tests/ViewerProtocolTests.cs @@ -845,11 +845,24 @@ public async Task AnAbsentOwnerIsNotAnError() await Assert.That(ViewerClient.TrySend(new(ViewerVerb.List), out _, port)).IsFalse(); } + /// + /// Drains the listener at the end of a test. + /// + /// On rather than a margin of its own. Unwinding needs the accept's + /// continuation to be scheduled, and that waits on the same thread pool everything else here + /// does - so five seconds was the same bet the client timeout above had already stopped + /// making, and it lost the same way. + /// + /// + /// Still throws when it runs out, because a listener that never unwinds is a real bug and this + /// is the only place that would notice. + /// + /// static async Task Wait(Task listening) { try { - await listening.WaitAsync(TimeSpan.FromSeconds(5)); + await listening.WaitAsync(underLoad); } catch (OperationCanceledException) {