Skip to content

Commit 4e9ede5

Browse files
committed
Add support for MODULARIZE with USE_PTHREADS
1 parent 053198d commit 4e9ede5

File tree

11 files changed

+355
-70
lines changed

11 files changed

+355
-70
lines changed

emcc.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,9 @@ def check(input_file):
10811081
if shared.Settings.MODULARIZE_INSTANCE:
10821082
shared.Settings.MODULARIZE = 1
10831083

1084+
if shared.Settings.MODULARIZE:
1085+
assert not options.proxy_to_worker, '-s MODULARIZE=1 and -s MODULARIZE_INSTANCE=1 are not compatible with --proxy-to-worker (if you want to run in a worker with -s MODULARIZE=1, you likely want to do the worker side setup manually)'
1086+
10841087
if shared.Settings.EMULATE_FUNCTION_POINTER_CASTS:
10851088
shared.Settings.ALIASING_FUNCTION_POINTERS = 0
10861089

@@ -1186,11 +1189,6 @@ def check(input_file):
11861189
exit_with_error('USE_PTHREADS=2 is not longer supported')
11871190
if shared.Settings.ALLOW_MEMORY_GROWTH:
11881191
exit_with_error('Memory growth is not yet supported with pthreads')
1189-
if shared.Settings.MODULARIZE:
1190-
# currently worker.js uses the global namespace, so it's setting of
1191-
# ENVIRONMENT_IS_PTHREAD is not picked up, in addition to all the other
1192-
# modifications it performs.
1193-
exit_with_error('MODULARIZE is not yet supported with pthreads')
11941192
# UTF8Decoder.decode doesn't work with a view of a SharedArrayBuffer
11951193
shared.Settings.TEXTDECODER = 0
11961194
options.js_libraries.append(shared.path_from_root('src', 'library_pthread.js'))
@@ -1225,6 +1223,21 @@ def check(input_file):
12251223
]
12261224

12271225
if shared.Settings.USE_PTHREADS:
1226+
if shared.Settings.MODULARIZE:
1227+
# MODULARIZE+USE_PTHREADS mode requires extra exports out to Module so that worker.js
1228+
# can access them:
1229+
1230+
# general threading variables:
1231+
shared.Settings.EXPORTED_RUNTIME_METHODS += ['PThread', 'ExitStatus']
1232+
1233+
# pthread stack setup:
1234+
shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$establishStackSpaceInJsModule']
1235+
shared.Settings.EXPORTED_FUNCTIONS += ['establishStackSpaceInJsModule']
1236+
1237+
# stack check:
1238+
if shared.Settings.STACK_OVERFLOW_CHECK:
1239+
shared.Settings.EXPORTED_RUNTIME_METHODS += ['writeStackCookie', 'checkStackCookie']
1240+
12281241
if shared.Settings.LINKABLE:
12291242
exit_with_error('-s LINKABLE=1 is not supported with -s USE_PTHREADS>0!')
12301243
if shared.Settings.SIDE_MODULE:
@@ -1960,8 +1973,9 @@ def repl(m):
19601973

19611974
if shared.Settings.USE_PTHREADS:
19621975
target_dir = os.path.dirname(os.path.abspath(target))
1963-
shutil.copyfile(shared.path_from_root('src', 'worker.js'),
1964-
os.path.join(target_dir, shared.Settings.PTHREAD_WORKER_FILE))
1976+
worker_output = os.path.join(target_dir, shared.Settings.PTHREAD_WORKER_FILE)
1977+
with open(worker_output, 'w') as f:
1978+
f.write(shared.read_and_preprocess(shared.path_from_root('src', 'worker.js'), expand_macros=True))
19651979

19661980
# Generate the fetch-worker.js script for multithreaded emscripten_fetch() support if targeting pthreads.
19671981
if shared.Settings.FETCH and shared.Settings.USE_PTHREADS:

src/library_pthread.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,12 +356,13 @@ var LibraryPThread = {
356356
// it could load up the same file. In that case, developer must either deliver the Blob
357357
// object in Module['mainScriptUrlOrBlob'], or a URL to it, so that pthread Workers can
358358
// independently load up the same main application file.
359-
urlOrBlob: Module['mainScriptUrlOrBlob'] || currentScriptUrl,
359+
urlOrBlob: Module['mainScriptUrlOrBlob'] || _scriptDir,
360360
#if WASM
361361
wasmMemory: wasmMemory,
362362
wasmModule: wasmModule,
363363
#else
364364
buffer: HEAPU8.buffer,
365+
asmJsUrlOrBlob: Module["asmJsUrlOrBlob"],
365366
#endif
366367
tempDoublePtr: tempDoublePtr,
367368
TOTAL_MEMORY: TOTAL_MEMORY,
@@ -1181,6 +1182,13 @@ var LibraryPThread = {
11811182
#endif
11821183
return func.apply(null, callArgs);
11831184
},
1185+
1186+
#if MODULARIZE
1187+
$establishStackSpaceInJsModule: function(stackBase, stackMax) {
1188+
STACK_BASE = STACKTOP = stackBase;
1189+
STACK_MAX = stackMax;
1190+
},
1191+
#endif
11841192
};
11851193

11861194
autoAddDeps(LibraryPThread, '$PThread');

src/modules.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,18 @@ function exportRuntime() {
447447
'getTempRet0',
448448
'setTempRet0',
449449
];
450+
if (MODULARIZE) {
451+
// In MODULARIZE=1 mode, the following functions need to be exported out to Module for worker.js to access.
452+
if (STACK_OVERFLOW_CHECK) {
453+
runtimeElements.push('writeStackCookie');
454+
runtimeElements.push('checkStackCookie');
455+
runtimeElements.push('abortStackOverflow');
456+
}
457+
if (USE_PTHREADS) {
458+
runtimeElements.push('PThread');
459+
runtimeElements.push('ExitStatus');
460+
}
461+
}
450462
if (SUPPORT_BASE64_EMBEDDING) {
451463
runtimeElements.push('intArrayFromBase64');
452464
runtimeElements.push('tryParseAsDataURI');

src/parseTools.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,34 @@ function makeStaticString(string) {
14771477
return '(stringToUTF8("' + string + '", ' + ptr + ', ' + len + '), ' + ptr + ')';
14781478
}
14791479

1480+
// Generates access to module exports variable in pthreads worker.js
1481+
function makeAsmExportAccessInPthread(variable) {
1482+
if (MODULARIZE) {
1483+
return "Module['" + variable + "']" // 'Module' is defined in worker.js local scope, so not EXPORT_NAME in this case.
1484+
} else {
1485+
return EXPORT_NAME + "['" + variable + "']"
1486+
}
1487+
}
1488+
1489+
// Generates access to a global scope variable in pthreads worker.js
1490+
function makeAsmGlobalAccessInPthread(variable) {
1491+
if (MODULARIZE) {
1492+
return "Module['" + variable + "']" // 'Module' is defined in worker.js local scope, so not EXPORT_NAME in this case.
1493+
} else {
1494+
return variable
1495+
}
1496+
}
1497+
1498+
// Generates access to both global scope variable and exported Module variable, e.g. "Module['foo'] = foo" or just plain "foo" depending on if we are MODULARIZEing.
1499+
// Used the be able to initialize both variables at the same time.
1500+
function makeAsmExportAndGlobalAccessInPthread(variable) {
1501+
if (MODULARIZE) {
1502+
return "Module['" + variable + "'] = " + variable // 'Module' is defined in worker.js local scope, so not EXPORT_NAME in this case.
1503+
} else {
1504+
return variable
1505+
}
1506+
}
1507+
14801508
// Some things, like the dynamic and stack bases, will be computed later and
14811509
// applied. Return them as {{{ STR }}} for that replacing later.
14821510

src/preamble.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -389,14 +389,6 @@ assert(DYNAMIC_BASE % 16 === 0, 'heap must start aligned');
389389
}
390390
#endif
391391

392-
#if USE_PTHREADS
393-
if (ENVIRONMENT_IS_PTHREAD) {
394-
#if SEPARATE_ASM != 0
395-
importScripts('{{{ SEPARATE_ASM }}}'); // load the separated-out asm.js
396-
#endif
397-
}
398-
#endif
399-
400392
#if EMTERPRETIFY
401393
function abortStackOverflowEmterpreter() {
402394
abort("Emterpreter stack overflow! Decrease the recursion level or increase EMT_STACK_MAX in tools/emterpretify.py (current value " + EMT_STACK_MAX + ").");
@@ -1059,7 +1051,13 @@ function createWasm(env) {
10591051

10601052
Module['asm'] = function(global, env, providedBuffer) {
10611053
// memory was already allocated (so js could use the buffer)
1062-
env['memory'] = wasmMemory;
1054+
env['memory'] = wasmMemory
1055+
#if MODULARIZE && USE_PTHREADS
1056+
// Pthreads assign wasmMemory in their worker startup. In MODULARIZE mode, they cannot assign inside the
1057+
// Module scope, so lookup via Module as well.
1058+
|| Module['wasmMemory']
1059+
#endif
1060+
;
10631061
// import table
10641062
env['table'] = wasmTable = new WebAssembly.Table({
10651063
'initial': {{{ getQuoted('WASM_TABLE_SIZE') }}},

src/shell.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,34 @@ if (Module['ENVIRONMENT']) {
8181
// 2) We could be the application main() thread proxied to worker. (with Emscripten -s PROXY_TO_WORKER=1) (ENVIRONMENT_IS_WORKER == true, ENVIRONMENT_IS_PTHREAD == false)
8282
// 3) We could be an application pthread running in a worker. (ENVIRONMENT_IS_WORKER == true and ENVIRONMENT_IS_PTHREAD == true)
8383
#if USE_PTHREADS
84-
var ENVIRONMENT_IS_PTHREAD;
85-
if (!ENVIRONMENT_IS_PTHREAD) ENVIRONMENT_IS_PTHREAD = false; // ENVIRONMENT_IS_PTHREAD=true will have been preset in worker.js. Make it false in the main runtime thread.
86-
var PthreadWorkerInit; // Collects together variables that are needed at initialization time for the web workers that host pthreads.
87-
if (!ENVIRONMENT_IS_PTHREAD) PthreadWorkerInit = {};
88-
var currentScriptUrl = (typeof document !== 'undefined' && document.currentScript) ? document.currentScript.src : undefined;
84+
85+
if (typeof ENVIRONMENT_IS_PTHREAD === 'undefined') {
86+
// ENVIRONMENT_IS_PTHREAD=true will have been preset in worker.js. Make it false in the main runtime thread.
87+
// N.B. this line needs to appear without 'var' keyword to avoid 'var hoisting' from occurring. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var)
88+
ENVIRONMENT_IS_PTHREAD = false;
89+
var PthreadWorkerInit = {}; // Collects together variables that are needed at initialization time for the web workers that host pthreads.
90+
}
91+
#if MODULARIZE
92+
else {
93+
// Grab imports from the pthread to local scope.
94+
var buffer = {{{EXPORT_NAME}}}.buffer;
95+
var tempDoublePtr = {{{EXPORT_NAME}}}.tempDoublePtr;
96+
var TOTAL_MEMORY = {{{EXPORT_NAME}}}.TOTAL_MEMORY;
97+
var STATICTOP = {{{EXPORT_NAME}}}.STATICTOP;
98+
var DYNAMIC_BASE = {{{EXPORT_NAME}}}.DYNAMIC_BASE;
99+
var DYNAMICTOP_PTR = {{{EXPORT_NAME}}}.DYNAMICTOP_PTR;
100+
var PthreadWorkerInit = {{{EXPORT_NAME}}}.PthreadWorkerInit;
101+
// Note that not all runtime fields are imported above. Values for STACK_BASE, STACKTOP and STACK_MAX are not yet known at worker.js load time.
102+
// These will be filled in at pthread startup time (the 'run' message for a pthread - pthread start establishes the stack frame)
103+
}
104+
#endif
105+
106+
#if !MODULARIZE
107+
// In MODULARIZE mode _scriptDir needs to be captured already at the very top of the page immediately when the page is parsed, so it is generated there
108+
// before the page load. In non-MODULARIZE modes generate it here.
109+
var _scriptDir = (typeof document !== 'undefined' && document.currentScript) ? document.currentScript.src : undefined;
110+
#endif
111+
89112
#endif // USE_PTHREADS
90113

91114
// `/` should be present at the end if `scriptDirectory` is not empty

0 commit comments

Comments
 (0)