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
18 changes: 14 additions & 4 deletions src/lib/libatomic.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,21 @@ addToLibrary({
},

#if ASYNCIFY
emscripten_atomic_wait_suspending__async: 'auto',
emscripten_atomic_wait_suspending__deps: ['$polyfillWaitAsync', '$atomicWaitStates'],
emscripten_atomic_wait_suspending: async (addr, val, maxWaitMilliseconds) => {
_emscripten_atomic_wait_promise__deps: ['$polyfillWaitAsync', '$atomicWaitStates', '$makePromise'],
_emscripten_atomic_wait_promise: (addr, val, maxWaitMilliseconds) => {
var wait = Atomics.waitAsync(HEAP32, {{{ getHeapOffset('addr', 'i32') }}}, val, maxWaitMilliseconds);
return atomicWaitStates.indexOf(await wait.value)
if (wait.async) {
// In the async case return the promise ID.
var chainedPromise = wait.value.then((value) => atomicWaitStates.indexOf(value));
var id = promiseMap.allocate({promise: chainedPromise});
return id;
}
// In the synchronous case return the negative result code
return -atomicWaitStates.indexOf(wait.value);
},
#else
_emscripten_atomic_wait_promise: (addr, val, maxWaitMilliseconds) => {
abort('Please compile your program with async support in order to use asynchronous operations like emscripten_atomic_wait_promising');
},
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/lib/libsigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ 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 @@ -592,7 +593,6 @@ sigs = {
emscripten_atomic_cancel_all_wait_asyncs_at_address__sig: 'ip',
emscripten_atomic_cancel_wait_async__sig: 'ii',
emscripten_atomic_wait_async__sig: 'ipippd',
emscripten_atomic_wait_suspending__sig: 'ipid',
emscripten_atomics_is_lock_free__sig: 'ii',
emscripten_audio_context_quantum_size__sig: 'ii',
emscripten_audio_context_sample_rate__sig: 'ii',
Expand Down
28 changes: 28 additions & 0 deletions system/lib/pthread/emscripten_atomic_wait_suspending.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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.
*/

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

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);
// A negative value is a synchronous result code.
if (res < 0) {
return (ATOMICS_WAIT_TOKEN_T)-res;
}
// Otherwise a positive value is a promise ID, and we can then `await` using
// ASYNCIFY/JSPI.
em_promise_t promise = (em_promise_t)res;
em_settled_result_t result = emscripten_promise_await(promise);
if (result.result != EM_PROMISE_FULFILL) {
__builtin_abort();
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Is there some way to avoid writing these three lines? i.e. should we have a version if emscripten_promise_await that lets rejected promises propogate to the top level instead of handling them ourselves?

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.

I'd have to page the current design back in... it's been too long since I've looked at this code.

return (ATOMICS_WAIT_TOKEN_T)(intptr_t)result.value;
}
7 changes: 7 additions & 0 deletions system/lib/pthread/threading_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#pragma once

#include <inttypes.h>
#include <pthread.h>
#include <stdbool.h>

Expand Down Expand Up @@ -131,3 +132,9 @@ 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_syspending.
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 @@ -384,13 +384,15 @@ def main(args):
'MAX_WEBGL_VERSION': 0,
'BUILD_AS_WORKER': 1,
'LINK_AS_CXX': 1,
'SHARED_MEMORY': 0,
'AUTO_JS_LIBRARIES': 0}, cxx=True)
extract_sig_info(sig_info, {'AUDIO_WORKLET': 1, 'WASM_WORKERS': 1, 'JS_LIBRARIES': ['libwasm_worker.js', 'libwebaudio.js']})
extract_sig_info(sig_info, {'USE_GLFW': 3}, ['-DGLFW3'])
extract_sig_info(sig_info, {'JS_LIBRARIES': ['libembind.js', 'libemval.js'],
'USE_SDL': 0,
'MAX_WEBGL_VERSION': 0,
'AUTO_JS_LIBRARIES': 0,
'SHARED_MEMORY': 0,
'ASYNCIFY': 1}, cxx=True, extra_cflags=['-std=c++20'])
extract_sig_info(sig_info, {'LEGACY_GL_EMULATION': 1}, ['-DGLES'])
extract_sig_info(sig_info, {'USE_GLFW': 2, 'FULL_ES3': 1, 'MAX_WEBGL_VERSION': 2})
Expand Down
1 change: 1 addition & 0 deletions tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,7 @@ def get_files(self):
'emscripten_thread_primitives.c',
'emscripten_futex_wait.c',
'emscripten_futex_wake.c',
'emscripten_atomic_wait_suspending.c',
])

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