From 31e96908e8db0a0ba205f894d715d4b18a2d1f97 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Tue, 13 Jan 2026 16:49:54 -0800 Subject: [PATCH] Return ENOTSUP from pthread_create when threads are not available. See https://github.com/WebAssembly/wasi-libc/pull/716 --- ChangeLog.md | 2 ++ system/lib/pthread/library_pthread_stub.c | 6 ++++- .../test_codesize_hello_dylink_all.json | 4 ++-- test/other/test_pthread_stub.c | 4 ++-- ...supported.cpp => test_pthread_supported.c} | 23 ++++++++++--------- test/test_browser.py | 2 +- test/test_other.py | 2 +- 7 files changed, 25 insertions(+), 18 deletions(-) rename test/pthread/{test_pthread_supported.cpp => test_pthread_supported.c} (59%) diff --git a/ChangeLog.md b/ChangeLog.md index 44123022ac6fa..7695aca6886c3 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -20,6 +20,8 @@ See docs/process.md for more on how version tagging works. 4.0.24 (in development) ----------------------- +- Calling pthread_create in a single-threaded build will now return ENOTSUP + rather then EAGAIN. (#26105) - compiler-rt and libunwind were updated to LLVM 21.1.8. (#26036 and #26045) - A new `-sEXECUTABLE` setting was added which adds a #! line to the resulting JavaScript and makes it executable. This setting defaults to true when the diff --git a/system/lib/pthread/library_pthread_stub.c b/system/lib/pthread/library_pthread_stub.c index 0a5bf624f4ea2..7d1a8100bf97f 100644 --- a/system/lib/pthread/library_pthread_stub.c +++ b/system/lib/pthread/library_pthread_stub.c @@ -92,7 +92,11 @@ int pthread_barrier_destroy(pthread_barrier_t* mutex) { return 0; } int pthread_barrier_wait(pthread_barrier_t* mutex) { return 0; } int __pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) { - return EAGAIN; + // ENOTSUP, while not mentioned in the pthread_create docs, does better + // describe the situation. + // See https://github.com/WebAssembly/wasi-libc/pull/716 for discussion + // on this error code vs, for example, EAGAIN. + return ENOTSUP; } weak_alias(__pthread_create, emscripten_builtin_pthread_create); diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index 0c51070991c3e..02ed3a0710407 100644 --- a/test/codesize/test_codesize_hello_dylink_all.json +++ b/test/codesize/test_codesize_hello_dylink_all.json @@ -1,7 +1,7 @@ { "a.out.js": 244848, - "a.out.nodebug.wasm": 577875, - "total": 822723, + "a.out.nodebug.wasm": 577876, + "total": 822724, "sent": [ "IMG_Init", "IMG_Load", diff --git a/test/other/test_pthread_stub.c b/test/other/test_pthread_stub.c index 0def71e12cca5..7a01191c7a587 100644 --- a/test/other/test_pthread_stub.c +++ b/test/other/test_pthread_stub.c @@ -22,12 +22,12 @@ void* start_pthread(void* arg) { } #define CHECK(X, EXPECTED) rtn = X; if (rtn != EXPECTED) printf(#X " returned %s\n", strerror(rtn)); assert(rtn == EXPECTED) -#define CHECK_FAIL(X) CHECK(X, EAGAIN) +#define CHECK_FAIL(X) CHECK(X, ENOTSUP) #define CHECK_SUCCESS(X) CHECK(X, 0) #define CHECK_C11(X, expected) rtn = X; if (rtn != expected) printf(#X " returned %d\n", rtn); assert(rtn == expected) #define CHECK_C11_SUCCESS(X) CHECK_C11(X, thrd_success) -#define CHECK_C11_FAIL(X) CHECK_C11(X, thrd_nomem) +#define CHECK_C11_FAIL(X) CHECK_C11(X, thrd_error) void test_c11_threads() { printf("test_c11_threads\n"); diff --git a/test/pthread/test_pthread_supported.cpp b/test/pthread/test_pthread_supported.c similarity index 59% rename from test/pthread/test_pthread_supported.cpp rename to test/pthread/test_pthread_supported.c index 60c07efb2107e..2afade934fb91 100644 --- a/test/pthread/test_pthread_supported.cpp +++ b/test/pthread/test_pthread_supported.c @@ -11,19 +11,20 @@ #include #include -void *ThreadMain(void *arg) -{ - pthread_exit(NULL); +void *ThreadMain(void *arg) { + pthread_exit(NULL); } -int main() -{ - pthread_t thread; - int rc = pthread_create(&thread, NULL, ThreadMain, NULL); - pthread_join(thread, NULL); +int main() { + pthread_t thread; + int rc = pthread_create(&thread, NULL, ThreadMain, NULL); + pthread_join(thread, NULL); - if (emscripten_has_threading_support()) assert(rc == 0); - else assert(rc == EAGAIN); + if (emscripten_has_threading_support()) { + assert(rc == 0); + } else { + assert(rc == ENOTSUP); + } - return 0; + return 0; } diff --git a/test/test_browser.py b/test/test_browser.py index 00ab423bf3690..b3b70a385eeae 100644 --- a/test/test_browser.py +++ b/test/test_browser.py @@ -3911,7 +3911,7 @@ def test_pthread_file_io(self): 'mt': (['-pthread', '-sPTHREAD_POOL_SIZE=8'],), }) def test_pthread_supported(self, args): - self.btest_exit('pthread/test_pthread_supported.cpp', cflags=['-O3'] + args) + self.btest_exit('pthread/test_pthread_supported.c', cflags=['-O3'] + args) def test_pthread_dispatch_after_exit(self): self.btest_exit('pthread/test_pthread_dispatch_after_exit.c', cflags=['-pthread']) diff --git a/test/test_other.py b/test/test_other.py index a39eadcbd75d1..555a7fe251cc5 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -15095,7 +15095,7 @@ def test_libcxx_errors(self): # Since we are building without -pthread the thread constructor will fail, # and in debug mode at least we expect to see the error message from libc++ - expected = 'system_error was thrown in -fno-exceptions mode with error 6 and message "thread constructor failed"' + expected = 'system_error was thrown in -fno-exceptions mode with error 138 and message "thread constructor failed"' self.do_runf('main.cpp', expected, assert_returncode=NON_ZERO) def test_parsetools_make_removed_fs_assert(self):