Skip to content

Commit 76860cc

Browse files
authored
[WASM_WORKERS] Inline .ww.js file into the main output .js file (#24163)
Similar to what we did for pthreads in #21701. This is win for both code size and complexity. Importantly it avoid the issue how to package and deploy the extra `.ww.js` file. This also completely removes the need for the `-sWASM_WORKERS=2` mode.
1 parent f6f883e commit 76860cc

29 files changed

+247
-233
lines changed

ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ See docs/process.md for more on how version tagging works.
2020

2121
4.0.8 (in development)
2222
----------------------
23+
- Programs built with `-sWASM_WORKERS` no longer generate a separate `.ww.js`
24+
file. This is similar to the change that was already made for pthreads in
25+
#21701. This saves on complexity, code size and network requests (#24163)
2326

2427
4.0.7 - 04/15/25
2528
----------------

site/source/docs/tools_reference/settings_reference.rst

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2507,13 +2507,9 @@ Default value: false
25072507
WASM_WORKERS
25082508
============
25092509

2510-
If 1, enables support for Wasm Workers. Wasm Workers enable applications
2510+
Enables support for Wasm Workers. Wasm Workers enable applications
25112511
to create threads using a lightweight web-specific API that builds on top
2512-
of Wasm SharedArrayBuffer + Atomics API. When enabled, a new build output
2513-
file a.ww.js will be generated to bootstrap the Wasm Worker JS contexts.
2514-
If 2, enables support for Wasm Workers, but without using a separate a.ww.js
2515-
file on the side. This can simplify deployment of builds, but will have a
2516-
downside that the generated build will no longer be csp-eval compliant.
2512+
of Wasm SharedArrayBuffer + Atomics API.
25172513
[compile+link] - affects user code at compile and system libraries at link.
25182514

25192515
Default value: 0

src/audio_worklet.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ function createWasmAudioWorkletProcessor(audioParams) {
140140
class BootstrapMessages extends AudioWorkletProcessor {
141141
constructor(arg) {
142142
super();
143+
// Audio worklets need to show up as wasm workers. This is the way we signal
144+
// that.
145+
globalThis.name = 'em-ww';
143146
// Initialize the global Emscripten Module object that contains e.g. the
144147
// Wasm Module and Memory objects. After this we are ready to load in the
145148
// main application JS script, which the main thread will addModule()

src/lib/libwasm_worker.js

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,6 @@
44
* SPDX-License-Identifier: MIT
55
*/
66

7-
#if WASM_WORKERS == 2
8-
// Helpers for _wasmWorkerBlobUrl used in WASM_WORKERS == 2 mode
9-
{{{
10-
const captureModuleArg = () => MODULARIZE ? '' : 'self.Module=d;';
11-
const instantiateModule = () => MODULARIZE ? `${EXPORT_NAME}(d);` : '';
12-
const instantiateWasm = () => MINIMAL_RUNTIME ? '' : 'd[`instantiateWasm`]=(i,r)=>{var n=new WebAssembly.Instance(d[`wasm`],i);return r(n,d[`wasm`]);};';
13-
}}}
14-
#endif
15-
167
#if WASM_WORKERS
178

189
#if !SHARED_MEMORY
@@ -37,18 +28,28 @@
3728
{{{
3829
const workerSupportsFutexWait = () => AUDIO_WORKLET ? "typeof AudioWorkletGlobalScope === 'undefined'" : '1';
3930
const wasmWorkerJs = `
40-
#if WASM_WORKERS == 2
41-
_wasmWorkerBlobUrl
42-
#elif MINIMAL_RUNTIME
31+
#if MINIMAL_RUNTIME
4332
#if ENVIRONMENT_MAY_BE_NODE
44-
Module['$wb'] || './${WASM_WORKER_FILE}'
33+
Module['js'] || './${TARGET_JS_NAME}'
4534
#else
46-
Module['$wb']
35+
Module['js']
4736
#endif
4837
#else
49-
locateFile('${WASM_WORKER_FILE}')
38+
locateFile('${TARGET_JS_NAME}')
5039
#endif
5140
`;
41+
const wasmWorkerOptions = `{
42+
#if ENVIRONMENT_MAY_BE_NODE
43+
// This is the way that we signal to the node worker that it is hosting
44+
// a wasm worker.
45+
'workerData': 'em-ww',
46+
#endif
47+
#if ENVIRONMENT_MAY_BE_WEB || ENVIRONMENT_MAY_BE_WORKER
48+
// This is the way that we signal to the Web Worker that it is hosting
49+
// a pthread.
50+
'name': 'em-ww',
51+
#endif
52+
}`;
5253
}}}
5354

5455
#endif // ~WASM_WORKERS
@@ -92,9 +93,13 @@ addToLibrary({
9293
$_wasmWorkerInitializeRuntime: () => {
9394
let m = Module;
9495
#if ASSERTIONS
96+
assert(m && m['$ww']);
9597
assert(m['sb'] % 16 == 0);
9698
assert(m['sz'] % 16 == 0);
9799
#endif
100+
#if RUNTIME_DEBUG
101+
dbg("wasmWorkerInitializeRuntime $ww:", m['$ww']);
102+
#endif
98103

99104
#if !MINIMAL_RUNTIME && isSymbolNeeded('$noExitRuntime')
100105
// Wasm workers basically never exit their runtime
@@ -149,20 +154,9 @@ addToLibrary({
149154
#endif
150155
},
151156

152-
#if WASM_WORKERS == 2
153-
// In WASM_WORKERS == 2 build mode, we create the Wasm Worker global scope
154-
// script from a string bundled in the main application JS file. This
155-
// simplifies the number of deployed JS files with the app, but has a downside
156-
// that the generated build output will no longer be csp-eval compliant.
157-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#unsafe_eval_expressions
158-
$_wasmWorkerBlobUrl: "URL.createObjectURL(new Blob(['onmessage=function(d){onmessage=null;d=d.data;{{{ captureModuleArg() }}}{{{ instantiateWasm() }}}importScripts(d.js);{{{ instantiateModule() }}}d.wasm=d.mem=d.js=0;}'],{type:'application/javascript'}))",
159-
#endif
160157
_emscripten_create_wasm_worker__deps: [
161158
'$_wasmWorkers', '$_wasmWorkersID',
162159
'$_wasmWorkerAppendToQueue', '$_wasmWorkerRunPostMessage',
163-
#if WASM_WORKERS == 2
164-
'$_wasmWorkerBlobUrl',
165-
#endif
166160
#if ASSERTIONS
167161
'emscripten_has_threading_support',
168162
#endif
@@ -174,7 +168,7 @@ if (ENVIRONMENT_IS_WASM_WORKER
174168
&& !ENVIRONMENT_IS_AUDIO_WORKLET
175169
#endif
176170
) {
177-
_wasmWorkers[0] = this;
171+
_wasmWorkers[0] = globalThis;
178172
addEventListener("message", _wasmWorkerAppendToQueue);
179173
}`,
180174
_emscripten_create_wasm_worker: (stackLowestAddress, stackSize) => {
@@ -191,28 +185,21 @@ if (ENVIRONMENT_IS_WASM_WORKER
191185
var p = trustedTypes.createPolicy(
192186
'emscripten#workerPolicy1', { createScriptURL: (ignored) => {{{ wasmWorkerJs }}}}
193187
);
194-
worker = _wasmWorkers[_wasmWorkersID] = new Worker(p.createScriptURL('ignored'));
188+
worker = _wasmWorkers[_wasmWorkersID] = new Worker(p.createScriptURL('ignored'), {{{ wasmWorkerOptions }}});
195189
} else
196190
#endif
197-
worker = _wasmWorkers[_wasmWorkersID] = new Worker({{{ wasmWorkerJs }}});
191+
worker = _wasmWorkers[_wasmWorkersID] = new Worker({{{ wasmWorkerJs }}}, {{{ wasmWorkerOptions }}});
198192
// Craft the Module object for the Wasm Worker scope:
199193
worker.postMessage({
200194
// Signal with a non-zero value that this Worker will be a Wasm Worker,
201195
// and not the main browser thread.
202196
'$ww': _wasmWorkersID,
203197
#if MINIMAL_RUNTIME
204198
'wasm': Module['wasm'],
205-
#if ENVIRONMENT_MAY_BE_NODE
206-
'js': Module['js'] || './{{{ TARGET_JS_NAME }}}',
207-
#else
208-
'js': Module['js'],
209-
#endif
210-
'mem': wasmMemory,
211199
#else
212200
'wasm': wasmModule,
213-
'js': Module['mainScriptUrlOrBlob'] || _scriptName,
214-
'wasmMemory': wasmMemory,
215201
#endif
202+
'mem': wasmMemory,
216203
'sb': stackLowestAddress, // sb = stack bottom (lowest stack address, SP points at this when stack is full)
217204
'sz': stackSize, // sz = stack size
218205
});

src/parseTools.mjs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,10 +1081,6 @@ function getPerformanceNow() {
10811081
}
10821082
}
10831083

1084-
function implicitSelf() {
1085-
return ENVIRONMENT.includes('node') ? 'self.' : '';
1086-
}
1087-
10881084
function ENVIRONMENT_IS_MAIN_THREAD() {
10891085
return `(!${ENVIRONMENT_IS_WORKER_THREAD()})`;
10901086
}
@@ -1143,7 +1139,6 @@ addToCompileTimeContext({
11431139
getPerformanceNow,
11441140
getUnsharedTextDecoderView,
11451141
hasExportedSymbol,
1146-
implicitSelf,
11471142
isSymbolNeeded,
11481143
makeDynCall,
11491144
makeEval,

src/postamble_minimal.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ var wasmExports;
9595
var wasmModule;
9696
#endif
9797

98-
#if PTHREADS
98+
#if PTHREADS || WASM_WORKERS
9999
function loadModule() {
100100
assignWasmImports();
101101
#endif
@@ -255,12 +255,12 @@ WebAssembly.instantiate(Module['wasm'], imports).then((output) => {
255255
#endif // ASSERTIONS || WASM == 2
256256
);
257257

258-
#if PTHREADS
258+
#if PTHREADS || WASM_WORKERS
259259
}
260260

261-
if (!ENVIRONMENT_IS_PTHREAD) {
262-
// When running in a pthread we delay module loading untill we have
263-
// received the module via postMessage
264-
loadModule();
265-
}
261+
// When running in a background thread we delay module loading until we have
262+
#if AUDIO_WORKLET
263+
if (ENVIRONMENT_IS_AUDIO_WORKLET) loadModule();
264+
#endif
265+
{{{ runIfMainThread('loadModule();') }}}
266266
#endif

src/preamble.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,7 @@ function exitRuntime() {
248248
#if STACK_OVERFLOW_CHECK
249249
checkStackCookie();
250250
#endif
251-
#if PTHREADS
252-
if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
253-
#endif
251+
{{{ runIfWorkerThread('return;') }}} // PThreads reuse the runtime from the main thread.
254252
#if !STANDALONE_WASM
255253
___funcs_on_exit(); // Native atexit() functions
256254
#endif
@@ -266,9 +264,7 @@ function postRun() {
266264
#if STACK_OVERFLOW_CHECK
267265
checkStackCookie();
268266
#endif
269-
#if PTHREADS
270-
if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
271-
#endif
267+
{{{ runIfWorkerThread('return;') }}} // PThreads reuse the runtime from the main thread.
272268

273269
#if expectToReceiveOnModule('postRun')
274270
if (Module['postRun']) {
@@ -823,7 +819,7 @@ async function instantiateAsync(binary, binaryFile, imports) {
823819

824820
#if !WASM_ESM_INTEGRATION
825821
function getWasmImports() {
826-
#if PTHREADS
822+
#if PTHREADS || WASM_WORKERS
827823
assignWasmImports();
828824
#endif
829825
#if ASYNCIFY && (ASSERTIONS || ASYNCIFY == 2)
@@ -1008,8 +1004,8 @@ function getWasmImports() {
10081004
}
10091005
#endif
10101006

1011-
#if PTHREADS
1012-
if (ENVIRONMENT_IS_PTHREAD) {
1007+
#if PTHREADS || WASM_WORKERS
1008+
if ({{{ ENVIRONMENT_IS_WORKER_THREAD() }}}) {
10131009
return new Promise((resolve) => {
10141010
wasmModuleReceived = (module) => {
10151011
// Instantiate from the module posted from the main thread.

src/runtime_debug.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ var runtimeDebug = true; // Switch to false at runtime to disable logging at the
193193
// Used by XXXXX_DEBUG settings to output debug messages.
194194
function dbg(...args) {
195195
if (!runtimeDebug && typeof runtimeDebug != 'undefined') return;
196-
#if ENVIRONMENT_MAY_BE_NODE && PTHREADS
196+
#if ENVIRONMENT_MAY_BE_NODE && (PTHREADS || WASM_WORKERS)
197197
// Avoid using the console for debugging in multi-threaded node applications
198198
// See https://github.com/emscripten-core/emscripten/issues/14804
199199
if (ENVIRONMENT_IS_NODE) {

src/runtime_init_memory.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
1313

1414
function initMemory() {
15-
#if PTHREADS
16-
if (ENVIRONMENT_IS_PTHREAD) return;
17-
#endif // PTHREADS
15+
#if AUDIO_WORKLET
16+
if (!ENVIRONMENT_IS_AUDIO_WORKLET)
17+
#endif
18+
{{{ runIfWorkerThread('return') }}}
1819

1920
#if expectToReceiveOnModule('wasmMemory')
2021
if (Module['wasmMemory']) {

src/runtime_pthread.js

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,6 @@ var sharedModules = {};
2121
#endif
2222

2323
if (ENVIRONMENT_IS_PTHREAD) {
24-
#if !MINIMAL_RUNTIME
25-
var wasmModuleReceived;
26-
#endif
27-
28-
#if ENVIRONMENT_MAY_BE_NODE
29-
// Node.js support
30-
if (ENVIRONMENT_IS_NODE) {
31-
// Create as web-worker-like an environment as we can.
32-
33-
var parentPort = worker_threads['parentPort'];
34-
parentPort.on('message', (msg) => onmessage({ data: msg }));
35-
36-
Object.assign(globalThis, {
37-
self: global,
38-
postMessage: (msg) => parentPort.postMessage(msg),
39-
});
40-
}
41-
#endif // ENVIRONMENT_MAY_BE_NODE
42-
4324
// Thread-local guard variable for one-time init of the JS state
4425
var initializedJS = false;
4526

src/runtime_shared.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,32 @@
2121
#include "runtime_asan.js"
2222
#endif
2323

24+
#if PTHREADS || WASM_WORKERS
25+
#if !MINIMAL_RUNTIME
26+
var wasmModuleReceived;
27+
#endif
28+
29+
#if ENVIRONMENT_MAY_BE_NODE
30+
if (ENVIRONMENT_IS_NODE && {{{ ENVIRONMENT_IS_WORKER_THREAD() }}}) {
31+
// Create as web-worker-like an environment as we can.
32+
var parentPort = worker_threads['parentPort'];
33+
parentPort.on('message', (msg) => global.onmessage?.({ data: msg }));
34+
Object.assign(globalThis, {
35+
self: global,
36+
postMessage: (msg) => parentPort['postMessage'](msg),
37+
});
38+
}
39+
#endif // ENVIRONMENT_MAY_BE_NODE
40+
#endif
41+
2442
#if PTHREADS
2543
#include "runtime_pthread.js"
2644
#endif
2745

46+
#if WASM_WORKERS
47+
#include "wasm_worker.js"
48+
#endif
49+
2850
#if LOAD_SOURCE_MAP
2951
var wasmSourceMap;
3052
#include "source_map_support.js"

src/settings.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,13 +1637,9 @@ var USE_SQLITE3 = false;
16371637
// [compile+link] - affects user code at compile and system libraries at link.
16381638
var SHARED_MEMORY = false;
16391639

1640-
// If 1, enables support for Wasm Workers. Wasm Workers enable applications
1640+
// Enables support for Wasm Workers. Wasm Workers enable applications
16411641
// to create threads using a lightweight web-specific API that builds on top
1642-
// of Wasm SharedArrayBuffer + Atomics API. When enabled, a new build output
1643-
// file a.ww.js will be generated to bootstrap the Wasm Worker JS contexts.
1644-
// If 2, enables support for Wasm Workers, but without using a separate a.ww.js
1645-
// file on the side. This can simplify deployment of builds, but will have a
1646-
// downside that the generated build will no longer be csp-eval compliant.
1642+
// of Wasm SharedArrayBuffer + Atomics API.
16471643
// [compile+link] - affects user code at compile and system libraries at link.
16481644
var WASM_WORKERS = 0;
16491645

src/settings_internal.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,6 @@ var USER_EXPORTS = [];
134134
// name of the file containing wasm binary, if relevant
135135
var WASM_BINARY_FILE = '';
136136

137-
// name of the file containing the Wasm Worker *.ww.js, if relevant
138-
var WASM_WORKER_FILE = '';
139-
140137
// Base URL the source mapfile, if relevant
141138
var SOURCE_MAP_BASE = '';
142139

src/shell.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ if (ENVIRONMENT_IS_PTHREAD) {
102102
#endif
103103
#endif
104104

105+
#if WASM_WORKERS
106+
// The way we signal to a worker that it is hosting a pthread is to construct
107+
// it with a specific name.
108+
var ENVIRONMENT_IS_WASM_WORKER = globalThis.name == 'em-ww';
109+
#endif
110+
105111
#if ENVIRONMENT_MAY_BE_NODE
106112
if (ENVIRONMENT_IS_NODE) {
107113
#if EXPORT_ES6
@@ -121,14 +127,13 @@ if (ENVIRONMENT_IS_NODE) {
121127
// is hosting a pthread.
122128
ENVIRONMENT_IS_PTHREAD = ENVIRONMENT_IS_WORKER && worker_threads['workerData'] == 'em-pthread'
123129
#endif // PTHREADS
130+
#if WASM_WORKERS
131+
ENVIRONMENT_IS_WASM_WORKER = ENVIRONMENT_IS_WORKER && worker_threads['workerData'] == 'em-ww'
132+
#endif
124133
#endif // PTHREADS || WASM_WORKERS
125134
}
126135
#endif // ENVIRONMENT_MAY_BE_NODE
127136

128-
#if WASM_WORKERS
129-
var ENVIRONMENT_IS_WASM_WORKER = !!Module['$ww'];
130-
#endif
131-
132137
// --pre-jses are emitted after the Module integration code, so that they can
133138
// refer to Module (if they choose; they can also define Module)
134139
{{{ preJS() }}}

0 commit comments

Comments
 (0)