-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: await promise code from sync c #23043
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -591,12 +591,15 @@ typedef struct proxied_js_func_t { | |
double* argBuffer; | ||
double result; | ||
bool owned; | ||
// Only used when the underlying js func is async. | ||
// Can be null when the function is sync. | ||
em_proxying_ctx * ctx; | ||
} proxied_js_func_t; | ||
|
||
static void run_js_func(void* arg) { | ||
proxied_js_func_t* f = (proxied_js_func_t*)arg; | ||
f->result = _emscripten_receive_on_main_thread_js( | ||
f->funcIndex, f->emAsmAddr, f->callingThread, f->numArgs, f->argBuffer); | ||
f->funcIndex, f->emAsmAddr, f->callingThread, f->numArgs, f->argBuffer, f->ctx); | ||
if (f->owned) { | ||
free(f->argBuffer); | ||
free(f); | ||
|
@@ -615,6 +618,7 @@ double _emscripten_run_on_main_thread_js(int func_index, | |
.numArgs = num_args, | ||
.argBuffer = buffer, | ||
.owned = false, | ||
.ctx = NULL, | ||
}; | ||
|
||
em_proxying_queue* q = emscripten_proxy_get_system_queue(); | ||
|
@@ -642,3 +646,45 @@ double _emscripten_run_on_main_thread_js(int func_index, | |
} | ||
return 0; | ||
} | ||
|
||
static void call_proxied_js_task_with_ctx(em_proxying_ctx* ctx, void* arg) { | ||
task* t = arg; | ||
proxied_js_func_t* p = t->arg; | ||
p->ctx = ctx; | ||
t->func(t->arg); | ||
} | ||
|
||
double _emscripten_await_on_main_thread_js(int func_index, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do think there is enough code shared with |
||
void* em_asm_addr, | ||
int num_args, | ||
double* buffer) { | ||
em_proxying_queue* q = emscripten_proxy_get_system_queue(); | ||
pthread_t target = emscripten_main_runtime_thread_id(); | ||
|
||
proxied_js_func_t f = { | ||
.funcIndex = func_index, | ||
.emAsmAddr = em_asm_addr, | ||
.callingThread = pthread_self(), | ||
.numArgs = num_args, | ||
.argBuffer = buffer, | ||
.owned = false, | ||
}; | ||
task t = {.func = run_js_func, .arg = &f}; | ||
|
||
if (!emscripten_proxy_sync_with_ctx(q, target, call_proxied_js_task_with_ctx, &t)) { | ||
assert(false && "emscripten_proxy_sync_with_ctx failed"); | ||
return 0; | ||
} | ||
return f.result; | ||
} | ||
|
||
void _emscripten_proxy_promise_finish(em_proxying_ctx* ctx, void* res) { | ||
task* t = (task*)ctx->arg; | ||
proxied_js_func_t* func = (proxied_js_func_t*)t->arg; | ||
if (res == NULL) { | ||
func->result = 0; | ||
} else { | ||
func->result = (double)(intptr_t)res; | ||
} | ||
emscripten_proxy_finish(ctx); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright 2024 The Emscripten Authors. All rights reserved. | ||
// Emscripten is available under two separate licenses, the MIT license and the | ||
// University of Illinois/NCSA Open Source License. Both these licenses can be | ||
// found in the LICENSE file. | ||
|
||
#include <emscripten.h> | ||
#include <stdio.h> | ||
|
||
int main() | ||
{ | ||
printf("Before MAIN_THREAD_EM_ASM_AWAIT\n"); | ||
int res = MAIN_THREAD_EM_ASM_AWAIT({ | ||
out('Inside MAIN_THREAD_EM_ASM_AWAIT: ' + $0 + ' ' + $1); | ||
const asyncOp = new Promise((resolve,reject) => { | ||
setTimeout(() => { | ||
out('Inside asyncOp'); | ||
resolve(2); | ||
}, 1000); | ||
}); | ||
return asyncOp; | ||
}, 42, 3.5); | ||
printf("After MAIN_THREAD_EM_ASM_AWAIT\n"); | ||
printf("result: %d\n", res); | ||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Before MAIN_THREAD_EM_ASM_AWAIT | ||
Inside MAIN_THREAD_EM_ASM_AWAIT: 42 3.5 | ||
Inside asyncOp | ||
After MAIN_THREAD_EM_ASM_AWAIT | ||
result: 2 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Copyright 2024 The Emscripten Authors. All rights reserved. | ||
// Emscripten is available under two separate licenses, the MIT license and the | ||
// University of Illinois/NCSA Open Source License. Both these licenses can be | ||
// found in the LICENSE file. | ||
|
||
#include <emscripten.h> | ||
#include <stdio.h> | ||
#include <pthread.h> | ||
|
||
int main() | ||
{ | ||
// start new thread | ||
pthread_t thread; | ||
pthread_create(&thread, NULL, [](void*) -> void* { | ||
printf("Before MAIN_THREAD_EM_ASM_AWAIT\n"); | ||
int res = MAIN_THREAD_EM_ASM_AWAIT({ | ||
out('Inside MAIN_THREAD_EM_ASM_AWAIT: ' + $0 + ' ' + $1); | ||
const asyncOp = new Promise((resolve,reject) => { | ||
setTimeout(() => { | ||
out('Inside asyncOp'); | ||
reject(new Error('asyncOp rejected')); | ||
}, 1000); | ||
}); | ||
return asyncOp; | ||
}, 42, 3.5); | ||
printf("After MAIN_THREAD_EM_ASM_AWAIT rejected\n"); | ||
printf("result: %d\n", res); | ||
return NULL; | ||
}, NULL); | ||
|
||
// wait for thread to finish | ||
pthread_join(thread, NULL); | ||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Before MAIN_THREAD_EM_ASM_AWAIT | ||
Inside MAIN_THREAD_EM_ASM_AWAIT: 42 3.5 | ||
Inside asyncOp | ||
After MAIN_THREAD_EM_ASM_AWAIT rejected | ||
result: 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
8176 | ||
9712 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the new code in this file does not implement the API surface in proxying.h, it would be best to move it to a different file and leave proxying.c unchanged. It would also be best to implement the new functionality in terms of the public API in proxying.h rather than using internal implementation details. For example, there should be no need to reach inside the
em_proxying_ctx
object.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although I see we already have
_emscripten_run_on_main_thread_js
at the end of proxying.c. Adding more code there would be fine, but it should still use only the public API from proxying.h.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you prefer that I introduce a new function similar to
_emscripten_run_on_main_thread_js
but that uses only public proxying.c API?The problem with introducing a new function similar to
emscripten_run_on_main_thread_js
is the "path" to that function call.It'd a bit tedious. And it'd modify the whole stack leading to this logic.
This is why I chose to introduce the new function param
asyncAwait
(previouslypromise
) just so I could "ride" the whole functions call path.But, I don't insist on this at all.
As for using the public API - I agree that we can do that if I indeed fork from
_emscripten_run_on_main_thread_js
Let me know what you think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need to use internal details of the proxying mechanism if you don't create an entirely new version of
_emscripten_run_on_main_thread_js
? Can't you useemscripten_proxy_sync_with_ctx
to synchronously wait for the promise to be resolved on the main thread instead of introducing a newemscripten_proxy_async_await
function?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The challenging part was to pass the
em_proxying_ctx
all the way down to_emscripten_receive_on_main_thread_js
, through thetask
andproxied_js_func_t
wrappers, because I have to call_emscripten_proxy_promise_finish
from within_emscripten_receive_on_main_thread_js
.This is also why I added
em_proxying_ctx * ctx
toproxied_js_func_t
and moved it upwards.I just pushed a significant simplification and deleted most of the previous code in
proxying.c
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I think of it, it is a little weird that
proxied_js_func_t
has a fieldctx
that if set, causes_emscripten_receive_on_main_thread_js
to expect a promise. Maybe I should name it differently?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the
ctx
andpromiseCtx
names are fine, but it would be good to add comments explaining that they can be null.