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