-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Start connections on a fresh ExecutionContext #29995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- 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.
_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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you choose this route rather than:
await AwaitableThreadPool.Yield();
Task t;
using (ExecutionContext.SuppressFlow())
t = connectionDelegate(this);
await t;
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I generally don't like using suppress flow. I prefer the more explicit call that doesn't capture the ExecutionContext
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the fact that SuppressFlow throws if the flow is already suppressed makes it annoying. You need to always code around the fact that somebody further up the stack might have already suppressed it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to always code around the fact that somebody further up the stack might have already suppressed it.
There's no user code higher up the stack here. You shouldn't need to check or need additional code around it.
I generally don't like using suppress flow
Understood in general. In practice here, though, the code using SuppressFlow would seem to be less convoluted and (I'd need to measure to know for sure) less expensive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no user code higher up the stack here. You shouldn't need to check or need additional code around it.
There is. That's where the async locals come from and why this change is happening in the first place.
Understood in general. In practice here, though, the code using SuppressFlow would seem to be less convoluted and (I'd need to measure to know for sure) less expensive.
I think the code does look less convoluted but we use this pattern in other places in ASP.NET Core and it's a stylistic thing. It's the same reason I prefer using *Unsafe APIs instead of suppressing the flow in various cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you saying:
ExecutionContext.SuppressFlow();
await Task.Yield();
ExecutionContext.SuppressFlow();
Works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other thing I don't like about that is that it's too subtle vs a call that says "This will not capture the execution context" and the code sucks a little bit because. You know what I really want? Task.UnsafeRun
. That would work here as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you saying ... works?
Forgetting for the moment the fact that Task.Yield() respects current SynchronizationContext as well (which your AwaitableThreadPool.Yield() does not), yes... suppression itself doesn't flow, and the thread pool ensures a work item can't leak a change to suppression to the next work item.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You know what I really want? Task.UnsafeRun. That would work here as well.
Why do you want to queue to the ThreadPool? I thought this this change was strictly about the ExecutionContext.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see this change removed the old await AwaitableThreadPool.Yield();
Never mind me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you file an issue to track the follow up work on this? I thought one of the tests was failing with this change.
src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs
Outdated
Show resolved
Hide resolved
I haven't fixed it. I wanted to look at it before merging |
…nContext.cs Co-authored-by: Pranav K <[email protected]>
So any logging wont have the connection id associated with it? Do we want to manually add it back? |
Which logging exactly? The Kestrel connection id would be lost and the activity would be lost. |
SignalR's connection id we put in the logging scope aspnetcore/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionDispatcher.cs Line 319 in 5638880
|
argh good catch. |
ExecutionContext
from new connections, this removes the possibility of capturing request scoped async locals and flowing them to the hub. This will ensure that theIHttpContextAccessor
andActivity
for hub invocations is always null and aren't carried over from the incoming request.Related to #29846
This breaks the localization middleware for SignalR and for Blazor scenarios
aspnetcore/src/Components/test/testassets/TestServer/InternationalizationStartup.cs
Line 42 in c925f99
PS: I need to write a test but nothing works in visual studio...
cc @stephentoub you were asking about dotnet/runtime#47998, this is the change.