Skip to content

Commit

Permalink
Fix keepRuntimeAlive in the case where EXIT_RUNTIME=0 and noExitRunti…
Browse files Browse the repository at this point in the history
…me 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
  • Loading branch information
sbc100 committed Sep 9, 2024
1 parent 5fc175d commit 0fed818
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 5 deletions.
16 changes: 15 additions & 1 deletion src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions test/other/test_pthread_strict.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <pthread.h>
#include <emscripten/console.h>

void _emscripten_thread_set_strongref(pthread_t thread);

pthread_mutex_t ready_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER;
_Atomic bool ready = false;

void* thread_main(void* arg) {
pthread_mutex_lock(&ready_lock);
if (!ready) {
pthread_cond_wait(&ready_cond, &ready_lock);
}
pthread_mutex_unlock(&ready_lock);
emscripten_outf("Hello from thread");
return NULL;
}

int main() {
pthread_t t;
pthread_create(&t, NULL, thread_main, NULL);
pthread_detach(t);
emscripten_outf("thread created, returning from main");

// Make sure the thread output doesn't appear until after the above message
pthread_mutex_lock(&ready_lock);
ready = true;
pthread_cond_signal(&ready_cond);
pthread_mutex_unlock(&ready_lock);

// 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;
}
2 changes: 2 additions & 0 deletions test/other/test_pthread_strict.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
thread created, returning from main
Hello from thread
4 changes: 4 additions & 0 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -12071,6 +12071,10 @@ def test_pthread_reuse(self):
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 <string>')
outputStdin = self.run_process([EMCC, '-x', 'c++', '-dM', '-E', '-'], input="#include <string>", stdout=PIPE).stdout
Expand Down
6 changes: 3 additions & 3 deletions tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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([
Expand Down

0 comments on commit 0fed818

Please sign in to comment.