Skip to content

Commit f02fff2

Browse files
committed
awaitable inlined js
1 parent 242af00 commit f02fff2

17 files changed

+213
-19
lines changed

Diff for: src/jsifier.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ function(${args}) {
357357
return `
358358
${async_}function(${args}) {
359359
if (ENVIRONMENT_IS_PTHREAD)
360-
return ${proxyFunc}(${proxiedFunctionTable.length}, 0, ${+sync}${args ? ', ' : ''}${args});
360+
return ${proxyFunc}(${proxiedFunctionTable.length}, 0, ${+sync}, 0${args ? ', ' : ''}${args});
361361
${body}
362362
}\n`;
363363
});

Diff for: src/lib/libcore.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -1594,12 +1594,13 @@ addToLibrary({
15941594
return runEmAsmFunction(code, sigPtr, argbuf);
15951595
},
15961596

1597+
$runMainThreadEmAsm__docs: '/** @param {number=} asyncAwait */',
15971598
$runMainThreadEmAsm__deps: ['$readEmAsmArgs',
15981599
#if PTHREADS
15991600
'$proxyToMainThread'
16001601
#endif
16011602
],
1602-
$runMainThreadEmAsm: (emAsmAddr, sigPtr, argbuf, sync) => {
1603+
$runMainThreadEmAsm: (emAsmAddr, sigPtr, argbuf, sync, asyncAwait) => {
16031604
var args = readEmAsmArgs(sigPtr, argbuf);
16041605
#if PTHREADS
16051606
if (ENVIRONMENT_IS_PTHREAD) {
@@ -1612,7 +1613,7 @@ addToLibrary({
16121613
// of using __proxy. (And dor simplicity, do the same in the sync
16131614
// case as well, even though it's not strictly necessary, to keep the two
16141615
// code paths as similar as possible on both sides.)
1615-
return proxyToMainThread(0, emAsmAddr, sync, ...args);
1616+
return proxyToMainThread(0, emAsmAddr, sync, asyncAwait, ...args);
16161617
}
16171618
#endif
16181619
#if ASSERTIONS
@@ -1623,6 +1624,19 @@ addToLibrary({
16231624
emscripten_asm_const_int_sync_on_main_thread__deps: ['$runMainThreadEmAsm'],
16241625
emscripten_asm_const_int_sync_on_main_thread: (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1),
16251626

1627+
emscripten_asm_const_int_await_on_main_thread__deps: ['$runMainThreadEmAsm'],
1628+
emscripten_asm_const_int_await_on_main_thread: (emAsmAddr, sigPtr, argbuf) => {
1629+
#if PTHREADS
1630+
if (ENVIRONMENT_IS_PTHREAD) {
1631+
return runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, /*sync=*/1, /*asyncAwait=*/1);
1632+
}
1633+
#endif
1634+
#if ASSERTIONS
1635+
assert((typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD), "emscripten_asm_const_int_await_on_main_thread is not available on the main thread");
1636+
#endif
1637+
return runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, /*sync*/1, /*asyncAwait=*/1);
1638+
},
1639+
16261640
emscripten_asm_const_ptr_sync_on_main_thread__deps: ['$runMainThreadEmAsm'],
16271641
emscripten_asm_const_ptr_sync_on_main_thread: (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1),
16281642

Diff for: src/lib/libpthread.js

+32-5
Original file line numberDiff line numberDiff line change
@@ -894,9 +894,9 @@ var LibraryPThread = {
894894
$proxyToMainThreadPtr: (...args) => BigInt(proxyToMainThread(...args)),
895895
#endif
896896
897-
$proxyToMainThread__deps: ['$stackSave', '$stackRestore', '$stackAlloc', '_emscripten_run_on_main_thread_js', ...i53ConversionDeps],
898-
$proxyToMainThread__docs: '/** @type{function(number, (number|boolean), ...number)} */',
899-
$proxyToMainThread: (funcIndex, emAsmAddr, sync, ...callArgs) => {
897+
$proxyToMainThread__deps: ['$stackSave', '$stackRestore', '$stackAlloc', '_emscripten_run_on_main_thread_js', '_emscripten_await_on_main_thread_js', ...i53ConversionDeps],
898+
$proxyToMainThread__docs: '/** @type{function(number, (number|boolean), number, (number|undefined), ...number)} */',
899+
$proxyToMainThread: (funcIndex, emAsmAddr, sync, asyncAwait, ...callArgs) => {
900900
// EM_ASM proxying is done by passing a pointer to the address of the EM_ASM
901901
// content as `emAsmAddr`. JS library proxying is done by passing an index
902902
// into `proxiedJSCallArgs` as `funcIndex`. If `emAsmAddr` is non-zero then
@@ -934,7 +934,12 @@ var LibraryPThread = {
934934
HEAPF64[b + i] = arg;
935935
#endif
936936
}
937-
var rtn = __emscripten_run_on_main_thread_js(funcIndex, emAsmAddr, serializedNumCallArgs, args, sync);
937+
var rtn;
938+
if (asyncAwait) {
939+
rtn = __emscripten_await_on_main_thread_js(funcIndex, emAsmAddr, serializedNumCallArgs, args);
940+
} else {
941+
rtn = __emscripten_run_on_main_thread_js(funcIndex, emAsmAddr, serializedNumCallArgs, args, sync);
942+
}
938943
stackRestore(sp);
939944
return rtn;
940945
},
@@ -945,7 +950,11 @@ var LibraryPThread = {
945950
_emscripten_receive_on_main_thread_js__deps: [
946951
'$proxyToMainThread',
947952
'$proxiedJSCallArgs'],
948-
_emscripten_receive_on_main_thread_js: (funcIndex, emAsmAddr, callingThread, numCallArgs, args) => {
953+
/**
954+
* @param {number=} promiseCtx Optionally, when set, expect func to return a Promise
955+
* and use promiseCtx to signal awaiting pthread.
956+
*/
957+
_emscripten_receive_on_main_thread_js: (funcIndex, emAsmAddr, callingThread, numCallArgs, args, promiseCtx) => {
949958
// Sometimes we need to backproxy events to the calling thread (e.g.
950959
// HTML5 DOM events handlers such as
951960
// emscripten_set_mousemove_callback()), so keep track in a globally
@@ -984,6 +993,24 @@ var LibraryPThread = {
984993
PThread.currentProxiedOperationCallerThread = callingThread;
985994
var rtn = func(...proxiedJSCallArgs);
986995
PThread.currentProxiedOperationCallerThread = 0;
996+
if (promiseCtx) {
997+
#if ASSERTIONS
998+
assert(!!rtn.then, 'Return value of proxied function expected to be a Promise but got' + rtn);
999+
#endif
1000+
rtn.then(res => {
1001+
#if MEMORY64
1002+
// In memory64 mode some proxied functions return bigint/pointer but
1003+
// our return type is i53/double.
1004+
if (typeof res == "bigint") {
1005+
res = bigintToI53Checked(res);
1006+
}
1007+
#endif
1008+
__emscripten_proxy_promise_finish(promiseCtx, res);
1009+
}).catch(err => {
1010+
__emscripten_proxy_promise_finish(promiseCtx, 0);
1011+
});
1012+
return;
1013+
}
9871014
#if MEMORY64
9881015
// In memory64 mode some proxied functions return bigint/pointer but
9891016
// our return type is i53/double.

Diff for: src/lib/libsigs.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ sigs = {
324324
_emscripten_notify_mailbox_postmessage__sig: 'vpp',
325325
_emscripten_push_main_loop_blocker__sig: 'vppp',
326326
_emscripten_push_uncounted_main_loop_blocker__sig: 'vppp',
327-
_emscripten_receive_on_main_thread_js__sig: 'dippip',
327+
_emscripten_receive_on_main_thread_js__sig: 'dippipp',
328328
_emscripten_runtime_keepalive_clear__sig: 'v',
329329
_emscripten_system__sig: 'ip',
330330
_emscripten_thread_cleanup__sig: 'vp',
@@ -571,6 +571,7 @@ sigs = {
571571
emscripten_asm_const_double__sig: 'dppp',
572572
emscripten_asm_const_double_sync_on_main_thread__sig: 'dppp',
573573
emscripten_asm_const_int__sig: 'ippp',
574+
emscripten_asm_const_int_await_on_main_thread__sig: 'ippp',
574575
emscripten_asm_const_int_sync_on_main_thread__sig: 'ippp',
575576
emscripten_asm_const_ptr__sig: 'pppp',
576577
emscripten_asm_const_ptr_sync_on_main_thread__sig: 'pppp',

Diff for: 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().

Diff for: system/lib/pthread/proxying.c

+43-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,41 @@ 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+
func->result = (double)(intptr_t)res;
685+
emscripten_proxy_finish(ctx);
686+
}

Diff for: 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

Diff for: test/core/test_main_thread_async_em_asm_await.cpp

+25
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+
}

Diff for: test/core/test_main_thread_async_em_asm_await.out

+5
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
+34
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(2);
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: 2
+9-7
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)

Diff for: 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

Diff for: test/test_core.py

+20
Original file line numberDiff line numberDiff line change
@@ -1886,6 +1886,26 @@ def test_main_thread_em_asm(self, args):
18861886
def test_main_thread_async_em_asm(self, args, force_c=False):
18871887
self.do_core_test('test_main_thread_async_em_asm.cpp', emcc_args=args, force_c=force_c)
18881888

1889+
@needs_dylink
1890+
@parameterized({
1891+
'': (['-sASSERTIONS'], False),
1892+
'pthreads': (['-pthread', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'], False),
1893+
})
1894+
def test_main_thread_async_em_asm_await(self, args, force_c=False):
1895+
if '-sPROXY_TO_PTHREAD' not in args:
1896+
# expect runtime to error
1897+
output = self.do_runf('core/test_main_thread_async_em_asm_await.cpp', expected_output=None, assert_returncode=NON_ZERO, emcc_args=args)
1898+
self.assertContained('emscripten_asm_const_int_await_on_main_thread is not available on the main thread', output)
1899+
else:
1900+
self.do_core_test('test_main_thread_async_em_asm_await.cpp', emcc_args=args, force_c=force_c)
1901+
1902+
@needs_dylink
1903+
@parameterized({
1904+
'pthreads': (['-pthread', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME', '-sASSERTIONS'], False),
1905+
})
1906+
def test_main_thread_async_em_asm_await_reject(self, args, force_c=False):
1907+
self.do_core_test('test_main_thread_async_em_asm_await_reject.cpp', emcc_args=args, force_c=force_c)
1908+
18891909
# Tests MAIN_THREAD_EM_ASM_INT() function call with different signatures.
18901910
def test_main_thread_em_asm_signatures(self):
18911911
self.do_core_test('test_em_asm_signatures.cpp')

Diff for: test/third_party/googletest

Submodule googletest updated 134 files

0 commit comments

Comments
 (0)