From 6df94ddc56611e450bd646b5798a031f96b26359 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Mon, 9 Sep 2024 10:31:49 -0700 Subject: [PATCH] Fix keepRuntimeAlive in the case where EXIT_RUNTIME=0 and noExitRuntime is not referenced Essentially the `keepRuntimeAlive` was relying on the `noExitRuntime` variable being set based on `EXIT_RUNTIME` but when `noExitRuntime` was absent the `EXIT_RUNTIME` settings was being ignored and the runtime was exiting even though `EXIT_RUNTIME=0` was set (the default). Fixes: #20636 --- src/library.js | 16 +++++++++++++++- test/other/test_no_exit_runtime_strict.c | 22 ++++++++++++++++++++++ test/other/test_no_exit_runtime_strict.out | 2 ++ test/test_other.py | 3 +++ tools/link.py | 6 +++--- 5 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 test/other/test_no_exit_runtime_strict.c create mode 100644 test/other/test_no_exit_runtime_strict.out diff --git a/src/library.js b/src/library.js index 09b4f4fb607d..93d0bf3dddac 100644 --- a/src/library.js +++ b/src/library.js @@ -2142,10 +2142,24 @@ addToLibrary({ $runtimeKeepaliveCounter__internal: true, $runtimeKeepaliveCounter: 0, - $keepRuntimeAlive__deps: ['$runtimeKeepaliveCounter'], #if isSymbolNeeded('$noExitRuntime') + // If the `noExitRuntime` symbol is included in the build then + // keepRuntimeAlive is always conditional since its state can change + // at runtime. + $keepRuntimeAlive__deps: ['$runtimeKeepaliveCounter'], $keepRuntimeAlive: () => noExitRuntime || runtimeKeepaliveCounter > 0, +#elif !EXIT_RUNTIME + // When `noExitRuntime` is not include and EXIT_RUNTIME=0 then we know the + // runtime can never exit (i.e. should always be kept alive). + // However for pthreads we always default to allowing the runtime to exit + // otherwise threads never exit and are not joinable. +#if PTHREADS + $keepRuntimeAlive: () => !ENVIRONMENT_IS_PTHREAD, #else + $keepRuntimeAlive: () => true, +#endif +#else + $keepRuntimeAlive__deps: ['$runtimeKeepaliveCounter'], $keepRuntimeAlive: () => runtimeKeepaliveCounter > 0, #endif diff --git a/test/other/test_no_exit_runtime_strict.c b/test/other/test_no_exit_runtime_strict.c new file mode 100644 index 000000000000..4885d1446b63 --- /dev/null +++ b/test/other/test_no_exit_runtime_strict.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +// Verify that `keepRuntimeAlive()` always returns true by default (i.e. when +// EXIT_RUNTIME=0). + +EM_JS(void, timeout_func, (), { + console.log("timeout_func: keepRuntimeAlive() ->", keepRuntimeAlive()); + // once this timeout is done the node process should exit with 0 +}); + +int main() { + int keep_alive = EM_ASM_INT({ + setTimeout(timeout_func); + return keepRuntimeAlive(); + }, emscripten_force_exit); + printf("main done: %d\n", keep_alive); + assert(keep_alive == 1); + return 0; +} diff --git a/test/other/test_no_exit_runtime_strict.out b/test/other/test_no_exit_runtime_strict.out new file mode 100644 index 000000000000..94f988be8150 --- /dev/null +++ b/test/other/test_no_exit_runtime_strict.out @@ -0,0 +1,2 @@ +main done: 1 +timeout_func: keepRuntimeAlive() -> true diff --git a/test/test_other.py b/test/test_other.py index 8ed5c4786f71..83361d52b98c 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -5038,6 +5038,9 @@ def test(cxx, no_exit, assertions, flush=0, keepalive=0, filesystem=1): for flush in (0, 1): test(cxx, no_exit, assertions, flush) + def test_no_exit_runtime_strict(self): + self.do_other_test('test_no_exit_runtime_strict.c', emcc_args=['-sSTRICT']) + def test_extra_opt_levels(self): # Opt levels that we don't tend to test elsewhere for opt in ('-Og', '-Ofast'): diff --git a/tools/link.py b/tools/link.py index 0892b9f15368..afece066b3e6 100644 --- a/tools/link.py +++ b/tools/link.py @@ -658,9 +658,6 @@ def phase_linker_setup(options, state, newargs): # Add `#!` line to output JS and make it executable. options.executable = True - if 'noExitRuntime' in settings.INCOMING_MODULE_JS_API: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.append('$noExitRuntime') - if settings.OPT_LEVEL >= 1: default_setting('ASSERTIONS', 0) @@ -914,6 +911,9 @@ def phase_linker_setup(options, state, newargs): else: default_setting('INCOMING_MODULE_JS_API', []) + if 'noExitRuntime' in settings.INCOMING_MODULE_JS_API: + settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.append('$noExitRuntime') + if not settings.MINIMAL_RUNTIME and not settings.STRICT: # Export the HEAP object by default, when not running in STRICT mode settings.EXPORTED_RUNTIME_METHODS.extend([