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):