Proxy exitJS synchronously, just like the low level proc_exit This simplifies the code and reduces code size. Add testing of both `exit` and `_exit` in test_pthread_exit_runtime.
diff --git a/emcc.py b/emcc.py index ae1db63..fd47555 100755 --- a/emcc.py +++ b/emcc.py
@@ -1677,9 +1677,6 @@ '__dl_seterr', ] - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [ - '$exitOnMainThread', - ] # Some symbols are required by worker.js. # Because emitDCEGraph only considers the main js file, and not worker.js # we have explicitly mark these symbols as user-exported so that they will
diff --git a/src/library.js b/src/library.js index cb8af40..249a0ec 100644 --- a/src/library.js +++ b/src/library.js
@@ -61,6 +61,11 @@ }, #if !MINIMAL_RUNTIME + // Handles exiting the entire program. This function only ever runs on the + // main thread and will be proxied synchronously when called from a worker. + // In either case this function never returns. + // Programs that call the lower level `_exit` end will bypass this code and + $exitJS__proxy: 'sync', $exitJS__docs: '/** @param {boolean|number=} implicit */', $exitJS__deps: ['proc_exit'], $exitJS: function(status, implicit) { @@ -70,26 +75,12 @@ checkUnflushedContent(); #endif // ASSERTIONS && !EXIT_RUNTIME -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) { - // implict exit can never happen on a pthread -#if ASSERTIONS - assert(!implicit); +#if PROXY_TO_PTHREAD + {{{ runtimeKeepalivePop() }}}; #endif -#if PTHREADS_DEBUG - dbg('Pthread ' + ptrToString(_pthread_self()) + ' called exit(), posting exitOnMainThread.'); +#if USE_PTHREADS && PTHREADS_DEBUG + dbg('exit called: keepRuntimeAlive=' + keepRuntimeAlive() + ' (counter=' + runtimeKeepaliveCounter + ')'); #endif - // When running in a pthread we propagate the exit back to the main thread - // where it can decide if the whole process should be shut down or not. - // The pthread may have decided not to exit its own runtime, for example - // because it runs a main loop, but that doesn't affect the main thread. - exitOnMainThread(status); - throw 'unwind'; - } -#if PTHREADS_DEBUG - err('main thread called exit: keepRuntimeAlive=' + keepRuntimeAlive() + ' (counter=' + runtimeKeepaliveCounter + ')'); -#endif // PTHREADS_DEBUG -#endif // USE_PTHREADS #if EXIT_RUNTIME if (!keepRuntimeAlive()) {
diff --git a/src/library_pthread.js b/src/library_pthread.js index 3255dc0..511fce6 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js
@@ -928,28 +928,8 @@ return 0; }, - // This function is call by a pthread to signal that exit() was called and - // that the entire process should exit. - // This function is always called from a pthread, but is executed on the - // main thread due the __proxy attribute. - $exitOnMainThread__deps: ['exit', -#if !MINIMAL_RUNTIME - '$handleException', -#endif - ], - $exitOnMainThread__proxy: 'async', - $exitOnMainThread: function(returnCode) { -#if PTHREADS_DEBUG - dbg('exitOnMainThread'); -#endif -#if PROXY_TO_PTHREAD - {{{ runtimeKeepalivePop() }}}; -#endif - _exit(returnCode); - }, - emscripten_proxy_to_main_thread_js__deps: ['$withStackSave', '_emscripten_run_in_main_runtime_thread_js'], - emscripten_proxy_to_main_thread_js__docs: '/** @type{function(number, (number|boolean), ...(number|boolean))} */', + emscripten_proxy_to_main_thread_js__docs: '/** @type{function(number, (number|boolean), ...(number|boolean|undefined))} */', emscripten_proxy_to_main_thread_js: function(index, sync) { // Additional arguments are passed after those two, which are the actual // function arguments.
diff --git a/src/library_wasi.js b/src/library_wasi.js index 97e3cbc..344f28d 100644 --- a/src/library_wasi.js +++ b/src/library_wasi.js
@@ -15,6 +15,10 @@ proc_exit__deps: ['$ExitStatus'], #endif + // Handles exiting the entire program. This function only ever runs on the + // main thread and will be proxied synchronously when called from a worker. + // (wrapSyscallFunction handles the proxying). In either case this function + // never returns. proc_exit__nothrow: true, proc_exit__sig: 'vi', proc_exit: function(code) {
diff --git a/test/core/pthread/test_pthread_exit_runtime.c b/test/core/pthread/test_pthread_exit_runtime.c index b90e2b4..91d4d4b 100644 --- a/test/core/pthread/test_pthread_exit_runtime.c +++ b/test/core/pthread/test_pthread_exit_runtime.c
@@ -4,21 +4,34 @@ #include <pthread.h> #include <stdlib.h> #include <stdio.h> +#include <unistd.h> #include <emscripten/emscripten.h> pthread_t t; -void* thread_main_exit(void* arg) { - printf("calling exit\n"); - exit(42); -} - // This location should never get set to true. // We verify that it false from JS after the program exits. -atomic_bool join_returned = false; +// If the main thread ever returns from `join` or the worker thread returns +// from `exit` this gets set to true, which would be bug. +atomic_bool fail = false; -EMSCRIPTEN_KEEPALIVE atomic_bool* join_returned_address() { - return &join_returned; +EMSCRIPTEN_KEEPALIVE atomic_bool* fail_address() { + return &fail; +} + +void* thread_main_exit(void* arg) { + // This test run with both _EXIT defined and without to test low level + // and high level exit. +#ifdef _EXIT + printf("calling _exit\n"); + _exit(43); +#else + printf("calling exit\n"); + exit(42); +#endif + fail = true; + printf("after exit -- should never get here\n"); + __builtin_trap(); } int main() { @@ -30,7 +43,7 @@ assert(rc == 0); // pthread_join should never return because the runtime should // exit first. - join_returned = true; + fail = true; printf("done join %d -- should never get here\n", rc); __builtin_trap(); }
diff --git a/test/core/pthread/test_pthread_exit_runtime.pre.js b/test/core/pthread/test_pthread_exit_runtime.pre.js index 646b3a5..bce2806 100644 --- a/test/core/pthread/test_pthread_exit_runtime.pre.js +++ b/test/core/pthread/test_pthread_exit_runtime.pre.js
@@ -1,7 +1,7 @@ var address = 0; Module.onRuntimeInitialized = function() { - address = Module['_join_returned_address'](); + address = Module['_fail_address'](); assert(address); assert(HEAP8[address] == 0); } @@ -10,7 +10,7 @@ out('onExit status: ' + status); // Verify that the join never returned assert(address); - assert(HEAP8[address] == 0, 'pthread_join should not have returned!'); + assert(HEAP8[address] == 0, 'fail should never get set!'); if (typeof reportResultToServer !== 'undefined') { reportResultToServer('onExit status: ' + status); }
diff --git a/test/core/pthread/test_pthread_exit_runtime_immediate.out b/test/core/pthread/test_pthread_exit_runtime_immediate.out new file mode 100644 index 0000000..12aa1be --- /dev/null +++ b/test/core/pthread/test_pthread_exit_runtime_immediate.out
@@ -0,0 +1 @@ +onExit status: 43
diff --git a/test/other/metadce/test_metadce_minimal_pthreads.jssize b/test/other/metadce/test_metadce_minimal_pthreads.jssize index 2a37d47..5c94783 100644 --- a/test/other/metadce/test_metadce_minimal_pthreads.jssize +++ b/test/other/metadce/test_metadce_minimal_pthreads.jssize
@@ -1 +1 @@ -15855 +15687
diff --git a/test/test_core.py b/test/test_core.py index 3e161ad..cfd5237 100644 --- a/test/test_core.py +++ b/test/test_core.py
@@ -9295,8 +9295,12 @@ def test_pthread_exit_process(self): self.set_setting('PROXY_TO_PTHREAD') self.set_setting('EXIT_RUNTIME') - self.emcc_args += ['-DEXIT_RUNTIME', '--pre-js', test_file('core/pthread/test_pthread_exit_runtime.pre.js')] + self.emcc_args += ['--pre-js', test_file('core/pthread/test_pthread_exit_runtime.pre.js')] self.do_run_in_out_file_test('core/pthread/test_pthread_exit_runtime.c', assert_returncode=42) + # Run the same test again but with `_exit` rather than `exit` + self.emcc_args += ['-D_EXIT'] + self.do_run_in_out_file_test('core/pthread/test_pthread_exit_runtime.c', assert_returncode=43, + out_suffix='_immediate') @node_pthreads def test_pthread_keepalive(self):