diff --git a/src/library.js b/src/library.js index 09b4f4fb607d1..93d0bf3dddac6 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/src/library_pthread.js b/src/library_pthread.js index 956413960afb3..c435a02283746 100644 --- a/src/library_pthread.js +++ b/src/library_pthread.js @@ -203,7 +203,7 @@ var LibraryPThread = { // worker pool as an unused worker. worker.pthread_ptr = 0; -#if ENVIRONMENT_MAY_BE_NODE && PROXY_TO_PTHREAD +#if ENVIRONMENT_MAY_BE_NODE if (ENVIRONMENT_IS_NODE) { // Once the proxied main thread has finished, mark it as weakly // referenced so that its existence does not prevent Node.js from diff --git a/test/other/test_pthread_strict.c b/test/other/test_pthread_strict.c new file mode 100644 index 0000000000000..26c42fde0fb92 --- /dev/null +++ b/test/other/test_pthread_strict.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +void _emscripten_thread_set_strongref(pthread_t thread); + +void* thread_main(void* arg) { + usleep(100000); + emscripten_outf("Hello from thread"); + return NULL; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, thread_main, NULL); + pthread_detach(t); + emscripten_outf("returning from main"); + + // This needed under node to make sure the process doesn't exit before the + // thread is done. + // TODO(sbc): We probably want all running threads to keep node from exiting + // like they would under POSIX. + _emscripten_thread_set_strongref(t); + return 0; +} + diff --git a/test/other/test_pthread_strict.out b/test/other/test_pthread_strict.out new file mode 100644 index 0000000000000..43632997b313f --- /dev/null +++ b/test/other/test_pthread_strict.out @@ -0,0 +1,2 @@ +returning from main +Hello from thread diff --git a/test/test_other.py b/test/test_other.py index 1c61d55ba1777..5a1099f1a3d34 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -12064,17 +12064,21 @@ def test_pthread_asyncify(self): self.set_setting('PTHREADS_DEBUG') self.set_setting('ASYNCIFY') self.set_setting('PTHREAD_POOL_SIZE', 2) - self.do_run_in_out_file_test('other/test_pthread_asyncify.c') + self.do_other_test('test_pthread_asyncify.c') @node_pthreads def test_pthread_reuse(self): self.set_setting('PTHREAD_POOL_SIZE', 1) - self.do_run_in_out_file_test('other/test_pthread_reuse.c') + self.do_other_test('test_pthread_reuse.c') @node_pthreads def test_pthread_relocatable(self): self.do_run_in_out_file_test('hello_world.c', emcc_args=['-sRELOCATABLE']) + @node_pthreads + def test_pthread_strict(self): + self.do_other_test('test_pthread_strict.c', emcc_args=['-pthread', '-sPTHREAD_POOL_SIZE=1', '-sSTRICT']) + def test_stdin_preprocess(self): create_file('temp.h', '#include ') outputStdin = self.run_process([EMCC, '-x', 'c++', '-dM', '-E', '-'], input="#include ", stdout=PIPE).stdout diff --git a/tools/link.py b/tools/link.py index 04eb551353c0c..2685cd3e4e943 100644 --- a/tools/link.py +++ b/tools/link.py @@ -651,9 +651,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) @@ -907,6 +904,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([