Thanks to visit codestin.com
Credit goes to github.com

Skip to content

dotnet.js fails to initialize when running in a web worker with onmessage handler #114918

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

Open
bcairns-google opened this issue Apr 22, 2025 · 5 comments
Assignees
Labels
arch-wasm WebAssembly architecture area-VM-threading-mono os-browser Browser variant of arch-wasm
Milestone

Comments

@bcairns-google
Copy link

bcairns-google commented Apr 22, 2025

Description

When dotnet.js is started in a web worker with an onmessage handler, the promises for asset instantiation are never resolved and therefore initialization hangs.

There appears to be a check in browser/runtime/loader/assets.ts which checks for ENVIRONMENT_IS_WORKER and deliberately avoids resolving the promises:

if (!ENVIRONMENT_IS_WORKER) {

and
if (!ENVIRONMENT_IS_WORKER) {

If ENVIRONMENT_IS_WORKER is manually set to false in the debugger, loading will complete normally and dotnet.js functions correctly in a web worker.

ENVIRONMENT_IS_WORKER is set here:

export const ENVIRONMENT_IS_WORKER = ENVIRONMENT_IS_WEB_WORKER && !ENVIRONMENT_IS_SIDECAR; // we redefine what ENVIRONMENT_IS_WORKER, we replace it in emscripten internals, so that sidecar works

The logic is a bit odd:
ENVIRONMENT_IS_WORKER is true if both of the following are true:

  • importScripts is a function
  • globalThis.onmessage is truthy

If globalThis.onmessage is undefined, globalThis.dotnetSidecar is set to true, ENVIRONMENT_IS_WORKER will be false, and loading works normally.

Reproduction Steps

 <script>
  const dotnetUrl = 'dotnet.js';
  const wasmConfig = {} // blazor.boot.json or equivalent here
  const workerSrc = `self.onmessage = async (initialMsg) => {
    const { dotnet } = await import('${dotnetUrl}');
    const { setModuleImports, getAssemblyExports } = await dotnet.withConfig(${JSON.stringify(wasmConfig)}).create();
    const assembly = await getAssemblyExports('WasmWorker');
  };`;
  const workerBlob = new Blob([workerSrc], {type: 'application/javascript'});
  const workerUrl = URL.createObjectURL(workerBlob);
  const worker = new Worker(workerUrl, { type: 'module'});
  worker.postMessage('init');
</script>

Expected behavior

dotnet.js initializes successfully in the web worker.

Actual behavior

The dotnet.create() promise is never resolved and initialization hangs.

Regression?

This worked correctly in .NET 8.

This commit introduced the logic which breaks loading in a web worker with an onmessage handler:
8698d3d

Known Workarounds

Delete any globalThis.onmessage handler before calling dotnet.create()

Configuration

.NET SDK 9.0.203
Behavior can be reproduced in Chrome, Safari, Firefox, and presumably any other browser that Blazor supports.

Other information

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 22, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Apr 22, 2025
@pavelsavara pavelsavara self-assigned this Apr 22, 2025
@pavelsavara pavelsavara added arch-wasm WebAssembly architecture os-browser Browser variant of arch-wasm area-VM-threading-mono labels Apr 22, 2025
@pavelsavara pavelsavara modified the milestones: Future, 10.0.0 Apr 22, 2025
@pavelsavara pavelsavara removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners untriaged New issue has not been triaged by the area owner labels Apr 22, 2025
Copy link
Contributor

Tagging subscribers to 'arch-wasm': @lewing
See info in area-owners.md if you want to be subscribed.

@pavelsavara
Copy link
Member

@bcairns-google I think you can do self.dotnetSidecar=true in worker to enable this scenario without needing to kill onmessage.

I did it here #92280

Does that work for you ?

@bcairns-google
Copy link
Author

Setting self.dotnetSidecar=true probably would work around this issue, but it also sets ENVIRONMENT_IS_SIDECAR to true. I'm a bit concerned that there might be side effects from doing so in the future.

Is there a particular reason to not resolve the promises in

if (!ENVIRONMENT_IS_WORKER) {
when ENVIRONMENT_IS_WORKER is true? You said something earlier about multi-threading, I'm just trying to understand why this would interact with the loading behavior.

@pavelsavara
Copy link
Member

You said something earlier about multi-threading, I'm just trying to understand why this would interact with the loading behavior.

When dotnet starts in multi-threading mode it also has to load some of it's assets (think JS files). Those which are loaded into linear memory, are already loaded by main thread only.

runtimeHelpers.deputyWorker.thread!.postMessageToWorker({

which arrives by

runtimeHelpers.allAssetsInMemory.promise_control.resolve();

@VYDocuWare
Copy link

VYDocuWare commented Apr 30, 2025

I'm running into the same issue, and

self.dotnetSidecar=true

doesn't seem to fix the issue for me:

const { getAssemblyExports, getConfig } = await dotnet
	.withDiagnosticTracing(false)
	.create();

still hangs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arch-wasm WebAssembly architecture area-VM-threading-mono os-browser Browser variant of arch-wasm
Projects
None yet
Development

No branches or pull requests

3 participants