From a90464e1a3457c791161da640d06c25f653dc59e Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 10 Aug 2022 00:23:21 -0400 Subject: [PATCH 01/10] [wasm-mt] Override Emscripten PThread.allocateUnusedWorker We want to use our own allocateUnusedWorker because we want to load `dotnet.worker.js` using our asset loading machinery. Unfortunately, Emscripten first calls allocateUnusedWorker very early (from `PThread.init`) to pre-allocate the pthread worker pool. So we set Emscripten's own pthread worker pool to size 0 and make our own. This requires calling `loadWasmModuleToWorker` during our startup because Emscripten deletes their code that normally does it (in "receiveInstance" in "createWasm" in "emscripten/src/preamble.js") when the pthread pool size is 0. Also added a pthreadPoolSize field to MonoConfig to allow specifying the initial pthread pool size in mono-config.json --- src/mono/wasm/runtime/dotnet.d.ts | 1 + src/mono/wasm/runtime/es6/dotnet.es6.lib.js | 3 ++ src/mono/wasm/runtime/polyfills.ts | 16 ++------- .../wasm/runtime/pthreads/browser/index.ts | 34 ++++++++++++++++++- .../shared/emscripten-replacements.ts | 34 +++++++++++++++++++ .../runtime/pthreads/shared/tsconfig.json | 7 +++- src/mono/wasm/runtime/startup.ts | 11 ++++++ src/mono/wasm/runtime/types.ts | 14 ++++++-- src/mono/wasm/wasm.proj | 2 +- 9 files changed, 103 insertions(+), 19 deletions(-) create mode 100644 src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts index eacddc7a017bbe..832513510e3a03 100644 --- a/src/mono/wasm/runtime/dotnet.d.ts +++ b/src/mono/wasm/runtime/dotnet.d.ts @@ -81,6 +81,7 @@ declare type MonoConfig = { coverageProfilerOptions?: CoverageProfilerOptions; ignorePdbLoadErrors?: boolean; waitForDebugger?: number; + pthreadPoolSize?: number; }; interface ResourceRequest { name: string; diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index 6c5ed1b2ea1175..b98e4c7f88c27e 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -26,6 +26,8 @@ let __dotnet_replacement_PThread = ${usePThreads} ? {} : undefined; if (${usePThreads}) { __dotnet_replacement_PThread.loadWasmModuleToWorker = PThread.loadWasmModuleToWorker; __dotnet_replacement_PThread.threadInitTLS = PThread.threadInitTLS; + __dotnet_replacement_PThread.allocateUnusedWorker = PThread.allocateUnusedWorker; + __dotnet_replacement_PThread.PThread = PThread; } let __dotnet_replacements = {scriptUrl: import.meta.url, fetch: globalThis.fetch, require, updateGlobalBufferAndViews, pthreadReplacements: __dotnet_replacement_PThread}; if (ENVIRONMENT_IS_NODE) { @@ -47,6 +49,7 @@ var noExitRuntime = __dotnet_replacements.noExitRuntime; if (${usePThreads}) { PThread.loadWasmModuleToWorker = __dotnet_replacements.pthreadReplacements.loadWasmModuleToWorker; PThread.threadInitTLS = __dotnet_replacements.pthreadReplacements.threadInitTLS; + PThread.allocateUnusedWorker = __dotnet_replacements.pthreadReplacements.allocateUnusedWorker; } `, }; diff --git a/src/mono/wasm/runtime/polyfills.ts b/src/mono/wasm/runtime/polyfills.ts index afd92c71074896..13a912926c844b 100644 --- a/src/mono/wasm/runtime/polyfills.ts +++ b/src/mono/wasm/runtime/polyfills.ts @@ -2,8 +2,7 @@ import BuildConfiguration from "consts:configuration"; import MonoWasmThreads from "consts:monoWasmThreads"; import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, ENVIRONMENT_IS_WORKER, INTERNAL, Module, runtimeHelpers } from "./imports"; import { afterUpdateGlobalBufferAndViews } from "./memory"; -import { afterLoadWasmModuleToWorker } from "./pthreads/browser"; -import { afterThreadInitTLS } from "./pthreads/worker"; +import { replaceEmscriptenPThreadLibrary } from "./pthreads/shared/emscripten-replacements"; import { DotnetModuleConfigImports, EarlyReplacements } from "./types"; let node_fs: any | undefined = undefined; @@ -173,16 +172,7 @@ export function init_polyfills(replacements: EarlyReplacements): void { // threads if (MonoWasmThreads) { if (replacements.pthreadReplacements) { - const originalLoadWasmModuleToWorker = replacements.pthreadReplacements.loadWasmModuleToWorker; - replacements.pthreadReplacements.loadWasmModuleToWorker = (worker: Worker, onFinishedLoading: Function): void => { - originalLoadWasmModuleToWorker(worker, onFinishedLoading); - afterLoadWasmModuleToWorker(worker); - }; - const originalThreadInitTLS = replacements.pthreadReplacements.threadInitTLS; - replacements.pthreadReplacements.threadInitTLS = (): void => { - originalThreadInitTLS(); - afterThreadInitTLS(); - }; + replaceEmscriptenPThreadLibrary(replacements.pthreadReplacements); } } @@ -297,4 +287,4 @@ function isPathAbsolute(path: string): boolean { // windows file:///C:/x.json // windows http://C:/x.json return protocolRx.test(path); -} \ No newline at end of file +} diff --git a/src/mono/wasm/runtime/pthreads/browser/index.ts b/src/mono/wasm/runtime/pthreads/browser/index.ts index f71ad533cfaea3..afec88767d0f82 100644 --- a/src/mono/wasm/runtime/pthreads/browser/index.ts +++ b/src/mono/wasm/runtime/pthreads/browser/index.ts @@ -5,6 +5,7 @@ import { Module } from "../../imports"; import { pthread_ptr, MonoWorkerMessageChannelCreated, isMonoWorkerMessageChannelCreated, monoSymbol } from "../shared"; import { MonoThreadMessage } from "../shared"; import { PromiseController, createPromiseController } from "../../promise-controller"; +import { MonoConfig } from "../../types"; const threads: Map = new Map(); @@ -103,6 +104,26 @@ export function afterLoadWasmModuleToWorker(worker: Worker): void { console.debug("MONO_WASM: afterLoadWasmModuleToWorker added message event handler", worker); } +export function preAllocatePThreadWorkerPool(defaultPthreadPoolSize: number, config: MonoConfig): void { + const n = config?.pthreadPoolSize ?? defaultPthreadPoolSize; + for (let i = 0; i < n; i++) { + Internals.allocateUnusedWorker(); + } +} + +export async function instantiateWasmPThreadWorkerPool(): Promise { + // this is largely copied from emscripten's "receiveInstance" in "createWasm" in "src/preamble.js" + const workers = Internals.getUnusedWorkerPool(); + const allLoaded = createPromiseController(); + let leftToLoad = workers.length; + workers.forEach((w) => { + Internals.loadWasmModuleToWorker(w, function () { + if (!--leftToLoad) allLoaded.promise_control.resolve(); + }); + }); + await allLoaded.promise; +} + /// These utility functions dig into Emscripten internals const Internals = { getWorker: (pthread_ptr: pthread_ptr): Worker => { @@ -117,7 +138,18 @@ const Internals = { return undefined; } return emscriptenThreadInfo.threadInfoStruct; + }, + allocateUnusedWorker: (): void => { + /// See library_pthread.js in Emscripten. + /// This function allocates a new worker and adds it to the pool of workers. + /// It's called when the pool of workers is empty and a new thread is created. + (Module).PThread.allocateUnusedWorker(); + }, + getUnusedWorkerPool: (): Worker[] => { + return (Module).PThread.unusedWorkers; + }, + loadWasmModuleToWorker: (worker: Worker, onFinishedLoading: () => void): void => { + (Module).PThread.loadWasmModuleToWorker(worker, onFinishedLoading); } }; - diff --git a/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts b/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts new file mode 100644 index 00000000000000..a707802e86be2e --- /dev/null +++ b/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import MonoWasmThreads from "consts:monoWasmThreads"; +import { PThreadReplacements } from "../../types"; +import { afterLoadWasmModuleToWorker } from "../browser"; +import { afterThreadInitTLS } from "../worker"; + + +/** @module emscripten-replacements Replacements for individual functions in the emscripten PThreads library. + * These have a hard dependency on the version of Emscripten that we are using and may need to be kept in sync with + * {@linkcode file://./../../../emsdk/upstream/emscripten/src/library_pthread.js} + */ + +export function replaceEmscriptenPThreadLibrary(replacements: PThreadReplacements): void { + if (MonoWasmThreads) { + // const PThread = replacements.PThread; + const originalLoadWasmModuleToWorker = replacements.loadWasmModuleToWorker; + replacements.loadWasmModuleToWorker = (worker: Worker, onFinishedLoading?: (worker: Worker) => void): void => { + originalLoadWasmModuleToWorker(worker, onFinishedLoading); + afterLoadWasmModuleToWorker(worker); + }; + const originalThreadInitTLS = replacements.threadInitTLS; + replacements.threadInitTLS = (): void => { + originalThreadInitTLS(); + afterThreadInitTLS(); + }; + const originalAllocateUnusedWorker = replacements.allocateUnusedWorker; + replacements.allocateUnusedWorker = () => { + // TODO: replace this with our own implementation based on asset loading + originalAllocateUnusedWorker(); + }; + } +} diff --git a/src/mono/wasm/runtime/pthreads/shared/tsconfig.json b/src/mono/wasm/runtime/pthreads/shared/tsconfig.json index 7b8ecd91fcc4f1..8986477dd8fc35 100644 --- a/src/mono/wasm/runtime/pthreads/shared/tsconfig.json +++ b/src/mono/wasm/runtime/pthreads/shared/tsconfig.json @@ -1,3 +1,8 @@ { - "extends": "../../tsconfig.shared.json" + "extends": "../../tsconfig.shared.json", + "include": [ + "../../**/*.ts", + "../../**/*.d.ts" + ] + } diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 4e35254d7e456b..68653ffa1ac3f5 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -28,6 +28,7 @@ import { instantiate_wasm_asset, mono_download_assets, resolve_asset_path, start import { BINDING, MONO } from "./net6-legacy/imports"; import { readSymbolMapFile } from "./logging"; import { mono_wasm_init_diagnostics } from "./diagnostics"; +import { preAllocatePThreadWorkerPool, instantiateWasmPThreadWorkerPool } from "./pthreads/browser"; let config: MonoConfig = undefined as any; let configLoaded = false; @@ -41,6 +42,9 @@ export const beforeOnRuntimeInitialized = createPromiseController(); export const afterOnRuntimeInitialized = createPromiseController(); export const afterPostRun = createPromiseController(); +// default size if MonoConfig.pthreadPoolSize is undefined +const MONO_PTHREAD_POOL_SIZE = 4; + // we are making emscripten startup async friendly // emscripten is executing the events without awaiting it and so we need to block progress via PromiseControllers above export function configure_emscripten_startup(module: DotnetModule, exportedAPI: DotnetPublicAPI): void { @@ -149,6 +153,9 @@ async function preRunAsync(userPreRun: (() => void)[]) { await afterInstantiateWasm.promise; await afterPreInit.promise; if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: preRunAsync"); + if (MonoWasmThreads) { + await instantiateWasmPThreadWorkerPool(); + } try { // all user Module.preRun callbacks userPreRun.map(fn => fn()); @@ -247,6 +254,10 @@ async function mono_wasm_pre_init_essential_async(): Promise { await mono_wasm_load_config(Module.configSrc); init_crypto(); + if (MonoWasmThreads) { + preAllocatePThreadWorkerPool(MONO_PTHREAD_POOL_SIZE, config); + } + Module.removeRunDependency("mono_wasm_pre_init_essential_async"); } diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts index d9edc82f9587b6..8c769c96c09a2f 100644 --- a/src/mono/wasm/runtime/types.ts +++ b/src/mono/wasm/runtime/types.ts @@ -83,7 +83,8 @@ export type MonoConfig = { aotProfilerOptions?: AOTProfilerOptions, // dictionary-style Object. If omitted, aot profiler will not be initialized. coverageProfilerOptions?: CoverageProfilerOptions, // dictionary-style Object. If omitted, coverage profiler will not be initialized. ignorePdbLoadErrors?: boolean, - waitForDebugger?: number + waitForDebugger?: number, + pthreadPoolSize?: number, // initial number of workers to add to the emscripten pthread pool }; export type MonoConfigError = { @@ -298,8 +299,15 @@ export interface ExitStatusError { new(status: number): any; } export type PThreadReplacements = { - loadWasmModuleToWorker: Function, - threadInitTLS: Function + loadWasmModuleToWorker: (worker: Worker, onFinishedLoading?: (worker: Worker) => void) => void, + threadInitTLS: () => void, + allocateUnusedWorker: () => void, + readonly PThread: PThreadLibrary; +} + +/// The parts of the emscripten PThread library (library_pthread.js) that we may want to access in our replacement functions +export type PThreadLibrary = Readonly & { + unusedWorkers: Worker[], } /// Always throws. Used to handle unreachable switch branches when TypeScript refines the type of a variable diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 5d7f1facca07a9..8112b86ba11348 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -125,7 +125,7 @@ <_EmccCommonFlags Condition="'$(WasmEnableSIMD)' == 'true'" Include="-msimd128" /> <_EmccCommonFlags Condition="'$(MonoWasmThreads)' == 'true'" Include="-s USE_PTHREADS=1" /> <_EmccLinkFlags Condition="'$(MonoWasmThreads)' == 'true'" Include="-Wno-pthreads-mem-growth" /> - <_EmccLinkFlags Condition="'$(MonoWasmThreads)' == 'true'" Include="-s PTHREAD_POOL_SIZE=4" /> + <_EmccLinkFlags Condition="'$(MonoWasmThreads)' == 'true'" Include="-s PTHREAD_POOL_SIZE=0" /> <_EmccLinkFlags Condition="'$(MonoWasmThreads)' == 'true'" Include="-s PTHREAD_POOL_SIZE_STRICT=2" /> <_EmccLinkFlags Include="-s ALLOW_MEMORY_GROWTH=1" /> <_EmccLinkFlags Include="-s NO_EXIT_RUNTIME=1" /> From 673cd1fec97d1aa2de2eaba31ddaeb0e14a27364 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 10 Aug 2022 11:11:15 -0400 Subject: [PATCH 02/10] Add IncludeThreadsWorker and PThreadPoolSize props to WasmAppBuilder IncludeThreadsWorker adds the js-module-threads asset to the mono-config PThreadPoolSize can be -1 or >=0 to specify the number of workers that will be pre-allocated at startup for the pthread worker pool. -1 means use the default compiled into dotnet.js --- src/mono/wasm/build/WasmApp.targets | 11 ++++++++++- src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index d9a5f1f06ed70f..602896b55da0f0 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -316,6 +316,12 @@ DependsOnTargets="_WasmGenerateRuntimeConfig;_GetWasmGenerateAppBundleDependencies"> + + <_WasmAppIncludeThreadsWorker Condition="'$(WasmEnableThreads)' == 'true' or '$(WasmEnablePerfTracing)' == 'true'">true + + <_WasmPThreadPoolSize Condition="'$(_WasmPThreadPoolSize)' == '' and ('$(WasmEnableThreads)' == 'true' or '$(WasmEnablePerfTracing)' == 'true')">-1 + + + DebugLevel="$(WasmDebugLevel)" + IncludeThreadsWorker="$(_WasmAppIncludeThreadsWorker)" + PThreadPoolSize="$(_WasmPThreadPoolSize)" + > diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 65f1a9a5b0f1ad..240e8c1af8b8b6 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -46,6 +46,8 @@ public class WasmAppBuilder : Task public bool InvariantGlobalization { get; set; } public ITaskItem[]? ExtraFilesToDeploy { get; set; } public string? MainHTMLPath { get; set; } + public bool IncludeThreadsWorker {get; set; } + public int PThreadPoolSize {get; set; } // // Extra json elements to add to mono-config.json @@ -314,6 +316,8 @@ private bool ExecuteInternal () config.Assets.Add(new VfsEntry ("dotnet.timezones.blat") { VirtualPath = "/usr/share/zoneinfo/"}); config.Assets.Add(new WasmEntry ("dotnet.wasm") ); config.Assets.Add(new CryptoWorkerEntry ("dotnet-crypto-worker.js") ); + if (IncludeThreadsWorker) + config.Assets.Add(new ThreadsWorkerEntry ("dotnet.worker.js") ); if (RemoteSources?.Length > 0) { @@ -322,6 +326,11 @@ private bool ExecuteInternal () config.RemoteSources.Add(source.ItemSpec); } + if (PThreadPoolSize < -1) { + throw new LogAsErrorException($"PThreadPoolSize must be -1, 0 or positive, but got {PThreadPoolSize}"); + } else + config.Extra["pthreadPoolSize"] = PThreadPoolSize; + foreach (ITaskItem extra in ExtraConfig ?? Enumerable.Empty()) { string name = extra.ItemSpec; From e12de7bbdd774c797163ff4a1f5b210c2ba6a624 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 10 Aug 2022 11:15:02 -0400 Subject: [PATCH 03/10] Move emscripten PThread internals access to a separate module and add types --- src/mono/wasm/runtime/es6/dotnet.es6.lib.js | 1 - .../wasm/runtime/pthreads/browser/index.ts | 57 ++++++-------- .../pthreads/shared/emscripten-internals.ts | 78 +++++++++++++++++++ .../wasm/runtime/pthreads/shared/index.ts | 4 +- .../wasm/runtime/pthreads/shared/types.ts | 5 ++ .../wasm/runtime/pthreads/worker/events.ts | 3 +- .../wasm/runtime/pthreads/worker/index.ts | 3 +- src/mono/wasm/runtime/types.ts | 6 -- 8 files changed, 111 insertions(+), 46 deletions(-) create mode 100644 src/mono/wasm/runtime/pthreads/shared/emscripten-internals.ts create mode 100644 src/mono/wasm/runtime/pthreads/shared/types.ts diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index b98e4c7f88c27e..74aed3030e8a86 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -27,7 +27,6 @@ if (${usePThreads}) { __dotnet_replacement_PThread.loadWasmModuleToWorker = PThread.loadWasmModuleToWorker; __dotnet_replacement_PThread.threadInitTLS = PThread.threadInitTLS; __dotnet_replacement_PThread.allocateUnusedWorker = PThread.allocateUnusedWorker; - __dotnet_replacement_PThread.PThread = PThread; } let __dotnet_replacements = {scriptUrl: import.meta.url, fetch: globalThis.fetch, require, updateGlobalBufferAndViews, pthreadReplacements: __dotnet_replacement_PThread}; if (ENVIRONMENT_IS_NODE) { diff --git a/src/mono/wasm/runtime/pthreads/browser/index.ts b/src/mono/wasm/runtime/pthreads/browser/index.ts index afec88767d0f82..e33c5d0026b1ac 100644 --- a/src/mono/wasm/runtime/pthreads/browser/index.ts +++ b/src/mono/wasm/runtime/pthreads/browser/index.ts @@ -1,11 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { Module } from "../../imports"; -import { pthread_ptr, MonoWorkerMessageChannelCreated, isMonoWorkerMessageChannelCreated, monoSymbol } from "../shared"; +import { MonoWorkerMessageChannelCreated, isMonoWorkerMessageChannelCreated, monoSymbol } from "../shared"; +import { pthread_ptr } from "../shared/types"; import { MonoThreadMessage } from "../shared"; import { PromiseController, createPromiseController } from "../../promise-controller"; -import { MonoConfig } from "../../types"; +import { MonoConfig, mono_assert } from "../../types"; +import Internals from "../shared/emscripten-internals"; const threads: Map = new Map(); @@ -104,13 +105,31 @@ export function afterLoadWasmModuleToWorker(worker: Worker): void { console.debug("MONO_WASM: afterLoadWasmModuleToWorker added message event handler", worker); } +/// We call on the main thread this during startup to pre-allocate a pool of pthread workers. +/// At this point asset resolution needs to be working (ie we loaded MonoConfig). +/// This is used instead of the Emscripten PThread.initMainThread because we call it later. export function preAllocatePThreadWorkerPool(defaultPthreadPoolSize: number, config: MonoConfig): void { - const n = config?.pthreadPoolSize ?? defaultPthreadPoolSize; + const poolSizeSpec = config?.pthreadPoolSize; + let n: number; + if (poolSizeSpec === undefined) { + n = defaultPthreadPoolSize; + } else { + mono_assert(typeof poolSizeSpec === "number", "pthreadPoolSize must be a number"); + if (poolSizeSpec < 0) + n = defaultPthreadPoolSize; + else + n = poolSizeSpec; + } for (let i = 0; i < n; i++) { Internals.allocateUnusedWorker(); } } +/// We call this on the main thread during startup once we fetched WasmModule. +/// This sends a message to each pre-allocated worker to load the WasmModule and dotnet.js and to set up +/// message handling. +/// This is used instead of the Emscripten "receiveInstance" in "createWasm" because that code is +/// conditioned on a non-zero PTHREAD_POOL_SIZE (but we set it to 0 to avoid early worker allocation). export async function instantiateWasmPThreadWorkerPool(): Promise { // this is largely copied from emscripten's "receiveInstance" in "createWasm" in "src/preamble.js" const workers = Internals.getUnusedWorkerPool(); @@ -123,33 +142,3 @@ export async function instantiateWasmPThreadWorkerPool(): Promise { }); await allLoaded.promise; } - -/// These utility functions dig into Emscripten internals -const Internals = { - getWorker: (pthread_ptr: pthread_ptr): Worker => { - // see https://github.com/emscripten-core/emscripten/pull/16239 - return (Module).PThread.pthreads[pthread_ptr].worker; - }, - getThreadId: (worker: Worker): pthread_ptr | undefined => { - /// See library_pthread.js in Emscripten. - /// They hang a "pthread" object from the worker if the worker is running a thread, and remove it when the thread stops by doing `pthread_exit` or when it's joined using `pthread_join`. - const emscriptenThreadInfo = (worker)["pthread"]; - if (emscriptenThreadInfo === undefined) { - return undefined; - } - return emscriptenThreadInfo.threadInfoStruct; - }, - allocateUnusedWorker: (): void => { - /// See library_pthread.js in Emscripten. - /// This function allocates a new worker and adds it to the pool of workers. - /// It's called when the pool of workers is empty and a new thread is created. - (Module).PThread.allocateUnusedWorker(); - }, - getUnusedWorkerPool: (): Worker[] => { - return (Module).PThread.unusedWorkers; - }, - loadWasmModuleToWorker: (worker: Worker, onFinishedLoading: () => void): void => { - (Module).PThread.loadWasmModuleToWorker(worker, onFinishedLoading); - } -}; - diff --git a/src/mono/wasm/runtime/pthreads/shared/emscripten-internals.ts b/src/mono/wasm/runtime/pthreads/shared/emscripten-internals.ts new file mode 100644 index 00000000000000..43761678cfac59 --- /dev/null +++ b/src/mono/wasm/runtime/pthreads/shared/emscripten-internals.ts @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { Module } from "../../imports"; +import { pthread_ptr } from "./types"; + +/** @module emscripten-internals accessors to the functions in the emscripten PThreads library, including + * the low-level representations of {@linkcode pthread_ptr} thread info structs, etc. + * Additionally, note that some of these functions are replaced by {@linkcode file://./emscripten-replacements.ts}. + * These have a hard dependency on the version of Emscripten that we are using and may need to be kept in sync with + * {@linkcode file://./../../../emsdk/upstream/emscripten/src/library_pthread.js} + */ + +// This is what we know about the Emscripten PThread library +interface PThreadLibrary { + unusedWorkers: Worker[]; + pthreads: PThreadInfoMap; + allocateUnusedWorker: () => void; + loadWasmModuleToWorker: (worker: Worker, onFinishedLoading?: (worker: Worker) => void) => void; +} + +interface EmscriptenPThreadInfo { + threadInfoStruct: pthread_ptr; +} + +interface PThreadWorker extends Worker { + pthread?: EmscriptenPThreadInfo; +} + +interface PThreadObject { + worker: PThreadWorker; +} + +interface PThreadInfoMap { + [key: pthread_ptr]: PThreadObject | undefined; +} + + +function isRunningPThreadWorker(w: Worker): w is PThreadWorker { + return (w).pthread !== undefined; +} + +/// These utility functions dig into Emscripten internals +const Internals = { + get modulePThread(): PThreadLibrary { + return (Module).PThread as PThreadLibrary; + }, + getWorker: (pthread_ptr: pthread_ptr): PThreadWorker | undefined => { + // see https://github.com/emscripten-core/emscripten/pull/16239 + return Internals.modulePThread.pthreads[pthread_ptr]?.worker; + }, + getThreadId: (worker: Worker): pthread_ptr | undefined => { + /// See library_pthread.js in Emscripten. + /// They hang a "pthread" object from the worker if the worker is running a thread, and remove it when the thread stops by doing `pthread_exit` or when it's joined using `pthread_join`. + if (!isRunningPThreadWorker(worker)) + return undefined; + const emscriptenThreadInfo = worker.pthread; + if (emscriptenThreadInfo === undefined) { + return undefined; + } + return emscriptenThreadInfo.threadInfoStruct; + }, + allocateUnusedWorker: (): void => { + /// See library_pthread.js in Emscripten. + /// This function allocates a new worker and adds it to the pool of workers. + /// It's called when the pool of workers is empty and a new thread is created. + Internals.modulePThread.allocateUnusedWorker(); + }, + getUnusedWorkerPool: (): Worker[] => { + return Internals.modulePThread.unusedWorkers; + }, + loadWasmModuleToWorker: (worker: Worker, onFinishedLoading: () => void): void => { + Internals.modulePThread.loadWasmModuleToWorker(worker, onFinishedLoading); + } +}; + + +export default Internals; diff --git a/src/mono/wasm/runtime/pthreads/shared/index.ts b/src/mono/wasm/runtime/pthreads/shared/index.ts index ef71b31022af30..de488ca0de7f28 100644 --- a/src/mono/wasm/runtime/pthreads/shared/index.ts +++ b/src/mono/wasm/runtime/pthreads/shared/index.ts @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import { Module } from "../../imports"; - -/// pthread_t in C -export type pthread_ptr = number; +import { pthread_ptr } from "./types"; export interface PThreadInfo { readonly pthread_id: pthread_ptr; diff --git a/src/mono/wasm/runtime/pthreads/shared/types.ts b/src/mono/wasm/runtime/pthreads/shared/types.ts new file mode 100644 index 00000000000000..daa1113ee60aea --- /dev/null +++ b/src/mono/wasm/runtime/pthreads/shared/types.ts @@ -0,0 +1,5 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/// pthread_t in C +export type pthread_ptr = number; diff --git a/src/mono/wasm/runtime/pthreads/worker/events.ts b/src/mono/wasm/runtime/pthreads/worker/events.ts index 7022497d0a4fc4..a170345ef2b0d1 100644 --- a/src/mono/wasm/runtime/pthreads/worker/events.ts +++ b/src/mono/wasm/runtime/pthreads/worker/events.ts @@ -1,5 +1,6 @@ import MonoWasmThreads from "consts:monoWasmThreads"; -import type { pthread_ptr, PThreadInfo, MonoThreadMessage } from "../shared"; +import type { pthread_ptr } from "../shared/types"; +import type { PThreadInfo, MonoThreadMessage } from "../shared"; /// Identification of the current thread executing on a worker export interface PThreadSelf extends PThreadInfo { diff --git a/src/mono/wasm/runtime/pthreads/worker/index.ts b/src/mono/wasm/runtime/pthreads/worker/index.ts index 810a396db8f96d..cb77a4e30728bb 100644 --- a/src/mono/wasm/runtime/pthreads/worker/index.ts +++ b/src/mono/wasm/runtime/pthreads/worker/index.ts @@ -5,7 +5,8 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { Module, ENVIRONMENT_IS_PTHREAD } from "../../imports"; -import { makeChannelCreatedMonoMessage, pthread_ptr } from "../shared"; +import { makeChannelCreatedMonoMessage } from "../shared"; +import type { pthread_ptr } from "../shared/types"; import { mono_assert, is_nullish } from "../../types"; import type { MonoThreadMessage } from "../shared"; import { diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts index 8c769c96c09a2f..d3fae6c690cc1a 100644 --- a/src/mono/wasm/runtime/types.ts +++ b/src/mono/wasm/runtime/types.ts @@ -302,12 +302,6 @@ export type PThreadReplacements = { loadWasmModuleToWorker: (worker: Worker, onFinishedLoading?: (worker: Worker) => void) => void, threadInitTLS: () => void, allocateUnusedWorker: () => void, - readonly PThread: PThreadLibrary; -} - -/// The parts of the emscripten PThread library (library_pthread.js) that we may want to access in our replacement functions -export type PThreadLibrary = Readonly & { - unusedWorkers: Worker[], } /// Always throws. Used to handle unreachable switch branches when TypeScript refines the type of a variable From a0e71e4a2c970dbc09cd9f72dbe09e091cfb8d5d Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 10 Aug 2022 11:15:49 -0400 Subject: [PATCH 04/10] Load js-module-threads asset in replacement allocateUnusedWorker --- .../shared/emscripten-replacements.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts b/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts index a707802e86be2e..86fa90f087f485 100644 --- a/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts +++ b/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts @@ -5,6 +5,9 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { PThreadReplacements } from "../../types"; import { afterLoadWasmModuleToWorker } from "../browser"; import { afterThreadInitTLS } from "../worker"; +import Internals from "./emscripten-internals"; +import { resolve_asset_path } from "../../assets"; +import { mono_assert } from "../../types"; /** @module emscripten-replacements Replacements for individual functions in the emscripten PThreads library. @@ -14,7 +17,6 @@ import { afterThreadInitTLS } from "../worker"; export function replaceEmscriptenPThreadLibrary(replacements: PThreadReplacements): void { if (MonoWasmThreads) { - // const PThread = replacements.PThread; const originalLoadWasmModuleToWorker = replacements.loadWasmModuleToWorker; replacements.loadWasmModuleToWorker = (worker: Worker, onFinishedLoading?: (worker: Worker) => void): void => { originalLoadWasmModuleToWorker(worker, onFinishedLoading); @@ -25,10 +27,17 @@ export function replaceEmscriptenPThreadLibrary(replacements: PThreadReplacement originalThreadInitTLS(); afterThreadInitTLS(); }; - const originalAllocateUnusedWorker = replacements.allocateUnusedWorker; - replacements.allocateUnusedWorker = () => { - // TODO: replace this with our own implementation based on asset loading - originalAllocateUnusedWorker(); - }; + // const originalAllocateUnusedWorker = replacements.allocateUnusedWorker; + replacements.allocateUnusedWorker = replacementAllocateUnusedWorker; } } + +/// We replace Module["PThreads"].allocateUnusedWorker with this version that knows about assets +function replacementAllocateUnusedWorker(): void { + console.debug("MONO_WASM: replacementAllocateUnusedWorker"); + const asset = resolve_asset_path("js-module-threads"); + const uri = asset.resolvedUrl; + mono_assert(uri !== undefined, "could not resolve the uri for the js-module-threads asset"); + const worker = new Worker(uri); + Internals.getUnusedWorkerPool().push(worker); +} From 5db8e8cdac3e48eb75b010808863099da4f86e92 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 10 Aug 2022 11:16:18 -0400 Subject: [PATCH 05/10] Update samples to explicitly enable threading / perftracing Makes the WasmAppBuilder include the threads worker module --- src/mono/sample/wasm/browser-eventpipe/Makefile | 2 ++ .../browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj | 4 ++++ src/mono/sample/wasm/browser-threads/Makefile | 2 ++ .../wasm/browser-threads/Wasm.Browser.Threads.Sample.csproj | 4 ++++ 4 files changed, 12 insertions(+) diff --git a/src/mono/sample/wasm/browser-eventpipe/Makefile b/src/mono/sample/wasm/browser-eventpipe/Makefile index 6f4130b5b64b55..b029e47ddbd15d 100644 --- a/src/mono/sample/wasm/browser-eventpipe/Makefile +++ b/src/mono/sample/wasm/browser-eventpipe/Makefile @@ -2,6 +2,8 @@ TOP=../../../../.. include ../wasm.mk +override MSBUILD_ARGS+=/p:WasmEnablePerfTracing=true + ifneq ($(AOT),) override MSBUILD_ARGS+=/p:RunAOTCompilation=true endif diff --git a/src/mono/sample/wasm/browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj b/src/mono/sample/wasm/browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj index eb58b28f8cfd2d..5f698b4d9a4b6c 100644 --- a/src/mono/sample/wasm/browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj +++ b/src/mono/sample/wasm/browser-eventpipe/Wasm.Browser.EventPipe.Sample.csproj @@ -13,6 +13,10 @@ CA2007 + + + + true diff --git a/src/mono/sample/wasm/browser-threads/Makefile b/src/mono/sample/wasm/browser-threads/Makefile index 6b60d3bcc93b64..ca2c7d1c22fed4 100644 --- a/src/mono/sample/wasm/browser-threads/Makefile +++ b/src/mono/sample/wasm/browser-threads/Makefile @@ -2,6 +2,8 @@ TOP=../../../../.. include ../wasm.mk +override MSBUILD_ARGS+=/p:WasmEnableThreads=true + ifneq ($(AOT),) override MSBUILD_ARGS+=/p:RunAOTCompilation=true endif diff --git a/src/mono/sample/wasm/browser-threads/Wasm.Browser.Threads.Sample.csproj b/src/mono/sample/wasm/browser-threads/Wasm.Browser.Threads.Sample.csproj index e53241bccba160..06dd22e1141f49 100644 --- a/src/mono/sample/wasm/browser-threads/Wasm.Browser.Threads.Sample.csproj +++ b/src/mono/sample/wasm/browser-threads/Wasm.Browser.Threads.Sample.csproj @@ -16,6 +16,10 @@ + + + + From af2b21a437e8d802b8d2491ba14b69930e9d3075 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 10 Aug 2022 11:30:51 -0400 Subject: [PATCH 06/10] tighten up Internals types --- .../wasm/runtime/pthreads/shared/emscripten-internals.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mono/wasm/runtime/pthreads/shared/emscripten-internals.ts b/src/mono/wasm/runtime/pthreads/shared/emscripten-internals.ts index 43761678cfac59..606907ec19b1fa 100644 --- a/src/mono/wasm/runtime/pthreads/shared/emscripten-internals.ts +++ b/src/mono/wasm/runtime/pthreads/shared/emscripten-internals.ts @@ -23,8 +23,9 @@ interface EmscriptenPThreadInfo { threadInfoStruct: pthread_ptr; } +/// N.B. emscripten deletes the `pthread` property from the worker when it is not actively running a pthread interface PThreadWorker extends Worker { - pthread?: EmscriptenPThreadInfo; + pthread: EmscriptenPThreadInfo; } interface PThreadObject { @@ -55,9 +56,6 @@ const Internals = { if (!isRunningPThreadWorker(worker)) return undefined; const emscriptenThreadInfo = worker.pthread; - if (emscriptenThreadInfo === undefined) { - return undefined; - } return emscriptenThreadInfo.threadInfoStruct; }, allocateUnusedWorker: (): void => { From 0a8357bb51c3c6d808a99368788b71c7097db317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Fri, 12 Aug 2022 13:47:14 -0400 Subject: [PATCH 07/10] apply review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marek FiĊĦera --- .../wasm/runtime/pthreads/shared/emscripten-replacements.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts b/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts index 86fa90f087f485..be70efa5ac738c 100644 --- a/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts +++ b/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts @@ -34,7 +34,8 @@ export function replaceEmscriptenPThreadLibrary(replacements: PThreadReplacement /// We replace Module["PThreads"].allocateUnusedWorker with this version that knows about assets function replacementAllocateUnusedWorker(): void { - console.debug("MONO_WASM: replacementAllocateUnusedWorker"); + if (runtimeHelpers.diagnosticTracing) + console.debug("MONO_WASM: replacementAllocateUnusedWorker"); const asset = resolve_asset_path("js-module-threads"); const uri = asset.resolvedUrl; mono_assert(uri !== undefined, "could not resolve the uri for the js-module-threads asset"); From e655a46d4cfb28fd87a730b9c8ff7a1b6a0f1b31 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 12 Aug 2022 14:45:52 -0400 Subject: [PATCH 08/10] fix import --- .../wasm/runtime/pthreads/shared/emscripten-replacements.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts b/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts index be70efa5ac738c..53b9f0b0be373a 100644 --- a/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts +++ b/src/mono/wasm/runtime/pthreads/shared/emscripten-replacements.ts @@ -8,7 +8,7 @@ import { afterThreadInitTLS } from "../worker"; import Internals from "./emscripten-internals"; import { resolve_asset_path } from "../../assets"; import { mono_assert } from "../../types"; - +import { runtimeHelpers } from "../../imports"; /** @module emscripten-replacements Replacements for individual functions in the emscripten PThreads library. * These have a hard dependency on the version of Emscripten that we are using and may need to be kept in sync with From 8c686a28c91e12fa15ee04f466cafa42e271599b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Sat, 13 Aug 2022 14:39:10 -0400 Subject: [PATCH 09/10] Apply suggestions from code review Co-authored-by: Ankit Jain --- src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 240e8c1af8b8b6..1de5abc301a869 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -317,7 +317,7 @@ private bool ExecuteInternal () config.Assets.Add(new WasmEntry ("dotnet.wasm") ); config.Assets.Add(new CryptoWorkerEntry ("dotnet-crypto-worker.js") ); if (IncludeThreadsWorker) - config.Assets.Add(new ThreadsWorkerEntry ("dotnet.worker.js") ); + config.Assets.Add(new ThreadsWorkerEntry ("dotnet.worker.js") ); if (RemoteSources?.Length > 0) { @@ -326,10 +326,14 @@ private bool ExecuteInternal () config.RemoteSources.Add(source.ItemSpec); } - if (PThreadPoolSize < -1) { + if (PThreadPoolSize < -1) + { throw new LogAsErrorException($"PThreadPoolSize must be -1, 0 or positive, but got {PThreadPoolSize}"); - } else + } + else + { config.Extra["pthreadPoolSize"] = PThreadPoolSize; + } foreach (ITaskItem extra in ExtraConfig ?? Enumerable.Empty()) { From aa2d0707ac7b6296a482dcec54641330c696a820 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Sat, 13 Aug 2022 15:54:30 -0400 Subject: [PATCH 10/10] proxy pthread worker messages to websocket, if enabled use a new MonoThreadMessageApplyMonoConfig message to send the MonoConfig from the main thread to each worker when the workers set up the communication channel to the main thread. then if the diagnosticTracing property is true, redirect the worker console logging to a websocket. Fixes https://github.com/dotnet/runtime/issues/72606 --- .../wasm/runtime/pthreads/browser/index.ts | 4 ++- .../wasm/runtime/pthreads/shared/index.ts | 24 +++++++++++++++ .../wasm/runtime/pthreads/worker/index.ts | 30 +++++++++++++++++-- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/mono/wasm/runtime/pthreads/browser/index.ts b/src/mono/wasm/runtime/pthreads/browser/index.ts index e33c5d0026b1ac..021bc6c70a666d 100644 --- a/src/mono/wasm/runtime/pthreads/browser/index.ts +++ b/src/mono/wasm/runtime/pthreads/browser/index.ts @@ -1,12 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { MonoWorkerMessageChannelCreated, isMonoWorkerMessageChannelCreated, monoSymbol } from "../shared"; +import { MonoWorkerMessageChannelCreated, isMonoWorkerMessageChannelCreated, monoSymbol, makeMonoThreadMessageApplyMonoConfig } from "../shared"; import { pthread_ptr } from "../shared/types"; import { MonoThreadMessage } from "../shared"; import { PromiseController, createPromiseController } from "../../promise-controller"; import { MonoConfig, mono_assert } from "../../types"; import Internals from "../shared/emscripten-internals"; +import { runtimeHelpers } from "../../imports"; const threads: Map = new Map(); @@ -94,6 +95,7 @@ function monoWorkerMessageHandler(worker: Worker, ev: MessageEvent monoDedicatedChannelMessageFromWorkerToMain(ev, thread)); port.start(); + port.postMessage(makeMonoThreadMessageApplyMonoConfig(runtimeHelpers.config)); resolvePromises(pthread_id, thread); } } diff --git a/src/mono/wasm/runtime/pthreads/shared/index.ts b/src/mono/wasm/runtime/pthreads/shared/index.ts index de488ca0de7f28..b03207afb231a1 100644 --- a/src/mono/wasm/runtime/pthreads/shared/index.ts +++ b/src/mono/wasm/runtime/pthreads/shared/index.ts @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import { Module } from "../../imports"; +import { MonoConfig } from "../../types"; import { pthread_ptr } from "./types"; export interface PThreadInfo { @@ -42,6 +43,29 @@ export function isMonoThreadMessage(x: unknown): x is MonoThreadMessage { return typeof (xmsg.type) === "string" && typeof (xmsg.cmd) === "string"; } +// message from the main thread to the pthread worker that passes the MonoConfig to the worker +export interface MonoThreadMessageApplyMonoConfig extends MonoThreadMessage { + type: "pthread"; + cmd: "apply_mono_config"; + config: string; +} + +export function isMonoThreadMessageApplyMonoConfig(x: unknown): x is MonoThreadMessageApplyMonoConfig { + if (!isMonoThreadMessage(x)) { + return false; + } + const xmsg = x as MonoThreadMessageApplyMonoConfig; + return xmsg.type === "pthread" && xmsg.cmd === "apply_mono_config" && typeof (xmsg.config) === "string"; +} + +export function makeMonoThreadMessageApplyMonoConfig(config: MonoConfig): MonoThreadMessageApplyMonoConfig { + return { + type: "pthread", + cmd: "apply_mono_config", + config: JSON.stringify(config) + }; +} + /// Messages sent using the worker object's postMessage() method /// /// a symbol that we use as a key on messages on the global worker-to-main channel to identify our own messages diff --git a/src/mono/wasm/runtime/pthreads/worker/index.ts b/src/mono/wasm/runtime/pthreads/worker/index.ts index cb77a4e30728bb..69900f8da7fb87 100644 --- a/src/mono/wasm/runtime/pthreads/worker/index.ts +++ b/src/mono/wasm/runtime/pthreads/worker/index.ts @@ -5,9 +5,9 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { Module, ENVIRONMENT_IS_PTHREAD } from "../../imports"; -import { makeChannelCreatedMonoMessage } from "../shared"; +import { isMonoThreadMessageApplyMonoConfig, makeChannelCreatedMonoMessage } from "../shared"; import type { pthread_ptr } from "../shared/types"; -import { mono_assert, is_nullish } from "../../types"; +import { mono_assert, is_nullish, MonoConfig } from "../../types"; import type { MonoThreadMessage } from "../shared"; import { PThreadSelf, @@ -16,6 +16,7 @@ import { dotnetPthreadAttached, WorkerThreadEventTarget } from "./events"; +import { setup_proxy_console } from "../../logging"; // re-export some of the events types export { @@ -54,7 +55,16 @@ export let pthread_self: PThreadSelf = null as any as PThreadSelf; export const currentWorkerThreadEvents: WorkerThreadEventTarget = MonoWasmThreads ? new EventTarget() : null as any as WorkerThreadEventTarget; // treeshake if threads are disabled + +// this is the message handler for the worker that receives messages from the main thread +// extend this with new cases as needed function monoDedicatedChannelMessageFromMainToWorker(event: MessageEvent): void { + if (isMonoThreadMessageApplyMonoConfig(event.data)) { + const config = JSON.parse(event.data.config) as MonoConfig; + console.debug("MONO_WASM: applying mono config from main", event.data.config); + onMonoConfigReceived(config); + return; + } console.debug("MONO_WASM: got message from main on the dedicated channel", event.data); } @@ -70,6 +80,22 @@ function setupChannelToMainThread(pthread_ptr: pthread_ptr): PThreadSelf { return pthread_self; } +// TODO: should we just assign to Module.config here? +let workerMonoConfig: MonoConfig = null as unknown as MonoConfig; + +// called when the main thread sends us the mono config +function onMonoConfigReceived(config: MonoConfig): void { + if (workerMonoConfig !== null) { + console.debug("MONO_WASM: mono config already received"); + return; + } + workerMonoConfig = config; + console.debug("MONO_WASM: mono config received", config); + if (workerMonoConfig.diagnosticTracing) { + setup_proxy_console("pthread-worker", console, self.location.href); + } +} + /// This is an implementation detail function. /// Called in the worker thread from mono when a pthread becomes attached to the mono runtime. export function mono_wasm_pthread_on_pthread_attached(pthread_id: pthread_ptr): void {