-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[blazor][wasm] Dispatch rendering to main thread #48991
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
src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyDispatcher.cs
Outdated
Show resolved
Hide resolved
6d0e683 to
1ab5597
Compare
1ab5597 to
db1c1c5
Compare
src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyDispatcher.cs
Show resolved
Hide resolved
|
@mkArtakMSFT could we get this reviewed and merged for the next preview ? |
src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyDispatcher.cs
Show resolved
Hide resolved
| self.Send((_) => | ||
| { | ||
| try | ||
| { | ||
| body(); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| exc = ex; | ||
| } | ||
| }, null); | ||
| if (exc != null) | ||
| { | ||
| throw exc; | ||
| } |
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.
Doesn't this block? My understanding is that this runs the callback inline, isn't it?
I'm trying to make sense of how this works when you are in a background thread and try to dispatch back to the "main" thread.
My understanding is that the contract for send must block the thread.
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.
Yes, this will block the sender thread. If this is the main thread, it would just invoke it.
src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs
Show resolved
Hide resolved
src/Components/WebAssembly/testassets/ThreadingApp/Pages/Counter.razor
Outdated
Show resolved
Hide resolved
# Conflicts: # AspNetCore.sln # src/Components/Components.slnf # src/Components/ComponentsNoDeps.slnf
|
I think that CI failure is unrelated to this PR. @javiercn are there further questions ? |
| } | ||
|
|
||
| _context!.InvokeAsync(workItem); | ||
| return Task.CompletedTask; |
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.
This looks like a mistake, unless I'm confused.
Shouldn't this return the task returned by _context.InvokeAsync, instead of Task.CompletedTask?
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.
It will invoke the void SynchronizationContextExtension.InvokeAsync and so there is no Task. Also it's blocking synchronous call.
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.
More details about the expected behavior in https://github.com/dotnet/aspnetcore/pull/48991/files#r1263731418
| _context = SynchronizationContext.Current; | ||
| } | ||
|
|
||
| public override bool CheckAccess() => SynchronizationContext.Current == _context || _context == null; |
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.
Could you explain the || _context == null part of this check?
I would have thought that if SynchronizationContext.Current == null, then we'd only consider ourselves "having access" if the original _context was also null. So it seems like the logic would be more correct if the || _context == null part of this check was removed.
| throw exc; | ||
| } | ||
| return value!; | ||
| } |
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 logic around async and exception handling looks strange to me. At least, it will behave very differently from how RendererSynchronizationContext does.
Dispatcher.InvokeAsync(Action)- With
WebAssemblyDispatcher, it blocks the caller until the action is fully complete. If the action throws, then it re-throws instead of returning any task. - With
RendererSynchronizationContextDispatcher, it doesn't block the caller, and returns aTaskthat completes when the action is complete. If the action throws, theTaskis faulted.
- With
Dispatcher.InvokeAsync(Func<Task>)- With
WebAssemblyDispatcher, it blocks the caller while the dispatch is in process (up until the recipient yields), and then returns theTaskreturned by the recipient. If the recipient throws synchronously, then it re-throws instead of returning any task. - With
RendererSynchronizationContextDispatcher, it does not block the caller, and returns a aTaskthat completes when the dispatch and recipient's Task is complete. If the recipent throws synchronously, the returnedTaskis faulted.
- With
Are these differences in behavior intentional? My guess is we should behave the same as RendererSynchronizationContextDispatcher/RendererSynchronizationContext.
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.
Similarly please see how RendererSynchronizationContext deals with unhandled exceptions by calling OnUnhandledException on the Dispatcher.
|
Looks like this PR hasn't been active for some time and the codebase could have been changed in the meantime. |

This PR implements
WebAssemblyDispatcherwhich will dispatch the call toComponent.InvokeAsyncback to main thread, where all the rendering/JS interop/networking could happen.Context: with
<WasmEnableThreads>true</WasmEnableThreads>we could use thread pool in C#.Any async continuations with
.ConfigureAwait(false)will likely run there.Added new
src/Components/WebAssembly/testassets/ThreadingAppInvokeAsyncto render it.Contributes to dotnet/runtime#85592
Fixes #48768