Skip to content

Commit fab6375

Browse files
committed
Add support for MODULARIZE with USE_PTHREADS
1 parent 6b6c0ec commit fab6375

File tree

11 files changed

+213
-72
lines changed

11 files changed

+213
-72
lines changed

emcc.py

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,15 @@ 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+
1087+
if not shared.Settings.EXPORT_NAME:
1088+
# If we are building directly to .html with -s MODULARIZE=1 (but not -s MODULARIZE_INSTANCE=1), default to exporting
1089+
# Emscripten code under function EmscriptenCode() so that it does not collide with the Module object. (When not modularizing,
1090+
# or if building with MODULARIZE_INSTANCE, or if building to .js manually, there is no conflict, so default to 'Module' in those cases)
1091+
shared.Settings.EXPORT_NAME = 'EmscriptenCode' if final_suffix == '.html' and shared.Settings.MODULARIZE and not shared.Settings.MODULARIZE_INSTANCE else 'Module'
1092+
10841093
if shared.Settings.EMULATE_FUNCTION_POINTER_CASTS:
10851094
shared.Settings.ALIASING_FUNCTION_POINTERS = 0
10861095

@@ -1186,11 +1195,6 @@ def check(input_file):
11861195
exit_with_error('USE_PTHREADS=2 is not longer supported')
11871196
if shared.Settings.ALLOW_MEMORY_GROWTH:
11881197
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')
11941198
# UTF8Decoder.decode doesn't work with a view of a SharedArrayBuffer
11951199
shared.Settings.TEXTDECODER = 0
11961200
options.js_libraries.append(shared.path_from_root('src', 'library_pthread.js'))
@@ -1225,6 +1229,21 @@ def check(input_file):
12251229
]
12261230

12271231
if shared.Settings.USE_PTHREADS:
1232+
if shared.Settings.MODULARIZE:
1233+
# MODULARIZE+USE_PTHREADS mode requires extra exports out to Module so that worker.js
1234+
# can access them:
1235+
1236+
# general threading variables:
1237+
shared.Settings.EXPORTED_RUNTIME_METHODS += ['PThread', 'ExitStatus']
1238+
1239+
# pthread stack setup:
1240+
shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$establishStackSpaceInJsModule']
1241+
shared.Settings.EXPORTED_FUNCTIONS += ['establishStackSpaceInJsModule']
1242+
1243+
# stack check:
1244+
if shared.Settings.STACK_OVERFLOW_CHECK:
1245+
shared.Settings.EXPORTED_RUNTIME_METHODS += ['writeStackCookie', 'checkStackCookie']
1246+
12281247
if shared.Settings.LINKABLE:
12291248
exit_with_error('-s LINKABLE=1 is not supported with -s USE_PTHREADS>0!')
12301249
if shared.Settings.SIDE_MODULE:
@@ -1952,8 +1971,8 @@ def repl(m):
19521971

19531972
if shared.Settings.USE_PTHREADS:
19541973
target_dir = os.path.dirname(os.path.abspath(target))
1955-
shutil.copyfile(shared.path_from_root('src', 'worker.js'),
1956-
os.path.join(target_dir, shared.Settings.PTHREAD_WORKER_FILE))
1974+
worker_output = os.path.join(target_dir, shared.Settings.PTHREAD_WORKER_FILE)
1975+
open(worker_output, 'w').write(shared.read_and_preprocess(shared.path_from_root('src', 'worker.js'), expand_macros=True))
19571976

19581977
# Generate the fetch-worker.js script for multithreaded emscripten_fetch() support if targeting pthreads.
19591978
if shared.Settings.FETCH and shared.Settings.USE_PTHREADS:
@@ -2934,18 +2953,28 @@ def un_src(self):
29342953
"""Use this if you want to modify the script and need it to be inline."""
29352954
if self.src is None:
29362955
return
2956+
run_module = ''
2957+
if shared.Settings.MODULARIZE and not shared.Settings.MODULARIZE_INSTANCE:
2958+
run_module = 'script.onload = function() { %s(Module); };' % shared.Settings.EXPORT_NAME
2959+
29372960
self.inline = '''
29382961
var script = document.createElement('script');
29392962
script.src = "%s";
2963+
%s
29402964
document.body.appendChild(script);
2941-
''' % self.src
2965+
''' % (self.src, run_module)
29422966
self.src = None
29432967

29442968
def replacement(self):
2969+
onload = ''
2970+
if shared.Settings.MODULARIZE and not shared.Settings.MODULARIZE_INSTANCE:
2971+
assert shared.Settings.EXPORT_NAME != 'Module', 'EXPORT_NAME cannot be called "Module"!'
2972+
onload = 'onload="%s(Module)"' % shared.Settings.EXPORT_NAME
2973+
29452974
"""Returns the script tag to replace the {{{ SCRIPT }}} tag in the target"""
29462975
assert (self.src or self.inline) and not (self.src and self.inline)
29472976
if self.src:
2948-
return '<script async type="text/javascript" src="%s"></script>' % quote(self.src)
2977+
return '<script async type="text/javascript" src="%s" %s></script>' % (quote(self.src), onload)
29492978
else:
29502979
return '<script>\n%s\n</script>' % self.inline
29512980

src/library_pthread.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ var LibraryPThread = {
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
@@ -426,6 +426,18 @@ function exportRuntime() {
426426
'getTempRet0',
427427
'setTempRet0',
428428
];
429+
if (MODULARIZE) {
430+
// In MODULARIZE=1 mode, the following functions need to be exported out to Module for worker.js to access.
431+
if (STACK_OVERFLOW_CHECK) {
432+
runtimeElements.push('writeStackCookie');
433+
runtimeElements.push('checkStackCookie');
434+
runtimeElements.push('abortStackOverflow');
435+
}
436+
if (USE_PTHREADS) {
437+
runtimeElements.push('PThread');
438+
runtimeElements.push('ExitStatus');
439+
}
440+
}
429441
if (SUPPORT_BASE64_EMBEDDING) {
430442
runtimeElements.push('intArrayFromBase64');
431443
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
@@ -404,14 +404,6 @@ assert(DYNAMIC_BASE % 16 === 0, 'heap must start aligned');
404404
}
405405
#endif
406406

407-
#if USE_PTHREADS
408-
if (ENVIRONMENT_IS_PTHREAD) {
409-
#if SEPARATE_ASM != 0
410-
importScripts('{{{ SEPARATE_ASM }}}'); // load the separated-out asm.js
411-
#endif
412-
}
413-
#endif
414-
415407
#if EMTERPRETIFY
416408
function abortStackOverflowEmterpreter() {
417409
abort("Emterpreter stack overflow! Decrease the recursion level or increase EMT_STACK_MAX in tools/emterpretify.py (current value " + EMT_STACK_MAX + ").");
@@ -1099,7 +1091,13 @@ function createWasm(env) {
10991091

11001092
Module['asm'] = function(global, env, providedBuffer) {
11011093
// memory was already allocated (so js could use the buffer)
1102-
env['memory'] = wasmMemory;
1094+
env['memory'] = wasmMemory
1095+
#if MODULARIZE && USE_PTHREADS
1096+
// Pthreads assign wasmMemory in their worker startup. In MODULARIZE mode, they cannot assign inside the
1097+
// Module scope, so lookup via Module as well.
1098+
|| Module['wasmMemory']
1099+
#endif
1100+
;
11031101
// import table
11041102
env['table'] = wasmTable = new WebAssembly.Table({
11051103
'initial': {{{ getQuoted('WASM_TABLE_SIZE') }}},

src/settings.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,10 @@ var EXPLICIT_ZEXT = 0;
926926

927927
// Global variable to export the module as for environments without a
928928
// standardized module loading system (e.g. the browser and SM shell).
929-
var EXPORT_NAME = 'Module';
929+
// Default EXPORT_NAME is 'Module', but if building directly to .html
930+
// with -s MODULARIZE=1 but not with -s MODULARIZE_INSTANCE=1, then default EXPORT_NAME
931+
// is 'EmscriptenCode' to not conflict with the default provided Module object.
932+
var EXPORT_NAME = '';
930933

931934
// When set to 0, we do not emit eval() and new Function(), which disables some functionality
932935
// (causing runtime errors if attempted to be used), but allows the emitted code to be

src/shell.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,29 @@ 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+
var currentScriptUrl = typeof _scriptDir !== 'undefined' ? _scriptDir : ((typeof document !== 'undefined' && document.currentScript) ? document.currentScript.src : undefined);
89107
#endif // USE_PTHREADS
90108

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

0 commit comments

Comments
 (0)