Skip to content

Add support for MODULARIZE with USE_PTHREADS #7667

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,9 @@ def check(input_file):
if shared.Settings.MODULARIZE_INSTANCE:
shared.Settings.MODULARIZE = 1

if shared.Settings.MODULARIZE:
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)'

if shared.Settings.EMULATE_FUNCTION_POINTER_CASTS:
shared.Settings.ALIASING_FUNCTION_POINTERS = 0

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

if shared.Settings.USE_PTHREADS:
if shared.Settings.MODULARIZE:
# MODULARIZE+USE_PTHREADS mode requires extra exports out to Module so that worker.js
# can access them:

# general threading variables:
shared.Settings.EXPORTED_RUNTIME_METHODS += ['PThread', 'ExitStatus']

# pthread stack setup:
shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$establishStackSpaceInJsModule']
shared.Settings.EXPORTED_FUNCTIONS += ['establishStackSpaceInJsModule']

# stack check:
if shared.Settings.STACK_OVERFLOW_CHECK:
shared.Settings.EXPORTED_RUNTIME_METHODS += ['writeStackCookie', 'checkStackCookie']

if shared.Settings.LINKABLE:
exit_with_error('-s LINKABLE=1 is not supported with -s USE_PTHREADS>0!')
if shared.Settings.SIDE_MODULE:
Expand Down Expand Up @@ -1994,8 +2007,9 @@ def repl(m):

if shared.Settings.USE_PTHREADS:
target_dir = os.path.dirname(os.path.abspath(target))
shutil.copyfile(shared.path_from_root('src', 'worker.js'),
os.path.join(target_dir, shared.Settings.PTHREAD_WORKER_FILE))
worker_output = os.path.join(target_dir, shared.Settings.PTHREAD_WORKER_FILE)
with open(worker_output, 'w') as f:
f.write(shared.read_and_preprocess(shared.path_from_root('src', 'worker.js'), expand_macros=True))

# Generate the fetch-worker.js script for multithreaded emscripten_fetch() support if targeting pthreads.
if shared.Settings.FETCH and shared.Settings.USE_PTHREADS:
Expand Down
10 changes: 9 additions & 1 deletion src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,13 @@ var LibraryPThread = {
// it could load up the same file. In that case, developer must either deliver the Blob
// object in Module['mainScriptUrlOrBlob'], or a URL to it, so that pthread Workers can
// independently load up the same main application file.
urlOrBlob: Module['mainScriptUrlOrBlob'] || currentScriptUrl,
urlOrBlob: Module['mainScriptUrlOrBlob'] || _scriptDir,
#if WASM
wasmMemory: wasmMemory,
wasmModule: wasmModule,
#else
buffer: HEAPU8.buffer,
asmJsUrlOrBlob: Module["asmJsUrlOrBlob"],
#endif
tempDoublePtr: tempDoublePtr,
TOTAL_MEMORY: TOTAL_MEMORY,
Expand Down Expand Up @@ -1181,6 +1182,13 @@ var LibraryPThread = {
#endif
return func.apply(null, callArgs);
},

#if MODULARIZE
$establishStackSpaceInJsModule: function(stackBase, stackMax) {
STACK_BASE = STACKTOP = stackBase;
STACK_MAX = stackMax;
},
#endif
};

autoAddDeps(LibraryPThread, '$PThread');
Expand Down
15 changes: 15 additions & 0 deletions src/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -454,9 +454,24 @@ function exportRuntime() {
'getTempRet0',
'setTempRet0',
];

if (!MINIMAL_RUNTIME) {
runtimeElements.push('Pointer_stringify');
}

if (MODULARIZE) {
// In MODULARIZE=1 mode, the following functions need to be exported out to Module for worker.js to access.
if (STACK_OVERFLOW_CHECK) {
runtimeElements.push('writeStackCookie');
runtimeElements.push('checkStackCookie');
runtimeElements.push('abortStackOverflow');
}
if (USE_PTHREADS) {
runtimeElements.push('PThread');
runtimeElements.push('ExitStatus');
}
}

if (SUPPORT_BASE64_EMBEDDING) {
runtimeElements.push('intArrayFromBase64');
runtimeElements.push('tryParseAsDataURI');
Expand Down
31 changes: 31 additions & 0 deletions src/parseTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -1477,6 +1477,37 @@ function makeStaticString(string) {
return '(stringToUTF8("' + string + '", ' + ptr + ', ' + len + '), ' + ptr + ')';
}

// Generates access to module exports variable in pthreads worker.js. Depending on whether main code is built with MODULARIZE
// or not, asm module exports need to either be accessed via a local exports object obtained from instantiating the module (in src/worker.js), or via
// the global Module exports object.
function makeAsmExportAccessInPthread(variable) {
if (MODULARIZE) {
return "Module['" + variable + "']"; // 'Module' is defined in worker.js local scope, so not EXPORT_NAME in this case.
} else {
return EXPORT_NAME + "['" + variable + "']";
}
}

// Generates access to a JS global scope variable in pthreads worker.js. In MODULARIZE mode the JS scope is not directly accessible, so all the relevant variables
// are exported via Module. In non-MODULARIZE mode, we can directly access the variables in global scope.
function makeAsmGlobalAccessInPthread(variable) {
if (MODULARIZE) {
return "Module['" + variable + "']"; // 'Module' is defined in worker.js local scope, so not EXPORT_NAME in this case.
} else {
return variable;
}
}

// 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.
// Used the be able to initialize both variables at the same time in scenarios where a variable exists in both global scope and in Module.
function makeAsmExportAndGlobalAssignTargetInPthread(variable) {
if (MODULARIZE) {
return "Module['" + variable + "'] = " + variable; // 'Module' is defined in worker.js local scope, so not EXPORT_NAME in this case.
} else {
return variable;
}
}

// Some things, like the dynamic and stack bases, will be computed later and
// applied. Return them as {{{ STR }}} for that replacing later.

Expand Down
16 changes: 7 additions & 9 deletions src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,14 +358,6 @@ assert(DYNAMIC_BASE % 16 === 0, 'heap must start aligned');
}
#endif

#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) {
#if SEPARATE_ASM != 0
importScripts('{{{ SEPARATE_ASM }}}'); // load the separated-out asm.js
#endif
}
#endif

#if EMTERPRETIFY
function abortStackOverflowEmterpreter() {
abort("Emterpreter stack overflow! Decrease the recursion level or increase EMT_STACK_MAX in tools/emterpretify.py (current value " + EMT_STACK_MAX + ").");
Expand Down Expand Up @@ -1028,7 +1020,13 @@ function createWasm(env) {

Module['asm'] = function(global, env, providedBuffer) {
// memory was already allocated (so js could use the buffer)
env['memory'] = wasmMemory;
env['memory'] = wasmMemory
#if MODULARIZE && USE_PTHREADS
// Pthreads assign wasmMemory in their worker startup. In MODULARIZE mode, they cannot assign inside the
// Module scope, so lookup via Module as well.
|| Module['wasmMemory']
#endif
;
// import table
env['table'] = wasmTable = new WebAssembly.Table({
'initial': {{{ getQuoted('WASM_TABLE_SIZE') }}},
Expand Down
18 changes: 7 additions & 11 deletions src/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,13 @@ if (Module['ENVIRONMENT']) {
}
#endif

// Three configurations we can be running in:
// 1) We could be the application main() thread running in the main JS UI thread. (ENVIRONMENT_IS_WORKER == false and ENVIRONMENT_IS_PTHREAD == false)
// 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)
// 3) We could be an application pthread running in a worker. (ENVIRONMENT_IS_WORKER == true and ENVIRONMENT_IS_PTHREAD == true)
#if USE_PTHREADS
var ENVIRONMENT_IS_PTHREAD;
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.
var PthreadWorkerInit; // Collects together variables that are needed at initialization time for the web workers that host pthreads.
if (!ENVIRONMENT_IS_PTHREAD) PthreadWorkerInit = {};
var currentScriptUrl = (typeof document !== 'undefined' && document.currentScript) ? document.currentScript.src : undefined;
#endif // USE_PTHREADS
#include "shell_pthreads.js"

#if USE_PTHREADS && !MODULARIZE
// 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
// before the page load. In non-MODULARIZE modes generate it here.
var _scriptDir = (typeof document !== 'undefined' && document.currentScript) ? document.currentScript.src : undefined;
#endif

// `/` should be present at the end if `scriptDirectory` is not empty
var scriptDirectory = '';
Expand Down
28 changes: 28 additions & 0 deletions src/shell_pthreads.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Three configurations we can be running in:
// 1) We could be the application main() thread running in the main JS UI thread. (ENVIRONMENT_IS_WORKER == false and ENVIRONMENT_IS_PTHREAD == false)
// 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)
// 3) We could be an application pthread running in a worker. (ENVIRONMENT_IS_WORKER == true and ENVIRONMENT_IS_PTHREAD == true)
#if USE_PTHREADS

if (typeof ENVIRONMENT_IS_PTHREAD === 'undefined') {
// ENVIRONMENT_IS_PTHREAD=true will have been preset in worker.js. Make it false in the main runtime thread.
// 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)
ENVIRONMENT_IS_PTHREAD = false;
var PthreadWorkerInit = {}; // Collects together variables that are needed at initialization time for the web workers that host pthreads.
}
#if MODULARIZE
else {
// Grab imports from the pthread to local scope.
var buffer = {{{EXPORT_NAME}}}.buffer;
var tempDoublePtr = {{{EXPORT_NAME}}}.tempDoublePtr;
var TOTAL_MEMORY = {{{EXPORT_NAME}}}.TOTAL_MEMORY;
var STATICTOP = {{{EXPORT_NAME}}}.STATICTOP;
var DYNAMIC_BASE = {{{EXPORT_NAME}}}.DYNAMIC_BASE;
var DYNAMICTOP_PTR = {{{EXPORT_NAME}}}.DYNAMICTOP_PTR;
var PthreadWorkerInit = {{{EXPORT_NAME}}}.PthreadWorkerInit;
// 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.
// These will be filled in at pthread startup time (the 'run' message for a pthread - pthread start establishes the stack frame)
}
#endif

#endif
Loading