Skip to content

Commit 8134613

Browse files
committed
Add support for MODULARIZE with USE_PTHREADS
1 parent 12fafad commit 8134613

File tree

11 files changed

+354
-70
lines changed

11 files changed

+354
-70
lines changed

emcc.py

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

1085+
if shared.Settings.MODULARIZE:
1086+
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)'
1087+
10851088
if shared.Settings.EMULATE_FUNCTION_POINTER_CASTS:
10861089
shared.Settings.ALIASING_FUNCTION_POINTERS = 0
10871090

@@ -1187,11 +1190,6 @@ def check(input_file):
11871190
exit_with_error('USE_PTHREADS=2 is not longer supported')
11881191
if shared.Settings.ALLOW_MEMORY_GROWTH:
11891192
exit_with_error('Memory growth is not yet supported with pthreads')
1190-
if shared.Settings.MODULARIZE:
1191-
# currently worker.js uses the global namespace, so it's setting of
1192-
# ENVIRONMENT_IS_PTHREAD is not picked up, in addition to all the other
1193-
# modifications it performs.
1194-
exit_with_error('MODULARIZE is not yet supported with pthreads')
11951193
# UTF8Decoder.decode doesn't work with a view of a SharedArrayBuffer
11961194
shared.Settings.TEXTDECODER = 0
11971195
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:
@@ -1994,8 +2007,9 @@ def repl(m):
19942007

19952008
if shared.Settings.USE_PTHREADS:
19962009
target_dir = os.path.dirname(os.path.abspath(target))
1997-
shutil.copyfile(shared.path_from_root('src', 'worker.js'),
1998-
os.path.join(target_dir, shared.Settings.PTHREAD_WORKER_FILE))
2010+
worker_output = os.path.join(target_dir, shared.Settings.PTHREAD_WORKER_FILE)
2011+
with open(worker_output, 'w') as f:
2012+
f.write(shared.read_and_preprocess(shared.path_from_root('src', 'worker.js'), expand_macros=True))
19992013

20002014
# Generate the fetch-worker.js script for multithreaded emscripten_fetch() support if targeting pthreads.
20012015
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
@@ -455,6 +455,18 @@ function exportRuntime() {
455455
'getTempRet0',
456456
'setTempRet0',
457457
];
458+
if (MODULARIZE) {
459+
// In MODULARIZE=1 mode, the following functions need to be exported out to Module for worker.js to access.
460+
if (STACK_OVERFLOW_CHECK) {
461+
runtimeElements.push('writeStackCookie');
462+
runtimeElements.push('checkStackCookie');
463+
runtimeElements.push('abortStackOverflow');
464+
}
465+
if (USE_PTHREADS) {
466+
runtimeElements.push('PThread');
467+
runtimeElements.push('ExitStatus');
468+
}
469+
}
458470
if (SUPPORT_BASE64_EMBEDDING) {
459471
runtimeElements.push('intArrayFromBase64');
460472
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
@@ -358,14 +358,6 @@ assert(DYNAMIC_BASE % 16 === 0, 'heap must start aligned');
358358
}
359359
#endif
360360

361-
#if USE_PTHREADS
362-
if (ENVIRONMENT_IS_PTHREAD) {
363-
#if SEPARATE_ASM != 0
364-
importScripts('{{{ SEPARATE_ASM }}}'); // load the separated-out asm.js
365-
#endif
366-
}
367-
#endif
368-
369361
#if EMTERPRETIFY
370362
function abortStackOverflowEmterpreter() {
371363
abort("Emterpreter stack overflow! Decrease the recursion level or increase EMT_STACK_MAX in tools/emterpretify.py (current value " + EMT_STACK_MAX + ").");
@@ -1028,7 +1020,13 @@ function createWasm(env) {
10281020

10291021
Module['asm'] = function(global, env, providedBuffer) {
10301022
// memory was already allocated (so js could use the buffer)
1031-
env['memory'] = wasmMemory;
1023+
env['memory'] = wasmMemory
1024+
#if MODULARIZE && USE_PTHREADS
1025+
// Pthreads assign wasmMemory in their worker startup. In MODULARIZE mode, they cannot assign inside the
1026+
// Module scope, so lookup via Module as well.
1027+
|| Module['wasmMemory']
1028+
#endif
1029+
;
10321030
// import table
10331031
env['table'] = wasmTable = new WebAssembly.Table({
10341032
'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)