Spaces:
Running
Running
/** | |
* @license | |
* Copyright 2015 The Emscripten Authors | |
* SPDX-License-Identifier: MIT | |
*/ | |
// Pthread Web Worker startup routine: | |
// This is the entry point file that is loaded first by each Web Worker | |
// that executes pthreads on the Emscripten application. | |
; | |
var Module = {}; | |
// Thread-local guard variable for one-time init of the JS state | |
var initializedJS = false; | |
function assert(condition, text) { | |
if (!condition) abort('Assertion failed: ' + text); | |
} | |
function threadPrintErr() { | |
var text = Array.prototype.slice.call(arguments).join(' '); | |
console.error(text); | |
} | |
function threadAlert() { | |
var text = Array.prototype.slice.call(arguments).join(' '); | |
postMessage({cmd: 'alert', text, threadId: Module['_pthread_self']()}); | |
} | |
// We don't need out() for now, but may need to add it if we want to use it | |
// here. Or, if this code all moves into the main JS, that problem will go | |
// away. (For now, adding it here increases code size for no benefit.) | |
var out = () => { throw 'out() is not defined in worker.js.'; } | |
var err = threadPrintErr; | |
self.alert = threadAlert; | |
var dbg = threadPrintErr; | |
Module['instantiateWasm'] = (info, receiveInstance) => { | |
// Instantiate from the module posted from the main thread. | |
// We can just use sync instantiation in the worker. | |
var module = Module['wasmModule']; | |
// We don't need the module anymore; new threads will be spawned from the main thread. | |
Module['wasmModule'] = null; | |
var instance = new WebAssembly.Instance(module, info); | |
// TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, | |
// the above line no longer optimizes out down to the following line. | |
// When the regression is fixed, we can remove this if/else. | |
return receiveInstance(instance); | |
} | |
// Turn unhandled rejected promises into errors so that the main thread will be | |
// notified about them. | |
self.onunhandledrejection = (e) => { | |
throw e.reason || e; | |
}; | |
function handleMessage(e) { | |
try { | |
if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code. | |
// 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. | |
self.startWorker = (instance) => { | |
Module = instance; | |
// 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; | |
}; | |
// Module and memory were sent from main thread | |
Module['wasmModule'] = e.data.wasmModule; | |
// Use `const` here to ensure that the variable is scoped only to | |
// that iteration, allowing safe reference from a closure. | |
for (const handler of e.data.handlers) { | |
Module[handler] = (...args) => { | |
postMessage({ cmd: 'callHandler', handler, args: args }); | |
} | |
} | |
Module['wasmMemory'] = e.data.wasmMemory; | |
Module['buffer'] = Module['wasmMemory'].buffer; | |
Module['workerID'] = e.data.workerID; | |
Module['ENVIRONMENT_IS_PTHREAD'] = true; | |
if (typeof e.data.urlOrBlob == 'string') { | |
importScripts(e.data.urlOrBlob); | |
} else { | |
var objectUrl = URL.createObjectURL(e.data.urlOrBlob); | |
importScripts(objectUrl); | |
URL.revokeObjectURL(objectUrl); | |
} | |
Godot(Module); | |
} else if (e.data.cmd === 'run') { | |
// Pass the thread address to wasm to store it for fast access. | |
Module['__emscripten_thread_init'](e.data.pthread_ptr, /*is_main=*/0, /*is_runtime=*/0, /*can_block=*/1); | |
// Await mailbox notifications with `Atomics.waitAsync` so we can start | |
// using the fast `Atomics.notify` notification path. | |
Module['__emscripten_thread_mailbox_await'](e.data.pthread_ptr); | |
assert(e.data.pthread_ptr); | |
// Also call inside JS module to set up the stack frame for this pthread in JS module scope | |
Module['establishStackSpace'](); | |
Module['PThread'].receiveObjectTransfer(e.data); | |
Module['PThread'].threadInitTLS(); | |
if (!initializedJS) { | |
initializedJS = true; | |
} | |
try { | |
Module['invokeEntryPoint'](e.data.start_routine, e.data.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; | |
} | |
} | |
} else if (e.data.cmd === 'cancel') { // Main thread is asking for a pthread_cancel() on this thread. | |
if (Module['_pthread_self']()) { | |
Module['__emscripten_thread_exit'](-1); | |
} | |
} else if (e.data.target === 'setimmediate') { | |
// no-op | |
} else if (e.data.cmd === 'checkMailbox') { | |
if (initializedJS) { | |
Module['checkMailbox'](); | |
} | |
} else if (e.data.cmd) { | |
// The received message looks like something that should be handled by this message | |
// handler, (since there is a e.data.cmd field present), but is not one of the | |
// recognized commands: | |
err(`worker.js received unknown command ${e.data.cmd}`); | |
err(e.data); | |
} | |
} catch(ex) { | |
err(`worker.js onmessage() captured an uncaught exception: ${ex}`); | |
if (ex?.stack) err(ex.stack); | |
Module['__emscripten_thread_crashed']?.(); | |
throw ex; | |
} | |
}; | |
self.onmessage = handleMessage; | |