File size: 7,152 Bytes
00df61d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | /**
* @license
* Copyright 2015 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
// Pthread Web Worker handling code.
// This code runs only on pthread web workers and handles pthread setup
// and communication with the main thread via postMessage.
#if ASSERTIONS
// Unique ID of the current pthread worker (zero on non-pthread-workers
// including the main thread).
var workerID = 0;
#endif
#if MAIN_MODULE
// Map of modules to be shared with new threads. This gets populated by the
// main thread and shared with all new workers via the initial `load` message.
var sharedModules = {};
#endif
var startWorker;
if (ENVIRONMENT_IS_PTHREAD) {
// Thread-local guard variable for one-time init of the JS state
var initializedJS = false;
#if LOAD_SOURCE_MAP
// When using postMessage to send an object, it is processed by the structured
// clone algorithm. The prototype, and hence methods, on that object is then
// lost. This function adds back the lost prototype. This does not work with
// nested objects that has prototypes, but it suffices for WasmSourceMap and
// WasmOffsetConverter.
function resetPrototype(constructor, attrs) {
var object = Object.create(constructor.prototype);
return Object.assign(object, attrs);
}
#endif
// Turn unhandled rejected promises into errors so that the main thread will be
// notified about them.
self.onunhandledrejection = (e) => { throw e.reason || e; };
{{{ asyncIf(ASYNCIFY == 2) }}}function handleMessage(e) {
try {
var msgData = e['data'];
//dbg('msgData: ' + Object.keys(msgData));
var cmd = msgData.cmd;
if (cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code.
#if ASSERTIONS
workerID = msgData.workerID;
#endif
#if PTHREADS_DEBUG
dbg('worker: loading module')
#endif
// Until we initialize the runtime, queue up any further incoming messages.
let messageQueue = [];
self.onmessage = (e) => messageQueue.push(e);
// And add a callback for when the runtime is initialized.
startWorker = () => {
// Notify the main thread that this thread has loaded.
postMessage({ cmd: 'loaded' });
// Process any messages that were queued before the thread was ready.
for (let msg of messageQueue) {
handleMessage(msg);
}
// Restore the real message handler.
self.onmessage = handleMessage;
};
#if MAIN_MODULE
dynamicLibraries = msgData.dynamicLibraries;
sharedModules = msgData.sharedModules;
#if RUNTIME_DEBUG
dbg(`worker: received ${Object.keys(msgData.sharedModules).length} shared modules: ${Object.keys(msgData.sharedModules)}`);
#endif
#endif
// Use `const` here to ensure that the variable is scoped only to
// that iteration, allowing safe reference from a closure.
for (const handler of msgData.handlers) {
// If the main module has a handler for a certain event, but no
// handler exists on the pthread worker, then proxy that handler
// back to the main thread.
if (!Module[handler] || Module[handler].proxy) {
#if RUNTIME_DEBUG
dbg(`worker: installer proxying handler: ${handler}`);
#endif
Module[handler] = (...args) => {
#if RUNTIME_DEBUG
dbg(`worker: calling handler on main thread: ${handler}`);
#endif
postMessage({ cmd: 'callHandler', handler, args: args });
}
// Rebind the out / err handlers if needed
if (handler == 'print') out = Module[handler];
if (handler == 'printErr') err = Module[handler];
}
#if RUNTIME_DEBUG
else dbg(`worker: using thread-local handler: ${handler}`);
#endif
}
#if !WASM_ESM_INTEGRATION
wasmMemory = msgData.wasmMemory;
updateMemoryViews();
#endif
#if LOAD_SOURCE_MAP
wasmSourceMap = resetPrototype(WasmSourceMap, msgData.wasmSourceMap);
#endif
#if !WASM_ESM_INTEGRATION
#if MINIMAL_RUNTIME
// Pass the shared Wasm module in the Module object for MINIMAL_RUNTIME.
Module['wasm'] = msgData.wasmModule;
loadModule();
#else
wasmModule = msgData.wasmModule;
#if MODULARIZE == 'instance'
init();
#else
createWasm();
run();
#endif
#endif // MINIMAL_RUNTIME
#endif
} else if (cmd === 'run') {
#if ASSERTIONS
assert(msgData.pthread_ptr);
#endif
// Call inside JS module to set up the stack frame for this pthread in JS module scope.
// This needs to be the first thing that we do, as we cannot call to any C/C++ functions
// until the thread stack is initialized.
establishStackSpace(msgData.pthread_ptr);
// Pass the thread address to wasm to store it for fast access.
__emscripten_thread_init(msgData.pthread_ptr, /*is_main=*/0, /*is_runtime=*/0, /*can_block=*/1, 0, 0);
#if OFFSCREENCANVAS_SUPPORT
PThread.receiveOffscreenCanvases(msgData);
#endif
PThread.threadInitTLS();
// Await mailbox notifications with `Atomics.waitAsync` so we can start
// using the fast `Atomics.notify` notification path.
__emscripten_thread_mailbox_await(msgData.pthread_ptr);
if (!initializedJS) {
#if EMBIND
#if PTHREADS_DEBUG
dbg(`worker: Pthread 0x${_pthread_self().toString(16)} initializing embind.`);
#endif
// Embind must initialize itself on all threads, as it generates support JS.
// We only do this once per worker since they get reused
__embind_initialize_bindings();
#endif // EMBIND
initializedJS = true;
}
try {
{{{ awaitIf(ASYNCIFY == 2) }}}invokeEntryPoint(msgData.start_routine, msgData.arg);
} catch(ex) {
if (ex != 'unwind') {
// The pthread "crashed". Do not call `_emscripten_thread_exit` (which
// would make this thread joinable). Instead, re-throw the exception
// and let the top level handler propagate it back to the main thread.
throw ex;
}
#if RUNTIME_DEBUG
dbg(`worker: Pthread 0x${_pthread_self().toString(16)} completed its main entry point with an 'unwind', keeping the worker alive for asynchronous operation.`);
#endif
}
} else if (msgData.target === 'setimmediate') {
// no-op
} else if (cmd === 'checkMailbox') {
if (initializedJS) {
checkMailbox();
}
} else if (cmd) {
// The received message looks like something that should be handled by this message
// handler, (since there is a cmd field present), but is not one of the
// recognized commands:
err(`worker: received unknown command ${cmd}`);
err(msgData);
}
} catch(ex) {
#if ASSERTIONS
err(`worker: onmessage() captured an uncaught exception: ${ex}`);
if (ex?.stack) err(ex.stack);
#endif
__emscripten_thread_crashed();
throw ex;
}
};
self.onmessage = handleMessage;
} // ENVIRONMENT_IS_PTHREAD
|