diff --git a/src/lib/libcore.js b/src/lib/libcore.js index 928777d174877..2e93d64553d15 100644 --- a/src/lib/libcore.js +++ b/src/lib/libcore.js @@ -2169,6 +2169,9 @@ addToLibrary({ #if PTHREADS '_emscripten_thread_exit', #endif +#if PROXY_TO_PTHREAD + '$isProxiedMainThread', +#endif #if RUNTIME_DEBUG >= 2 '$runtimeKeepaliveCounter', #endif @@ -2188,7 +2191,11 @@ addToLibrary({ #endif try { #if PTHREADS - if (ENVIRONMENT_IS_PTHREAD) { + if (ENVIRONMENT_IS_PTHREAD +#if PROXY_TO_PTHREAD + && !isProxiedMainThread +#endif + ) { // exit the current thread, but only if there is one active. // TODO(https://github.com/emscripten-core/emscripten/issues/25076): // Unify this check with the runtimeExited check above diff --git a/src/lib/libpthread.js b/src/lib/libpthread.js index 7e68b6e14d7d8..2fb3349cf0419 100644 --- a/src/lib/libpthread.js +++ b/src/lib/libpthread.js @@ -986,6 +986,16 @@ var LibraryPThread = { _exit(returnCode); }, +#if PROXY_TO_PTHREAD + $isProxiedMainThread__internal: true, + $isProxiedMainThread: false, + + __emscripten_set_proxied_main_thread__deps: ['$isProxiedMainThread'], + __emscripten_set_proxied_main_thread: () => { + isProxiedMainThread = true; + }, +#endif + #if MEMORY64 // Calls proxyToMainThread but returns a bigint rather than a number $proxyToMainThreadPtr__deps: ['$proxyToMainThread'], @@ -1144,6 +1154,10 @@ var LibraryPThread = { #if !MINIMAL_RUNTIME '$keepRuntimeAlive', '$runtimeKeepaliveCounter', +#endif +#if PROXY_TO_PTHREAD + '$isProxiedMainThread', + 'exit', #endif ], $invokeEntryPoint: {{{ asyncIf(ASYNCIFY == 2) }}}(ptr, arg) => { @@ -1166,6 +1180,9 @@ var LibraryPThread = { noExitRuntime = 0; #endif #endif +#if PROXY_TO_PTHREAD + isProxiedMainThread = false; +#endif #if MAIN_MODULE // Before we call the thread entry point, make sure any shared libraries @@ -1190,13 +1207,18 @@ var LibraryPThread = { #endif function finish(result) { #if !MINIMAL_RUNTIME + EXITSTATUS = result; // In MINIMAL_RUNTIME the noExitRuntime concept does not apply to // pthreads. To exit a pthread with live runtime, use the function // emscripten_unwind_to_js_event_loop() in the pthread body. if (keepRuntimeAlive()) { - EXITSTATUS = result; return; } +#endif +#if PROXY_TO_PTHREAD + if (isProxiedMainThread) { + _exit(result); + } #endif __emscripten_thread_exit(result); } diff --git a/system/lib/libc/crt1_proxy_main.c b/system/lib/libc/crt1_proxy_main.c index 2d178ca2e473a..37d5008beee8f 100644 --- a/system/lib/libc/crt1_proxy_main.c +++ b/system/lib/libc/crt1_proxy_main.c @@ -8,10 +8,11 @@ #include #include +#include + #include #include #include -#include #include "threading_internal.h" @@ -27,12 +28,10 @@ weak int __main_void(void) { static void* _main_thread(void* param) { // This is the main runtime thread for the application. emscripten_set_thread_name(pthread_self(), "Application main thread"); + __emscripten_set_proxied_main_thread(); // Will either call user's __main_void or weak version above. int rtn = __main_void(); - if (!emscripten_runtime_keepalive_check()) { - exit(rtn); - } - return NULL; + return (void*)(intptr_t)rtn; } EMSCRIPTEN_KEEPALIVE int _emscripten_proxy_main(int argc, char** argv) { diff --git a/system/lib/pthread/threading_internal.h b/system/lib/pthread/threading_internal.h index 097595e3ba6b9..73df3a0ab057b 100644 --- a/system/lib/pthread/threading_internal.h +++ b/system/lib/pthread/threading_internal.h @@ -63,6 +63,8 @@ void _emscripten_init_main_thread_js(void* tb); void _emscripten_thread_profiler_enable(); void _emscripten_thread_cleanup(pthread_t thread); +void __emscripten_set_proxied_main_thread(void); + hidden void* _emscripten_tls_init(void); hidden void _emscripten_tls_free(void); diff --git a/test/other/test_proxied_main_keepalive_exit.c b/test/other/test_proxied_main_keepalive_exit.c new file mode 100644 index 0000000000000..949a89bde444a --- /dev/null +++ b/test/other/test_proxied_main_keepalive_exit.c @@ -0,0 +1,47 @@ +// Proxied main must exit with its status after keepalives run. +#include +#include +#include +#include + +void at_exit(void) { printf("done\n"); } + +#ifdef MODE_CLEARED +int long_id; + +void never(void* arg) { + printf("cleared callback ran\n"); + abort(); +} +#endif + +void fired(void* arg) { + printf("fired\n"); +#ifdef MODE_CLEARED + emscripten_clear_timeout(long_id); +#elif defined(MODE_FORCE_EXIT) + // exit() beats a waiting return. + emscripten_force_exit(7); +#elif defined(MODE_EXIT) + exit(7); +#endif +} + +int main(void) { +#ifdef MODE_NEGATIVE + // Check the value, not just the code. + MAIN_THREAD_EM_ASM({ Module['onExit'] = (c) => { out('exited:' + c); }; }); +#else + MAIN_THREAD_EM_ASM({ Module['onExit'] = () => { out('exited'); }; }); +#endif + atexit(at_exit); +#ifdef MODE_CLEARED + long_id = emscripten_set_timeout(never, 10000, NULL); +#endif + emscripten_set_timeout(fired, 10, NULL); +#ifdef MODE_NEGATIVE + return -1; +#else + return 3; +#endif +} diff --git a/test/test_other.py b/test/test_other.py index bd63374a7c13b..7eba2116a1916 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -11603,6 +11603,21 @@ def test_proxy_to_pthread_stack(self): '-sSTACK_SIZE=128kb', '-sEXIT_RUNTIME', '--profiling-funcs']) + @requires_pthreads + @parameterized({ + '': ([], 3, 'fired\ndone\nexited\n'), + 'cleared': (['-DMODE_CLEARED'], 3, 'fired\ndone\nexited\n'), + 'force_exit': (['-DMODE_FORCE_EXIT'], 7, 'fired\ndone\nexited\n'), + 'exit': (['-DMODE_EXIT'], 7, 'fired\ndone\nexited\n'), + 'negative': (['-DMODE_NEGATIVE'], NON_ZERO, 'fired\ndone\nexited:-1\n'), + }) + def test_proxied_main_keepalive_exit(self, cflags, returncode, expected): + # See https://github.com/emscripten-core/emscripten/issues/27721 + # Proxied main that returns with a keepalive must exit with its status. + self.do_runf('other/test_proxied_main_keepalive_exit.c', expected, + cflags=['-pthread', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'] + cflags, + assert_returncode=returncode) + @crossplatform @no_windows('ptys and select are not available on windows') def test_color_diagnostics(self):