Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/lib/libatomic.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,22 @@ addToLibrary({
},

#if ASYNCIFY
_emscripten_atomic_wait_promise__deps: ['$polyfillWaitAsync', '$atomicWaitStates', '$addPromise'],
_emscripten_atomic_wait_promise__deps: ['$polyfillWaitAsync', '$atomicWaitStates'],
_emscripten_atomic_wait_promise: (addr, val, maxWaitMilliseconds) => {
var wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('addr', 'i32') }}}, val, maxWaitMilliseconds);
if (wait.async) {
// In the async case return the promise ID.
var chainedPromise = wait.value.then((value) => atomicWaitStates.indexOf(value));
var id = addPromise(chainedPromise);
return id;
#if RUNTIME_DEBUG
dbg("returning promise, -1 pair")
#endif
return [chainedPromise, -1];
}
#if RUNTIME_DEBUG
dbg("_emscripten_atomic_wait_promise returning sync result: " + wait.value)
#endif
// In the synchronous case return the negative result code
return -atomicWaitStates.indexOf(wait.value);
return [undefined, atomicWaitStates.indexOf(wait.value)]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null would make more sense than undefined here. That way you end up with a null externref on the Wasm side. With undefined, you end up with a non-null externref.

},
#else
_emscripten_atomic_wait_promise: (addr, val, maxWaitMilliseconds) => {
Expand Down
14 changes: 14 additions & 0 deletions src/lib/libpromise.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,17 @@ addToLibrary({
#endif
return getPromise(id);
},

// Just like emscripten_promise_await_unchecked above except the argument
// is an actual JS promise. We call this from Wasm when we have an `externref`
// that refers to a JS promise object.
emscripten_promise_ref_await_unchecked__async: 'auto',
emscripten_promise_ref_await_unchecked: (promise) => {
#if RUNTIME_DEBUG
dbg(`emscripten_promise_ref_await_unchecked: ${promise}`);
#endif
return promise;
},
#else
emscripten_promise_await: (returnValuePtr, id) => {
abort('emscripten_promise_await is only available with ASYNCIFY');
Expand All @@ -276,5 +287,8 @@ addToLibrary({
abort('emscripten_promise_await_unchecked is only available with ASYNCIFY');
return 0;
},
emscripten_promise_ref_await_unchecked: (id) => {
abort('emscripten_promise_await_unchecked is only available with ASYNCIFY');
},
#endif
});
2 changes: 1 addition & 1 deletion src/lib/libsigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,6 @@ sigs = {
_embind_register_value_object__sig: 'vpppppp',
_embind_register_value_object_field__sig: 'vpppppppppp',
_embind_register_void__sig: 'vpp',
_emscripten_atomic_wait_promise__sig: 'ppid',
_emscripten_create_audio_worklet__sig: 'viipippp',
_emscripten_create_wasm_worker__sig: 'iipip',
_emscripten_dlopen_js__sig: 'vpppp',
Expand Down Expand Up @@ -728,6 +727,7 @@ sigs = {
emscripten_promise_create__sig: 'p',
emscripten_promise_destroy__sig: 'vp',
emscripten_promise_race__sig: 'ppp',
emscripten_promise_ref_await_unchecked__sig: 'pr',
emscripten_promise_resolve__sig: 'vpip',
emscripten_promise_then__sig: 'ppppp',
emscripten_random__sig: 'f',
Expand Down
2 changes: 2 additions & 0 deletions system/include/emscripten/promise.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ emscripten_promise_await(em_promise_t promise);
// chain, or result in a top level unhandled rejection.
[[nodiscard]] void* emscripten_promise_await_unchecked(em_promise_t promise);

[[nodiscard]] void* emscripten_promise_ref_await_unchecked(__externref_t promise);

#ifdef __cplusplus
}
#endif
35 changes: 35 additions & 0 deletions system/lib/pthread/emscripten_atomic_wait_promise_asm.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2026 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*
* Helper function for emscripten_atomic_wait_suspending.
*
* This function uses multi-value so it cannot be expressed in C code. W
* call out to the `_emscripten_atomic_wait_promise` JS library function which
* returns a pair of [ Promise|undefined, result code ]. We then write the
* result code to an out parameter and return the Promise as an externref
*/

#ifdef __wasm64__
#define PTR i64
#else
#define PTR i32
#endif

.functype _emscripten_atomic_wait_promise (PTR, i32, f64) -> (externref, i32)

.globl _emscripten_atomic_wait_promise_asm
_emscripten_atomic_wait_promise_asm:
.functype _emscripten_atomic_wait_promise_asm (PTR, i32, f64, PTR) -> (externref)
.local i32
local.get 0
local.get 1
local.get 2
call _emscripten_atomic_wait_promise
local.set 4
local.get 3
local.get 4
i32.store 0
end_function
20 changes: 14 additions & 6 deletions system/lib/pthread/emscripten_atomic_wait_suspending.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@
* found in the LICENSE file.
*/

#include <emscripten/promise.h>
#include <emscripten/atomic.h>
#include <emscripten/console.h>
#include <emscripten/promise.h>
#include "threading_internal.h"

// Internal promise-returning API used to implement
// emscripten_atomic_wait_syspending.
__externref_t _emscripten_atomic_wait_promise_asm(volatile void *addr,
uint32_t value,
double maxWaitMilliseconds,
int32_t* result);

ATOMICS_WAIT_TOKEN_T emscripten_atomic_wait_suspending(volatile void * _Nonnull addr,
uint32_t value,
double maxWaitMilliseconds) {
intptr_t res = _emscripten_atomic_wait_promise(addr, value, maxWaitMilliseconds);
int32_t result = 42;
__externref_t promise = _emscripten_atomic_wait_promise_asm(addr, value, maxWaitMilliseconds, &result);
// A negative value is a synchronous result code.
if (res < 0) {
return (ATOMICS_WAIT_TOKEN_T)-res;
if (result != -1) {
return (ATOMICS_WAIT_TOKEN_T)result;
}
// Otherwise a positive value is a promise ID, and we can then `await` using
// ASYNCIFY/JSPI.
em_promise_t promise = (em_promise_t)res;
void* await_result = emscripten_promise_await_unchecked(promise);
void* await_result = emscripten_promise_ref_await_unchecked(promise);
return (ATOMICS_WAIT_TOKEN_T)(intptr_t)await_result;
}
6 changes: 0 additions & 6 deletions system/lib/pthread/threading_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,3 @@ void* _emscripten_init_pthread(void *base, size_t* size, pid_t tid);
// __builtin_wasm_memory_atomic_waitXX then they will not be woken by
// this method.
void _emscripten_thread_notify(pthread_t thread);

// Internal, promise-returning API used to implement
// emscripten_atomic_wait_suspending.
intptr_t _emscripten_atomic_wait_promise(volatile void *addr,
uint32_t value,
double maxWaitMilliseconds);
2 changes: 2 additions & 0 deletions tools/maint/gen_sig_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ def ignore_symbol(s, cxx):
return True
if s in {'__stack_base', '__memory_base', '__table_base', '__global_base', '__heap_base',
'__stack_pointer', '__stack_high', '__stack_low',
'_emscripten_atomic_wait_promise',
# legacy aliases, not callable from native code.
'stackSave', 'stackRestore', 'stackAlloc', 'getTempRet0', 'setTempRet0',
}:
Expand Down Expand Up @@ -207,6 +208,7 @@ def valuetype_to_chr(t, t64):
webassembly.Type.I64: 'j',
webassembly.Type.F32: 'f',
webassembly.Type.F64: 'd',
webassembly.Type.EXTERNREF: 'r',
}[t]


Expand Down
1 change: 1 addition & 0 deletions tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,7 @@ def get_files(self):
'emscripten_futex_wait.c',
'emscripten_futex_wake.c',
'emscripten_atomic_wait_suspending.c',
'emscripten_atomic_wait_promise_asm.S',
])

# These files are in libc directories, but only built in libc_optz.
Expand Down
Loading