Skip to content

Commit 5a8a8dd

Browse files
authored
[Proxying] Add proxying functions that return promises (#18825)
Add `emscripten_proxy_promise` and `emscripten_proxy_promise_with_ctx` that return `em_promise_t` handles to promises that are fulfilled when the proxied work is completed or rejected when the target thread dies before it can complete the work. Do not document these new APIs yet since they depend on the promise API in emscripten/promise.h, which is not yet documented. Similarly, do not add a C++ wrapper yet because emscripten/promise.h does not yet have a C++ API.
1 parent c4693fe commit 5a8a8dd

File tree

6 files changed

+392
-107
lines changed

6 files changed

+392
-107
lines changed

system/include/emscripten/proxying.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#pragma once
99

1010
#include <emscripten/emscripten.h>
11+
#include <emscripten/promise.h>
1112
#include <pthread.h>
1213

1314
#ifdef __cplusplus
@@ -102,6 +103,18 @@ int emscripten_proxy_callback_with_ctx(em_proxying_queue* q,
102103
void (*cancel)(void*),
103104
void* arg);
104105

106+
__attribute__((warn_unused_result)) em_promise_t
107+
emscripten_proxy_promise(em_proxying_queue* q,
108+
pthread_t target_thread,
109+
void (*func)(void*),
110+
void* arg);
111+
112+
__attribute__((warn_unused_result)) em_promise_t
113+
emscripten_proxy_promise_with_ctx(em_proxying_queue* q,
114+
pthread_t target_thread,
115+
void (*func)(em_proxying_ctx*, void*),
116+
void* arg);
117+
105118
#ifdef __cplusplus
106119
} // extern "C"
107120

system/lib/pthread/proxying.c

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ int emscripten_proxy_sync_with_ctx(em_proxying_queue* q,
389389
}
390390

391391
// Helper for signaling the end of the task after the user function returns.
392-
static void call_then_finish_sync(em_proxying_ctx* ctx, void* arg) {
392+
static void call_then_finish_task(em_proxying_ctx* ctx, void* arg) {
393393
task* t = arg;
394394
t->func(t->arg);
395395
emscripten_proxy_finish(ctx);
@@ -401,7 +401,7 @@ int emscripten_proxy_sync(em_proxying_queue* q,
401401
void* arg) {
402402
task t = {.func = func, .arg = arg};
403403
return emscripten_proxy_sync_with_ctx(
404-
q, target_thread, call_then_finish_sync, &t);
404+
q, target_thread, call_then_finish_task, &t);
405405
}
406406

407407
static int do_proxy_callback(em_proxying_queue* q,
@@ -465,7 +465,8 @@ int emscripten_proxy_callback(em_proxying_queue* q,
465465
void (*callback)(void*),
466466
void (*cancel)(void*),
467467
void* arg) {
468-
// Allocate the em_proxying_ctx and the user ctx as a single block.
468+
// Allocate the em_proxying_ctx and the user ctx as a single block that will
469+
// be freed when the `em_proxying_ctx` is freed.
469470
struct block {
470471
em_proxying_ctx ctx;
471472
callback_ctx cb_ctx;
@@ -483,3 +484,99 @@ int emscripten_proxy_callback(em_proxying_queue* q,
483484
&block->cb_ctx,
484485
&block->ctx);
485486
}
487+
488+
typedef struct promise_ctx {
489+
void (*func)(em_proxying_ctx*, void*);
490+
void* arg;
491+
em_promise_t promise;
492+
} promise_ctx;
493+
494+
static void promise_call(em_proxying_ctx* ctx, void* arg) {
495+
promise_ctx* promise_ctx = arg;
496+
promise_ctx->func(ctx, promise_ctx->arg);
497+
}
498+
499+
static void promise_fulfill(void* arg) {
500+
promise_ctx* promise_ctx = arg;
501+
emscripten_promise_resolve(promise_ctx->promise, EM_PROMISE_FULFILL, NULL);
502+
emscripten_promise_destroy(promise_ctx->promise);
503+
}
504+
505+
static void promise_reject(void* arg) {
506+
promise_ctx* promise_ctx = arg;
507+
emscripten_promise_resolve(promise_ctx->promise, EM_PROMISE_REJECT, NULL);
508+
emscripten_promise_destroy(promise_ctx->promise);
509+
}
510+
511+
static em_promise_t do_proxy_promise(em_proxying_queue* q,
512+
pthread_t target_thread,
513+
void (*func)(em_proxying_ctx*, void*),
514+
void* arg,
515+
em_promise_t promise,
516+
em_proxying_ctx* ctx,
517+
promise_ctx* promise_ctx) {
518+
*promise_ctx = (struct promise_ctx){func, arg, promise};
519+
if (!do_proxy_callback(q,
520+
target_thread,
521+
promise_call,
522+
promise_fulfill,
523+
promise_reject,
524+
promise_ctx,
525+
ctx)) {
526+
emscripten_promise_resolve(promise, EM_PROMISE_REJECT, NULL);
527+
return promise;
528+
}
529+
// Return a separate promise to ensure that the internal promise will stay
530+
// alive until the callbacks are called.
531+
em_promise_t ret = emscripten_promise_create();
532+
emscripten_promise_resolve(ret, EM_PROMISE_MATCH, promise);
533+
return ret;
534+
}
535+
536+
em_promise_t emscripten_proxy_promise_with_ctx(em_proxying_queue* q,
537+
pthread_t target_thread,
538+
void (*func)(em_proxying_ctx*,
539+
void*),
540+
void* arg) {
541+
em_promise_t promise = emscripten_promise_create();
542+
// Allocate the em_proxying_ctx and promise ctx as a single block that will be
543+
// freed when the `em_proxying_ctx` is freed.
544+
struct block {
545+
em_proxying_ctx ctx;
546+
promise_ctx promise_ctx;
547+
};
548+
struct block* block = malloc(sizeof(*block));
549+
if (block == NULL) {
550+
emscripten_promise_resolve(promise, EM_PROMISE_REJECT, NULL);
551+
return promise;
552+
}
553+
return do_proxy_promise(
554+
q, target_thread, func, arg, promise, &block->ctx, &block->promise_ctx);
555+
}
556+
557+
em_promise_t emscripten_proxy_promise(em_proxying_queue* q,
558+
pthread_t target_thread,
559+
void (*func)(void*),
560+
void* arg) {
561+
em_promise_t promise = emscripten_promise_create();
562+
// Allocate the em_proxying_ctx, promise ctx, and user task as a single block
563+
// that will be freed when the `em_proxying_ctx` is freed.
564+
struct block {
565+
em_proxying_ctx ctx;
566+
promise_ctx promise_ctx;
567+
task task;
568+
};
569+
struct block* block = malloc(sizeof(*block));
570+
if (block == NULL) {
571+
emscripten_promise_resolve(promise, EM_PROMISE_REJECT, NULL);
572+
return promise;
573+
}
574+
block->task = (task){.func = func, .arg = arg};
575+
return do_proxy_promise(q,
576+
target_thread,
577+
call_then_finish_task,
578+
&block->task,
579+
promise,
580+
&block->ctx,
581+
&block->promise_ctx);
582+
}

0 commit comments

Comments
 (0)