/**1* @license2* Copyright 2015 The Emscripten Authors3* SPDX-License-Identifier: MIT4*/56// Pthread Web Worker handling code.7// This code runs only on pthread web workers and handles pthread setup8// and communication with the main thread via postMessage.910#if ASSERTIONS11// Unique ID of the current pthread worker (zero on non-pthread-workers12// including the main thread).13var workerID = 0;14#endif1516#if MAIN_MODULE17// Map of modules to be shared with new threads. This gets populated by the18// main thread and shared with all new workers via the initial `load` message.19var sharedModules = {};20#endif2122var startWorker;2324if (ENVIRONMENT_IS_PTHREAD) {25// Thread-local guard variable for one-time init of the JS state26var initializedJS = false;2728#if LOAD_SOURCE_MAP29// When using postMessage to send an object, it is processed by the structured30// clone algorithm. The prototype, and hence methods, on that object is then31// lost. This function adds back the lost prototype. This does not work with32// nested objects that has prototypes, but it suffices for WasmSourceMap and33// WasmOffsetConverter.34function resetPrototype(constructor, attrs) {35var object = Object.create(constructor.prototype);36return Object.assign(object, attrs);37}38#endif3940// Turn unhandled rejected promises into errors so that the main thread will be41// notified about them.42self.onunhandledrejection = (e) => { throw e.reason || e; };4344{{{ asyncIf(ASYNCIFY == 2) }}}function handleMessage(e) {45try {46var msgData = e['data'];47//dbg('msgData: ' + Object.keys(msgData));48var cmd = msgData.cmd;49if (cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code.50#if ASSERTIONS51workerID = msgData.workerID;52#endif53#if PTHREADS_DEBUG54dbg('worker: loading module')55#endif5657// Until we initialize the runtime, queue up any further incoming messages.58let messageQueue = [];59self.onmessage = (e) => messageQueue.push(e);6061// And add a callback for when the runtime is initialized.62startWorker = () => {63// Notify the main thread that this thread has loaded.64postMessage({ cmd: 'loaded' });65// Process any messages that were queued before the thread was ready.66for (let msg of messageQueue) {67handleMessage(msg);68}69// Restore the real message handler.70self.onmessage = handleMessage;71};7273#if MAIN_MODULE74dynamicLibraries = msgData.dynamicLibraries;75sharedModules = msgData.sharedModules;76#if RUNTIME_DEBUG77dbg(`worker: received ${Object.keys(msgData.sharedModules).length} shared modules: ${Object.keys(msgData.sharedModules)}`);78#endif79#endif8081// Use `const` here to ensure that the variable is scoped only to82// that iteration, allowing safe reference from a closure.83for (const handler of msgData.handlers) {84// The the main module has a handler for a certain even, but no85// handler exists on the pthread worker, then proxy that handler86// back to the main thread.87if (!Module[handler] || Module[handler].proxy) {88#if RUNTIME_DEBUG89dbg(`worker: installer proxying handler: ${handler}`);90#endif91Module[handler] = (...args) => {92#if RUNTIME_DEBUG93dbg(`worker: calling handler on main thread: ${handler}`);94#endif95postMessage({ cmd: 'callHandler', handler, args: args });96}97// Rebind the out / err handlers if needed98if (handler == 'print') out = Module[handler];99if (handler == 'printErr') err = Module[handler];100}101#if RUNTIME_DEBUG102else dbg(`worker: using thread-local handler: ${handler}`);103#endif104}105106#if !WASM_ESM_INTEGRATION107wasmMemory = msgData.wasmMemory;108updateMemoryViews();109#endif110111#if LOAD_SOURCE_MAP112wasmSourceMap = resetPrototype(WasmSourceMap, msgData.wasmSourceMap);113#endif114115#if !WASM_ESM_INTEGRATION116#if MINIMAL_RUNTIME117// Pass the shared Wasm module in the Module object for MINIMAL_RUNTIME.118Module['wasm'] = msgData.wasmModule;119loadModule();120#else121wasmModule = msgData.wasmModule;122#if MODULARIZE == 'instance'123init();124#else125createWasm();126run();127#endif128#endif // MINIMAL_RUNTIME129#endif130} else if (cmd === 'run') {131#if ASSERTIONS132assert(msgData.pthread_ptr);133#endif134// Call inside JS module to set up the stack frame for this pthread in JS module scope.135// This needs to be the first thing that we do, as we cannot call to any C/C++ functions136// until the thread stack is initialized.137establishStackSpace(msgData.pthread_ptr);138139// Pass the thread address to wasm to store it for fast access.140__emscripten_thread_init(msgData.pthread_ptr, /*is_main=*/0, /*is_runtime=*/0, /*can_block=*/1, 0, 0);141142#if OFFSCREENCANVAS_SUPPORT143PThread.receiveOffscreenCanvases(msgData);144#endif145PThread.threadInitTLS();146147// Await mailbox notifications with `Atomics.waitAsync` so we can start148// using the fast `Atomics.notify` notification path.149__emscripten_thread_mailbox_await(msgData.pthread_ptr);150151if (!initializedJS) {152#if EMBIND153#if PTHREADS_DEBUG154dbg(`worker: Pthread 0x${_pthread_self().toString(16)} initializing embind.`);155#endif156// Embind must initialize itself on all threads, as it generates support JS.157// We only do this once per worker since they get reused158__embind_initialize_bindings();159#endif // EMBIND160initializedJS = true;161}162163try {164{{{ awaitIf(ASYNCIFY == 2) }}}invokeEntryPoint(msgData.start_routine, msgData.arg);165} catch(ex) {166if (ex != 'unwind') {167// The pthread "crashed". Do not call `_emscripten_thread_exit` (which168// would make this thread joinable). Instead, re-throw the exception169// and let the top level handler propagate it back to the main thread.170throw ex;171}172#if RUNTIME_DEBUG173dbg(`worker: Pthread 0x${_pthread_self().toString(16)} completed its main entry point with an 'unwind', keeping the worker alive for asynchronous operation.`);174#endif175}176} else if (msgData.target === 'setimmediate') {177// no-op178} else if (cmd === 'checkMailbox') {179if (initializedJS) {180checkMailbox();181}182} else if (cmd) {183// The received message looks like something that should be handled by this message184// handler, (since there is a cmd field present), but is not one of the185// recognized commands:186err(`worker: received unknown command ${cmd}`);187err(msgData);188}189} catch(ex) {190#if ASSERTIONS191err(`worker: onmessage() captured an uncaught exception: ${ex}`);192if (ex?.stack) err(ex.stack);193#endif194__emscripten_thread_crashed();195throw ex;196}197};198199self.onmessage = handleMessage;200201} // ENVIRONMENT_IS_PTHREAD202203204