From 94cac5e4d4ed9c7113c89ee24525c001a3f85844 Mon Sep 17 00:00:00 2001 From: Viktor Vaczi Date: Thu, 13 Aug 2026 09:04:43 +0200 Subject: [PATCH] port staging 29c61b8's zero-duration nanosleep guard (allocator must not suspend) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mimalloc's mi_atomic_yield() is sleep(0) on wasm, reached from malloc's slow path under cross-thread delayed-free contention. Under JSPI a yield there suspends the activation INSIDE the allocator — any other activation that runs next can re-enter mimalloc mid-operation. ms==0 now busy-returns like stock emscripten. (The removelist half of the staging fix is obsolete here — no asyncify, no removelist. The mimalloc-storm harness + spec arrive with the staging merge.) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01NDeBaKKhQztd8KiVtHuyXr --- wasm/shims/nanosleep_yield.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/wasm/shims/nanosleep_yield.c b/wasm/shims/nanosleep_yield.c index 511e586..b031840 100644 --- a/wasm/shims/nanosleep_yield.c +++ b/wasm/shims/nanosleep_yield.c @@ -23,6 +23,16 @@ * emscripten_thread_sleep (the real underlying blocking sleep — workers may block). * * SCOPE: only the main browser thread yields; only it must never block the event loop. + * + * ZERO-DURATION GUARD (ported from staging 29c61b8): a nanosleep of 0 ms + * returns immediately WITHOUT yielding. mimalloc's spin-wait hint + * mi_atomic_yield() is sleep(0) on wasm, reached from malloc's slow path + * under cross-thread delayed-free contention. Under JSPI a yield there + * SUSPENDS the activation inside the allocator — any other activation that + * then runs can re-enter mimalloc mid-operation. A zero-duration sleep never + * promised an event-loop turn — stock emscripten busy-waits and returns + * immediately. mimalloc is the module's only zero-duration sleeper; the + * worker-boot deadlock this shim fixes needs only the ms-scale yields. */ #include #include @@ -69,13 +79,16 @@ int nanosleep( const struct timespec* req, struct timespec* rem ) if( req ) { double ms = (double) req->tv_sec * 1000.0 + (double) req->tv_nsec / 1.0e6; - if( emscripten_is_main_runtime_thread() ) + if( ms > 0.0 ) { - if( !pcbjam_context_sleep_ms || !pcbjam_context_sleep_ms( ms ) ) - __wasm_main_thread_yield_ms( ms ); /* yield -> event loop runs -> Worker boots */ + if( emscripten_is_main_runtime_thread() ) + { + if( !pcbjam_context_sleep_ms || !pcbjam_context_sleep_ms( ms ) ) + __wasm_main_thread_yield_ms( ms ); /* yield -> event loop runs -> Worker boots */ + } + else + emscripten_thread_sleep( ms ); /* worker: real blocking sleep */ } - else - emscripten_thread_sleep( ms ); /* worker: real blocking sleep */ } if( rem ) {