-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Minimal runtime pthreads #10123
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
Minimal runtime pthreads #10123
Changes from all commits
d486dc1
4a787fe
4b8db00
f8e47fe
fa9759a
ae6f617
8a215be
b532545
8713949
405d731
e91ee95
cbb28db
8c7e3d3
1a0d4e2
ddd2ff8
ef424d9
e09448f
46e4e03
374ab6d
a2dd850
4b8e4c5
26395c7
eac77f1
9188ac7
ad9e79c
eea85f6
61d0ea0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -943,8 +943,8 @@ def get_exported_implemented_functions(all_exported_functions, all_implemented, | |
| funcs.append('_emscripten_replace_memory') | ||
| if not shared.Settings.SIDE_MODULE and not shared.Settings.MINIMAL_RUNTIME: | ||
| funcs += ['stackAlloc', 'stackSave', 'stackRestore'] | ||
| if shared.Settings.USE_PTHREADS: | ||
| funcs += ['establishStackSpace'] | ||
| if shared.Settings.USE_PTHREADS: | ||
| funcs += ['asmJsEstablishStackFrame'] | ||
|
|
||
| if shared.Settings.EMTERPRETIFY: | ||
| funcs += ['emterpret'] | ||
|
|
@@ -1675,8 +1675,6 @@ def create_asm_runtime_funcs(): | |
| funcs = [] | ||
| if not (shared.Settings.WASM and shared.Settings.SIDE_MODULE) and not shared.Settings.MINIMAL_RUNTIME: | ||
| funcs += ['stackAlloc', 'stackSave', 'stackRestore'] | ||
| if shared.Settings.USE_PTHREADS: | ||
| funcs += ['establishStackSpace'] | ||
| return funcs | ||
|
|
||
|
|
||
|
|
@@ -1887,9 +1885,13 @@ def create_runtime_funcs_asmjs(exports, metadata): | |
| } | ||
| ''' % stack_check] | ||
|
|
||
| if shared.Settings.MINIMAL_RUNTIME: | ||
| # MINIMAL_RUNTIME moves stack functions to library. | ||
| funcs = [] | ||
|
|
||
| if shared.Settings.USE_PTHREADS: | ||
| funcs.append(''' | ||
| function establishStackSpace(stackBase, stackMax) { | ||
| function asmJsEstablishStackFrame(stackBase, stackMax) { | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renamed this to highlight the fact that this functionality is only needed for old fastcomp threading support. |
||
| stackBase = stackBase|0; | ||
| stackMax = stackMax|0; | ||
| STACKTOP = stackBase; | ||
|
|
@@ -1899,10 +1901,6 @@ def create_runtime_funcs_asmjs(exports, metadata): | |
| } | ||
| ''') | ||
|
|
||
| if shared.Settings.MINIMAL_RUNTIME: | ||
| # MINIMAL_RUNTIME moves stack functions to library. | ||
| funcs = [] | ||
|
|
||
| if shared.Settings.EMTERPRETIFY: | ||
| funcs.append(''' | ||
| function emterpret(pc) { // this will be replaced when the emterpreter code is generated; adding it here allows validation until then | ||
|
|
@@ -2645,7 +2643,13 @@ def create_receiving_wasm(exports, initializers): | |
| # In wasm2js exports can be directly processed at top level, i.e. | ||
| # var asm = Module["asm"](asmGlobalArg, asmLibraryArg, buffer); | ||
| # var _main = asm["_main"]; | ||
| receiving += ['var ' + asmjs_mangle(s) + ' = asm["' + asmjs_mangle(s) + '"];' for s in exports_that_are_not_initializers] | ||
| if shared.Settings.USE_PTHREADS and shared.Settings.MODULARIZE: | ||
| # TODO: As a temp solution, multithreaded MODULARIZEd MINIMAL_RUNTIME builds export all symbols like regular runtime does. | ||
| # Fix this by migrating worker.js code to reside inside the Module so it is in the same scope as the rest of the JS code, or | ||
| # by defining an export syntax to MINIMAL_RUNTIME that multithreaded MODULARIZEd builds can export on. | ||
| receiving += [asmjs_mangle(s) + ' = Module["' + asmjs_mangle(s) + '"] = asm["' + s + '"];' for s in exports_that_are_not_initializers] | ||
| else: | ||
| receiving += ['var ' + asmjs_mangle(s) + ' = asm["' + asmjs_mangle(s) + '"];' for s in exports_that_are_not_initializers] | ||
| else: | ||
| receiving += ['var ' + asmjs_mangle(s) + ' = Module["' + asmjs_mangle(s) + '"] = asm["' + s + '"];' for s in exports] | ||
| else: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -275,6 +275,12 @@ LibraryManager.library = { | |
| _Exit__sig: 'vi', | ||
| _Exit: 'exit', | ||
|
|
||
| #if MINIMAL_RUNTIME | ||
| $exit: function(status) { | ||
| throw 'exit(' + status + ')'; | ||
| }, | ||
| #endif | ||
|
|
||
| fork__deps: ['__setErrNo'], | ||
| fork: function() { | ||
| // pid_t fork(void); | ||
|
|
@@ -1353,17 +1359,6 @@ LibraryManager.library = { | |
| }, | ||
| #endif | ||
|
|
||
| #if USE_PTHREADS | ||
| $establishStackSpace__asm: true, | ||
| $establishStackSpace__sig: 'vii', | ||
| $establishStackSpace: function(stackBase, stackMax) { | ||
| stackBase = stackBase|0; | ||
| stackMax = stackMax|0; | ||
| STACKTOP = stackBase; | ||
| STACK_MAX = stackMax; | ||
| }, | ||
| #endif | ||
|
|
||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This deletion is not strictly related to MINIMAL_RUNTIME multithreading support, but while doing this PR I realized this whole function is redundant, so simplified the code. |
||
| #if WASM_BACKEND == 0 | ||
| $setThrew__asm: true, | ||
| $setThrew__sig: 'vii', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,7 +29,9 @@ var LibraryPThread = { | |
| _emscripten_register_main_browser_thread_id(PThread.mainThreadBlock); | ||
| }, | ||
| initMainThreadBlock: function() { | ||
| if (ENVIRONMENT_IS_PTHREAD) return; | ||
| #if ASSERTIONS | ||
| assert(!ENVIRONMENT_IS_PTHREAD); | ||
| #endif | ||
|
|
||
| #if PTHREAD_POOL_SIZE | ||
| var pthreadPoolSize = {{{ PTHREAD_POOL_SIZE }}}; | ||
|
|
@@ -374,8 +376,8 @@ var LibraryPThread = { | |
| #endif | ||
|
|
||
| #if ASSERTIONS && WASM | ||
| assert(wasmMemory, 'WebAssembly memory should have been loaded by now!'); | ||
| assert(wasmModule, 'WebAssembly Module should have been loaded by now!'); | ||
| assert(wasmMemory instanceof WebAssembly.Memory, 'WebAssembly memory should have been loaded by now!'); | ||
| assert(wasmModule instanceof WebAssembly.Module, 'WebAssembly Module should have been loaded by now!'); | ||
| #endif | ||
|
|
||
| // Ask the new worker to load up the Emscripten-compiled page. This is a heavy operation. | ||
|
|
@@ -400,17 +402,23 @@ var LibraryPThread = { | |
| 'buffer': HEAPU8.buffer, | ||
| 'asmJsUrlOrBlob': Module["asmJsUrlOrBlob"], | ||
| #endif | ||
| #if !MINIMAL_RUNTIME | ||
| 'DYNAMIC_BASE': DYNAMIC_BASE, | ||
| #endif | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In MINIMAL_RUNTIME the |
||
| 'DYNAMICTOP_PTR': DYNAMICTOP_PTR | ||
| }); | ||
| }, | ||
|
|
||
| // Creates a new web Worker and places it in the unused worker pool to wait for its use. | ||
| allocateUnusedWorker: function() { | ||
| #if MINIMAL_RUNTIME | ||
| var pthreadMainJs = Module['worker']; | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This use of |
||
| #else | ||
| // Allow HTML module to configure the location where the 'worker.js' file will be loaded from, | ||
| // via Module.locateFile() function. If not specified, then the default URL 'worker.js' relative | ||
| // to the main html file is loaded. | ||
| var pthreadMainJs = locateFile('{{{ PTHREAD_WORKER_FILE }}}'); | ||
| #endif | ||
| #if PTHREADS_DEBUG | ||
| out('Allocating a new web worker from ' + pthreadMainJs); | ||
| #endif | ||
|
|
@@ -550,7 +558,7 @@ var LibraryPThread = { | |
| return navigator['hardwareConcurrency']; | ||
| }, | ||
|
|
||
| {{{ USE_LSAN || USE_ASAN ? 'emscripten_builtin_' : '' }}}pthread_create__deps: ['_spawn_thread', 'pthread_getschedparam', 'pthread_self', 'memalign'], | ||
| {{{ USE_LSAN || USE_ASAN ? 'emscripten_builtin_' : '' }}}pthread_create__deps: ['_spawn_thread', 'pthread_getschedparam', 'pthread_self', 'memalign', '$resetPrototype'], | ||
| {{{ USE_LSAN || USE_ASAN ? 'emscripten_builtin_' : '' }}}pthread_create: function(pthread_ptr, attr, start_routine, arg) { | ||
| if (typeof SharedArrayBuffer === 'undefined') { | ||
| err('Current environment does not support SharedArrayBuffer, pthreads are not available!'); | ||
|
|
@@ -769,17 +777,30 @@ var LibraryPThread = { | |
| if (canceled == 2) throw 'Canceled!'; | ||
| }, | ||
|
|
||
| #if MINIMAL_RUNTIME | ||
| emscripten_check_blocking_allowed__deps: ['$warnOnce'], | ||
| #endif | ||
| emscripten_check_blocking_allowed: function() { | ||
| #if ASSERTIONS | ||
| assert(ENVIRONMENT_IS_WEB); | ||
| warnOnce('Blocking on the main thread is very dangerous, see https://emscripten.org/docs/porting/pthreads.html#blocking-on-the-main-browser-thread'); | ||
| #if ASSERTIONS || IN_TEST_HARNESS || !MINIMAL_RUNTIME || !ALLOW_BLOCKING_ON_MAIN_THREAD | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, that is a |
||
| #if ENVIRONMENT_MAY_BE_NODE | ||
| if (ENVIRONMENT_IS_NODE) return; | ||
| #endif | ||
|
|
||
| if (ENVIRONMENT_IS_PTHREAD) return; // Blocking in a pthread is fine. | ||
|
|
||
| warnOnce('Blocking on the main thread is very dangerous, see https://emscripten.org/docs/porting/pthreads.html#blocking-on-the-main-browser-thread'); | ||
| #if !ALLOW_BLOCKING_ON_MAIN_THREAD | ||
| abort('Blocking on the main thread is not allowed by default. See https://emscripten.org/docs/porting/pthreads.html#blocking-on-the-main-browser-thread'); | ||
| #endif | ||
|
|
||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In MINIMAL_RUNTIME we do not want to pay code size for |
||
| #endif | ||
| }, | ||
|
|
||
| _emscripten_do_pthread_join__deps: ['_cleanup_thread', '_pthread_testcancel_js', 'emscripten_main_thread_process_queued_calls', 'emscripten_futex_wait', 'emscripten_check_blocking_allowed'], | ||
| _emscripten_do_pthread_join__deps: ['_cleanup_thread', '_pthread_testcancel_js', 'emscripten_main_thread_process_queued_calls', 'emscripten_futex_wait', | ||
| #if ASSERTIONS || IN_TEST_HARNESS || !MINIMAL_RUNTIME || !ALLOW_BLOCKING_ON_MAIN_THREAD | ||
| 'emscripten_check_blocking_allowed' | ||
| #endif | ||
| ], | ||
| _emscripten_do_pthread_join: function(thread, status, block) { | ||
| if (!thread) { | ||
| err('pthread_join attempted on a null thread pointer!'); | ||
|
|
@@ -805,9 +826,11 @@ var LibraryPThread = { | |
| return ERRNO_CODES.EINVAL; // The thread is already detached, can no longer join it! | ||
| } | ||
|
|
||
| if (block && ENVIRONMENT_IS_WEB) { | ||
| #if ASSERTIONS || IN_TEST_HARNESS || !MINIMAL_RUNTIME || !ALLOW_BLOCKING_ON_MAIN_THREAD | ||
| if (block) { | ||
| _emscripten_check_blocking_allowed(); | ||
| } | ||
| #endif | ||
|
|
||
| for (;;) { | ||
| var threadStatus = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.threadStatus }}} ) >> 2); | ||
|
|
@@ -1057,9 +1080,11 @@ var LibraryPThread = { | |
| pthread_cleanup_push: function(routine, arg) { | ||
| if (PThread.exitHandlers === null) { | ||
| PThread.exitHandlers = []; | ||
| #if EXIT_RUNTIME | ||
| if (!ENVIRONMENT_IS_PTHREAD) { | ||
| __ATEXIT__.push(function() { PThread.runExitHandlers(); }); | ||
| } | ||
| #endif | ||
| } | ||
| PThread.exitHandlers.push(function() { {{{ makeDynCall('vi') }}}(routine, arg) }); | ||
| }, | ||
|
|
@@ -1297,7 +1322,7 @@ var LibraryPThread = { | |
| return func.apply(null, _emscripten_receive_on_main_thread_js_callArgs); | ||
| }, | ||
|
|
||
| $establishStackSpaceInJsModule: function(stackTop, stackMax) { | ||
| $establishStackSpace: function(stackTop, stackMax) { | ||
| STACK_BASE = STACKTOP = stackTop; | ||
| STACK_MAX = stackMax; | ||
|
|
||
|
|
@@ -1318,15 +1343,34 @@ var LibraryPThread = { | |
| #if STACK_OVERFLOW_CHECK | ||
| writeStackCookie(); | ||
| #endif | ||
| // Call inside asm.js/wasm module to set up the stack frame for this pthread in asm.js/wasm module scope | ||
| establishStackSpace(stackTop, stackMax); | ||
|
|
||
| #if WASM_BACKEND | ||
| // Call inside wasm module to set up the stack frame for this pthread in asm.js/wasm module scope | ||
| stackRestore(stackTop); | ||
| #else | ||
| // In old asm.js backend, use a dedicated function to establish the stack frame. | ||
| asmJsEstablishStackFrame(stackTop, stackMax); | ||
| #endif | ||
| }, | ||
|
|
||
| // allow pthreads to check if noExitRuntime from worker.js | ||
| $getNoExitRuntime: function() { | ||
| return noExitRuntime; | ||
| }, | ||
|
|
||
| // When using postMessage to send an object, it is processed by the structured clone algorithm. | ||
| // The prototype, and hence methods, on that object is then lost. This function adds back the lost prototype. | ||
| // This does not work with nested objects that has prototypes, but it suffices for WasmSourceMap and WasmOffsetConverter. | ||
| $resetPrototype: function(constructor, attrs) { | ||
| var object = Object.create(constructor.prototype); | ||
| for (var key in attrs) { | ||
| if (attrs.hasOwnProperty(key)) { | ||
| object[key] = attrs[key]; | ||
| } | ||
| } | ||
| return object; | ||
| }, | ||
|
|
||
| // This function is called internally to notify target thread ID that it has messages it needs to | ||
| // process in its message queue inside the Wasm heap. As a helper, the caller must also pass the | ||
| // ID of the main browser thread to this function, to avoid needlessly ping-ponging between JS and | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| #if MEMORYPROFILER | ||
|
|
||
| // Copyright 2015 The Emscripten Authors. All rights reserved. | ||
| // Emscripten is available under two separate licenses, the MIT license and the | ||
| // University of Illinois/NCSA Open Source License. Both these licenses can be | ||
|
|
@@ -597,3 +599,5 @@ var emscriptenMemoryProfiler = { | |
| function memoryprofiler_add_hooks() { emscriptenMemoryProfiler.initialize(); } | ||
|
|
||
| if (typeof Module !== 'undefined' && typeof document !== 'undefined' && typeof window !== 'undefined' && typeof process === 'undefined') emscriptenMemoryProfiler.initialize(); | ||
|
|
||
| #endif | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving this #ifdef was done to be more idiomatic with include guards (the file itself guards, rather than the includer of that file) |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| // These externs are needed for MINIMAL_RUNTIME + USE_PTHREADS + !MODULARIZE | ||
| // This file should go away in the future when worker.js is refactored to live inside the JS module. | ||
|
|
||
| var ENVIRONMENT_IS_PTHREAD; | ||
| /** @suppress {duplicate} */ | ||
| var wasmMemory; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does modularize_instance matter here? (and, why not modularize?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MODULARIZE_INSTANCE creates the Module on the next line, emitting
would be redundant for code size.