-
Notifications
You must be signed in to change notification settings - Fork 10.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f31dd94
commit 9800678
Showing
28 changed files
with
778 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyDispatcher.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace Microsoft.AspNetCore.Components.WebAssembly.Rendering; | ||
|
||
// When Blazor is deployed with multi-threaded runtime, WebAssemblyDispatcher will help to dispatch all Blazor JS interop calls to the main thread. | ||
// This is necessary because all JS objects have thread affinity. They are only available on the thread (WebWorker) which created them. | ||
// Also DOM is only available on the main (browser) thread. | ||
// Because all of the Dispatcher.InvokeAsync methods return Task, we don't need to propagate errors via OnUnhandledException handler | ||
internal sealed class WebAssemblyDispatcher : Dispatcher | ||
{ | ||
private readonly SynchronizationContext _mainSyncContext; | ||
|
||
public WebAssemblyDispatcher(SynchronizationContext mainSyncContext) | ||
{ | ||
_mainSyncContext = mainSyncContext; | ||
} | ||
|
||
public override bool CheckAccess() => SynchronizationContext.Current == _mainSyncContext; | ||
|
||
public override Task InvokeAsync(Action workItem) | ||
{ | ||
ArgumentNullException.ThrowIfNull(workItem); | ||
if (CheckAccess()) | ||
{ | ||
// this branch executes on correct thread and solved JavaScript objects thread affinity | ||
// but it executes out of order, if there are some pending jobs in the _mainSyncContext already, same as RendererSynchronizationContextDispatcher | ||
workItem(); | ||
// it can throw synchronously, same as RendererSynchronizationContextDispatcher | ||
return Task.CompletedTask; | ||
} | ||
|
||
var tcs = new TaskCompletionSource(); | ||
|
||
// RendererSynchronizationContext doesn't need to deal with thread affinity and so it could execute jobs on calling thread as optimization. | ||
// we could not do it for WASM/JavaScript, because we need to solve for thread affinity of JavaScript objects, so we always Post into the queue. | ||
_mainSyncContext.Post(static (object? o) => | ||
{ | ||
var state = ((TaskCompletionSource tcs, Action workItem))o!; | ||
try | ||
{ | ||
state.workItem(); | ||
state.tcs.SetResult(); | ||
} | ||
catch (Exception ex) | ||
{ | ||
state.tcs.SetException(ex); | ||
} | ||
}, (tcs, workItem)); | ||
|
||
return tcs.Task; | ||
} | ||
|
||
public override Task<TResult> InvokeAsync<TResult>(Func<TResult> workItem) | ||
{ | ||
ArgumentNullException.ThrowIfNull(workItem); | ||
if (CheckAccess()) | ||
{ | ||
// it can throw synchronously, same as RendererSynchronizationContextDispatcher | ||
return Task.FromResult(workItem()); | ||
} | ||
|
||
var tcs = new TaskCompletionSource<TResult>(); | ||
|
||
_mainSyncContext.Post(static (object? o) => | ||
{ | ||
var state = ((TaskCompletionSource<TResult> tcs, Func<TResult> workItem))o!; | ||
try | ||
{ | ||
var res = state.workItem(); | ||
state.tcs.SetResult(res); | ||
} | ||
catch (Exception ex) | ||
{ | ||
state.tcs.SetException(ex); | ||
} | ||
}, (tcs, workItem)); | ||
|
||
return tcs.Task; | ||
} | ||
|
||
public override Task InvokeAsync(Func<Task> workItem) | ||
{ | ||
ArgumentNullException.ThrowIfNull(workItem); | ||
if (CheckAccess()) | ||
{ | ||
// this branch executes on correct thread and solved JavaScript objects thread affinity | ||
// but it executes out of order, if there are some pending jobs in the _mainSyncContext already, same as RendererSynchronizationContextDispatcher | ||
return workItem(); | ||
// it can throw synchronously, same as RendererSynchronizationContextDispatcher | ||
} | ||
|
||
var tcs = new TaskCompletionSource(); | ||
|
||
_mainSyncContext.Post(static (object? o) => | ||
{ | ||
var state = ((TaskCompletionSource tcs, Func<Task> workItem))o!; | ||
|
||
try | ||
{ | ||
state.workItem().ContinueWith(t => | ||
{ | ||
if (t.IsFaulted) | ||
{ | ||
state.tcs.SetException(t.Exception); | ||
} | ||
else | ||
{ | ||
state.tcs.SetResult(); | ||
} | ||
}, TaskScheduler.FromCurrentSynchronizationContext()); | ||
} | ||
catch (Exception ex) | ||
{ | ||
// it could happen that the workItem will throw synchronously | ||
state.tcs.SetException(ex); | ||
} | ||
}, (tcs, workItem)); | ||
|
||
return tcs.Task; | ||
} | ||
|
||
public override Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> workItem) | ||
{ | ||
ArgumentNullException.ThrowIfNull(workItem); | ||
if (CheckAccess()) | ||
{ | ||
// this branch executes on correct thread and solved JavaScript objects thread affinity | ||
// but it executes out of order, if there are some pending jobs in the _mainSyncContext already, same as RendererSynchronizationContextDispatcher | ||
return workItem(); | ||
// it can throw synchronously, same as RendererSynchronizationContextDispatcher | ||
} | ||
|
||
var tcs = new TaskCompletionSource<TResult>(); | ||
|
||
_mainSyncContext.Post(static (object? o) => | ||
{ | ||
var state = ((TaskCompletionSource<TResult> tcs, Func<Task<TResult>> workItem))o!; | ||
try | ||
{ | ||
state.workItem().ContinueWith(t => | ||
{ | ||
if (t.IsFaulted) | ||
{ | ||
state.tcs.SetException(t.Exception); | ||
} | ||
else | ||
{ | ||
state.tcs.SetResult(t.Result); | ||
} | ||
}, TaskScheduler.FromCurrentSynchronizationContext()); | ||
} | ||
catch (Exception ex) | ||
{ | ||
state.tcs.SetException(ex); | ||
} | ||
}, (tcs, workItem)); | ||
|
||
return tcs.Task; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.