unoplatform / Uno.Wasm.Bootstrap

A simple nuget package to run C# code in a WASM-compatible browser
Other
367 stars 57 forks source link

JS Interop only works on main thread when using multiple threads #799

Open Rippletank opened 10 months ago

Rippletank commented 10 months ago

UnoWASMnet80TheadsAndJSInterop.zip

Background

Working on porting an existing app to Uno platform. Have implemented javascript replacements for many features not currently supported by WASM, such as cryptography, database, speech etc.

Issue

Using the JSImportAttribute and JSHost.ImportAsync works fine on the main thread but throws an exception "Please use dedicated worker for working with Javacsript interop" when run on a thread pool thread.

The exception seems to be thrown because there is no CurrentJSSynchronizationContext for the given thread in JSSynchronizationContext .

I can't find where this is actually set for the main thread. There is a WebWorker class which does install the required synchronisation contexts. However, I can't find any way to access this class anywhere. I did also try a UseWasmExperimental setting that was mentioned somewhere for .net7.0 but this didn't appear to do anything.

Reproduction

Attached project is minimal reproduction of the issue (reporting to console) for a simple synchronous and async call.

Workaround

Redirect all interop calls to main thread. However, this means that the main thread is blocked and animations stutter and controls are unresponsive as would be expected. I have set up a JS worker to handle the database calls once on the JS side.

Solution?

I am not familiar with how the Dotnet and Uno Platform git repos correspond with what is packaged, but is it possible to make the WebWorker class accessible? Failing that, is there any other way of getting more than one thread setup to run JS calls?

jeromelaban commented 10 months ago

I'm not sure about all the specifics that you are mentioning, but overall, Uno does not provide any API over what .NET provides. If you're building with a net8.0 TFM and .NET 8 RC 2 , WebWorker should be available like any other API and should be useable.

Rippletank commented 10 months ago

Ok, I understand. I'm still trying to familiarise myself with where different parts are coming from.

In your experience, would you suggest the dotnet/runtime Github be the most effective place to ask about this?

For the record, the Microsoft.NETCore.App\8.0.0-rc.2.23479.6\System.Runtime.InteropServices.JavaScript.dll does not appear to have the WebWorker class according to dotPeek.

As an addition to the workaround above, I would add careful profiling because some unexpected parts of WASM/Dotnet seem to be really slow. Avoid any cryptography, even the few algorithms that are implemented are hundreds of times slower than sending it over to JS and doing it all there using SubtleCrypto. And serialization is also very slow. Check to make sure you are only doing it once and for large numbers of simple types it can be much faster to avoid it at all and use StringBuilder and Uno.Foundation.WebAssemblyRuntime.EscapeJS.

SerratedSharp commented 10 months ago

Yep, most of the newer interop features that were introduced in .NET 7 including everything under System.Runtime.InteropServices.JavaScript are maintained under dotnet/runtime. It can be a bit confusing sometimes figuring out where the line is drawn. Before .NET 7 had these features, Uno libraries provided these interop capabilities to fill that need.

jeromelaban commented 10 months ago

In your experience, would you suggest the dotnet/runtime Github be the most effective place to ask about this?

dotnet/runtime would be the best place to ask questions about it, definitely. Uno only bundles the runtime as-is (with some minor configuration changes).