From 391e679b581e2fc8cc8a59ef012828f0fd580638 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Sun, 10 May 2026 11:01:30 +0200 Subject: [PATCH 1/3] Sync `_emscripten_thread_exit()` with musl's `pthread_exit` --- system/lib/pthread/pthread_create.c | 33 ++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/system/lib/pthread/pthread_create.c b/system/lib/pthread/pthread_create.c index 5fe3d9bdd90f7..88af01e01a972 100644 --- a/system/lib/pthread/pthread_create.c +++ b/system/lib/pthread/pthread_create.c @@ -32,6 +32,8 @@ static void dummy_0() {} weak_alias(dummy_0, __pthread_tsd_run_dtors); +weak_alias(dummy_0, __do_orphaned_stdio_locks); +weak_alias(dummy_0, __dl_thread_cleanup); static void __run_cleanup_handlers() { pthread_t self = __pthread_self(); @@ -306,11 +308,21 @@ void _emscripten_thread_exit(void* result) { // Call into the musl function that runs destructors of all thread-specific data. __pthread_tsd_run_dtors(); + // If this is the main runtime thread, don't proceed with + // termination of the thread, but prepare for exit to call + // atexit handlers. + if (emscripten_is_main_runtime_thread()) { + exit(0); + } + + // At this point we are committed to thread termination. + + // The thread list lock must be AS-safe. __tl_lock(); - /* Process robust list in userspace to handle non-pshared mutexes - * and the detached thread case where the robust list head will - * be invalid when the kernel would process it. */ + // Process robust list in userspace to handle non-pshared mutexes + // and the detached thread case where the robust list head will + // be invalid when the kernel would process it. __vm_lock(); volatile void *volatile *rp; while ((rp=self->robust_list.head) && rp != &self->robust_list.head) { @@ -327,19 +339,18 @@ void _emscripten_thread_exit(void* result) { } __vm_unlock(); - if (!--libc.threads_minus_1) libc.need_locks = 0; + __do_orphaned_stdio_locks(); + __dl_thread_cleanup(); + // Last, unlink thread from the list. This change will not be visible + // until the lock is released via __tl_unlock() below. + if (!--libc.threads_minus_1) libc.need_locks = 0; self->next->prev = self->prev; self->prev->next = self->next; self->prev = self->next = self; __tl_unlock(); - if (emscripten_is_main_runtime_thread()) { - exit(0); - return; - } - // Not hosting a pthread anymore in this worker set __pthread_self to NULL __set_thread_state(NULL, 0, 0, 1); @@ -357,8 +368,10 @@ void _emscripten_thread_exit(void* result) { // When dynamic linking is enabled we need to keep track of zombie threads _emscripten_thread_exit_joinable(self); #endif + + // Wake any joiner. a_store(&self->detach_state, DT_EXITED); - __wake(&self->detach_state, 1, 1); // Wake any joiner. + __wake(&self->detach_state, 1, 1); } } From bc98a7476499e0367f1d0ea7bc0f73c28af58502 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Sun, 10 May 2026 09:03:52 +0000 Subject: [PATCH 2/3] Automatic rebaseline of codesize expectations. NFC This is an automatic change generated by tools/maint/rebaseline_tests.py. The following (2) test expectation files were updated by running the tests with `--rebaseline`: ``` codesize/test_codesize_minimal_pthreads.json: 26419 => 26419 [+0 bytes / +0.00%] codesize/test_codesize_minimal_pthreads_memgrowth.json: 26829 => 26829 [+0 bytes / +0.00%] Average change: +0.00% (+0.00% - +0.00%) ``` --- test/codesize/test_codesize_minimal_pthreads.json | 4 ++-- test/codesize/test_codesize_minimal_pthreads_memgrowth.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/codesize/test_codesize_minimal_pthreads.json b/test/codesize/test_codesize_minimal_pthreads.json index 204c9a5c926e0..6edabda270871 100644 --- a/test/codesize/test_codesize_minimal_pthreads.json +++ b/test/codesize/test_codesize_minimal_pthreads.json @@ -2,9 +2,9 @@ "a.out.js": 7372, "a.out.js.gz": 3594, "a.out.nodebug.wasm": 19047, - "a.out.nodebug.wasm.gz": 8795, + "a.out.nodebug.wasm.gz": 8796, "total": 26419, - "total_gz": 12389, + "total_gz": 12390, "sent": [ "a (memory)", "b (exit)", diff --git a/test/codesize/test_codesize_minimal_pthreads_memgrowth.json b/test/codesize/test_codesize_minimal_pthreads_memgrowth.json index f2a49a3541479..3dfcb4c6c7ff3 100644 --- a/test/codesize/test_codesize_minimal_pthreads_memgrowth.json +++ b/test/codesize/test_codesize_minimal_pthreads_memgrowth.json @@ -2,9 +2,9 @@ "a.out.js": 7781, "a.out.js.gz": 3797, "a.out.nodebug.wasm": 19048, - "a.out.nodebug.wasm.gz": 8796, + "a.out.nodebug.wasm.gz": 8797, "total": 26829, - "total_gz": 12593, + "total_gz": 12594, "sent": [ "a (memory)", "b (exit)", From 820f24890517cc72b747a252645028f0eff2e000 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Sun, 10 May 2026 19:43:57 +0200 Subject: [PATCH 3/3] Use C-style comments for musl code --- system/lib/pthread/pthread_create.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/system/lib/pthread/pthread_create.c b/system/lib/pthread/pthread_create.c index 88af01e01a972..959f872ba0093 100644 --- a/system/lib/pthread/pthread_create.c +++ b/system/lib/pthread/pthread_create.c @@ -315,14 +315,14 @@ void _emscripten_thread_exit(void* result) { exit(0); } - // At this point we are committed to thread termination. + /* At this point we are committed to thread termination. */ - // The thread list lock must be AS-safe. + /* The thread list lock must be AS-safe. */ __tl_lock(); - // Process robust list in userspace to handle non-pshared mutexes - // and the detached thread case where the robust list head will - // be invalid when the kernel would process it. + /* Process robust list in userspace to handle non-pshared mutexes + * and the detached thread case where the robust list head will + * be invalid when the kernel would process it. */ __vm_lock(); volatile void *volatile *rp; while ((rp=self->robust_list.head) && rp != &self->robust_list.head) { @@ -342,8 +342,8 @@ void _emscripten_thread_exit(void* result) { __do_orphaned_stdio_locks(); __dl_thread_cleanup(); - // Last, unlink thread from the list. This change will not be visible - // until the lock is released via __tl_unlock() below. + /* Last, unlink thread from the list. This change will not be visible + * until the lock is released via __tl_unlock() below. */ if (!--libc.threads_minus_1) libc.need_locks = 0; self->next->prev = self->prev; self->prev->next = self->next; @@ -354,8 +354,8 @@ void _emscripten_thread_exit(void* result) { // Not hosting a pthread anymore in this worker set __pthread_self to NULL __set_thread_state(NULL, 0, 0, 1); - // This atomic potentially competes with a concurrent pthread_detach - // call; the loser is responsible for freeing thread resources. + /* This atomic potentially competes with a concurrent pthread_detach + * call; the loser is responsible for freeing thread resources. */ int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING); if (state == DT_DETACHED) { @@ -369,7 +369,7 @@ void _emscripten_thread_exit(void* result) { _emscripten_thread_exit_joinable(self); #endif - // Wake any joiner. + /* Wake any joiner. */ a_store(&self->detach_state, DT_EXITED); __wake(&self->detach_state, 1, 1); }