From 3f7dff60fbcfd70595d3760eaad7ee33e2bc44aa Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Thu, 19 Feb 2026 17:08:29 -0800 Subject: [PATCH] Improve emscripten_futex_wait stub. NFC The test for wasm workers with `emscripten_futex_wait` was added in back in #21618 but AFAICT it was only even working when pthread support was also enabled. However because the stub was simply returning 0 in all cases it was enough to make it seems as if the API was working when it wasn't. Indeed the `test/webaudio/audioworklet_worker.c` test seems to have been written under the assumption that this API was available for use in wasm workers. It seems better to return ENOTSUP unless the API is actually available. --- system/lib/pthread/library_pthread_stub.c | 14 ++++++--- .../test_codesize_hello_dylink_all.json | 4 +-- test/test_browser.py | 6 ++-- test/wasm_worker/wasm_worker_futex_wait.c | 30 +++++++++++++++---- test/webaudio/audioworklet_worker.c | 13 ++++---- 5 files changed, 47 insertions(+), 20 deletions(-) diff --git a/system/lib/pthread/library_pthread_stub.c b/system/lib/pthread/library_pthread_stub.c index 6c9712378de00..e380f8ef2a9c9 100644 --- a/system/lib/pthread/library_pthread_stub.c +++ b/system/lib/pthread/library_pthread_stub.c @@ -21,10 +21,16 @@ bool emscripten_has_threading_support() { return false; } int emscripten_num_logical_cores() { return 1; } -int emscripten_futex_wait( - volatile void /*uint32_t*/* addr, uint32_t val, double maxWaitMilliseconds) { - // nop - return 0; // success +int emscripten_futex_wait(volatile void /*uint32_t*/* addr, + uint32_t val, + double maxWaitMilliseconds) { + if (!addr) { + return -EINVAL; + } + if (*(uint32_t*)addr != val) { + return -EWOULDBLOCK; + } + return -ENOTSUP; } int emscripten_futex_wake(volatile void /*uint32_t*/* addr, int count) { diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index b15a472089760..e8ec37864352d 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": 244691, - "a.out.nodebug.wasm": 577678, - "total": 822369, + "a.out.nodebug.wasm": 577699, + "total": 822390, "sent": [ "IMG_Init", "IMG_Load", diff --git a/test/test_browser.py b/test/test_browser.py index c651383f4dd4b..cbbf261148409 100644 --- a/test/test_browser.py +++ b/test/test_browser.py @@ -5039,9 +5039,11 @@ def test_wasm_worker_hello_wasm2js(self): def test_wasm_worker_hello_embedded(self): self.btest_exit('wasm_worker/hello_wasm_worker.c', cflags=['-sWASM_WORKERS=2']) - # Tests that it is possible to call emscripten_futex_wait() in Wasm Workers. + # Tests that it is possible to call emscripten_futex_wait() in Wasm Workers when pthreads + # are also enabled. @parameterized({ - '': ([],), + # Without pthreads we expect the stub version of the futex API + '': (['-DEXPECT_STUB'],), 'pthread': (['-pthread'],), }) def test_wasm_worker_futex_wait(self, args): diff --git a/test/wasm_worker/wasm_worker_futex_wait.c b/test/wasm_worker/wasm_worker_futex_wait.c index 7830fca63b397..579a3ef2e23e1 100644 --- a/test/wasm_worker/wasm_worker_futex_wait.c +++ b/test/wasm_worker/wasm_worker_futex_wait.c @@ -1,5 +1,7 @@ // Test that emscripten_futex_wait() works in a Wasm Worker. +#include +#include #include #include #include @@ -12,21 +14,37 @@ _Atomic uint32_t futex_value = 0; void wake_worker_after_delay(void *user_data) { futex_value = 1; - emscripten_futex_wake(&futex_value, INT_MAX); + emscripten_futex_wake(&futex_value, INT_MAX); } void wake_worker() { - printf("Waking worker thread from futex wait.\n"); + printf("Waking worker thread from futex wait...\n"); emscripten_set_timeout(wake_worker_after_delay, 500, 0); } void worker_main() { - printf("Worker sleeping for futex wait.\n"); + printf("Worker sleeping on futex...\n"); + double start = emscripten_performance_now(); + int rc = emscripten_futex_wait(&futex_value, 0, 100); + double end = emscripten_performance_now(); + printf("emscripten_futex_wait returned: %d after %.2fms\n", rc, end - start); +#if EXPECT_STUB + // The stub implemenation returns -ENOTSUP + assert(rc == -ENOTSUP); +#else + assert(rc == -ETIMEDOUT); + assert((end - start) >= 100); + + printf("Worker sleeping on futex with wakeup...\n"); emscripten_wasm_worker_post_function_v(0, wake_worker); - int rc = emscripten_futex_wait(&futex_value, 0, INFINITY); - printf("emscripten_futex_wait returned with code %d.\n", rc); + rc = emscripten_futex_wait(&futex_value, 0, INFINITY); + printf("emscripten_futex_wait returned: %d\n", rc); + assert(rc == 0); + assert(futex_value == 1); +#endif + #ifdef REPORT_RESULT - REPORT_RESULT(rc); + REPORT_RESULT(0); #endif } diff --git a/test/webaudio/audioworklet_worker.c b/test/webaudio/audioworklet_worker.c index 046ac8992a692..1887986f6123c 100644 --- a/test/webaudio/audioworklet_worker.c +++ b/test/webaudio/audioworklet_worker.c @@ -2,14 +2,14 @@ #include #include #include +#include // Tests that // - audioworklets and workers can be used at the same time. // - an audioworklet can emscripten_futex_wake() a waiting worker. // - global values can be shared between audioworklets and workers. -int workletToWorkerFutexLocation = 0; -int workletToWorkerFlag = 0; +_Atomic bool workletToWorkerFlag = false; EMSCRIPTEN_WEBAUDIO_T context; void do_exit() { @@ -19,8 +19,10 @@ void do_exit() { } void run_in_worker() { - while (0 == emscripten_futex_wait(&workletToWorkerFutexLocation, 0, 30000)) { - if (workletToWorkerFlag == 1) { + // TODO: Convert to emscripten_futex_wait once it becomes available in Wasm + // Workers. + while (1) { + if (workletToWorkerFlag == true) { emscripten_out("Test success"); emscripten_wasm_worker_post_function_v(EMSCRIPTEN_WASM_WORKER_ID_PARENT, &do_exit); break; @@ -31,8 +33,7 @@ void run_in_worker() { // This event will fire on the audio worklet thread. void MessageReceivedInAudioWorkletThread() { assert(emscripten_current_thread_is_audio_worklet()); - workletToWorkerFlag = 1; - emscripten_futex_wake(&workletToWorkerFutexLocation, 1); + workletToWorkerFlag = true; } void WebAudioWorkletThreadInitialized(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData) {