Skip to content

Commit fb3de64

Browse files
committed
Add emscripten_promise_await_unchecked API
This works with ASYNCIFY like the existing `emscripten_promise_await` but is a lot simpler since it only handles the fulfilled case. In this case we can simple return the result directly without needing to allocate a `em_promise_result_t` struct to deal with the out param (i.e. no memory access needed). This can be useful in cases where we don't want to handle the rejections case.
1 parent 2918b79 commit fb3de64

9 files changed

Lines changed: 87 additions & 6 deletions

File tree

.circleci/config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,8 @@ jobs:
10951095
core0.test_cubescript_jspi
10961096
core0.test_pthread_wait_suspending*
10971097
core0.test_poll_blocking_asyncify_jspi
1098+
core0.test_promise_await*
1099+
wasm64.test_promise_await*
10981100
wasm64.test_pthread_join_and_asyncify
10991101
"
11001102
- upload-test-results

src/lib/libpromise.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,21 +247,33 @@ addToLibrary({
247247
return id;
248248
},
249249
250-
emscripten_promise_await__async: 'auto',
251250
#if ASYNCIFY
251+
emscripten_promise_await__async: 'auto',
252252
emscripten_promise_await__deps: ['$getPromise', '$setPromiseResult'],
253-
#endif
254253
emscripten_promise_await: (returnValuePtr, id) => {
255-
#if ASYNCIFY
256254
#if RUNTIME_DEBUG
257255
dbg(`emscripten_promise_await: ${id}`);
258256
#endif
259257
return getPromise(id).then(
260258
value => setPromiseResult(returnValuePtr, true, value),
261259
error => setPromiseResult(returnValuePtr, false, error)
262260
);
261+
},
262+
263+
emscripten_promise_await_unchecked__async: 'auto',
264+
emscripten_promise_await_unchecked__deps: ['$getPromise'],
265+
emscripten_promise_await_unchecked: (id) => {
266+
#if RUNTIME_DEBUG
267+
dbg(`emscripten_promise_await_unchecked: ${id}`);
268+
#endif
269+
return getPromise(id);
270+
},
263271
#else
272+
emscripten_promise_await: (returnValuePtr, id) => {
264273
abort('emscripten_promise_await is only available with ASYNCIFY');
265-
#endif
266274
},
275+
emscripten_promise_await_unchecked: (id) => {
276+
abort('emscripten_promise_await_unchecked is only available with ASYNCIFY');
277+
},
278+
#endif
267279
});

src/lib/libsigs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,7 @@ sigs = {
724724
emscripten_promise_all_settled__sig: 'pppp',
725725
emscripten_promise_any__sig: 'pppp',
726726
emscripten_promise_await__sig: 'vpp',
727+
emscripten_promise_await_unchecked__sig: 'pp',
727728
emscripten_promise_create__sig: 'p',
728729
emscripten_promise_destroy__sig: 'vp',
729730
emscripten_promise_race__sig: 'ppp',

system/include/emscripten/promise.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ typedef struct em_settled_result_t {
147147
[[nodiscard]] em_settled_result_t
148148
emscripten_promise_await(em_promise_t promise);
149149

150+
// Just like emscripten_promise_await but does not include a rejection handler
151+
// and simply returns result if/when the promise is fulfilled.
152+
// If the promise is rejected it would then get handled elsewhere in the promise
153+
// chain, or result in a top level unhandled rejection.
154+
[[nodiscard]] void* emscripten_promise_await_unchecked(em_promise_t promise);
155+
150156
#ifdef __cplusplus
151157
}
152158
#endif

test/codesize/test_codesize_hello_dylink_all.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"a.out.js": 268089,
2+
"a.out.js": 268202,
33
"a.out.nodebug.wasm": 587563,
4-
"total": 855652,
4+
"total": 855765,
55
"sent": [
66
"IMG_Init",
77
"IMG_Load",
@@ -842,6 +842,7 @@
842842
"emscripten_promise_all_settled",
843843
"emscripten_promise_any",
844844
"emscripten_promise_await",
845+
"emscripten_promise_await_unchecked",
845846
"emscripten_promise_create",
846847
"emscripten_promise_destroy",
847848
"emscripten_promise_race",

test/core/test_promise.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ test_null_handlers
3535
expected success: 42
3636
expected error: 43
3737
finish
38+
warning: emscripten_force_exit cannot actually shut down the runtime, as the build does not have EXIT_RUNTIME set

test/core/test_promise_await.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ void test_already_fulfilled() {
2323
emscripten_promise_destroy(p);
2424
}
2525

26+
void test_already_fulfilled_unchecked() {
27+
// Test waiting on an already fulfilled promise.
28+
em_promise_t p = emscripten_promise_create();
29+
emscripten_promise_resolve(p, EM_PROMISE_FULFILL, (void*)42);
30+
31+
printf("waiting on promise (unchecked): %p\n", p);
32+
void* res = emscripten_promise_await_unchecked(p);
33+
printf(".. done wait: %ld\n", (intptr_t)res);
34+
35+
assert(res == (void*)42);
36+
emscripten_promise_destroy(p);
37+
}
38+
2639
void test_not_yet_fulfilled() {
2740
em_promise_t p = emscripten_promise_create();
2841
emscripten_async_call(fulfill_from_timeout, p, 0);
@@ -36,6 +49,18 @@ void test_not_yet_fulfilled() {
3649
emscripten_promise_destroy(p);
3750
}
3851

52+
void test_not_yet_fulfilled_unchecked() {
53+
em_promise_t p = emscripten_promise_create();
54+
emscripten_async_call(fulfill_from_timeout, p, 0);
55+
56+
printf("waiting on promise (unchecked): %p\n", p);
57+
void* res = emscripten_promise_await_unchecked(p);
58+
printf(".. done wait: %ld\n", (intptr_t)res);
59+
60+
assert(res == (void*)43);
61+
emscripten_promise_destroy(p);
62+
}
63+
3964
void test_rejected() {
4065
em_promise_t p = emscripten_promise_create();
4166
emscripten_promise_resolve(p, EM_PROMISE_REJECT, (void*)44);
@@ -53,7 +78,9 @@ int main() {
5378
printf("main\n");
5479

5580
test_already_fulfilled();
81+
test_already_fulfilled_unchecked();
5682
test_not_yet_fulfilled();
83+
test_not_yet_fulfilled_unchecked();
5784
test_rejected();
5885

5986
printf("main done\n");

test/core/test_promise_await.out

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
main
22
waiting on promise: 0x1
33
.. done wait: 0 42
4+
waiting on promise (unchecked): 0x1
5+
.. done wait: 42
46
waiting on promise: 0x1
57
.. done wait: 0 43
8+
waiting on promise (unchecked): 0x1
9+
.. done wait: 43
610
waiting on promise: 0x1
711
.. done wait: 3 44
812
main done

test/test_core.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9676,6 +9676,33 @@ def test_promise_await_error(self):
96769676
self.do_runf('core/test_promise_await.c', 'emscripten_promise_await is only available with ASYNCIFY',
96779677
assert_returncode=NON_ZERO)
96789678

9679+
# Include @requires_node_25 explictly here so that this test will be disabled
9680+
# by EMTEST_SKIP_NODE_25. Without this, the `requires_node` and `requires_jspi` can
9681+
# end with conflicting requirements because we often run with both v8 (which satisfies
9682+
# the `requires_jspi` part have node (which satisfies the `requires_node` part).
9683+
# FIXME: This should not be needed.
9684+
@requires_node_25
9685+
@with_asyncify_and_jspi
9686+
def test_promise_await_unchecked_rejected(self):
9687+
create_file('test.c', r'''
9688+
#include <emscripten/promise.h>
9689+
#include <stdio.h>
9690+
#include <stdlib.h>
9691+
9692+
int main() {
9693+
em_promise_t p = emscripten_promise_create();
9694+
emscripten_promise_resolve(p, EM_PROMISE_REJECT, (void*)45);
9695+
9696+
printf("waiting on promise (unchecked, rejected)\n");
9697+
(void)emscripten_promise_await_unchecked(p);
9698+
printf("ERROR: should not be reached\n");
9699+
__builtin_abort();
9700+
return 1;
9701+
}
9702+
''')
9703+
# We expect an unhandled rejection, which in Node.js results in a non-zero exit code.
9704+
self.do_runf('test.c', 'UnhandledPromiseRejection', assert_returncode=NON_ZERO)
9705+
96799706
@no_modularize_instance('uses Module object directly')
96809707
def test_emscripten_async_load_script(self):
96819708
create_file('script1.js', 'Module._set(456);''')

0 commit comments

Comments
 (0)