Skip to content

Commit 2c30e73

Browse files
committed
awaitable inlined js
1 parent 8686d5b commit 2c30e73

19 files changed

+219
-21
lines changed

src/jsifier.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ function(${args}) {
374374
return `
375375
${async_}function(${args}) {
376376
if (ENVIRONMENT_IS_PTHREAD)
377-
return ${proxyFunc}(${proxiedFunctionTable.length}, 0, ${+sync}${args ? ', ' : ''}${args});
377+
return ${proxyFunc}(${proxiedFunctionTable.length}, 0, ${+sync}, 0${args ? ', ' : ''}${args});
378378
${body}
379379
}\n`;
380380
});

src/lib/libcore.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -1589,12 +1589,13 @@ addToLibrary({
15891589
return runEmAsmFunction(code, sigPtr, argbuf);
15901590
},
15911591

1592+
$runMainThreadEmAsm__docs: '/** @param {number=} asyncAwait */',
15921593
$runMainThreadEmAsm__deps: ['$readEmAsmArgs',
15931594
#if PTHREADS
15941595
'$proxyToMainThread'
15951596
#endif
15961597
],
1597-
$runMainThreadEmAsm: (emAsmAddr, sigPtr, argbuf, sync) => {
1598+
$runMainThreadEmAsm: (emAsmAddr, sigPtr, argbuf, sync, asyncAwait) => {
15981599
var args = readEmAsmArgs(sigPtr, argbuf);
15991600
#if PTHREADS
16001601
if (ENVIRONMENT_IS_PTHREAD) {
@@ -1607,7 +1608,7 @@ addToLibrary({
16071608
// of using __proxy. (And dor simplicity, do the same in the sync
16081609
// case as well, even though it's not strictly necessary, to keep the two
16091610
// code paths as similar as possible on both sides.)
1610-
return proxyToMainThread(0, emAsmAddr, sync, ...args);
1611+
return proxyToMainThread(0, emAsmAddr, sync, asyncAwait, ...args);
16111612
}
16121613
#endif
16131614
#if ASSERTIONS
@@ -1618,6 +1619,19 @@ addToLibrary({
16181619
emscripten_asm_const_int_sync_on_main_thread__deps: ['$runMainThreadEmAsm'],
16191620
emscripten_asm_const_int_sync_on_main_thread: (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1),
16201621

1622+
emscripten_asm_const_int_await_on_main_thread__deps: ['$runMainThreadEmAsm'],
1623+
emscripten_asm_const_int_await_on_main_thread: (emAsmAddr, sigPtr, argbuf) => {
1624+
#if PTHREADS
1625+
if (ENVIRONMENT_IS_PTHREAD) {
1626+
return runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, /*sync=*/1, /*asyncAwait=*/1);
1627+
}
1628+
#endif
1629+
#if ASSERTIONS
1630+
assert((typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD), "emscripten_asm_const_int_await_on_main_thread is not available on the main thread");
1631+
#endif
1632+
return runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, /*sync*/1, /*asyncAwait=*/1);
1633+
},
1634+
16211635
emscripten_asm_const_ptr_sync_on_main_thread__deps: ['$runMainThreadEmAsm'],
16221636
emscripten_asm_const_ptr_sync_on_main_thread: (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1),
16231637

src/lib/libpthread.js

+32-5
Original file line numberDiff line numberDiff line change
@@ -899,9 +899,9 @@ var LibraryPThread = {
899899
$proxyToMainThreadPtr: (...args) => BigInt(proxyToMainThread(...args)),
900900
#endif
901901
902-
$proxyToMainThread__deps: ['$stackSave', '$stackRestore', '$stackAlloc', '_emscripten_run_on_main_thread_js', ...i53ConversionDeps],
903-
$proxyToMainThread__docs: '/** @type{function(number, (number|boolean), ...number)} */',
904-
$proxyToMainThread: (funcIndex, emAsmAddr, sync, ...callArgs) => {
902+
$proxyToMainThread__deps: ['$stackSave', '$stackRestore', '$stackAlloc', '_emscripten_run_on_main_thread_js', '_emscripten_await_on_main_thread_js', ...i53ConversionDeps],
903+
$proxyToMainThread__docs: '/** @type{function(number, (number|boolean), number, (number|undefined), ...number)} */',
904+
$proxyToMainThread: (funcIndex, emAsmAddr, sync, asyncAwait, ...callArgs) => {
905905
// EM_ASM proxying is done by passing a pointer to the address of the EM_ASM
906906
// content as `emAsmAddr`. JS library proxying is done by passing an index
907907
// into `proxiedJSCallArgs` as `funcIndex`. If `emAsmAddr` is non-zero then
@@ -939,7 +939,12 @@ var LibraryPThread = {
939939
HEAPF64[b + i] = arg;
940940
#endif
941941
}
942-
var rtn = __emscripten_run_on_main_thread_js(funcIndex, emAsmAddr, serializedNumCallArgs, args, sync);
942+
var rtn;
943+
if (asyncAwait) {
944+
rtn = __emscripten_await_on_main_thread_js(funcIndex, emAsmAddr, serializedNumCallArgs, args);
945+
} else {
946+
rtn = __emscripten_run_on_main_thread_js(funcIndex, emAsmAddr, serializedNumCallArgs, args, sync);
947+
}
943948
stackRestore(sp);
944949
return rtn;
945950
},
@@ -950,7 +955,11 @@ var LibraryPThread = {
950955
_emscripten_receive_on_main_thread_js__deps: [
951956
'$proxyToMainThread',
952957
'$proxiedJSCallArgs'],
953-
_emscripten_receive_on_main_thread_js: (funcIndex, emAsmAddr, callingThread, numCallArgs, args) => {
958+
/**
959+
* @param {number=} promiseCtx Optionally, when set, expect func to return a Promise
960+
* and use promiseCtx to signal awaiting pthread.
961+
*/
962+
_emscripten_receive_on_main_thread_js: (funcIndex, emAsmAddr, callingThread, numCallArgs, args, promiseCtx) => {
954963
// Sometimes we need to backproxy events to the calling thread (e.g.
955964
// HTML5 DOM events handlers such as
956965
// emscripten_set_mousemove_callback()), so keep track in a globally
@@ -989,6 +998,24 @@ var LibraryPThread = {
989998
PThread.currentProxiedOperationCallerThread = callingThread;
990999
var rtn = func(...proxiedJSCallArgs);
9911000
PThread.currentProxiedOperationCallerThread = 0;
1001+
if (promiseCtx) {
1002+
#if ASSERTIONS
1003+
assert(!!rtn.then, 'Return value of proxied function expected to be a Promise but got' + rtn);
1004+
#endif
1005+
rtn.then(res => {
1006+
#if MEMORY64
1007+
// In memory64 mode some proxied functions return bigint/pointer but
1008+
// our return type is i53/double.
1009+
if (typeof res == "bigint") {
1010+
res = bigintToI53Checked(res);
1011+
}
1012+
#endif
1013+
__emscripten_proxy_promise_finish(promiseCtx, res);
1014+
}).catch(err => {
1015+
__emscripten_proxy_promise_finish(promiseCtx, 0);
1016+
});
1017+
return;
1018+
}
9921019
#if MEMORY64
9931020
// In memory64 mode some proxied functions return bigint/pointer but
9941021
// our return type is i53/double.

src/lib/libsigs.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ sigs = {
325325
_emscripten_notify_mailbox_postmessage__sig: 'vpp',
326326
_emscripten_push_main_loop_blocker__sig: 'vppp',
327327
_emscripten_push_uncounted_main_loop_blocker__sig: 'vppp',
328-
_emscripten_receive_on_main_thread_js__sig: 'dippip',
328+
_emscripten_receive_on_main_thread_js__sig: 'dippipp',
329329
_emscripten_runtime_keepalive_clear__sig: 'v',
330330
_emscripten_system__sig: 'ip',
331331
_emscripten_thread_cleanup__sig: 'vp',
@@ -572,6 +572,7 @@ sigs = {
572572
emscripten_asm_const_double__sig: 'dppp',
573573
emscripten_asm_const_double_sync_on_main_thread__sig: 'dppp',
574574
emscripten_asm_const_int__sig: 'ippp',
575+
emscripten_asm_const_int_await_on_main_thread__sig: 'ippp',
575576
emscripten_asm_const_int_sync_on_main_thread__sig: 'ippp',
576577
emscripten_asm_const_ptr__sig: 'pppp',
577578
emscripten_asm_const_ptr_sync_on_main_thread__sig: 'pppp',

system/include/emscripten/em_asm.h

+11
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ __attribute__((nothrow))
2828
int emscripten_asm_const_int_sync_on_main_thread(
2929
const char* code, const char* arg_sigs, ...);
3030
__attribute__((nothrow))
31+
int emscripten_asm_const_int_await_on_main_thread(
32+
const char* code, const char* arg_sigs, ...);
33+
__attribute__((nothrow))
3134
void* emscripten_asm_const_ptr_sync_on_main_thread(
3235
const char* code, const char* arg_sigs, ...);
3336
__attribute__((nothrow))
@@ -51,6 +54,7 @@ void emscripten_asm_const_async_on_main_thread(
5154
#define EM_ASM_PTR(...) EM_ASM_ERROR
5255
#define EM_ASM_DOUBLE(...) EM_ASM_ERROR
5356
#define MAIN_THREAD_EM_ASM(...) EM_ASM_ERROR
57+
#define MAIN_THREAD_EM_ASM_AWAIT(...) EM_ASM_ERROR
5458
#define MAIN_THREAD_EM_ASM_INT(...) EM_ASM_ERROR
5559
#define MAIN_THREAD_EM_ASM_PTR(...) EM_ASM_ERROR
5660
#define MAIN_THREAD_EM_ASM_DOUBLE(...) EM_ASM_ERROR
@@ -250,6 +254,13 @@ const char __em_asm_sig_builder<__em_asm_type_tuple<Args...> >::buffer[] = { __e
250254
// functions.
251255
#define MAIN_THREAD_EM_ASM(code, ...) ((void)emscripten_asm_const_int_sync_on_main_thread(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__)))
252256

257+
// Runs the given Javascript code on the main browser thread.
258+
// It must be called from a non-main thread.
259+
// The code must return a promise, and this function will wait for the promise
260+
// to resolve or reject, essentially blocking the calling thread until then.
261+
// In either case the function will return an integer, which is the result of the promise.
262+
#define MAIN_THREAD_EM_ASM_AWAIT(code, ...) emscripten_asm_const_int_await_on_main_thread(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__))
263+
253264
// Runs the given JavaScript code synchronously on the main browser thread, and
254265
// returns an integer back.
255266
// The same considerations apply as with MAIN_THREAD_EM_ASM().

system/lib/pthread/proxying.c

+47-1
Original file line numberDiff line numberDiff line change
@@ -591,12 +591,15 @@ typedef struct proxied_js_func_t {
591591
double* argBuffer;
592592
double result;
593593
bool owned;
594+
// Only used when the underlying js func is async.
595+
// Can be null when the function is sync.
596+
em_proxying_ctx * ctx;
594597
} proxied_js_func_t;
595598

596599
static void run_js_func(void* arg) {
597600
proxied_js_func_t* f = (proxied_js_func_t*)arg;
598601
f->result = _emscripten_receive_on_main_thread_js(
599-
f->funcIndex, f->emAsmAddr, f->callingThread, f->numArgs, f->argBuffer);
602+
f->funcIndex, f->emAsmAddr, f->callingThread, f->numArgs, f->argBuffer, f->ctx);
600603
if (f->owned) {
601604
free(f->argBuffer);
602605
free(f);
@@ -615,6 +618,7 @@ double _emscripten_run_on_main_thread_js(int func_index,
615618
.numArgs = num_args,
616619
.argBuffer = buffer,
617620
.owned = false,
621+
.ctx = NULL,
618622
};
619623

620624
em_proxying_queue* q = emscripten_proxy_get_system_queue();
@@ -642,3 +646,45 @@ double _emscripten_run_on_main_thread_js(int func_index,
642646
}
643647
return 0;
644648
}
649+
650+
static void call_proxied_js_task_with_ctx(em_proxying_ctx* ctx, void* arg) {
651+
task* t = arg;
652+
proxied_js_func_t* p = t->arg;
653+
p->ctx = ctx;
654+
t->func(t->arg);
655+
}
656+
657+
double _emscripten_await_on_main_thread_js(int func_index,
658+
void* em_asm_addr,
659+
int num_args,
660+
double* buffer) {
661+
em_proxying_queue* q = emscripten_proxy_get_system_queue();
662+
pthread_t target = emscripten_main_runtime_thread_id();
663+
664+
proxied_js_func_t f = {
665+
.funcIndex = func_index,
666+
.emAsmAddr = em_asm_addr,
667+
.callingThread = pthread_self(),
668+
.numArgs = num_args,
669+
.argBuffer = buffer,
670+
.owned = false,
671+
};
672+
task t = {.func = run_js_func, .arg = &f};
673+
674+
if (!emscripten_proxy_sync_with_ctx(q, target, call_proxied_js_task_with_ctx, &t)) {
675+
assert(false && "emscripten_proxy_sync_with_ctx failed");
676+
return 0;
677+
}
678+
return f.result;
679+
}
680+
681+
void _emscripten_proxy_promise_finish(em_proxying_ctx* ctx, void* res) {
682+
task* t = (task*)ctx->arg;
683+
proxied_js_func_t* func = (proxied_js_func_t*)t->arg;
684+
if (res == NULL) {
685+
func->result = 0;
686+
} else {
687+
func->result = (double)(intptr_t)res;
688+
}
689+
emscripten_proxy_finish(ctx);
690+
}

system/lib/pthread/threading_internal.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ int __pthread_create_js(struct __pthread *thread, const pthread_attr_t *attr, vo
9696
int _emscripten_default_pthread_stack_size();
9797
void __set_thread_state(pthread_t ptr, int is_main, int is_runtime, int can_block);
9898

99-
double _emscripten_receive_on_main_thread_js(int funcIndex, void* emAsmAddr, pthread_t callingThread, int numCallArgs, double* args);
99+
double _emscripten_receive_on_main_thread_js(int funcIndex, void* emAsmAddr, pthread_t callingThread, int numCallArgs, double* args, void *ctx);
100100

101101
// Return non-zero if the calling thread supports Atomic.wait (For example
102102
// if called from the main browser thread, this function will return zero
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2024 The Emscripten Authors. All rights reserved.
2+
// Emscripten is available under two separate licenses, the MIT license and the
3+
// University of Illinois/NCSA Open Source License. Both these licenses can be
4+
// found in the LICENSE file.
5+
6+
#include <emscripten.h>
7+
#include <stdio.h>
8+
9+
int main()
10+
{
11+
printf("Before MAIN_THREAD_EM_ASM_AWAIT\n");
12+
int res = MAIN_THREAD_EM_ASM_AWAIT({
13+
out('Inside MAIN_THREAD_EM_ASM_AWAIT: ' + $0 + ' ' + $1);
14+
const asyncOp = new Promise((resolve,reject) => {
15+
setTimeout(() => {
16+
out('Inside asyncOp');
17+
resolve(2);
18+
}, 1000);
19+
});
20+
return asyncOp;
21+
}, 42, 3.5);
22+
printf("After MAIN_THREAD_EM_ASM_AWAIT\n");
23+
printf("result: %d\n", res);
24+
return 0;
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Before MAIN_THREAD_EM_ASM_AWAIT
2+
Inside MAIN_THREAD_EM_ASM_AWAIT: 42 3.5
3+
Inside asyncOp
4+
After MAIN_THREAD_EM_ASM_AWAIT
5+
result: 2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2024 The Emscripten Authors. All rights reserved.
2+
// Emscripten is available under two separate licenses, the MIT license and the
3+
// University of Illinois/NCSA Open Source License. Both these licenses can be
4+
// found in the LICENSE file.
5+
6+
#include <emscripten.h>
7+
#include <stdio.h>
8+
#include <pthread.h>
9+
10+
int main()
11+
{
12+
// start new thread
13+
pthread_t thread;
14+
pthread_create(&thread, NULL, [](void*) -> void* {
15+
printf("Before MAIN_THREAD_EM_ASM_AWAIT\n");
16+
int res = MAIN_THREAD_EM_ASM_AWAIT({
17+
out('Inside MAIN_THREAD_EM_ASM_AWAIT: ' + $0 + ' ' + $1);
18+
const asyncOp = new Promise((resolve,reject) => {
19+
setTimeout(() => {
20+
out('Inside asyncOp');
21+
reject(new Error('asyncOp rejected'));
22+
}, 1000);
23+
});
24+
return asyncOp;
25+
}, 42, 3.5);
26+
printf("After MAIN_THREAD_EM_ASM_AWAIT rejected\n");
27+
printf("result: %d\n", res);
28+
return NULL;
29+
}, NULL);
30+
31+
// wait for thread to finish
32+
pthread_join(thread, NULL);
33+
return 0;
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Before MAIN_THREAD_EM_ASM_AWAIT
2+
Inside MAIN_THREAD_EM_ASM_AWAIT: 42 3.5
3+
Inside asyncOp
4+
After MAIN_THREAD_EM_ASM_AWAIT rejected
5+
result: 0

test/other/codesize/test_codesize_hello_dylink.funcs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ $__fwritex
44
$__stdio_write
55
$__towrite
66
$__wasm_apply_data_relocs
7+
$__wasm_apply_global_relocs
78
$__wasm_call_ctors
8-
$__wasm_start
99
$_emscripten_stack_alloc
1010
$_emscripten_stack_restore
1111
$dlcalloc
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8176
1+
9712
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
A (_emscripten_thread_exit)
2-
B (_emscripten_check_mailbox)
3-
C (emscripten_stack_set_limits)
4-
D (_emscripten_stack_restore)
5-
E (_emscripten_stack_alloc)
6-
F (emscripten_stack_get_current)
1+
A (_emscripten_proxy_promise_finish)
2+
B (_emscripten_thread_free_data)
3+
C (_emscripten_thread_exit)
4+
D (_emscripten_check_mailbox)
5+
E (emscripten_stack_set_limits)
6+
F (_emscripten_stack_restore)
7+
G (_emscripten_stack_alloc)
8+
H (emscripten_stack_get_current)
79
p (__wasm_call_ctors)
810
q (add)
911
r (main)
@@ -14,4 +16,4 @@ v (_emscripten_proxy_main)
1416
w (_emscripten_thread_init)
1517
x (_emscripten_thread_crashed)
1618
y (_emscripten_run_on_main_thread_js)
17-
z (_emscripten_thread_free_data)
19+
z (_emscripten_await_on_main_thread_js)

test/other/codesize/test_codesize_minimal_pthreads.funcs

+5
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ $__wasi_syscall_ret
2525
$__wasm_call_ctors
2626
$__wasm_init_memory
2727
$__wasm_init_tls
28+
$_emscripten_await_on_main_thread_js
2829
$_emscripten_check_mailbox
2930
$_emscripten_proxy_main
31+
$_emscripten_proxy_promise_finish
3032
$_emscripten_run_on_main_thread_js
3133
$_emscripten_stack_alloc
3234
$_emscripten_stack_restore
@@ -50,6 +52,7 @@ $a_swap
5052
$add
5153
$call_callback_then_free_ctx
5254
$call_cancel_then_free_ctx
55+
$call_proxied_js_task_with_ctx
5356
$call_then_finish_task
5457
$call_with_ctx
5558
$cancel_active_ctxs
@@ -69,6 +72,8 @@ $emscripten_builtin_free
6972
$emscripten_builtin_malloc
7073
$emscripten_futex_wait
7174
$emscripten_futex_wake
75+
$emscripten_proxy_finish
76+
$emscripten_proxy_sync_with_ctx
7277
$emscripten_stack_get_current
7378
$emscripten_stack_set_limits
7479
$free_ctx

0 commit comments

Comments
 (0)