From 5b79f2d7589f02cc1bbd14510e71e77f48f9b578 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 29 Apr 2026 14:37:11 -0500 Subject: [PATCH] [dotnet watch] Enable CA2007 (ConfigureAwait) for startup hook assembly Enable CA2007 as an error in the HotReloadAgent.Host .editorconfig and add ConfigureAwait(false) to awaits in the startup hook assembly. This assembly runs inside the user's app process where a SynchronizationContext may be present (e.g. UIKitSynchronizationContext on iOS), so missing ConfigureAwait(false) can cause deadlocks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Dotnet.Watch/HotReloadAgent.Host/.editorconfig | 6 +++++- src/Dotnet.Watch/HotReloadAgent.Host/NamedPipeTransport.cs | 4 ++-- src/Dotnet.Watch/HotReloadAgent.Host/StartupHook.cs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Dotnet.Watch/HotReloadAgent.Host/.editorconfig b/src/Dotnet.Watch/HotReloadAgent.Host/.editorconfig index 4694508c4da5..f5fb55893e65 100644 --- a/src/Dotnet.Watch/HotReloadAgent.Host/.editorconfig +++ b/src/Dotnet.Watch/HotReloadAgent.Host/.editorconfig @@ -3,4 +3,8 @@ # IDE0240: Remove redundant nullable directive # The directive needs to be included since all sources in a source package are considered generated code # when referenced from a project via package reference. -dotnet_diagnostic.IDE0240.severity = none \ No newline at end of file +dotnet_diagnostic.IDE0240.severity = none + +# CA2007: This assembly runs inside the user's app process where a SynchronizationContext may be present. +# Missing ConfigureAwait(false) can cause deadlocks (e.g. iOS UIKitSynchronizationContext). +dotnet_diagnostic.CA2007.severity = error \ No newline at end of file diff --git a/src/Dotnet.Watch/HotReloadAgent.Host/NamedPipeTransport.cs b/src/Dotnet.Watch/HotReloadAgent.Host/NamedPipeTransport.cs index 962c4e85fe60..5d17528b9f1d 100644 --- a/src/Dotnet.Watch/HotReloadAgent.Host/NamedPipeTransport.cs +++ b/src/Dotnet.Watch/HotReloadAgent.Host/NamedPipeTransport.cs @@ -34,8 +34,8 @@ public override async ValueTask SendAsync(IResponse response, CancellationToken } } - await _pipeClient.WriteAsync((byte)response.Type, cancellationToken); - await response.WriteAsync(_pipeClient, cancellationToken); + await _pipeClient.WriteAsync((byte)response.Type, cancellationToken).ConfigureAwait(false); + await response.WriteAsync(_pipeClient, cancellationToken).ConfigureAwait(false); } public override ValueTask ReceiveAsync(CancellationToken cancellationToken) diff --git a/src/Dotnet.Watch/HotReloadAgent.Host/StartupHook.cs b/src/Dotnet.Watch/HotReloadAgent.Host/StartupHook.cs index c9fe36e20458..7cc311cb14b5 100644 --- a/src/Dotnet.Watch/HotReloadAgent.Host/StartupHook.cs +++ b/src/Dotnet.Watch/HotReloadAgent.Host/StartupHook.cs @@ -84,7 +84,7 @@ async Task SendAndForgetAsync() { try { - await listener.SendResponseAsync(new HotReloadExceptionCreatedNotification(code, message), CancellationToken.None); + await listener.SendResponseAsync(new HotReloadExceptionCreatedNotification(code, message), CancellationToken.None).ConfigureAwait(false); } catch {