diff --git a/AspNetCore.sln b/AspNetCore.sln index 70e0049f7bed..453c1dc25f79 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -8804,22 +8804,6 @@ Global {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|x64.Build.0 = Release|Any CPU {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|x86.ActiveCfg = Release|Any CPU {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|x86.Build.0 = Release|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Debug|arm64.ActiveCfg = Debug|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Debug|arm64.Build.0 = Debug|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Debug|x64.ActiveCfg = Debug|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Debug|x64.Build.0 = Debug|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Debug|x86.ActiveCfg = Debug|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Debug|x86.Build.0 = Debug|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Release|Any CPU.Build.0 = Release|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Release|arm64.ActiveCfg = Release|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Release|arm64.Build.0 = Release|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Release|x64.ActiveCfg = Release|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Release|x64.Build.0 = Release|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Release|x86.ActiveCfg = Release|Any CPU - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1}.Release|x86.Build.0 = Release|Any CPU {C1CDD339-B51B-42BE-99F2-F39A4EC0D404}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C1CDD339-B51B-42BE-99F2-F39A4EC0D404}.Debug|Any CPU.Build.0 = Debug|Any CPU {C1CDD339-B51B-42BE-99F2-F39A4EC0D404}.Debug|arm64.ActiveCfg = Debug|Any CPU @@ -10174,9 +10158,6 @@ Global {60B11F05-CA3B-4AD1-BAC1-912C19D37395} = {59ECEDFE-72E7-495D-BECB-784B0018544E} {5FE1FBC1-8CE3-4355-9866-44FE1307C5F1} = {60D51C98-2CC0-40DF-B338-44154EFEE2FF} {8DE6625B-C9E4-4949-A75C-89E3FF556724} = {5FE1FBC1-8CE3-4355-9866-44FE1307C5F1} - {9E6FF2AC-6424-49A5-8189-623FFB2A9B4C} = {60D51C98-2CC0-40DF-B338-44154EFEE2FF} - {BE5D6903-34B9-4C29-85A2-811A7EA06DAF} = {9E6FF2AC-6424-49A5-8189-623FFB2A9B4C} - {F3F89B56-66A9-4EBC-8658-80785827237E} = {9E6FF2AC-6424-49A5-8189-623FFB2A9B4C} {0CE1CC26-98CE-4022-A81C-E32AAFC9B819} = {60D51C98-2CC0-40DF-B338-44154EFEE2FF} {6276A9A0-791B-49C1-AD8F-50AC47CDC196} = {0CE1CC26-98CE-4022-A81C-E32AAFC9B819} {B81C7FA1-870F-4F21-A928-A5BE18754E6E} = {6276A9A0-791B-49C1-AD8F-50AC47CDC196} @@ -10658,7 +10639,6 @@ Global {E83B0BCC-A8E0-4FBD-BE51-9A533C9CB972} = {DFC4F588-B4B4-484B-AB93-B36721374AD3} {5D6F99C5-D292-4459-B8BD-8E4AD42E1B21} = {E83B0BCC-A8E0-4FBD-BE51-9A533C9CB972} {E2461809-D2EA-436D-B5C3-8A9EE0A283B8} = {7D2B0799-A634-42AC-AE77-5D167BA51389} - {CAFD1885-B87B-4A7A-8BE6-86B0C238C2B1} = {5FE1FBC1-8CE3-4355-9866-44FE1307C5F1} {C1CDD339-B51B-42BE-99F2-F39A4EC0D404} = {BB3D6EDD-AE71-4D25-B61B-7EBF7A1BA1D1} {BB3D6EDD-AE71-4D25-B61B-7EBF7A1BA1D1} = {1A304CA0-7795-4684-88E5-E66402966927} {399EF81E-C3B5-4D86-8BF1-DC7926252A63} = {05A169C7-4F20-4516-B10A-B13C5649D346} diff --git a/src/SignalR/SignalR.slnf b/src/SignalR/SignalR.slnf index ef811e94ea13..e551d9f5ac58 100644 --- a/src/SignalR/SignalR.slnf +++ b/src/SignalR/SignalR.slnf @@ -5,6 +5,7 @@ "src\\DataProtection\\Abstractions\\src\\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "src\\DataProtection\\Cryptography.Internal\\src\\Microsoft.AspNetCore.Cryptography.Internal.csproj", "src\\DataProtection\\DataProtection\\src\\Microsoft.AspNetCore.DataProtection.csproj", + "src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj", "src\\FileProviders\\Embedded\\src\\Microsoft.Extensions.FileProviders.Embedded.csproj", "src\\Hosting\\Abstractions\\src\\Microsoft.AspNetCore.Hosting.Abstractions.csproj", "src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj", @@ -13,7 +14,6 @@ "src\\Hosting\\TestHost\\src\\Microsoft.AspNetCore.TestHost.csproj", "src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj", "src\\Http\\Authentication.Core\\src\\Microsoft.AspNetCore.Authentication.Core.csproj", - "src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj", "src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj", "src\\Http\\Http.Abstractions\\src\\Microsoft.AspNetCore.Http.Abstractions.csproj", "src\\Http\\Http.Extensions\\src\\Microsoft.AspNetCore.Http.Extensions.csproj", @@ -40,8 +40,8 @@ "src\\Servers\\Kestrel\\Core\\src\\Microsoft.AspNetCore.Server.Kestrel.Core.csproj", "src\\Servers\\Kestrel\\Kestrel\\src\\Microsoft.AspNetCore.Server.Kestrel.csproj", "src\\Servers\\Kestrel\\Transport.Sockets\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj", - "src\\SignalR\\clients\\csharp\\Client.SourceGenerator\\src\\Microsoft.AspNetCore.SignalR.Client.SourceGenerator.csproj", "src\\SignalR\\clients\\csharp\\Client.Core\\src\\Microsoft.AspNetCore.SignalR.Client.Core.csproj", + "src\\SignalR\\clients\\csharp\\Client.SourceGenerator\\src\\Microsoft.AspNetCore.SignalR.Client.SourceGenerator.csproj", "src\\SignalR\\clients\\csharp\\Client\\src\\Microsoft.AspNetCore.SignalR.Client.csproj", "src\\SignalR\\clients\\csharp\\Client\\test\\FunctionalTests\\Microsoft.AspNetCore.SignalR.Client.FunctionalTests.csproj", "src\\SignalR\\clients\\csharp\\Client\\test\\UnitTests\\Microsoft.AspNetCore.SignalR.Client.Tests.csproj", @@ -73,4 +73,4 @@ "src\\WebEncoders\\src\\Microsoft.Extensions.WebEncoders.csproj" ] } -} +} \ No newline at end of file diff --git a/src/SignalR/clients/csharp/.editorconfig b/src/SignalR/clients/csharp/.editorconfig new file mode 100644 index 000000000000..2e160823941e --- /dev/null +++ b/src/SignalR/clients/csharp/.editorconfig @@ -0,0 +1,4 @@ +[*.{cs,vb}] + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = warning \ No newline at end of file diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs index fb2e40fd0c93..f7f1484efef3 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs @@ -238,13 +238,13 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau CheckDisposed(); using (_logger.BeginScope(_logScope)) { - await StartAsyncInner(cancellationToken).ForceAsync(); + await StartAsyncInner(cancellationToken).ConfigureAwait(false); } } private async Task StartAsyncInner(CancellationToken cancellationToken = default) { - await _state.WaitConnectionLockAsync(token: cancellationToken); + await _state.WaitConnectionLockAsync(token: cancellationToken).ConfigureAwait(false); try { if (!_state.TryChangeState(HubConnectionState.Disconnected, HubConnectionState.Connecting)) @@ -261,7 +261,7 @@ private async Task StartAsyncInner(CancellationToken cancellationToken = default using (CreateLinkedToken(cancellationToken, _state.StopCts.Token, out var linkedToken)) { - await StartAsyncCore(linkedToken); + await StartAsyncCore(linkedToken).ConfigureAwait(false); } _state.ChangeState(HubConnectionState.Connecting, HubConnectionState.Connected); @@ -291,7 +291,7 @@ public virtual async Task StopAsync(CancellationToken cancellationToken = defaul CheckDisposed(); using (_logger.BeginScope(_logScope)) { - await StopAsyncCore(disposing: false).ForceAsync(); + await StopAsyncCore(disposing: false).ConfigureAwait(false); } } @@ -307,7 +307,7 @@ public virtual async ValueTask DisposeAsync() { using (_logger.BeginScope(_logScope)) { - await StopAsyncCore(disposing: true).ForceAsync(); + await StopAsyncCore(disposing: true).ConfigureAwait(false); } } } @@ -374,7 +374,7 @@ public virtual void Remove(string methodName) { using (_logger.BeginScope(_logScope)) { - return await StreamAsChannelCoreAsyncCore(methodName, returnType, args, cancellationToken).ForceAsync(); + return await StreamAsChannelCoreAsyncCore(methodName, returnType, args, cancellationToken).ConfigureAwait(false); } } @@ -396,7 +396,7 @@ public virtual void Remove(string methodName) { using (_logger.BeginScope(_logScope)) { - return await InvokeCoreAsyncCore(methodName, returnType, args, cancellationToken).ForceAsync(); + return await InvokeCoreAsyncCore(methodName, returnType, args, cancellationToken).ConfigureAwait(false); } } @@ -415,7 +415,7 @@ public virtual async Task SendCoreAsync(string methodName, object?[] args, Cance { using (_logger.BeginScope(_logScope)) { - await SendCoreAsyncCore(methodName, args, cancellationToken).ForceAsync(); + await SendCoreAsyncCore(methodName, args, cancellationToken).ConfigureAwait(false); } } @@ -431,7 +431,7 @@ private async Task StartAsyncCore(CancellationToken cancellationToken) Log.Starting(_logger); // Start the connection - var connection = await _connectionFactory.ConnectAsync(_endPoint, cancellationToken); + var connection = await _connectionFactory.ConnectAsync(_endPoint, cancellationToken).ConfigureAwait(false); var startingConnectionState = new ConnectionState(connection, this); // From here on, if an error occurs we need to shut down the connection because @@ -439,14 +439,14 @@ private async Task StartAsyncCore(CancellationToken cancellationToken) try { Log.HubProtocol(_logger, _protocol.Name, _protocol.Version); - await HandshakeAsync(startingConnectionState, cancellationToken); + await HandshakeAsync(startingConnectionState, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { Log.ErrorStartingConnection(_logger, ex); // Can't have any invocations to cancel, we're in the lock. - await CloseAsync(startingConnectionState.Connection); + await CloseAsync(startingConnectionState.Connection).ConfigureAwait(false); throw; } @@ -457,7 +457,7 @@ private async Task StartAsyncCore(CancellationToken cancellationToken) // StartAsyncCore is invoked and awaited by StartAsyncInner and ReconnectAsync with the connection lock still acquired. if (!(connection.Features.Get()?.HasInherentKeepAlive ?? false)) { - await SendHubMessage(startingConnectionState, PingMessage.Instance, cancellationToken); + await SendHubMessage(startingConnectionState, PingMessage.Instance, cancellationToken).ConfigureAwait(false); } startingConnectionState.ReceiveTask = ReceiveLoop(startingConnectionState); @@ -481,7 +481,7 @@ private async Task StopAsyncCore(bool disposing) // Potentially wait for StartAsync to finish, and block a new StartAsync from // starting until we've finished stopping. - await _state.WaitConnectionLockAsync(token: default); + await _state.WaitConnectionLockAsync(token: default).ConfigureAwait(false); // Ensure that ReconnectingState.ReconnectTask is not accessed outside of the lock. var reconnectTask = _state.ReconnectTask; @@ -493,8 +493,8 @@ private async Task StopAsyncCore(bool disposing) // It should never throw, even if the reconnect attempts fail. // The StopCts should prevent the HubConnection from restarting until it is reset. _state.ReleaseConnectionLock(); - await reconnectTask; - await _state.WaitConnectionLockAsync(token: default); + await reconnectTask.ConfigureAwait(false); + await _state.WaitConnectionLockAsync(token: default).ConfigureAwait(false); } ConnectionState? connectionState; @@ -528,7 +528,7 @@ private async Task StopAsyncCore(bool disposing) _disposed = true; if (_serviceProvider is IAsyncDisposable asyncDispose) { - await asyncDispose.DisposeAsync(); + await asyncDispose.DisposeAsync().ConfigureAwait(false); } else { @@ -544,7 +544,7 @@ private async Task StopAsyncCore(bool disposing) // Now stop the connection we captured if (connectionState != null) { - await connectionState.StopAsync(); + await connectionState.StopAsync().ConfigureAwait(false); } } @@ -568,8 +568,8 @@ public virtual IAsyncEnumerable StreamAsyncCore(string methodN private async IAsyncEnumerable CastIAsyncEnumerable(string methodName, object?[] args, CancellationTokenSource cts) { - var reader = await StreamAsChannelCoreAsync(methodName, typeof(T), args, cts.Token); - while (await reader.WaitToReadAsync(cts.Token)) + var reader = await StreamAsChannelCoreAsync(methodName, typeof(T), args, cts.Token).ConfigureAwait(false); + while (await reader.WaitToReadAsync(cts.Token).ConfigureAwait(false)) { while (reader.TryRead(out var item)) { @@ -583,7 +583,7 @@ private async IAsyncEnumerable CastIAsyncEnumerable(string methodName, obj async Task OnStreamCanceled(InvocationRequest irq) { // We need to take the connection lock in order to ensure we a) have a connection and b) are the only one accessing the write end of the pipe. - await _state.WaitConnectionLockAsync(token: default); + await _state.WaitConnectionLockAsync(token: default).ConfigureAwait(false); try { if (_state.CurrentConnectionStateUnsynchronized != null) @@ -610,7 +610,7 @@ async Task OnStreamCanceled(InvocationRequest irq) var readers = default(Dictionary); CheckDisposed(); - var connectionState = await _state.WaitForActiveConnectionAsync(nameof(StreamAsChannelCoreAsync), token: cancellationToken); + var connectionState = await _state.WaitForActiveConnectionAsync(nameof(StreamAsChannelCoreAsync), token: cancellationToken).ConfigureAwait(false); ChannelReader channel; try @@ -622,7 +622,7 @@ async Task OnStreamCanceled(InvocationRequest irq) // I just want an excuse to use 'irq' as a variable name... var irq = InvocationRequest.Stream(cancellationToken, returnType, connectionState.GetNextId(), _loggerFactory, this, out channel); - await InvokeStreamCore(connectionState, methodName, irq, args, streamIds?.ToArray(), cancellationToken); + await InvokeStreamCore(connectionState, methodName, irq, args, streamIds?.ToArray(), cancellationToken).ConfigureAwait(false); if (cancellationToken.CanBeCanceled) { @@ -733,11 +733,11 @@ private Task SendStreamItems(ConnectionState connectionState, string streamId { async Task ReadChannelStream() { - while (await reader.WaitToReadAsync(tokenSource.Token)) + while (await reader.WaitToReadAsync(tokenSource.Token).ConfigureAwait(false)) { while (!tokenSource.Token.IsCancellationRequested && reader.TryRead(out var item)) { - await SendWithLock(connectionState, new StreamItemMessage(streamId, item), tokenSource.Token); + await SendWithLock(connectionState, new StreamItemMessage(streamId, item), tokenSource.Token).ConfigureAwait(false); Log.SendingStreamItem(_logger, streamId); } } @@ -755,7 +755,7 @@ async Task ReadAsyncEnumerableStream() await foreach (var streamValue in streamValues) { - await SendWithLock(connectionState, new StreamItemMessage(streamId, streamValue), tokenSource.Token); + await SendWithLock(connectionState, new StreamItemMessage(streamId, streamValue), tokenSource.Token).ConfigureAwait(false); Log.SendingStreamItem(_logger, streamId); } } @@ -769,7 +769,7 @@ private async Task CommonStreaming(ConnectionState connectionState, string strea string? responseError = null; try { - await createAndConsumeStream(); + await createAndConsumeStream().ConfigureAwait(false); } catch (OperationCanceledException) { @@ -786,7 +786,7 @@ private async Task CommonStreaming(ConnectionState connectionState, string strea // Don't use cancellation token here // this is triggered by a cancellation token to tell the server that the client is done streaming - await SendWithLock(connectionState, CompletionMessage.WithError(streamId, responseError), cancellationToken: default); + await SendWithLock(connectionState, CompletionMessage.WithError(streamId, responseError), cancellationToken: default).ConfigureAwait(false); } private async Task InvokeCoreAsyncCore(string methodName, Type returnType, object?[] args, CancellationToken cancellationToken) @@ -794,7 +794,7 @@ private async Task CommonStreaming(ConnectionState connectionState, string strea var readers = default(Dictionary); CheckDisposed(); - var connectionState = await _state.WaitForActiveConnectionAsync(nameof(InvokeCoreAsync), token: cancellationToken); + var connectionState = await _state.WaitForActiveConnectionAsync(nameof(InvokeCoreAsync), token: cancellationToken).ConfigureAwait(false); Task invocationTask; try @@ -804,7 +804,7 @@ private async Task CommonStreaming(ConnectionState connectionState, string strea readers = PackageStreamingParams(connectionState, ref args, out var streamIds); var irq = InvocationRequest.Invoke(cancellationToken, returnType, connectionState.GetNextId(), _loggerFactory, this, out invocationTask); - await InvokeCore(connectionState, methodName, irq, args, streamIds?.ToArray(), cancellationToken); + await InvokeCore(connectionState, methodName, irq, args, streamIds?.ToArray(), cancellationToken).ConfigureAwait(false); LaunchStreams(connectionState, readers, cancellationToken); } @@ -814,7 +814,7 @@ private async Task CommonStreaming(ConnectionState connectionState, string strea } // Wait for this outside the lock, because it won't complete until the server responds - return await invocationTask; + return await invocationTask.ConfigureAwait(false); } private async Task InvokeCore(ConnectionState connectionState, string methodName, InvocationRequest irq, object?[] args, string[]? streams, CancellationToken cancellationToken) @@ -832,7 +832,7 @@ private async Task InvokeCore(ConnectionState connectionState, string methodName try { - await SendHubMessage(connectionState, invocationMessage, cancellationToken); + await SendHubMessage(connectionState, invocationMessage, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -859,7 +859,7 @@ private async Task InvokeStreamCore(ConnectionState connectionState, string meth try { - await SendHubMessage(connectionState, invocationMessage, cancellationToken); + await SendHubMessage(connectionState, invocationMessage, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -878,7 +878,7 @@ private async Task SendHubMessage(ConnectionState connectionState, HubMessage hu #pragma warning disable CA2016 // Forward the 'CancellationToken' parameter to methods // REVIEW: If a token is passed in and is canceled during FlushAsync it seems to break .Complete()... - await connectionState.Connection.Transport.Output.FlushAsync(); + await connectionState.Connection.Transport.Output.FlushAsync().ConfigureAwait(false); #pragma warning restore CA2016 // Forward the 'CancellationToken' parameter to methods Log.MessageSent(_logger, hubMessage); @@ -891,7 +891,7 @@ private async Task SendCoreAsyncCore(string methodName, object?[] args, Cancella var readers = default(Dictionary); CheckDisposed(); - var connectionState = await _state.WaitForActiveConnectionAsync(nameof(SendCoreAsync), token: cancellationToken); + var connectionState = await _state.WaitForActiveConnectionAsync(nameof(SendCoreAsync), token: cancellationToken).ConfigureAwait(false); try { CheckDisposed(); @@ -900,7 +900,7 @@ private async Task SendCoreAsyncCore(string methodName, object?[] args, Cancella Log.PreparingNonBlockingInvocation(_logger, methodName, args.Length); var invocationMessage = new InvocationMessage(null, methodName, args, streamIds?.ToArray()); - await SendHubMessage(connectionState, invocationMessage, cancellationToken); + await SendHubMessage(connectionState, invocationMessage, cancellationToken).ConfigureAwait(false); LaunchStreams(connectionState, readers, cancellationToken); } @@ -913,14 +913,14 @@ private async Task SendCoreAsyncCore(string methodName, object?[] args, Cancella private async Task SendWithLock(ConnectionState expectedConnectionState, HubMessage message, CancellationToken cancellationToken, [CallerMemberName] string callerName = "") { CheckDisposed(); - var connectionState = await _state.WaitForActiveConnectionAsync(callerName, token: cancellationToken); + var connectionState = await _state.WaitForActiveConnectionAsync(callerName, token: cancellationToken).ConfigureAwait(false); try { CheckDisposed(); SafeAssert(ReferenceEquals(expectedConnectionState, connectionState), "The connection state changed unexpectedly!"); - await SendHubMessage(connectionState, message, cancellationToken); + await SendHubMessage(connectionState, message, cancellationToken).ConfigureAwait(false); } finally { @@ -943,7 +943,7 @@ private async Task SendWithLock(ConnectionState expectedConnectionState, HubMess break; case InvocationMessage invocation: Log.ReceivedInvocation(_logger, invocation.InvocationId, invocation.Target, invocation.Arguments); - await invocationMessageWriter.WriteAsync(invocation); + await invocationMessageWriter.WriteAsync(invocation).ConfigureAwait(false); break; case CompletionMessage completion: if (!connectionState.TryRemoveInvocation(completion.InvocationId!, out irq)) @@ -963,7 +963,7 @@ private async Task SendWithLock(ConnectionState expectedConnectionState, HubMess Log.DroppedStreamMessage(_logger, streamItem.InvocationId!); break; } - await DispatchInvocationStreamItemAsync(streamItem, irq); + await DispatchInvocationStreamItemAsync(streamItem, irq).ConfigureAwait(false); break; case CloseMessage close: if (string.IsNullOrEmpty(close.Error)) @@ -1001,7 +1001,7 @@ private async Task DispatchInvocationAsync(InvocationMessage invocation) { try { - await handler.InvokeAsync(invocation.Arguments); + await handler.InvokeAsync(invocation.Arguments).ConfigureAwait(false); } catch (Exception ex) { @@ -1018,7 +1018,7 @@ private async Task DispatchInvocationStreamItemAsync(StreamItemMessage streamIte { Log.CancelingStreamItem(_logger, irq.InvocationId); } - else if (!await irq.StreamItem(streamItem.Item)) + else if (!await irq.StreamItem(streamItem.Item).ConfigureAwait(false)) { Log.ReceivedStreamItemAfterClose(_logger, irq.InvocationId); } @@ -1054,7 +1054,7 @@ private async Task HandshakeAsync(ConnectionState startingConnectionState, Cance var handshakeRequest = new HandshakeRequestMessage(_protocol.Name, _protocol.Version); HandshakeProtocol.WriteRequestMessage(handshakeRequest, startingConnectionState.Connection.Transport.Output); - var sendHandshakeResult = await startingConnectionState.Connection.Transport.Output.FlushAsync(CancellationToken.None); + var sendHandshakeResult = await startingConnectionState.Connection.Transport.Output.FlushAsync(CancellationToken.None).ConfigureAwait(false); if (sendHandshakeResult.IsCompleted) { @@ -1075,7 +1075,7 @@ private async Task HandshakeAsync(ConnectionState startingConnectionState, Cance { while (true) { - var result = await input.ReadAsync(linkedToken); + var result = await input.ReadAsync(linkedToken).ConfigureAwait(false); var buffer = result.Buffer; var consumed = buffer.Start; @@ -1172,11 +1172,11 @@ private async Task ReceiveLoop(ConnectionState connectionState) async Task StartProcessingInvocationMessages(ChannelReader invocationMessageChannelReader) { - while (await invocationMessageChannelReader.WaitToReadAsync()) + while (await invocationMessageChannelReader.WaitToReadAsync().ConfigureAwait(false)) { while (invocationMessageChannelReader.TryRead(out var invocationMessage)) { - await DispatchInvocationAsync(invocationMessage); + await DispatchInvocationAsync(invocationMessage).ConfigureAwait(false); } } } @@ -1187,7 +1187,7 @@ async Task StartProcessingInvocationMessages(ChannelReader in { while (true) { - var result = await input.ReadAsync(); + var result = await input.ReadAsync().ConfigureAwait(false); var buffer = result.Buffer; try @@ -1206,7 +1206,7 @@ async Task StartProcessingInvocationMessages(ChannelReader in while (_protocol.TryParseMessage(ref buffer, connectionState, out var message)) { // We have data, process it - closeMessage = await ProcessMessagesAsync(message, connectionState, invocationMessageChannel.Writer); + closeMessage = await ProcessMessagesAsync(message, connectionState, invocationMessageChannel.Writer).ConfigureAwait(false); if (closeMessage != null) { @@ -1260,9 +1260,9 @@ async Task StartProcessingInvocationMessages(ChannelReader in { invocationMessageChannel.Writer.TryComplete(); timer.Stop(); - await timerTask; + await timerTask.ConfigureAwait(false); uploadStreamSource.Cancel(); - await HandleConnectionClose(connectionState); + await HandleConnectionClose(connectionState).ConfigureAwait(false); } } @@ -1283,7 +1283,7 @@ internal void OnServerTimeout() private async Task HandleConnectionClose(ConnectionState connectionState) { // Clear the connectionState field - await _state.WaitConnectionLockAsync(token: default); + await _state.WaitConnectionLockAsync(token: default).ConfigureAwait(false); try { SafeAssert(ReferenceEquals(_state.CurrentConnectionStateUnsynchronized, connectionState), @@ -1291,7 +1291,7 @@ private async Task HandleConnectionClose(ConnectionState connectionState) _state.CurrentConnectionStateUnsynchronized = null; // Dispose the connection - await CloseAsync(connectionState.Connection); + await CloseAsync(connectionState.Connection).ConfigureAwait(false); // Cancel any outstanding invocations within the connection lock connectionState.CancelOutstandingInvocations(connectionState.CloseException); @@ -1340,7 +1340,7 @@ async Task RunClosedEventAsync() try { Log.InvokingClosedEventHandler(_logger); - await closed.Invoke(closeException); + await closed.Invoke(closeException).ConfigureAwait(false); } catch (Exception ex) { @@ -1395,13 +1395,13 @@ private async Task ReconnectAsync(Exception? closeException) try { - await Task.Delay(nextRetryDelay.Value, _state.StopCts.Token); + await Task.Delay(nextRetryDelay.Value, _state.StopCts.Token).ConfigureAwait(false); } catch (OperationCanceledException ex) { Log.ReconnectingStoppedDuringRetryDelay(_logger); - await _state.WaitConnectionLockAsync(token: default); + await _state.WaitConnectionLockAsync(token: default).ConfigureAwait(false); try { _state.ChangeState(HubConnectionState.Reconnecting, HubConnectionState.Disconnected); @@ -1416,13 +1416,13 @@ private async Task ReconnectAsync(Exception? closeException) return; } - await _state.WaitConnectionLockAsync(token: default); + await _state.WaitConnectionLockAsync(token: default).ConfigureAwait(false); try { SafeAssert(ReferenceEquals(_state.CurrentConnectionStateUnsynchronized, null), "Someone other than Reconnect set the connection state!"); - await StartAsyncCore(_state.StopCts.Token); + await StartAsyncCore(_state.StopCts.Token).ConfigureAwait(false); Log.Reconnected(_logger, previousReconnectAttempts, DateTime.UtcNow - reconnectStartTime); @@ -1457,7 +1457,7 @@ private async Task ReconnectAsync(Exception? closeException) nextRetryDelay = GetNextRetryDelay(previousReconnectAttempts, DateTime.UtcNow - reconnectStartTime, retryReason); } - await _state.WaitConnectionLockAsync(token: default); + await _state.WaitConnectionLockAsync(token: default).ConfigureAwait(false); try { SafeAssert(ReferenceEquals(_state.CurrentConnectionStateUnsynchronized, null), @@ -1517,7 +1517,7 @@ async Task RunReconnectingEventAsync() try { - await reconnecting.Invoke(closeException); + await reconnecting.Invoke(closeException).ConfigureAwait(false); } catch (Exception ex) { @@ -1544,7 +1544,7 @@ async Task RunReconnectedEventAsync() try { - await reconnected.Invoke(ConnectionId); + await reconnected.Invoke(ConnectionId).ConfigureAwait(false); } catch (Exception ex) { @@ -1810,7 +1810,7 @@ private async Task StopAsyncCore() // Wait ServerTimeout for the server or transport to shut down. Log.WaitingForReceiveLoopToTerminate(_logger); - await (ReceiveTask ?? Task.CompletedTask); + await ((ReceiveTask ?? Task.CompletedTask).ConfigureAwait(false)); Log.Stopped(_logger); @@ -1830,7 +1830,7 @@ public async Task TimerLoop(TimerAwaitable timer) // await returns True until `timer.Stop()` is called in the `finally` block of `ReceiveLoop` while (await timer) { - await RunTimerActions(); + await RunTimerActions().ConfigureAwait(false); } } } @@ -1875,7 +1875,7 @@ internal async Task RunTimerActions() SafeAssert(ReferenceEquals(_hubConnection._state.CurrentConnectionStateUnsynchronized, this), "Something reset the connection state before the timer loop completed!"); - await _hubConnection.SendHubMessage(this, PingMessage.Instance); + await _hubConnection.SendHubMessage(this, PingMessage.Instance).ConfigureAwait(false); } } finally @@ -2007,7 +2007,7 @@ public bool TryAcquireConnectionLock() // Don't call this method in a try/finally that releases the lock since we're also potentially releasing the connection lock here. public async Task WaitForActiveConnectionAsync(string methodName, CancellationToken token, [CallerMemberName] string? memberName = null, [CallerFilePath] string? filePath = null, [CallerLineNumber] int lineNumber = 0) { - await WaitConnectionLockAsync(token, methodName); + await WaitConnectionLockAsync(token, methodName).ConfigureAwait(false); if (CurrentConnectionStateUnsynchronized == null || CurrentConnectionStateUnsynchronized.Stopping) { diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnectionExtensions.InvokeAsyncGeneric.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnectionExtensions.InvokeAsyncGeneric.cs index 869fea9267da..ba69faaae2d1 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/HubConnectionExtensions.InvokeAsyncGeneric.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnectionExtensions.InvokeAsyncGeneric.cs @@ -274,7 +274,7 @@ public static async Task InvokeCoreAsync(this HubConnection hu throw new ArgumentNullException(nameof(hubConnection)); } - return (TResult)(await hubConnection.InvokeCoreAsync(methodName, typeof(TResult), args, cancellationToken))!; + return (TResult)(await hubConnection.InvokeCoreAsync(methodName, typeof(TResult), args, cancellationToken).ConfigureAwait(false))!; } } diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnectionExtensions.StreamAsChannelAsync.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnectionExtensions.StreamAsChannelAsync.cs index e0f7ce84bf82..02e4155ffb9a 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/HubConnectionExtensions.StreamAsChannelAsync.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnectionExtensions.StreamAsChannelAsync.cs @@ -275,7 +275,7 @@ public static async Task> StreamAsChannelCoreAsync(); // Intentionally avoid passing the CancellationToken to RunChannel. The token is only meant to cancel the intial setup, not the enumeration. @@ -290,13 +290,13 @@ private static async Task RunChannel(ChannelReader inputChanne { try { - while (await inputChannel.WaitToReadAsync()) + while (await inputChannel.WaitToReadAsync().ConfigureAwait(false)) { while (inputChannel.TryRead(out var item)) { while (!outputChannel.Writer.TryWrite((TResult)item!)) { - if (!await outputChannel.Writer.WaitToWriteAsync()) + if (!await outputChannel.Writer.WaitToWriteAsync().ConfigureAwait(false)) { // Failed to write to the output channel because it was closed. Nothing really we can do but abort here. return; @@ -306,7 +306,7 @@ private static async Task RunChannel(ChannelReader inputChanne } // Manifest any errors in the completion task - await inputChannel.Completion; + await inputChannel.Completion.ConfigureAwait(false); } catch (Exception ex) { diff --git a/src/SignalR/clients/csharp/Client.Core/src/Internal/InvocationRequest.cs b/src/SignalR/clients/csharp/Client.Core/src/Internal/InvocationRequest.cs index aca484274ea0..b663627869e9 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/Internal/InvocationRequest.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/Internal/InvocationRequest.cs @@ -106,7 +106,7 @@ public override async ValueTask StreamItem(object? item) { while (!_channel.Writer.TryWrite(item)) { - if (!await _channel.Writer.WaitToWriteAsync()) + if (!await _channel.Writer.WaitToWriteAsync().ConfigureAwait(false)) { return false; } diff --git a/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj b/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj index 25e8b560b329..7c0548133757 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj +++ b/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj @@ -9,7 +9,6 @@ - diff --git a/src/SignalR/clients/csharp/Client/test/.editorconfig b/src/SignalR/clients/csharp/Client/test/.editorconfig new file mode 100644 index 000000000000..10b085cdfa24 --- /dev/null +++ b/src/SignalR/clients/csharp/Client/test/.editorconfig @@ -0,0 +1,4 @@ +[*.{cs,vb}] + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = silent \ No newline at end of file diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs index 98b5b09f0891..55e26e24c24c 100644 --- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs @@ -67,7 +67,7 @@ private HubConnection CreateHubConnection( return hubConnectionBuilder.Build(); } - private Func> GetHttpConnectionFactory(string url, ILoggerFactory loggerFactory, string path, HttpTransportType transportType, TransferFormat transferFormat) + private static Func> GetHttpConnectionFactory(string url, ILoggerFactory loggerFactory, string path, HttpTransportType transportType, TransferFormat transferFormat) { return async endPoint => { @@ -75,7 +75,8 @@ private Func> GetHttpConnectionFactory(st var options = new HttpConnectionOptions { Url = httpEndpoint.Uri, Transports = transportType, DefaultTransferFormat = transferFormat }; var connection = new HttpConnection(options, loggerFactory); - await connection.StartAsync(); + // This is used by CanBlockOnAsyncOperationsWithOneAtATimeSynchronizationContext, so the ConfigureAwait(false) is important. + await connection.StartAsync().ConfigureAwait(false); return connection; }; @@ -2090,6 +2091,90 @@ bool ExpectedErrors(WriteContext writeContext) } } + [Theory] + [MemberData(nameof(TransportTypes))] + public async Task CanBlockOnAsyncOperationsWithOneAtATimeSynchronizationContext(HttpTransportType transportType) + { + const int DefaultTimeout = Testing.TaskExtensions.DefaultTimeoutDuration; + + await using var server = await StartServer(); + await using var connection = CreateHubConnection(server.Url, "/default", transportType, HubProtocols["json"], LoggerFactory); + await using var oneAtATimeSynchronizationContext = new OneAtATimeSynchronizationContext(); + + var originalSynchronizationContext = SynchronizationContext.Current; + SynchronizationContext.SetSynchronizationContext(oneAtATimeSynchronizationContext); + + try + { + // Yield first so the rest of the test runs in the OneAtATimeSynchronizationContext.Run loop + await Task.Yield(); + + Assert.True(connection.StartAsync().Wait(DefaultTimeout)); + + var invokeTask = connection.InvokeAsync(nameof(TestHub.HelloWorld)); + Assert.True(invokeTask.Wait(DefaultTimeout)); + Assert.Equal("Hello World!", invokeTask.Result); + + Assert.True(connection.DisposeAsync().AsTask().Wait(DefaultTimeout)); + } + catch (Exception ex) + { + LoggerFactory.CreateLogger().LogError(ex, "{ExceptionType} from test", ex.GetType().FullName); + throw; + } + finally + { + SynchronizationContext.SetSynchronizationContext(originalSynchronizationContext); + } + } + + private class OneAtATimeSynchronizationContext : SynchronizationContext, IAsyncDisposable + { + private readonly Channel<(SendOrPostCallback, object)> _taskQueue = Channel.CreateUnbounded<(SendOrPostCallback, object)>(); + private readonly Task _runTask; + private bool _disposed; + + public OneAtATimeSynchronizationContext() + { + // Task.Run to avoid running with xUnit's AsyncTestSyncContext as well. + _runTask = Task.Run(Run); + } + + public override void Post(SendOrPostCallback d, object state) + { + if (_disposed) + { + // There should be no other calls to Post() after dispose. If there are calls, + // the test has most likely failed with a timeout. Let the callbacks run so the + // timeout exception gets reported accurately instead of as a long-running test. + d(state); + } + + _taskQueue.Writer.TryWrite((d, state)); + } + + public ValueTask DisposeAsync() + { + _disposed = true; + _taskQueue.Writer.Complete(); + return new ValueTask(_runTask); + } + + private async Task Run() + { + while (await _taskQueue.Reader.WaitToReadAsync()) + { + SetSynchronizationContext(this); + while (_taskQueue.Reader.TryRead(out var tuple)) + { + var (callback, state) = tuple; + callback(state); + } + SetSynchronizationContext(null); + } + } + } + private class PollTrackingMessageHandler : DelegatingHandler { public Task ActivePoll { get; private set; } diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs index 03c90830c1de..3f5b4b3c706c 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs @@ -204,7 +204,7 @@ public async Task StartAsync(TransferFormat transferFormat, CancellationToken ca { using (_logger.BeginScope(_logScope)) { - await StartAsyncCore(transferFormat, cancellationToken).ForceAsync(); + await StartAsyncCore(transferFormat, cancellationToken).ConfigureAwait(false); } } @@ -218,7 +218,7 @@ private async Task StartAsyncCore(TransferFormat transferFormat, CancellationTok return; } - await _connectionLock.WaitAsync(cancellationToken); + await _connectionLock.WaitAsync(cancellationToken).ConfigureAwait(false); try { CheckDisposed(); @@ -231,7 +231,7 @@ private async Task StartAsyncCore(TransferFormat transferFormat, CancellationTok Log.Starting(_logger); - await SelectAndStartTransport(transferFormat, cancellationToken); + await SelectAndStartTransport(transferFormat, cancellationToken).ConfigureAwait(false); _started = true; Log.Started(_logger); @@ -254,7 +254,7 @@ public override async ValueTask DisposeAsync() { using (_logger.BeginScope(_logScope)) { - await DisposeAsyncCore().ForceAsync(); + await DisposeAsyncCore().ConfigureAwait(false); } } @@ -265,7 +265,7 @@ private async Task DisposeAsyncCore() return; } - await _connectionLock.WaitAsync(); + await _connectionLock.WaitAsync().ConfigureAwait(false); try { if (!_disposed && _started) @@ -276,7 +276,7 @@ private async Task DisposeAsyncCore() // The transport should also have completed the pipe with this exception. try { - await _transport!.StopAsync(); + await _transport!.StopAsync().ConfigureAwait(false); } catch (Exception ex) { @@ -317,7 +317,7 @@ private async Task SelectAndStartTransport(TransferFormat transferFormat, Cancel if (_httpConnectionOptions.Transports == HttpTransportType.WebSockets) { Log.StartingTransport(_logger, _httpConnectionOptions.Transports, uri); - await StartTransport(uri, _httpConnectionOptions.Transports, transferFormat, cancellationToken); + await StartTransport(uri, _httpConnectionOptions.Transports, transferFormat, cancellationToken).ConfigureAwait(false); } else { @@ -331,7 +331,7 @@ private async Task SelectAndStartTransport(TransferFormat transferFormat, Cancel do { - negotiationResponse = await GetNegotiationResponseAsync(uri, cancellationToken); + negotiationResponse = await GetNegotiationResponseAsync(uri, cancellationToken).ConfigureAwait(false); if (negotiationResponse.Url != null) { @@ -402,12 +402,12 @@ private async Task SelectAndStartTransport(TransferFormat transferFormat, Cancel // The negotiation response gets cleared in the fallback scenario. if (negotiationResponse == null) { - negotiationResponse = await GetNegotiationResponseAsync(uri, cancellationToken); + negotiationResponse = await GetNegotiationResponseAsync(uri, cancellationToken).ConfigureAwait(false); connectUrl = CreateConnectUrl(uri, negotiationResponse.ConnectionToken); } Log.StartingTransport(_logger, transportType, uri); - await StartTransport(connectUrl, transportType, transferFormat, cancellationToken); + await StartTransport(connectUrl, transportType, transferFormat, cancellationToken).ConfigureAwait(false); break; } } @@ -468,11 +468,11 @@ private async Task NegotiateAsync(Uri url, HttpClient httpC // rather than buffer the entire response. This gives a small perf boost. // Note that it is important to dispose of the response when doing this to // avoid leaving the connection open. - using (var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken)) + using (var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false)) { response.EnsureSuccessStatusCode(); #pragma warning disable CA2016 // Forward the 'CancellationToken' parameter to methods - var responseBuffer = await response.Content.ReadAsByteArrayAsync(); + var responseBuffer = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); #pragma warning restore CA2016 // Forward the 'CancellationToken' parameter to methods var negotiateResponse = NegotiateProtocol.ParseResponse(responseBuffer); if (!string.IsNullOrEmpty(negotiateResponse.Error)) @@ -509,7 +509,7 @@ private async Task StartTransport(Uri connectUrl, HttpTransportType transportTyp // Start the transport, giving it one end of the pipe try { - await transport.StartAsync(connectUrl, transferFormat, cancellationToken); + await transport.StartAsync(connectUrl, transferFormat, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -683,7 +683,7 @@ private static bool IsWebSocketsSupported() private async Task GetNegotiationResponseAsync(Uri uri, CancellationToken cancellationToken) { - var negotiationResponse = await NegotiateAsync(uri, _httpClient!, _logger, cancellationToken); + var negotiationResponse = await NegotiateAsync(uri, _httpClient!, _logger, cancellationToken).ConfigureAwait(false); // If the negotiationVersion is greater than zero then we know that the negotiation response contains a // connectionToken that will be required to conenct. Otherwise we just set the connectionId and the // connectionToken on the client to the same value. diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnectionFactory.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnectionFactory.cs index cdde96f1dd7d..9d3cd003d393 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnectionFactory.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnectionFactory.cs @@ -68,13 +68,13 @@ public async ValueTask ConnectAsync(EndPoint endPoint, Cancel try { - await connection.StartAsync(cancellationToken); + await connection.StartAsync(cancellationToken).ConfigureAwait(false); return connection; } catch { // Make sure the connection is disposed, in case it allocated any resources before failing. - await connection.DisposeAsync(); + await connection.DisposeAsync().ConfigureAwait(false); throw; } } diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/AccessTokenHttpMessageHandler.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/AccessTokenHttpMessageHandler.cs index f2b7e75ccb59..23ff55912128 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/AccessTokenHttpMessageHandler.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/AccessTokenHttpMessageHandler.cs @@ -19,13 +19,13 @@ public AccessTokenHttpMessageHandler(HttpMessageHandler inner, HttpConnection ht protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - var accessToken = await _httpConnection.GetAccessTokenAsync(); + var accessToken = await _httpConnection.GetAccessTokenAsync().ConfigureAwait(false); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); } - return await base.SendAsync(request, cancellationToken); + return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } } diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/LoggingHttpMessageHandler.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/LoggingHttpMessageHandler.cs index 988ecefb2050..7765a8afccad 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/LoggingHttpMessageHandler.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/LoggingHttpMessageHandler.cs @@ -28,7 +28,7 @@ protected override async Task SendAsync(HttpRequestMessage { Log.SendingHttpRequest(_logger, request.Method, request.RequestUri!); - var response = await base.SendAsync(request, cancellationToken); + var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/LongPollingTransport.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/LongPollingTransport.cs index 0fb3627f3ca9..faee9c1c1f2e 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/LongPollingTransport.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/LongPollingTransport.cs @@ -51,7 +51,7 @@ public async Task StartAsync(Uri url, TransferFormat transferFormat, Cancellatio // Make initial long polling request // Server uses first long polling request to finish initializing connection and it returns without data var request = new HttpRequestMessage(HttpMethod.Get, url); - using (var response = await _httpClient.SendAsync(request, cancellationToken)) + using (var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false)) { response.EnsureSuccessStatusCode(); } @@ -74,7 +74,7 @@ private async Task ProcessAsync(Uri url) var sending = SendUtils.SendMessages(url, _application, _httpClient, _logger); // Wait for send or receive to complete - var trigger = await Task.WhenAny(receiving, sending); + var trigger = await Task.WhenAny(receiving, sending).ConfigureAwait(false); if (trigger == receiving) { @@ -87,7 +87,7 @@ private async Task ProcessAsync(Uri url) // Cancel the application so that ReadAsync yields _application.Input.CancelPendingRead(); - await sending; + await sending.ConfigureAwait(false); } else { @@ -100,10 +100,10 @@ private async Task ProcessAsync(Uri url) // Cancel any pending flush so that we can quit _application.Output.CancelPendingFlush(); - await receiving; + await receiving.ConfigureAwait(false); // Send the DELETE request to clean-up the connection on the server. - await SendDeleteRequest(url); + await SendDeleteRequest(url).ConfigureAwait(false); } } @@ -121,7 +121,7 @@ public async Task StopAsync() try { - await Running; + await Running.ConfigureAwait(false); } catch (Exception ex) { @@ -154,7 +154,7 @@ private async Task Poll(Uri pollUrl, CancellationToken cancellationToken) try { - response = await _httpClient.SendAsync(request, cancellationToken); + response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -186,13 +186,13 @@ private async Task Poll(Uri pollUrl, CancellationToken cancellationToken) { Log.ReceivedMessages(_logger); #if NETCOREAPP - await response.Content.CopyToAsync(applicationStream, cancellationToken); + await response.Content.CopyToAsync(applicationStream, cancellationToken).ConfigureAwait(false); #else - await response.Content.CopyToAsync(applicationStream); + await response.Content.CopyToAsync(applicationStream).ConfigureAwait(false); #endif - var flushResult = await _application.Output.FlushAsync(cancellationToken); + var flushResult = await _application.Output.FlushAsync(cancellationToken).ConfigureAwait(false); // We canceled in the middle of applying back pressure // or if the consumer is done @@ -227,7 +227,7 @@ private async Task SendDeleteRequest(Uri url) try { Log.SendingDeleteRequest(_logger, url); - var response = await _httpClient.DeleteAsync(url); + var response = await _httpClient.DeleteAsync(url).ConfigureAwait(false); if (response.StatusCode == HttpStatusCode.NotFound) { diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs index 7309841ea427..d0c9cc51b893 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/SendUtils.cs @@ -23,7 +23,7 @@ public static async Task SendMessages(Uri sendUrl, IDuplexPipe application, Http { while (true) { - var result = await application.Input.ReadAsync(cancellationToken); + var result = await application.Input.ReadAsync(cancellationToken).ConfigureAwait(false); var buffer = result.Buffer; try @@ -49,7 +49,7 @@ public static async Task SendMessages(Uri sendUrl, IDuplexPipe application, Http // rather than buffer the entire response. This gives a small perf boost. // Note that it is important to dispose of the response when doing this to // avoid leaving the connection open. - using (var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken)) + using (var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false)) { response.EnsureSuccessStatusCode(); } diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/ServerSentEventsTransport.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/ServerSentEventsTransport.cs index 4ba373b704ed..7c2d871b3cc4 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/ServerSentEventsTransport.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/ServerSentEventsTransport.cs @@ -61,7 +61,7 @@ public async Task StartAsync(Uri url, TransferFormat transferFormat, Cancellatio try { - response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); } catch @@ -96,7 +96,7 @@ private async Task ProcessAsync(Uri url, HttpResponseMessage response) var sending = SendUtils.SendMessages(url, _application, _httpClient, _logger, _inputCts.Token); // Wait for send or receive to complete - var trigger = await Task.WhenAny(receiving, sending); + var trigger = await Task.WhenAny(receiving, sending).ConfigureAwait(false); if (trigger == receiving) { @@ -109,7 +109,7 @@ private async Task ProcessAsync(Uri url, HttpResponseMessage response) // Cancel the application so that ReadAsync yields _application.Input.CancelPendingRead(); - await sending; + await sending.ConfigureAwait(false); } else { @@ -121,7 +121,7 @@ private async Task ProcessAsync(Uri url, HttpResponseMessage response) // Cancel any pending flush so that we can quit _application.Output.CancelPendingFlush(); - await receiving; + await receiving.ConfigureAwait(false); } } @@ -135,7 +135,7 @@ private async Task ProcessEventStream(HttpResponseMessage response, Cancellation using (response) #pragma warning disable CA2016 // Forward the 'CancellationToken' parameter to methods - using (var stream = await response.Content.ReadAsStreamAsync()) + using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) #pragma warning restore CA2016 // Forward the 'CancellationToken' parameter to methods { var reader = PipeReader.Create(stream); @@ -147,7 +147,7 @@ private async Task ProcessEventStream(HttpResponseMessage response, Cancellation while (true) { // We rely on the CancelReader callback to cancel pending reads. Do not pass the token to ReadAsync since that would result in an exception on cancelation. - var result = await reader.ReadAsync(default); + var result = await reader.ReadAsync(default).ConfigureAwait(false); var buffer = result.Buffer; var consumed = buffer.Start; var examined = buffer.End; @@ -174,7 +174,7 @@ private async Task ProcessEventStream(HttpResponseMessage response, Cancellation // When cancellationToken is canceled the next line will cancel pending flushes on the pipe unblocking the await. // Avoid passing the passed in context. - flushResult = await _application.Output.WriteAsync(message, default); + flushResult = await _application.Output.WriteAsync(message, default).ConfigureAwait(false); _parser.Reset(); break; @@ -237,7 +237,7 @@ public async Task StopAsync() try { - await Running; + await Running.ConfigureAwait(false); } catch (Exception ex) { diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/WebSocketsTransport.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/WebSocketsTransport.cs index 96ce387584d2..9fd9dc4543e2 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/WebSocketsTransport.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/WebSocketsTransport.cs @@ -117,7 +117,7 @@ private async ValueTask DefaultWebSocketFactory(WebSocketConnectionCo if (_httpConnectionOptions.AccessTokenProvider != null) { - var accessToken = await _httpConnectionOptions.AccessTokenProvider(); + var accessToken = await _httpConnectionOptions.AccessTokenProvider().ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(accessToken)) { // We can't use request headers in the browser, so instead append the token as a query string in that case @@ -138,7 +138,7 @@ private async ValueTask DefaultWebSocketFactory(WebSocketConnectionCo try { - await webSocket.ConnectAsync(url, cancellationToken); + await webSocket.ConnectAsync(url, cancellationToken).ConfigureAwait(false); } catch { @@ -171,7 +171,7 @@ public async Task StartAsync(Uri url, TransferFormat transferFormat, Cancellatio var context = new WebSocketConnectionContext(resolvedUrl, _httpConnectionOptions); var factory = _httpConnectionOptions.WebSocketFactory ?? DefaultWebSocketFactory; - _webSocket = await factory(context, cancellationToken); + _webSocket = await factory(context, cancellationToken).ConfigureAwait(false); if (_webSocket == null) { @@ -202,7 +202,7 @@ private async Task ProcessSocketAsync(WebSocket socket) var sending = StartSending(socket); // Wait for send or receive to complete - var trigger = await Task.WhenAny(receiving, sending); + var trigger = await Task.WhenAny(receiving, sending).ConfigureAwait(false); if (trigger == receiving) { @@ -215,7 +215,7 @@ private async Task ProcessSocketAsync(WebSocket socket) using (var delayCts = new CancellationTokenSource()) { - var resultTask = await Task.WhenAny(sending, Task.Delay(_closeTimeout, delayCts.Token)); + var resultTask = await Task.WhenAny(sending, Task.Delay(_closeTimeout, delayCts.Token)).ConfigureAwait(false); if (resultTask != sending) { @@ -258,7 +258,7 @@ private async Task StartReceiving(WebSocket socket) { #if NETSTANDARD2_1 || NETCOREAPP // Do a 0 byte read so that idle connections don't allocate a buffer when waiting for a read - var result = await socket.ReceiveAsync(Memory.Empty, CancellationToken.None); + var result = await socket.ReceiveAsync(Memory.Empty, CancellationToken.None).ConfigureAwait(false); if (result.MessageType == WebSocketMessageType.Close) { @@ -275,13 +275,13 @@ private async Task StartReceiving(WebSocket socket) var memory = _application.Output.GetMemory(); #if NETSTANDARD2_1 || NETCOREAPP // Because we checked the CloseStatus from the 0 byte read above, we don't need to check again after reading - var receiveResult = await socket.ReceiveAsync(memory, CancellationToken.None); + var receiveResult = await socket.ReceiveAsync(memory, CancellationToken.None).ConfigureAwait(false); #elif NETSTANDARD2_0 || NETFRAMEWORK var isArray = MemoryMarshal.TryGetArray(memory, out var arraySegment); Debug.Assert(isArray); // Exceptions are handled above where the send and receive tasks are being run. - var receiveResult = await socket.ReceiveAsync(arraySegment, CancellationToken.None); + var receiveResult = await socket.ReceiveAsync(arraySegment, CancellationToken.None).ConfigureAwait(false); #else #error TFMs need to be updated #endif @@ -302,7 +302,7 @@ private async Task StartReceiving(WebSocket socket) _application.Output.Advance(receiveResult.Count); - var flushResult = await _application.Output.FlushAsync(); + var flushResult = await _application.Output.FlushAsync().ConfigureAwait(false); // We canceled in the middle of applying back pressure // or if the consumer is done @@ -342,7 +342,7 @@ private async Task StartSending(WebSocket socket) { while (true) { - var result = await _application.Input.ReadAsync(); + var result = await _application.Input.ReadAsync().ConfigureAwait(false); var buffer = result.Buffer; // Get a frame from the application @@ -362,7 +362,7 @@ private async Task StartSending(WebSocket socket) if (WebSocketCanSend(socket)) { - await socket.SendAsync(buffer, _webSocketMessageType); + await socket.SendAsync(buffer, _webSocketMessageType).ConfigureAwait(false); } else { @@ -400,7 +400,7 @@ private async Task StartSending(WebSocket socket) try { // We're done sending, send the close frame to the client if the websocket is still open - await socket.CloseOutputAsync(error != null ? WebSocketCloseStatus.InternalServerError : WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + await socket.CloseOutputAsync(error != null ? WebSocketCloseStatus.InternalServerError : WebSocketCloseStatus.NormalClosure, "", CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { @@ -454,7 +454,7 @@ public async Task StopAsync() try { - await Running; + await Running.ConfigureAwait(false); } catch (Exception ex) { diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj b/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj index 48d0c41d0b1b..81fb6436b2dc 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/Microsoft.AspNetCore.Http.Connections.Client.csproj @@ -7,7 +7,6 @@ - diff --git a/src/SignalR/common/.editorconfig b/src/SignalR/common/.editorconfig new file mode 100644 index 000000000000..2e160823941e --- /dev/null +++ b/src/SignalR/common/.editorconfig @@ -0,0 +1,4 @@ +[*.{cs,vb}] + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = warning \ No newline at end of file diff --git a/src/SignalR/common/Http.Connections/.editorconfig b/src/SignalR/common/Http.Connections/.editorconfig new file mode 100644 index 000000000000..10b085cdfa24 --- /dev/null +++ b/src/SignalR/common/Http.Connections/.editorconfig @@ -0,0 +1,4 @@ +[*.{cs,vb}] + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = silent \ No newline at end of file diff --git a/src/SignalR/common/Shared/MemoryBufferWriter.cs b/src/SignalR/common/Shared/MemoryBufferWriter.cs index e9cd4830a481..ca4dc6407ad6 100644 --- a/src/SignalR/common/Shared/MemoryBufferWriter.cs +++ b/src/SignalR/common/Shared/MemoryBufferWriter.cs @@ -198,9 +198,9 @@ private async Task CopyToSlowAsync(Stream destination, CancellationToken cancell { var segment = _completedSegments[i]; #if NETCOREAPP - await destination.WriteAsync(segment.Buffer.AsMemory(0, segment.Length), cancellationToken); + await destination.WriteAsync(segment.Buffer.AsMemory(0, segment.Length), cancellationToken).ConfigureAwait(false); #else - await destination.WriteAsync(segment.Buffer, 0, segment.Length, cancellationToken); + await destination.WriteAsync(segment.Buffer, 0, segment.Length, cancellationToken).ConfigureAwait(false); #endif } } @@ -208,9 +208,9 @@ private async Task CopyToSlowAsync(Stream destination, CancellationToken cancell if (_currentSegment is not null) { #if NETCOREAPP - await destination.WriteAsync(_currentSegment.AsMemory(0, _position), cancellationToken); + await destination.WriteAsync(_currentSegment.AsMemory(0, _position), cancellationToken).ConfigureAwait(false); #else - await destination.WriteAsync(_currentSegment, 0, _position, cancellationToken); + await destination.WriteAsync(_currentSegment, 0, _position, cancellationToken).ConfigureAwait(false); #endif } } diff --git a/src/SignalR/common/Shared/PipeWriterStream.cs b/src/SignalR/common/Shared/PipeWriterStream.cs index c763fb889a96..d10f90f16179 100644 --- a/src/SignalR/common/Shared/PipeWriterStream.cs +++ b/src/SignalR/common/Shared/PipeWriterStream.cs @@ -91,7 +91,7 @@ private ValueTask WriteCoreAsync(ReadOnlyMemory source, CancellationToken static async ValueTask WriteSlowAsync(ValueTask flushTask) { - var flushResult = await flushTask; + var flushResult = await flushTask.ConfigureAwait(false); // Cancellation can be triggered by PipeWriter.CancelPendingFlush if (flushResult.IsCanceled) diff --git a/src/SignalR/common/Shared/StreamExtensions.cs b/src/SignalR/common/Shared/StreamExtensions.cs index 622bfafb69bc..8604a54760f4 100644 --- a/src/SignalR/common/Shared/StreamExtensions.cs +++ b/src/SignalR/common/Shared/StreamExtensions.cs @@ -34,12 +34,12 @@ private static async ValueTask WriteMultiSegmentAsync(Stream stream, ReadOnlySeq while (buffer.TryGet(ref position, out var segment)) { #if NETCOREAPP || NETSTANDARD2_1 - await stream.WriteAsync(segment, cancellationToken); + await stream.WriteAsync(segment, cancellationToken).ConfigureAwait(false); #else var isArray = MemoryMarshal.TryGetArray(segment, out var arraySegment); // We're using the managed memory pool which is backed by managed buffers Debug.Assert(isArray); - await stream.WriteAsync(arraySegment.Array, arraySegment.Offset, arraySegment.Count, cancellationToken); + await stream.WriteAsync(arraySegment.Array, arraySegment.Offset, arraySegment.Count, cancellationToken).ConfigureAwait(false); #endif } } diff --git a/src/SignalR/common/Shared/WebSocketExtensions.cs b/src/SignalR/common/Shared/WebSocketExtensions.cs index 21da7d7031d2..152b4e637632 100644 --- a/src/SignalR/common/Shared/WebSocketExtensions.cs +++ b/src/SignalR/common/Shared/WebSocketExtensions.cs @@ -45,22 +45,22 @@ private static async ValueTask SendMultiSegmentAsync(WebSocket webSocket, ReadOn while (buffer.TryGet(ref position, out var segment)) { #if NETCOREAPP - await webSocket.SendAsync(prevSegment, webSocketMessageType, endOfMessage: false, cancellationToken); + await webSocket.SendAsync(prevSegment, webSocketMessageType, endOfMessage: false, cancellationToken).ConfigureAwait(false); #else var isArray = MemoryMarshal.TryGetArray(prevSegment, out var arraySegment); Debug.Assert(isArray); - await webSocket.SendAsync(arraySegment, webSocketMessageType, endOfMessage: false, cancellationToken); + await webSocket.SendAsync(arraySegment, webSocketMessageType, endOfMessage: false, cancellationToken).ConfigureAwait(false); #endif prevSegment = segment; } // End of message frame #if NETCOREAPP - await webSocket.SendAsync(prevSegment, webSocketMessageType, endOfMessage: true, cancellationToken); + await webSocket.SendAsync(prevSegment, webSocketMessageType, endOfMessage: true, cancellationToken).ConfigureAwait(false); #else var isArrayEnd = MemoryMarshal.TryGetArray(prevSegment, out var arraySegmentEnd); Debug.Assert(isArrayEnd); - await webSocket.SendAsync(arraySegmentEnd, webSocketMessageType, endOfMessage: true, cancellationToken); + await webSocket.SendAsync(arraySegmentEnd, webSocketMessageType, endOfMessage: true, cancellationToken).ConfigureAwait(false); #endif } } diff --git a/src/SignalR/common/SignalR.Common/test/.editorconfig b/src/SignalR/common/SignalR.Common/test/.editorconfig new file mode 100644 index 000000000000..10b085cdfa24 --- /dev/null +++ b/src/SignalR/common/SignalR.Common/test/.editorconfig @@ -0,0 +1,4 @@ +[*.{cs,vb}] + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = silent \ No newline at end of file diff --git a/src/SignalR/common/testassets/.editorconfig b/src/SignalR/common/testassets/.editorconfig new file mode 100644 index 000000000000..10b085cdfa24 --- /dev/null +++ b/src/SignalR/common/testassets/.editorconfig @@ -0,0 +1,4 @@ +[*.{cs,vb}] + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = silent \ No newline at end of file diff --git a/src/SignalR/perf/Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj b/src/SignalR/perf/Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj index ff022626496b..61fef0884dad 100644 --- a/src/SignalR/perf/Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj +++ b/src/SignalR/perf/Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj @@ -8,6 +8,7 @@ +