Skip to content

Commit 1559315

Browse files
committed
Fix Node.js exit for library builds with pthreads
When building without a main function (a library build), and using pthreads to do background work, the main Node.js thread has no event loop work to keep Node.js alive. The workers are also unreferenced when they start executing, so Node.js will exit prematurely before the background thread can finish its work. To prevent this, change the worker reference logic so that we only `unref()` workers upon execution if `HAS_MAIN` is enabled. For library builds, we keep them referenced (`worker.ref()`) to keep the process alive while they are active. We also ensure idle workers returned to the pool are `unref()`'d across all Node builds (not just with `PROXY_TO_PTHREAD`) so that an idle pool doesn't leak references preventing exit. Fixes #23092
1 parent 3051725 commit 1559315

5 files changed

Lines changed: 47 additions & 4 deletions

File tree

src/lib/libpthread.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,11 @@ var LibraryPThread = {
208208
// worker pool as an unused worker.
209209
worker.pthread_ptr = 0;
210210

211-
#if ENVIRONMENT_MAY_BE_NODE && PROXY_TO_PTHREAD
211+
#if ENVIRONMENT_MAY_BE_NODE
212212
if (ENVIRONMENT_IS_NODE) {
213-
// Once the proxied main thread has finished, mark it as weakly
213+
// Once the worker is returned to the pool, mark it as weakly
214214
// referenced so that its existence does not prevent Node.js from
215-
// exiting. This has no effect if the worker is already weakly
215+
// exiting. This has no effect if the worker is already weakly
216216
// referenced.
217217
worker.unref();
218218
}
@@ -687,7 +687,7 @@ var LibraryPThread = {
687687
msg.moduleCanvasId = threadParams.moduleCanvasId;
688688
msg.offscreenCanvases = threadParams.offscreenCanvases;
689689
#endif
690-
#if ENVIRONMENT_MAY_BE_NODE
690+
#if ENVIRONMENT_MAY_BE_NODE && HAS_MAIN
691691
if (ENVIRONMENT_IS_NODE) {
692692
// Mark worker as weakly referenced once we start executing a pthread,
693693
// so that its existence does not prevent Node.js from exiting. This
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <pthread.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <assert.h>
5+
#include <emscripten.h>
6+
#include <emscripten/threading.h>
7+
8+
void* worker(void* arg) {
9+
printf("worker starting\n");
10+
fflush(stdout);
11+
emscripten_thread_sleep(100);
12+
13+
// proxy back to the main thread
14+
MAIN_THREAD_ASYNC_EM_ASM({
15+
resolve();
16+
});
17+
return NULL;
18+
}
19+
20+
EMSCRIPTEN_KEEPALIVE
21+
void create_thread_async() {
22+
pthread_t thread;
23+
int rc = pthread_create(&thread, NULL, worker, NULL);
24+
assert(rc == 0);
25+
pthread_detach(thread);
26+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
initialized
2+
worker starting
3+
exiting
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
let { promise, resolve } = Promise.withResolvers();
2+
promise.then(() => console.log('exiting'));
3+
4+
Module['onRuntimeInitialized'] = function() {
5+
console.log('initialized');
6+
_create_thread_async();
7+
};

test/test_core.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9164,6 +9164,13 @@ def test_pthread_keepalive(self):
91649164
def test_pthread_weak_ref(self):
91659165
self.do_core_test('pthread/test_pthread_weak_ref.c')
91669166

9167+
@no_asan('asan exits the runtime')
9168+
@requires_pthreads
9169+
def test_pthread_exit_library(self):
9170+
# Test that Node.js doesn't exit while there are still pthreads running when there is no main function.
9171+
self.cflags += ['--pre-js', test_file('core/pthread/test_pthread_exit_library.pre.js')]
9172+
self.do_core_test('pthread/test_pthread_exit_library.c')
9173+
91679174
@requires_pthreads
91689175
def test_pthread_exit_main(self):
91699176
self.do_core_test('pthread/test_pthread_exit_main.c')

0 commit comments

Comments
 (0)