Skip to content

Commit dfbf6a8

Browse files
authored
Implement emscripten_promise_all_settled in promise.h (#19152)
This is like `emscripten_promise_all`, but always fulfills and separately reports whether each input promise is fulfilled or rejected.
1 parent 6f3cfe3 commit dfbf6a8

8 files changed

+194
-17
lines changed

src/generated_struct_info32.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,6 +1330,11 @@
13301330
"table_addr": 20,
13311331
"table_size": 24
13321332
},
1333+
"em_settled_result_t": {
1334+
"__size__": 8,
1335+
"result": 0,
1336+
"value": 4
1337+
},
13331338
"emscripten_fetch_attr_t": {
13341339
"__size__": 92,
13351340
"attributes": 52,

src/generated_struct_info64.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,6 +1330,11 @@
13301330
"table_addr": 32,
13311331
"table_size": 40
13321332
},
1333+
"em_settled_result_t": {
1334+
"__size__": 16,
1335+
"result": 0,
1336+
"value": 8
1337+
},
13331338
"emscripten_fetch_attr_t": {
13341339
"__size__": 152,
13351340
"attributes": 72,

src/library_promise.js

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ mergeInto(LibraryManager.library, {
2727
return promiseInfo;
2828
},
2929

30+
$idsToPromises__deps: ['$promiseMap', '$getPromise'],
31+
$idsToPromises: function(idBuf, size) {
32+
var promises = [];
33+
for (var i = 0; i < size; i++) {
34+
var id = {{{ makeGetValue('idBuf', `i*${POINTER_SIZE}`, 'i32') }}};
35+
promises[i] = getPromise(id);
36+
}
37+
return promises;
38+
},
39+
3040
emscripten_promise_create__deps: ['$makePromise'],
3141
emscripten_promise_create: function() {
3242
return makePromise().id;
@@ -146,13 +156,9 @@ mergeInto(LibraryManager.library, {
146156
return newId;
147157
},
148158

149-
emscripten_promise_all__deps: ['$promiseMap', '$getPromise'],
159+
emscripten_promise_all__deps: ['$promiseMap', '$idsToPromises'],
150160
emscripten_promise_all: function(idBuf, resultBuf, size) {
151-
var promises = [];
152-
for (var i = 0; i < size; i++) {
153-
var id = {{{ makeGetValue('idBuf', `i*${POINTER_SIZE}`, 'i32') }}};
154-
promises[i] = getPromise(id);
155-
}
161+
var promises = idsToPromises(idBuf, size);
156162
#if RUNTIME_DEBUG
157163
dbg('emscripten_promise_all: ' + promises);
158164
#endif
@@ -169,6 +175,43 @@ mergeInto(LibraryManager.library, {
169175
});
170176
#if RUNTIME_DEBUG
171177
dbg('create: ' + id);
178+
#endif
179+
return id;
180+
},
181+
182+
emscripten_promise_all_settled__deps: ['$promiseMap', '$idsToPromises'],
183+
emscripten_promise_all_settled: function(idBuf, resultBuf, size) {
184+
var promises = idsToPromises(idBuf, size);
185+
#if RUNTIME_DEBUG
186+
dbg('emscripten_promise_all_settled: ' + promises);
187+
#endif
188+
var id = promiseMap.allocate({
189+
promise: Promise.allSettled(promises).then((results) => {
190+
if (resultBuf) {
191+
for (var i = 0; i < size; i++) {
192+
var baseOffset = i * {{{ C_STRUCTS.em_settled_result_t.__size__ }}};
193+
var resultOffset =
194+
baseOffset + {{{ C_STRUCTS.em_settled_result_t.result }}};
195+
var valueOffset =
196+
baseOffset + {{{ C_STRUCTS.em_settled_result_t.value }}};
197+
if (results[i].status === 'fulfilled') {
198+
var fulfill = {{{ cDefs.EM_PROMISE_FULFILL }}};
199+
{{{ makeSetValue('resultBuf', 'resultOffset', 'fulfill', 'i32') }}};
200+
{{{ makeSetValue('resultBuf', 'valueOffset', 'results[i].value', '*') }}};
201+
} else {
202+
var reject = {{{ cDefs.EM_PROMISE_REJECT }}};
203+
{{{ makeSetValue('resultBuf', 'resultOffset', 'reject', 'i32') }}};
204+
// Closure can't type `reason` in some contexts.
205+
var reason = /** @type {number} */ (results[i].reason);
206+
{{{ makeSetValue('resultBuf', 'valueOffset', 'reason', '*') }}};
207+
}
208+
}
209+
}
210+
return resultBuf;
211+
})
212+
});
213+
#if RUNTIME_DEBUG
214+
dbg('create: ' + id);
172215
#endif
173216
return id;
174217
},

src/library_sigs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ sigs = {
534534
emscripten_performance_now__sig: 'd',
535535
emscripten_print_double__sig: 'idpi',
536536
emscripten_promise_all__sig: 'pppp',
537+
emscripten_promise_all_settled__sig: 'pppp',
537538
emscripten_promise_create__sig: 'p',
538539
emscripten_promise_destroy__sig: 'vp',
539540
emscripten_promise_resolve__sig: 'vpip',

src/struct_info.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@
262262
"O_PATH",
263263
"O_NONBLOCK",
264264
"O_CLOEXEC",
265-
"F_GETOWN",
265+
"F_GETOWN",
266266
"F_GETOWN_EX",
267267
"F_SETFD",
268268
"O_EXCL",
@@ -1095,7 +1095,13 @@
10951095
"EM_PROMISE_MATCH",
10961096
"EM_PROMISE_MATCH_RELEASE",
10971097
"EM_PROMISE_REJECT"
1098-
]
1098+
],
1099+
"structs": {
1100+
"em_settled_result_t": [
1101+
"result",
1102+
"value"
1103+
]
1104+
}
10991105
},
11001106
{
11011107
"file": "AL/al.h",

system/include/emscripten/promise.h

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,29 @@ emscripten_promise_then(em_promise_t promise,
9292
void* data);
9393

9494
// Call Promise.all to create and return a new promise that is either fulfilled
95-
// once the `num_promises` input promises in the `promises` have been fulfilled
96-
// or is rejected once any of the input promises has been rejected. When the
97-
// returned promise is fulfilled, the values each of the input promises were
98-
// resolved with will be written to the `results` array and the returned promise
99-
// will be fulfilled with the address of that array as well.
95+
// once the `num_promises` input promises passed in `promises` have been
96+
// fulfilled or is rejected once any of the input promises has been rejected.
97+
// When the returned promise is fulfilled, the values each of the input promises
98+
// were resolved with will be written to the `results` array if it is non-null
99+
// and the returned promise will be fulfilled with the address of that array as
100+
// well.
100101
__attribute__((warn_unused_result)) em_promise_t emscripten_promise_all(
101102
em_promise_t* promises, void** results, size_t num_promises);
102103

103-
// TODO: emscripten_promise_all_settled
104-
// TODO: emscripten_promise_race
105-
// TODO: emscripten_promise_any
104+
typedef struct em_settled_result_t {
105+
em_promise_result_t result;
106+
void* value;
107+
} em_settled_result_t;
108+
109+
// Call Promise.allSettled to create and return a new promise that is fulfilled
110+
// once the `num_promises` input promises passed in `promises` have been
111+
// settled. When the returned promise is fulfilled, the `results` buffer will be
112+
// filled with the result comprising of either EM_PROMISE_FULFILL and the
113+
// fulfilled value or EM_PROMISE_REJECT and the rejection reason for each of the
114+
// input promises if `results` is non-null. The returned promise will be
115+
// fulfilled with the value of `results` as well.
116+
__attribute__((warn_unused_result)) em_promise_t emscripten_promise_all_settled(
117+
em_promise_t* promises, em_settled_result_t* results, size_t num_promises);
106118

107119
#ifdef __cplusplus
108120
}

test/core/test_promise.c

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,102 @@ static em_promise_result_t test_all(void** result, void* data, void* value) {
248248
return EM_PROMISE_MATCH_RELEASE;
249249
}
250250

251+
typedef struct promise_all_settled_state {
252+
size_t size;
253+
em_promise_t in[3];
254+
em_settled_result_t out[3];
255+
em_settled_result_t expected[3];
256+
} promise_all_settled_state;
257+
258+
static em_promise_result_t
259+
check_promise_all_settled_results(void** result, void* data, void* value) {
260+
promise_all_settled_state* state = (promise_all_settled_state*)data;
261+
assert(value == state->out);
262+
emscripten_console_log("promise_all_settled results:");
263+
for (size_t i = 0; i < state->size; ++i) {
264+
emscripten_console_logf(
265+
"%s %ld",
266+
state->out[i].result == EM_PROMISE_FULFILL ? "fulfill" : "reject",
267+
(uintptr_t)state->out[i].value);
268+
assert(state->out[i].result == state->expected[i].result);
269+
assert(state->out[i].value == state->expected[i].value);
270+
}
271+
free(state);
272+
return EM_PROMISE_FULFILL;
273+
}
274+
275+
static em_promise_result_t check_null(void** result, void* data, void* value) {
276+
assert(value == NULL);
277+
return EM_PROMISE_FULFILL;
278+
}
279+
280+
static em_promise_result_t
281+
test_all_settled(void** result, void* data, void* value) {
282+
emscripten_console_log("test_all_settled");
283+
assert(data == (void*)5);
284+
285+
// No input should be handled ok.
286+
promise_all_settled_state* state = malloc(sizeof(promise_all_settled_state));
287+
*state =
288+
(promise_all_settled_state){.size = 0, .in = {}, .out = {}, .expected = {}};
289+
em_promise_t empty =
290+
emscripten_promise_all_settled(state->in, state->out, state->size);
291+
em_promise_t empty_checked = emscripten_promise_then(
292+
empty, check_promise_all_settled_results, fail, state);
293+
emscripten_promise_destroy(empty);
294+
295+
// Fulfilled and rejected inputs should be reported.
296+
state = malloc(sizeof(promise_all_settled_state));
297+
*state = (promise_all_settled_state){.size = 3,
298+
.in = {emscripten_promise_create(),
299+
emscripten_promise_create(),
300+
emscripten_promise_create()},
301+
.out = {},
302+
.expected = {
303+
{EM_PROMISE_FULFILL, (void*)42},
304+
{EM_PROMISE_REJECT, (void*)43},
305+
{EM_PROMISE_FULFILL, (void*)44},
306+
}};
307+
em_promise_t full =
308+
emscripten_promise_all_settled(state->in, state->out, state->size);
309+
em_promise_t full_checked = emscripten_promise_then(
310+
full, check_promise_all_settled_results, fail, state);
311+
emscripten_promise_destroy(full);
312+
emscripten_promise_resolve(state->in[0], EM_PROMISE_FULFILL, (void*)42);
313+
emscripten_promise_resolve(state->in[1], EM_PROMISE_REJECT, (void*)43);
314+
emscripten_promise_resolve(state->in[2], EM_PROMISE_FULFILL, (void*)44);
315+
emscripten_promise_destroy(state->in[0]);
316+
emscripten_promise_destroy(state->in[1]);
317+
emscripten_promise_destroy(state->in[2]);
318+
319+
// Null buffer should be ok.
320+
em_promise_t null_in[3] = {
321+
emscripten_promise_create(),
322+
emscripten_promise_create(),
323+
emscripten_promise_create(),
324+
};
325+
em_promise_t null = emscripten_promise_all_settled(null_in, NULL, 3);
326+
em_promise_t null_checked =
327+
emscripten_promise_then(null, check_null, fail, NULL);
328+
emscripten_promise_destroy(null);
329+
emscripten_promise_resolve(null_in[0], EM_PROMISE_REJECT, (void*)42);
330+
emscripten_promise_resolve(null_in[1], EM_PROMISE_FULFILL, (void*)43);
331+
emscripten_promise_resolve(null_in[2], EM_PROMISE_REJECT, (void*)44);
332+
emscripten_promise_destroy(null_in[0]);
333+
emscripten_promise_destroy(null_in[1]);
334+
emscripten_promise_destroy(null_in[2]);
335+
336+
em_promise_t to_finish[3] = {empty_checked, full_checked, null_checked};
337+
em_promise_t finish_test_all = emscripten_promise_all(to_finish, NULL, 3);
338+
339+
emscripten_promise_destroy(empty_checked);
340+
emscripten_promise_destroy(full_checked);
341+
emscripten_promise_destroy(null_checked);
342+
343+
*result = finish_test_all;
344+
return EM_PROMISE_MATCH_RELEASE;
345+
}
346+
251347
static em_promise_result_t finish(void** result, void* data, void* value) {
252348
emscripten_console_logf("finish");
253349

@@ -292,8 +388,10 @@ int main() {
292388
em_promise_t test3 =
293389
emscripten_promise_then(test2, test_rejection, fail, (void*)3);
294390
em_promise_t test4 = emscripten_promise_then(test3, test_all, fail, (void*)4);
391+
em_promise_t test5 =
392+
emscripten_promise_then(test4, test_all_settled, fail, (void*)5);
295393
em_promise_t assert_stack =
296-
emscripten_promise_then(test4, check_stack, fail, NULL);
394+
emscripten_promise_then(test5, check_stack, fail, NULL);
297395
em_promise_t end = emscripten_promise_then(assert_stack, finish, fail, NULL);
298396

299397
emscripten_promise_resolve(start, EM_PROMISE_FULFILL, NULL);
@@ -304,6 +402,7 @@ int main() {
304402
emscripten_promise_destroy(test2);
305403
emscripten_promise_destroy(test3);
306404
emscripten_promise_destroy(test4);
405+
emscripten_promise_destroy(test5);
307406
emscripten_promise_destroy(assert_stack);
308407
emscripten_promise_destroy(end);
309408

test/core/test_promise.out

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,10 @@ promise_all results:
1515
1337
1616
0
1717
promise_all error: 1337
18+
test_all_settled
19+
promise_all_settled results:
20+
promise_all_settled results:
21+
fulfill 42
22+
reject 43
23+
fulfill 44
1824
finish

0 commit comments

Comments
 (0)