Skip to content

Commit 5625819

Browse files
authored
Add emscripten_promise_await based on ASYNCIFY (#19553)
1 parent b182182 commit 5625819

File tree

6 files changed

+112
-0
lines changed

6 files changed

+112
-0
lines changed

src/library_promise.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,4 +260,25 @@ mergeInto(LibraryManager.library, {
260260
#endif
261261
return id;
262262
},
263+
264+
emscripten_promise_await__async: true,
265+
emscripten_promise_await__deps: ['$getPromise', '$setPromiseResult'],
266+
emscripten_promise_await: (returnValuePtr, id) => {
267+
#if ASYNCIFY
268+
#if RUNTIME_DEBUG
269+
dbg(`emscripten_promise_await: ${id}`);
270+
#endif
271+
return Asyncify.handleSleep((wakeUp) => {
272+
getPromise(id).then((value) => {
273+
setPromiseResult(returnValuePtr, true, value);
274+
wakeUp();
275+
}, (value) => {
276+
setPromiseResult(returnValuePtr, false, value);
277+
wakeUp();
278+
});
279+
});
280+
#else
281+
abort('emscripten_promise_await is only available with ASYNCIFY');
282+
#endif
283+
},
263284
});

src/library_sigs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ sigs = {
687687
emscripten_promise_all__sig: 'pppp',
688688
emscripten_promise_all_settled__sig: 'pppp',
689689
emscripten_promise_any__sig: 'pppp',
690+
emscripten_promise_await__sig: 'vpp',
690691
emscripten_promise_create__sig: 'p',
691692
emscripten_promise_destroy__sig: 'vp',
692693
emscripten_promise_race__sig: 'ppp',

system/include/emscripten/promise.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,17 @@ __attribute__((warn_unused_result)) em_promise_t emscripten_promise_any(
135135
__attribute__((warn_unused_result)) em_promise_t
136136
emscripten_promise_race(em_promise_t* promises, size_t num_promises);
137137

138+
// Suspend the current Wasm execution context until the given promise has been
139+
// settled.
140+
//
141+
// Since the stack is not unwound while Wasm execution is suspended, it is
142+
// safe to pass pointers to the stack to asynchronous work that is waited on
143+
// with this function.
144+
//
145+
// This function can only be used in programs that were built with `-sASYNCIFY`.
146+
__attribute__((warn_unused_result)) em_settled_result_t
147+
emscripten_promise_await(em_promise_t promise);
148+
138149
#ifdef __cplusplus
139150
}
140151
#endif

test/core/test_promise_await.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#include <assert.h>
2+
#include <emscripten/promise.h>
3+
#include <emscripten/em_js.h>
4+
#include <emscripten/emscripten.h>
5+
#include <stdbool.h>
6+
#include <stdio.h>
7+
8+
void fulfill_from_timout(void* arg) {
9+
emscripten_promise_resolve((em_promise_t)arg, EM_PROMISE_FULFILL, (void*)43);
10+
}
11+
12+
void test_already_fulfilled() {
13+
// Test waiting on an already fulfilled promise.
14+
em_promise_t p = emscripten_promise_create();
15+
emscripten_promise_resolve(p, EM_PROMISE_FULFILL, (void*)42);
16+
17+
printf("waiting on promise: %p\n", p);
18+
em_settled_result_t res = emscripten_promise_await(p);
19+
printf(".. done wait: %d %ld\n", res.result, (intptr_t)res.value);
20+
21+
assert(res.result == EM_PROMISE_FULFILL);
22+
assert(res.value == (void*)42);
23+
emscripten_promise_destroy(p);
24+
}
25+
26+
void test_not_yet_fulfilled() {
27+
em_promise_t p = emscripten_promise_create();
28+
emscripten_async_call(fulfill_from_timout, p, 0);
29+
30+
printf("waiting on promise: %p\n", p);
31+
em_settled_result_t res = emscripten_promise_await(p);
32+
printf(".. done wait: %d %ld\n", res.result, (intptr_t)res.value);
33+
34+
assert(res.result == EM_PROMISE_FULFILL);
35+
assert(res.value == (void*)43);
36+
emscripten_promise_destroy(p);
37+
}
38+
39+
void test_rejected() {
40+
em_promise_t p = emscripten_promise_create();
41+
emscripten_promise_resolve(p, EM_PROMISE_REJECT, (void*)44);
42+
43+
printf("waiting on promise: %p\n", p);
44+
em_settled_result_t res = emscripten_promise_await(p);
45+
printf(".. done wait: %d %ld\n", res.result, (intptr_t)res.value);
46+
47+
assert(res.result == EM_PROMISE_REJECT);
48+
assert(res.value == (void*)44);
49+
emscripten_promise_destroy(p);
50+
}
51+
52+
int main() {
53+
printf("main\n");
54+
55+
test_already_fulfilled();
56+
test_not_yet_fulfilled();
57+
test_rejected();
58+
59+
printf("main done\n");
60+
return 0;
61+
}

test/core/test_promise_await.out

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
main
2+
waiting on promise: 0x1
3+
.. done wait: 0 42
4+
waiting on promise: 0x1
5+
.. done wait: 0 43
6+
waiting on promise: 0x1
7+
.. done wait: 3 44
8+
main done

test/test_core.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9724,6 +9724,16 @@ def test_promise(self):
97249724
self.set_setting('MIN_CHROME_VERSION', '85')
97259725
self.do_core_test('test_promise.c')
97269726

9727+
@no_wasm64('TODO: asyncify for wasm64')
9728+
@with_asyncify_and_jspi
9729+
def test_promise_await(self):
9730+
self.do_core_test('test_promise_await.c')
9731+
9732+
def test_promise_await_error(self):
9733+
# Check that the API is not available when ASYNCIFY is not set
9734+
self.do_runf(test_file('core/test_promise_await.c'), 'Aborted(emscripten_promise_await is only available with ASYNCIFY)',
9735+
assert_returncode=NON_ZERO)
9736+
97279737
def test_emscripten_async_load_script(self):
97289738
create_file('script1.js', 'Module._set(456);''')
97299739
create_file('file1.txt', 'first')

0 commit comments

Comments
 (0)