Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,8 @@ jobs:
core0.test_cubescript_jspi
core0.test_pthread_wait_suspending*
core0.test_poll_blocking_asyncify_jspi
core0.test_promise_await*
wasm64.test_promise_await*
wasm64.test_pthread_join_and_asyncify
"
- upload-test-results
Expand Down
20 changes: 16 additions & 4 deletions src/lib/libpromise.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,21 +247,33 @@ addToLibrary({
return id;
},

emscripten_promise_await__async: 'auto',
#if ASYNCIFY
emscripten_promise_await__async: 'auto',
emscripten_promise_await__deps: ['$getPromise', '$setPromiseResult'],
#endif
emscripten_promise_await: (returnValuePtr, id) => {
#if ASYNCIFY
#if RUNTIME_DEBUG
dbg(`emscripten_promise_await: ${id}`);
#endif
return getPromise(id).then(
value => setPromiseResult(returnValuePtr, true, value),
error => setPromiseResult(returnValuePtr, false, error)
);
},

emscripten_promise_await_unchecked__async: 'auto',
emscripten_promise_await_unchecked__deps: ['$getPromise'],
emscripten_promise_await_unchecked: (id) => {
#if RUNTIME_DEBUG
dbg(`emscripten_promise_await_unchecked: ${id}`);
#endif
return getPromise(id);
},
#else
emscripten_promise_await: (returnValuePtr, id) => {
abort('emscripten_promise_await is only available with ASYNCIFY');
#endif
},
emscripten_promise_await_unchecked: (id) => {
abort('emscripten_promise_await_unchecked is only available with ASYNCIFY');
},
#endif
});
1 change: 1 addition & 0 deletions src/lib/libsigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,7 @@ sigs = {
emscripten_promise_all_settled__sig: 'pppp',
emscripten_promise_any__sig: 'pppp',
emscripten_promise_await__sig: 'vpp',
emscripten_promise_await_unchecked__sig: 'pp',
emscripten_promise_create__sig: 'p',
emscripten_promise_destroy__sig: 'vp',
emscripten_promise_race__sig: 'ppp',
Expand Down
6 changes: 6 additions & 0 deletions system/include/emscripten/promise.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ typedef struct em_settled_result_t {
[[nodiscard]] em_settled_result_t
emscripten_promise_await(em_promise_t promise);

// Just like emscripten_promise_await but does not include a rejection handler
// and simply returns result if/when the promise is fulfilled.
// If the promise is rejected it would then get handled elsewhere in the promise
// chain, or result in a top level unhandled rejection.
[[nodiscard]] void* emscripten_promise_await_unchecked(em_promise_t promise);

#ifdef __cplusplus
}
#endif
5 changes: 3 additions & 2 deletions test/codesize/test_codesize_hello_dylink_all.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"a.out.js": 268089,
"a.out.js": 268202,
"a.out.nodebug.wasm": 587563,
"total": 855652,
"total": 855765,
"sent": [
"IMG_Init",
"IMG_Load",
Expand Down Expand Up @@ -842,6 +842,7 @@
"emscripten_promise_all_settled",
"emscripten_promise_any",
"emscripten_promise_await",
"emscripten_promise_await_unchecked",
"emscripten_promise_create",
"emscripten_promise_destroy",
"emscripten_promise_race",
Expand Down
1 change: 1 addition & 0 deletions test/core/test_promise.out
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ test_null_handlers
expected success: 42
expected error: 43
finish
warning: emscripten_force_exit cannot actually shut down the runtime, as the build does not have EXIT_RUNTIME set
27 changes: 27 additions & 0 deletions test/core/test_promise_await.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ void test_already_fulfilled() {
emscripten_promise_destroy(p);
}

void test_already_fulfilled_unchecked() {
// Test waiting on an already fulfilled promise.
em_promise_t p = emscripten_promise_create();
emscripten_promise_resolve(p, EM_PROMISE_FULFILL, (void*)42);

printf("waiting on promise (unchecked): %p\n", p);
void* res = emscripten_promise_await_unchecked(p);
printf(".. done wait: %ld\n", (intptr_t)res);

assert(res == (void*)42);
emscripten_promise_destroy(p);
}

void test_not_yet_fulfilled() {
em_promise_t p = emscripten_promise_create();
emscripten_async_call(fulfill_from_timeout, p, 0);
Expand All @@ -36,6 +49,18 @@ void test_not_yet_fulfilled() {
emscripten_promise_destroy(p);
}

void test_not_yet_fulfilled_unchecked() {
em_promise_t p = emscripten_promise_create();
emscripten_async_call(fulfill_from_timeout, p, 0);

printf("waiting on promise (unchecked): %p\n", p);
void* res = emscripten_promise_await_unchecked(p);
printf(".. done wait: %ld\n", (intptr_t)res);

assert(res == (void*)43);
emscripten_promise_destroy(p);
}

void test_rejected() {
em_promise_t p = emscripten_promise_create();
emscripten_promise_resolve(p, EM_PROMISE_REJECT, (void*)44);
Expand All @@ -53,7 +78,9 @@ int main() {
printf("main\n");

test_already_fulfilled();
test_already_fulfilled_unchecked();
test_not_yet_fulfilled();
test_not_yet_fulfilled_unchecked();
test_rejected();

printf("main done\n");
Expand Down
4 changes: 4 additions & 0 deletions test/core/test_promise_await.out
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
main
waiting on promise: 0x1
.. done wait: 0 42
waiting on promise (unchecked): 0x1
.. done wait: 42
waiting on promise: 0x1
.. done wait: 0 43
waiting on promise (unchecked): 0x1
.. done wait: 43
waiting on promise: 0x1
.. done wait: 3 44
main done
27 changes: 27 additions & 0 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9676,6 +9676,33 @@ def test_promise_await_error(self):
self.do_runf('core/test_promise_await.c', 'emscripten_promise_await is only available with ASYNCIFY',
assert_returncode=NON_ZERO)

# Include @requires_node_25 explictly here so that this test will be disabled
# by EMTEST_SKIP_NODE_25. Without this, the `requires_node` and `requires_jspi` can
# end with conflicting requirements because we often run with both v8 (which satisfies
# the `requires_jspi` part have node (which satisfies the `requires_node` part).
# FIXME: This should not be needed.
@requires_node_25
@with_asyncify_and_jspi
def test_promise_await_unchecked_rejected(self):
create_file('test.c', r'''
#include <emscripten/promise.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
em_promise_t p = emscripten_promise_create();
emscripten_promise_resolve(p, EM_PROMISE_REJECT, (void*)45);

printf("waiting on promise (unchecked, rejected)\n");
(void)emscripten_promise_await_unchecked(p);
printf("ERROR: should not be reached\n");
__builtin_abort();
return 1;
}
''')
# We expect an unhandled rejection, which in Node.js results in a non-zero exit code.
self.do_runf('test.c', 'UnhandledPromiseRejection', assert_returncode=NON_ZERO)

@no_modularize_instance('uses Module object directly')
def test_emscripten_async_load_script(self):
create_file('script1.js', 'Module._set(456);''')
Expand Down
Loading