diff --git a/src/library.js b/src/library.js index 0e58ea40ef0a0..ef05b87916cea 100644 --- a/src/library.js +++ b/src/library.js @@ -2136,10 +2136,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 0000000000000..4240ccdd169eb --- /dev/null +++ b/test/other/test_no_exit_runtime_strict.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +// Verify that `keepRuntimeAlive()` always returns true by default (i.e. when +// EXIT_RUNTIME=0). + +EM_JS_DEPS(deps, "$keepRuntimeAlive"); + +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(); + }); + 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 0000000000000..94f988be81500 --- /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/other/test_unoptimized_code_size_strict.js.size b/test/other/test_unoptimized_code_size_strict.js.size index ca751adf3e21e..3bc36694fd024 100644 --- a/test/other/test_unoptimized_code_size_strict.js.size +++ b/test/other/test_unoptimized_code_size_strict.js.size @@ -1 +1 @@ -53834 +53730 diff --git a/test/test_other.py b/test/test_other.py index 2e5754a0b5f3c..f8d7a5c7f993e 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -4997,6 +4997,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 44fee4acef50e..898188fce10ec 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) @@ -918,6 +915,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([