From 90d95695da259aaff00eca7a24f0f69977519fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 21 Sep 2026 12:05:54 +0200 Subject: [PATCH] Fix hot reload cancellation race Route cancellation through the synchronized shutdown path so the shutdown flag is set before the waiter is released. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../HotReloadHandler.cs | 11 ++--------- .../HotReloadTests.cs | 2 ++ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs index fdbf10dea7..f798401d33 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs @@ -106,7 +106,7 @@ public async Task ShouldRunAsync(Task? waitExecutionCompletion, Cancellati return false; } - cancellationToken.Register(() => s_shutdownProcess = true); + using CancellationTokenRegistration registration = cancellationToken.Register(RequestShutdown); if (waitExecutionCompletion is not null) { @@ -114,14 +114,7 @@ public async Task ShouldRunAsync(Task? waitExecutionCompletion, Cancellati await _outputDevice.DisplayAsync(_outputDeviceDataProducer, new TextOutputDeviceData(ExtensionResources.HotReloadSessionCompleted), cancellationToken).ConfigureAwait(false); } - try - { - await SemaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) - { - // We're closing - } + await SemaphoreSlim.WaitAsync(CancellationToken.None).ConfigureAwait(false); if (!_console.IsOutputRedirected && !IsClearNotSupported()) { diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HotReloadTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HotReloadTests.cs index de6889353e..edd49493ad 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HotReloadTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HotReloadTests.cs @@ -237,6 +237,8 @@ public async Task ShouldRunAsync_WaiterIsBlockedAndCancellationIsRequested_Stops await cancellationTokenSource.CancelAsync(); Assert.IsFalse(await shouldRunTask.WaitAsync(TimeSpan.FromSeconds(30), TestContext.CancellationToken)); + Assert.IsTrue(IsShutdownRequested()); + Assert.IsFalse(await handler.ShouldRunAsync(waitExecutionCompletion: null, CancellationToken.None)); } #else [TestMethod]