Skip to content

Commit 21861d0

Browse files
committed
Inline pthread worker.js file into the main output file
This method has many advantages over the previous method of generating a separate file: - Avoids separate output file simplifying deployment. - Avoids confusing laying of global scopes - Avoids exporting symbols on the Module simply for visibility within the worker file. - Avoids code duplication - Avoids the needs to importScripts call, and the node polyfill for this. - Allows optimizers such as closure and JSDCE to operate on the combined code. - `-sSINGLE_FILE` now works with pthreads - Fewer network requests - No need for locateFile logic to run on the worker to find the worker.js Fixes: #9796
1 parent 597ffc1 commit 21861d0

29 files changed

+470
-490
lines changed

src/library_async.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ addToLibrary({
3838
rewindArguments: {},
3939
#endif
4040
instrumentWasmImports(imports) {
41+
if (imports.instrumented) return;
42+
imports.instrumented = true;
4143
#if ASYNCIFY_DEBUG
4244
dbg('asyncify instrumenting imports');
4345
#endif

src/library_pthread.js

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ var LibraryPThread = {
7575
) {
7676
t = _pthread_self();
7777
}
78-
return 'w:' + (Module['workerID'] || 0) + ',t:' + ptrToString(t) + ': ';
78+
return 'w:' + (workerID || 0) + ',t:' + ptrToString(t) + ': ';
7979
}
8080

8181
// Prefix all err()/dbg() messages with the calling thread ID.
@@ -127,16 +127,6 @@ var LibraryPThread = {
127127
},
128128

129129
initWorker() {
130-
#if MAYBE_CLOSURE_COMPILER
131-
// worker.js is not compiled together with us, and must access certain
132-
// things.
133-
PThread['receiveObjectTransfer'] = PThread.receiveObjectTransfer;
134-
PThread['threadInitTLS'] = PThread.threadInitTLS;
135-
#if !MINIMAL_RUNTIME
136-
PThread['setExitStatus'] = PThread.setExitStatus;
137-
#endif
138-
#endif
139-
140130
#if isSymbolNeeded('$noExitRuntime')
141131
// The default behaviour for pthreads is always to exit once they return
142132
// from their entry point (or call pthread_exit). If we set noExitRuntime
@@ -440,34 +430,49 @@ var LibraryPThread = {
440430
// Creates a new web Worker and places it in the unused worker pool to wait for its use.
441431
allocateUnusedWorker() {
442432
var worker;
433+
var workerOptions = {
434+
{{{ EXPORT_ES6 ? "type: 'module'," : "" }}}
435+
#if ENVIRONMENT_MAY_BE_NODE
436+
// This is the way that we signal to the node worker that it is hosting
437+
// a pthread. On the web we append `?pthread=1` to the URL instead.
438+
'workerData': 'pthread',
439+
#endif
440+
};
443441
#if MINIMAL_RUNTIME
444-
var pthreadMainJs = Module['worker'] || './{{{ PTHREAD_WORKER_FILE }}}';
442+
var pthreadMainJs = Module['worker'] || './{{{ TARGET_JS_NAME }}}';
445443
#else
446444
#if EXPORT_ES6 && USE_ES6_IMPORT_META
447445
// If we're using module output and there's no explicit override, use bundler-friendly pattern.
448446
if (!Module['locateFile']) {
449447
#if PTHREADS_DEBUG
450-
dbg('Allocating a new web worker from ' + new URL('{{{ PTHREAD_WORKER_FILE }}}', import.meta.url));
448+
dbg('Allocating a new web worker from ' + import.meta.url);
451449
#endif
452450
#if TRUSTED_TYPES
453451
// Use Trusted Types compatible wrappers.
454452
if (typeof trustedTypes != 'undefined' && trustedTypes.createPolicy) {
455453
var p = trustedTypes.createPolicy(
456454
'emscripten#workerPolicy1',
457455
{
458-
createScriptURL: (ignored) => new URL('{{{ PTHREAD_WORKER_FILE }}}', import.meta.url);
456+
createScriptURL: (ignored) => new URL(import.meta.url);
459457
}
460458
);
461-
worker = new Worker(p.createScriptURL('ignored'), {type: 'module'});
459+
worker = new Worker(p.createScriptURL('ignored'), workerOptions);
462460
} else
463461
#endif
464-
worker = new Worker(new URL('{{{ PTHREAD_WORKER_FILE }}}', import.meta.url), {type: 'module'});
462+
worker = new Worker(new URL(import.meta.url), workerOptions);
465463
} else {
466464
#endif
467465
// Allow HTML module to configure the location where the 'worker.js' file will be loaded from,
468466
// via Module.locateFile() function. If not specified, then the default URL 'worker.js' relative
469467
// to the main html file is loaded.
470-
var pthreadMainJs = locateFile('{{{ PTHREAD_WORKER_FILE }}}');
468+
var pthreadMainJs = locateFile('{{{ TARGET_JS_NAME }}}');
469+
#if ENVIRONMENT_MAY_BE_WEB
470+
// On the web we use a URL parameter to determine if we are running a
471+
// pthread or not.
472+
if (ENVIRONMENT_IS_WEB) {
473+
pthreadMainJs += '?pthread=1';
474+
}
475+
#endif
471476
#endif
472477
#if PTHREADS_DEBUG
473478
dbg(`Allocating a new web worker from ${pthreadMainJs}`);
@@ -476,10 +481,10 @@ var LibraryPThread = {
476481
// Use Trusted Types compatible wrappers.
477482
if (typeof trustedTypes != 'undefined' && trustedTypes.createPolicy) {
478483
var p = trustedTypes.createPolicy('emscripten#workerPolicy2', { createScriptURL: (ignored) => pthreadMainJs });
479-
worker = new Worker(p.createScriptURL('ignored'){{{ EXPORT_ES6 ? ", {type: 'module'}" : '' }}});
484+
worker = new Worker(p.createScriptURL('ignored'), workerOptions);
480485
} else
481486
#endif
482-
worker = new Worker(pthreadMainJs{{{ EXPORT_ES6 ? ", {type: 'module'}" : '' }}});
487+
worker = new Worker(pthreadMainJs, workerOptions);
483488
#if EXPORT_ES6 && USE_ES6_IMPORT_META
484489
}
485490
#endif

src/library_sdl.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2905,14 +2905,14 @@ var LibrarySDL = {
29052905
return SDL.setGetVolume(SDL.music, volume);
29062906
},
29072907

2908-
Mix_LoadMUS_RW__docs: '/** @param {number} a1 */',
2909-
Mix_LoadMUS_RW: 'Mix_LoadWAV_RW',
2908+
Mix_LoadMUS_RW__deps: ['Mix_LoadWAV_RW'],
2909+
Mix_LoadMUS_RW: (filename) => _Mix_LoadWAV_RW(filename, 0),
29102910

29112911
Mix_LoadMUS__deps: ['Mix_LoadMUS_RW', 'SDL_RWFromFile', 'SDL_FreeRW'],
29122912
Mix_LoadMUS__proxy: 'sync',
29132913
Mix_LoadMUS: (filename) => {
29142914
var rwops = _SDL_RWFromFile(filename, 0);
2915-
var result = _Mix_LoadMUS_RW(rwops, 0);
2915+
var result = _Mix_LoadMUS_RW(rwops);
29162916
_SDL_FreeRW(rwops);
29172917
return result;
29182918
},

src/parseTools.mjs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -888,21 +888,6 @@ function buildStringArray(array) {
888888
}
889889
}
890890

891-
// Generates access to a JS imports scope variable in pthreads worker.js. In MODULARIZE mode these flow into the imports object for the Module.
892-
// In non-MODULARIZE mode, we can directly access the variables in global scope.
893-
function makeAsmImportsAccessInPthread(variable) {
894-
if (!MINIMAL_RUNTIME) {
895-
// Regular runtime uses the name "Module" for both imports and exports.
896-
return `Module['${variable}']`;
897-
}
898-
if (MODULARIZE) {
899-
// MINIMAL_RUNTIME uses 'imports' as the name for the imports object in MODULARIZE builds.
900-
return `imports['${variable}']`;
901-
}
902-
// In non-MODULARIZE builds, can access the imports from global scope.
903-
return `self.${variable}`;
904-
}
905-
906891
function _asmjsDemangle(symbol) {
907892
if (symbol.startsWith('dynCall_')) {
908893
return symbol;
@@ -1130,7 +1115,6 @@ addToCompileTimeContext({
11301115
hasExportedSymbol,
11311116
implicitSelf,
11321117
isSymbolNeeded,
1133-
makeAsmImportsAccessInPthread,
11341118
makeDynCall,
11351119
makeEval,
11361120
makeGetValue,

src/postamble.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ function stackCheckInit() {
144144

145145
#if MAIN_MODULE && PTHREADS
146146
// Map of modules to be shared with new threads. This gets populated by the
147-
// main thread and shared with all new workers.
148-
var sharedModules = Module['sharedModules'] || [];
147+
// main thread and shared with all new workers via the initial `load` message.
148+
var sharedModules = {};
149149
#endif
150150

151151
#if MAIN_READS_PARAMS

src/postamble_minimal.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,6 @@ function initRuntime(wasmExports) {
8989

9090
// Initialize wasm (asynchronous)
9191

92-
var imports = {
93-
#if MINIFY_WASM_IMPORTED_MODULES
94-
'a': wasmImports,
95-
#else // MINIFY_WASM_IMPORTED_MODULES
96-
'env': wasmImports,
97-
'{{{ WASI_MODULE_NAME }}}': wasmImports,
98-
#endif // MINIFY_WASM_IMPORTED_MODULES
99-
};
100-
10192
// In non-fastcomp non-asm.js builds, grab wasm exports to outer scope
10293
// for emscripten_get_exported_function() to be able to access them.
10394
#if LibraryManager.has('library_exports.js')
@@ -112,6 +103,20 @@ var wasmModule;
112103
<<< WASM_MODULE_EXPORTS_DECLARES >>>
113104
#endif
114105

106+
#if PTHREADS
107+
function loadModule() {
108+
assignWasmImports();
109+
#endif
110+
111+
var imports = {
112+
#if MINIFY_WASM_IMPORTED_MODULES
113+
'a': wasmImports,
114+
#else // MINIFY_WASM_IMPORTED_MODULES
115+
'env': wasmImports,
116+
'{{{ WASI_MODULE_NAME }}}': wasmImports,
117+
#endif // MINIFY_WASM_IMPORTED_MODULES
118+
};
119+
115120
#if MINIMAL_RUNTIME_STREAMING_WASM_INSTANTIATION
116121
// https://caniuse.com/#feat=wasm and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming
117122
// Firefox 52 added Wasm support, but only Firefox 58 added instantiateStreaming.
@@ -252,3 +257,13 @@ WebAssembly.instantiate(Module['wasm'], imports).then((output) => {
252257
}
253258
#endif // ASSERTIONS || WASM == 2
254259
);
260+
261+
#if PTHREADS
262+
}
263+
264+
if (!ENVIRONMENT_IS_PTHREAD) {
265+
// When running in a pthread we delay module loading untill we have
266+
// received the module via postMessage
267+
loadModule();
268+
}
269+
#endif

src/preamble.js

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
// An online HTML version (which may be of a different version of Emscripten)
1515
// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html
1616

17+
#if PTHREADS
18+
#include "runtime_pthread.js"
19+
#endif
20+
1721
#if RELOCATABLE
1822
{{{ makeModuleReceiveWithVar('dynamicLibraries', undefined, '[]', true) }}}
1923
#endif
@@ -897,11 +901,18 @@ function instantiateAsync(binary, binaryFile, imports, callback) {
897901
}
898902
#endif // WASM_ASYNC_COMPILATION
899903

900-
// Create the wasm instance.
901-
// Receives the wasm imports, returns the exports.
902-
function createWasm() {
904+
function getWasmImports() {
905+
#if PTHREADS
906+
assignWasmImports();
907+
#endif
908+
#if ASYNCIFY && (ASSERTIONS || ASYNCIFY == 2)
909+
// instrumenting imports is used in asyncify in two ways: to add assertions
910+
// that check for proper import use, and for ASYNCIFY=2 we use them to set up
911+
// the Promise API on the import side.
912+
Asyncify.instrumentWasmImports(wasmImports);
913+
#endif
903914
// prepare imports
904-
var info = {
915+
return {
905916
#if MINIFY_WASM_IMPORTED_MODULES
906917
'a': wasmImports,
907918
#else // MINIFY_WASM_IMPORTED_MODULES
@@ -915,7 +926,13 @@ function createWasm() {
915926
'GOT.mem': new Proxy(wasmImports, GOTHandler),
916927
'GOT.func': new Proxy(wasmImports, GOTHandler),
917928
#endif
918-
};
929+
}
930+
}
931+
932+
// Create the wasm instance.
933+
// Receives the wasm imports, returns the exports.
934+
function createWasm() {
935+
var info = getWasmImports();
919936
// Load the wasm module and create an instance of using native support in the JS engine.
920937
// handle a generated wasm instance, receiving its exports and
921938
// performing other necessary setup
@@ -1047,21 +1064,6 @@ function createWasm() {
10471064
// Also pthreads and wasm workers initialize the wasm instance through this
10481065
// path.
10491066
if (Module['instantiateWasm']) {
1050-
1051-
#if USE_OFFSET_CONVERTER
1052-
#if ASSERTIONS
1053-
{{{ runIfWorkerThread("assert(Module['wasmOffsetData'], 'wasmOffsetData not found on Module object');") }}}
1054-
#endif
1055-
{{{ runIfWorkerThread("wasmOffsetConverter = resetPrototype(WasmOffsetConverter, Module['wasmOffsetData']);") }}}
1056-
#endif
1057-
1058-
#if LOAD_SOURCE_MAP
1059-
#if ASSERTIONS
1060-
{{{ runIfWorkerThread("assert(Module['wasmSourceMapData'], 'wasmSourceMapData not found on Module object');") }}}
1061-
#endif
1062-
{{{ runIfWorkerThread("wasmSourceMap = resetPrototype(WasmSourceMap, Module['wasmSourceMapData']);") }}}
1063-
#endif
1064-
10651067
try {
10661068
return Module['instantiateWasm'](info, receiveInstance);
10671069
} catch(e) {

src/preamble_minimal.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
#include "runtime_asan.js"
1313
#endif
1414

15+
#if PTHREADS
16+
#include "runtime_pthread.js"
17+
#endif
18+
1519
#if ASSERTIONS
1620
/** @type {function(*, string=)} */
1721
function assert(condition, text) {
@@ -85,11 +89,19 @@ else {
8589
#endif // MODULARIZE
8690
#endif // PTHREADS
8791

92+
#if PTHREADS
93+
if (!ENVIRONMENT_IS_PTHREAD) {
94+
#endif
95+
8896
#if ASSERTIONS && SHARED_MEMORY
8997
assert(wasmMemory.buffer instanceof SharedArrayBuffer, 'requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag');
9098
#endif
9199

92100
updateMemoryViews();
101+
102+
#if PTHREADS
103+
}
104+
#endif
93105
#endif // IMPORTED_MEMORY
94106

95107
#include "runtime_stack_check.js"

src/runtime_init_memory.js

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,10 @@
99
{{{ throw "this file should not be be included when IMPORTED_MEMORY is set"; }}}
1010
#endif
1111

12-
{{{ makeModuleReceiveWithVar('INITIAL_MEMORY', undefined, INITIAL_MEMORY) }}}
13-
14-
#if ASSERTIONS
15-
assert(INITIAL_MEMORY >= {{{STACK_SIZE}}}, 'INITIAL_MEMORY should be larger than STACK_SIZE, was ' + INITIAL_MEMORY + '! (STACK_SIZE=' + {{{STACK_SIZE}}} + ')');
16-
#endif
17-
1812
// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
1913

2014
#if PTHREADS
21-
if (ENVIRONMENT_IS_PTHREAD) {
22-
wasmMemory = Module['wasmMemory'];
23-
} else {
15+
if (!ENVIRONMENT_IS_PTHREAD) {
2416
#endif // PTHREADS
2517

2618
#if expectToReceiveOnModule('wasmMemory')
@@ -29,6 +21,11 @@ if (ENVIRONMENT_IS_PTHREAD) {
2921
} else
3022
#endif
3123
{
24+
{{{ makeModuleReceiveWithVar('INITIAL_MEMORY', undefined, INITIAL_MEMORY) }}}
25+
26+
#if ASSERTIONS
27+
assert(INITIAL_MEMORY >= {{{STACK_SIZE}}}, 'INITIAL_MEMORY should be larger than STACK_SIZE, was ' + INITIAL_MEMORY + '! (STACK_SIZE=' + {{{STACK_SIZE}}} + ')');
28+
#endif
3229
wasmMemory = new WebAssembly.Memory({
3330
'initial': INITIAL_MEMORY / {{{ WASM_PAGE_SIZE }}},
3431
#if ALLOW_MEMORY_GROWTH
@@ -59,15 +56,8 @@ if (ENVIRONMENT_IS_PTHREAD) {
5956
#endif
6057
}
6158

59+
updateMemoryViews();
6260
#if PTHREADS
6361
}
6462
#endif
6563

66-
updateMemoryViews();
67-
68-
// If the user provides an incorrect length, just use that length instead rather than providing the user to
69-
// specifically provide the memory length with Module['INITIAL_MEMORY'].
70-
INITIAL_MEMORY = wasmMemory.buffer.byteLength;
71-
#if ASSERTIONS
72-
assert(INITIAL_MEMORY % {{{ WASM_PAGE_SIZE }}} === 0);
73-
#endif

0 commit comments

Comments
 (0)