Skip to content

Commit 147c781

Browse files
brendandahlkripken
authored andcommitted
[JSPI] Automatically add promising JSPI wrappers to thread entry points. (emscripten-core#22891)
Use a WebAssembly.Promising wrapper on the thread entry points to allow JSPI to be used from worker threads. The code that invokes the wrapper also adds async/await to handle this async call to entry point. Fixes emscripten-core#22354 --------- Co-authored-by: Alon Zakai <[email protected]>
1 parent 6da6ad5 commit 147c781

File tree

5 files changed

+22
-13
lines changed

5 files changed

+22
-13
lines changed

src/library_pthread.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,7 @@ var LibraryPThread = {
10431043
'$runtimeKeepaliveCounter',
10441044
#endif
10451045
],
1046-
$invokeEntryPoint: (ptr, arg) => {
1046+
$invokeEntryPoint: {{{ asyncIf(ASYNCIFY == 2) }}} (ptr, arg) => {
10471047
#if PTHREADS_DEBUG
10481048
dbg(`invokeEntryPoint: ${ptrToString(ptr)}`);
10491049
#endif
@@ -1079,7 +1079,9 @@ var LibraryPThread = {
10791079
// *ThreadMain(void *arg) form, or try linking with the Emscripten linker
10801080
// flag -sEMULATE_FUNCTION_POINTER_CASTS to add in emulation for this x86
10811081
// ABI extension.
1082-
var result = {{{ makeDynCall('pp', 'ptr') }}}(arg);
1082+
1083+
var result = {{{ makeDynCall('pp', 'ptr', ASYNCIFY == 2) }}}(arg);
1084+
10831085
#if STACK_OVERFLOW_CHECK
10841086
checkStackCookie();
10851087
#endif
@@ -1098,10 +1100,9 @@ var LibraryPThread = {
10981100
#endif
10991101
}
11001102
#if ASYNCIFY == 2
1101-
Promise.resolve(result).then(finish);
1102-
#else
1103-
finish(result);
1103+
result = await result;
11041104
#endif
1105+
finish(result);
11051106
},
11061107
11071108
#if MAIN_MODULE

src/parseTools.mjs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -597,11 +597,12 @@ function charCode(char) {
597597
return char.charCodeAt(0);
598598
}
599599

600-
function makeDynCall(sig, funcPtr) {
600+
function makeDynCall(sig, funcPtr, promising = false) {
601601
assert(
602602
!sig.includes('j'),
603603
'Cannot specify 64-bit signatures ("j" in signature string) with makeDynCall!',
604604
);
605+
assert(!(DYNCALLS && promising), 'DYNCALLS cannot be used with JSPI.');
605606

606607
let args = [];
607608
for (let i = 1; i < sig.length; ++i) {
@@ -672,10 +673,15 @@ Please update to new syntax.`);
672673
return `(() => ${dyncall}(${funcPtr}))`;
673674
}
674675

676+
let getWasmTableEntry = `getWasmTableEntry(${funcPtr})`;
677+
if (promising) {
678+
getWasmTableEntry = `WebAssembly.promising(${getWasmTableEntry})`;
679+
}
680+
675681
if (needArgConversion) {
676-
return `((${args}) => getWasmTableEntry(${funcPtr}).call(null, ${callArgs}))`;
682+
return `((${args}) => ${getWasmTableEntry}.call(null, ${callArgs}))`;
677683
}
678-
return `getWasmTableEntry(${funcPtr})`;
684+
return getWasmTableEntry;
679685
}
680686

681687
function makeEval(code) {

src/runtime_pthread.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ if (ENVIRONMENT_IS_PTHREAD) {
6666
// notified about them.
6767
self.onunhandledrejection = (e) => { throw e.reason || e; };
6868

69-
function handleMessage(e) {
69+
{{{ asyncIf(ASYNCIFY == 2) }}} function handleMessage(e) {
7070
try {
7171
var msgData = e['data'];
7272
//dbg('msgData: ' + Object.keys(msgData));
@@ -177,7 +177,7 @@ if (ENVIRONMENT_IS_PTHREAD) {
177177
}
178178

179179
try {
180-
invokeEntryPoint(msgData.start_routine, msgData.arg);
180+
{{{ awaitIf(ASYNCIFY == 2) }}} invokeEntryPoint(msgData.start_routine, msgData.arg);
181181
} catch(ex) {
182182
if (ex != 'unwind') {
183183
// The pthread "crashed". Do not call `_emscripten_thread_exit` (which

test/core/test_pthread_join_and_asyncify.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@ EM_ASYNC_JS(int, async_call, (), {
1313
return 42;
1414
});
1515

16-
// TODO Remove EMSCRIPTEN_KEEPALIVE when support for async attributes is enabled.
17-
EMSCRIPTEN_KEEPALIVE void *run_thread(void *args) {
16+
void *run_thread(void *args) {
1817
int ret = async_call();
1918
assert(ret == 42);
2019
return NULL;
2120
}
2221

2322
int main() {
2423
pthread_t id;
24+
// Test that JSPI works on the main thread.
25+
emscripten_sleep(1);
26+
// Also test that JSPI works on other threads.
2527
pthread_create(&id, NULL, run_thread, NULL);
2628
printf("joining thread!\n");
2729
pthread_join(id, NULL);

test/test_core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8312,7 +8312,7 @@ def test_pthread_join_and_asyncify(self):
83128312
# TODO Test with ASYNCIFY=1 https://github.com/emscripten-core/emscripten/issues/17552
83138313
self.require_jspi()
83148314
self.do_runf('core/test_pthread_join_and_asyncify.c', 'joining thread!\njoined thread!',
8315-
emcc_args=['-sJSPI_EXPORTS=run_thread',
8315+
emcc_args=['-sJSPI',
83168316
'-sEXIT_RUNTIME=1',
83178317
'-pthread', '-sPROXY_TO_PTHREAD'])
83188318

0 commit comments

Comments
 (0)