From 3756e8cda7be00971d199ef38a4cffb01c7c11c2 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 8 Feb 2021 12:49:48 -0400 Subject: [PATCH 1/2] Start connections on a fresh ExecutionContext - This change removes the ExecutionContext from new connections, this removes the possibility of capturing request scoped async locals and flowing them to the hub. This will ensure that the IHttpContextAccessor and Activity for hub invocations is always null and aren't carried over from the incoming request. --- .../src/Internal/HttpConnectionContext.cs | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs index 63f97c300ed6..f0d3c988d4d6 100644 --- a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs +++ b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs @@ -33,7 +33,8 @@ internal class HttpConnectionContext : ConnectionContext, IHttpContextFeature, IHttpTransportFeature, IConnectionInherentKeepAliveFeature, - IConnectionLifetimeFeature + IConnectionLifetimeFeature, + IThreadPoolWorkItem { private static long _tenSeconds = TimeSpan.FromSeconds(10).Ticks; @@ -47,6 +48,10 @@ internal class HttpConnectionContext : ConnectionContext, private IDictionary _items; private CancellationTokenSource _connectionClosedTokenSource; + // No need to RunContinuationsAsynchronously since we're at the tail of a threadpool thread + private TaskCompletionSource _connectionDelegateTcs = new TaskCompletionSource(); + private ConnectionDelegate _connectionDelegate; + private CancellationTokenSource _sendCts; private bool _activeSend; private long _startedSendTime; @@ -547,12 +552,13 @@ private async Task ExecuteApplication(ConnectionDelegate connectionDelegate) // Verify some initialization invariants Debug.Assert(TransportType != HttpTransportType.None, "Transport has not been initialized yet"); - // Jump onto the thread pool thread so blocking user code doesn't block the setup of the - // connection and transport - await AwaitableThreadPool.Yield(); + _connectionDelegate = connectionDelegate; + + // Queue the connection for execution + ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false); - // Running this in an async method turns sync exceptions into async ones - await connectionDelegate(this); + // Wait for that task to finish signaling the end of the application execution + await _connectionDelegateTcs.Task; } internal void StartSendCancellation() @@ -589,6 +595,29 @@ internal void StopSendCancellation() } } + public void Execute() + { + async Task ExecuteCore() + { + try + { + await _connectionDelegate(this); + + _connectionDelegateTcs.TrySetResult(); + } + catch (OperationCanceledException ex) + { + _connectionDelegateTcs.TrySetCanceled(ex.CancellationToken); + } + catch (Exception ex) + { + _connectionDelegateTcs.TrySetException(ex); + } + } + + _ = ExecuteCore(); + } + private static class Log { private static readonly Action _disposingConnection = From d94a6036159baab2dca85220fa70baefd4297f15 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Mon, 8 Feb 2021 21:39:20 -0400 Subject: [PATCH 2/2] Update src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs Co-authored-by: Pranav K --- .../Http.Connections/src/Internal/HttpConnectionContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs index f0d3c988d4d6..4e2e7ea8f1cf 100644 --- a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs +++ b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs @@ -49,7 +49,7 @@ internal class HttpConnectionContext : ConnectionContext, private CancellationTokenSource _connectionClosedTokenSource; // No need to RunContinuationsAsynchronously since we're at the tail of a threadpool thread - private TaskCompletionSource _connectionDelegateTcs = new TaskCompletionSource(); + private readonly TaskCompletionSource _connectionDelegateTcs = new (); private ConnectionDelegate _connectionDelegate; private CancellationTokenSource _sendCts;