Skip to content

Commit 6988f7a

Browse files
authored
[Proxying] Pass a result to the callback (#18404)
Allow the function proxied by `emscripten_proxy_async_with_callback` to return a `void*` result that will be passed as an argument to the callback. Previously this could be accomplished by allocating space for the result value on the context argument passed to the proxied function and the callback, but providing the ability to pass the value directly in the API is more convenient and can save a separate allocation. Fixes #18378.
1 parent 00cd741 commit 6988f7a

File tree

5 files changed

+71
-29
lines changed

5 files changed

+71
-29
lines changed

site/source/docs/api_reference/proxying.h.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,14 @@ Functions
7878
thread then return immediately without waiting for ``func`` to be executed.
7979
Returns 1 if the work was successfully enqueued or 0 otherwise.
8080
81-
.. c:function:: int emscripten_proxy_async_with_callback(em_proxying_queue* q, pthread_t target_thread, void (*func)(void*), void* arg, void (*callback)(void*), void* callback_arg)
81+
.. c:function:: int emscripten_proxy_async_with_callback(em_proxying_queue* q, pthread_t target_thread, void* (*func)(void*), void* arg, void (*callback)(void* arg, void* result), void* callback_arg)
8282
8383
Enqueue `func` on the given queue and thread. Once (and if) it finishes
8484
executing, it will asynchronously proxy `callback` back to the current thread
85-
on the same queue. Returns 1 if the initial work was successfully enqueued and
86-
the target thread notified or 0 otherwise. If the callback cannot be scheduled
87-
(for example due to OOM), the program is aborted.
85+
on the same queue. The result of the proxied function will be passed as the
86+
second argument to the callback. Returns 1 if the initial work was
87+
successfully enqueued and the target thread notified or 0 otherwise. If the
88+
callback cannot be scheduled (for example due to OOM), the program is aborted.
8889
8990
.. c:function:: int emscripten_proxy_sync(em_proxying_queue* q, pthread_t target_thread, void (*func)(void*), void* arg)
9091

system/include/emscripten/proxying.h

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,17 @@ int emscripten_proxy_async(em_proxying_queue* q,
5656

5757
// Enqueue `func` on the given queue and thread. Once (and if) it finishes
5858
// executing, it will asynchronously proxy `callback` back to the current thread
59-
// on the same queue. Returns 1 if the initial work was successfully enqueued
60-
// and the target thread notified or 0 otherwise. If the callback cannot be
61-
// scheduled (for example due to OOM), the program is aborted.
59+
// on the same queue. The result of the proxied function will be passed as the
60+
// second argument to the callback. Returns 1 if the initial work was
61+
// successfully enqueued and the target thread notified or 0 otherwise. If the
62+
// callback cannot be scheduled (for example due to OOM), the program is
63+
// aborted.
6264
int emscripten_proxy_async_with_callback(em_proxying_queue* q,
6365
pthread_t target_thread,
64-
void (*func)(void*),
66+
void* (*func)(void*),
6567
void* arg,
66-
void (*callback)(void*),
68+
void (*callback)(void* arg,
69+
void* result),
6770
void* callback_arg);
6871

6972
// Enqueue `func` on the given queue and thread and wait for it to finish
@@ -106,6 +109,19 @@ class ProxyingQueue {
106109
delete f;
107110
}
108111

112+
static void* runAndFreeWithResult(void* arg) {
113+
auto* f = (std::function<void*()>*)arg;
114+
void* result = (*f)();
115+
delete f;
116+
return result;
117+
}
118+
119+
static void runAndFreeCallback(void* arg, void* result) {
120+
auto* f = (std::function<void(void*)>*)arg;
121+
(*f)(result);
122+
delete f;
123+
}
124+
109125
static void run(void* arg) {
110126
auto* f = (std::function<void()>*)arg;
111127
(*f)();
@@ -163,13 +179,17 @@ class ProxyingQueue {
163179
}
164180

165181
bool proxyAsyncWithCallback(pthread_t target,
166-
std::function<void()>&& func,
167-
std::function<void()>&& callback) {
168-
std::function<void()>* arg = new std::function<void()>(std::move(func));
169-
std::function<void()>* callback_arg =
170-
new std::function<void()>(std::move(callback));
171-
return emscripten_proxy_async_with_callback(
172-
queue, target, runAndFree, (void*)arg, runAndFree, (void*)callback_arg);
182+
std::function<void*()>&& func,
183+
std::function<void(void*)>&& callback) {
184+
std::function<void*()>* arg = new std::function<void*()>(std::move(func));
185+
std::function<void(void*)>* callback_arg =
186+
new std::function<void(void*)>(std::move(callback));
187+
return emscripten_proxy_async_with_callback(queue,
188+
target,
189+
runAndFreeWithResult,
190+
(void*)arg,
191+
runAndFreeCallback,
192+
(void*)callback_arg);
173193
}
174194

175195
bool proxySync(const pthread_t target, const std::function<void()>& func) {

system/lib/pthread/proxying.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -362,23 +362,24 @@ int emscripten_proxy_sync(em_proxying_queue* q,
362362
struct callback {
363363
em_proxying_queue* q;
364364
pthread_t caller_thread;
365-
void (*func)(void*);
365+
void* (*func)(void*);
366366
void* arg;
367-
void (*callback)(void*);
367+
void (*callback)(void* arg, void* result);
368368
void* callback_arg;
369+
void* result;
369370
};
370371

371372
// Free the callback info on the same thread it was originally allocated on.
372373
// This may be more efficient.
373374
static void call_callback_then_free(void* arg) {
374375
struct callback* info = (struct callback*)arg;
375-
info->callback(info->callback_arg);
376+
info->callback(info->callback_arg, info->result);
376377
free(arg);
377378
}
378379

379380
static void call_then_schedule_callback(void* arg) {
380381
struct callback* info = (struct callback*)arg;
381-
info->func(info->arg);
382+
info->result = info->func(info->arg);
382383
if (!emscripten_proxy_async(
383384
info->q, info->caller_thread, call_callback_then_free, arg)) {
384385
// No way to gracefully report that we failed to schedule the callback, so
@@ -389,9 +390,10 @@ static void call_then_schedule_callback(void* arg) {
389390

390391
int emscripten_proxy_async_with_callback(em_proxying_queue* q,
391392
pthread_t target_thread,
392-
void (*func)(void*),
393+
void* (*func)(void*),
393394
void* arg,
394-
void (*callback)(void*),
395+
void (*callback)(void* arg,
396+
void* result),
395397
void* callback_arg) {
396398
struct callback* info = malloc(sizeof(*info));
397399
if (info == NULL) {
@@ -404,6 +406,7 @@ int emscripten_proxy_async_with_callback(em_proxying_queue* q,
404406
.arg = arg,
405407
.callback = callback,
406408
.callback_arg = callback_arg,
409+
.result = NULL,
407410
};
408411
return emscripten_proxy_async(
409412
q, target_thread, call_then_schedule_callback, info);

test/pthread/test_pthread_proxying.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ void await_widget(widget* w) {
9595

9696
void do_run_widget(void* arg) { run_widget((widget*)arg); }
9797

98+
void* do_run_widget_42(void* arg) {
99+
run_widget((widget*)arg);
100+
return (void*)42;
101+
}
102+
98103
void finish_running_widget(void* arg) {
99104
widget* w = (widget*)arg;
100105
run_widget(w);
@@ -199,7 +204,8 @@ void test_proxy_sync_with_ctx(void) {
199204
destroy_widget(&w7);
200205
}
201206

202-
void add_one(void* arg) {
207+
void add_one(void* arg, void* result) {
208+
assert((intptr_t)result == 42);
203209
int* j = (int*)arg;
204210
*j = *j + 1;
205211
}
@@ -217,7 +223,7 @@ void test_proxy_async_with_callback(void) {
217223

218224
// Proxy to ourselves.
219225
emscripten_proxy_async_with_callback(
220-
proxy_queue, pthread_self(), do_run_widget, &w8, add_one, &j);
226+
proxy_queue, pthread_self(), do_run_widget_42, &w8, add_one, &j);
221227
assert(!w8.done);
222228
assert(j == 0);
223229
emscripten_proxy_execute_queue(proxy_queue);
@@ -228,7 +234,7 @@ void test_proxy_async_with_callback(void) {
228234

229235
// Proxy to looper.
230236
emscripten_proxy_async_with_callback(
231-
proxy_queue, looper, do_run_widget, &w9, add_one, &j);
237+
proxy_queue, looper, do_run_widget_42, &w9, add_one, &j);
232238
await_widget(&w9);
233239
assert(i == 9);
234240
assert(w9.done);
@@ -242,7 +248,7 @@ void test_proxy_async_with_callback(void) {
242248

243249
// Proxy to returner.
244250
emscripten_proxy_async_with_callback(
245-
proxy_queue, returner, do_run_widget, &w10, add_one, &j);
251+
proxy_queue, returner, do_run_widget_42, &w10, add_one, &j);
246252
await_widget(&w10);
247253
assert(i == 10);
248254
assert(w10.done);

test/pthread/test_pthread_proxying_cpp.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,12 @@ void test_proxy_async_with_callback(void) {
157157
[&]() {
158158
i = 1;
159159
executor = std::this_thread::get_id();
160+
return (void*)42;
160161
},
161-
[&]() { j++; });
162+
[&](void* result) {
163+
assert((intptr_t)result == 42);
164+
j++;
165+
});
162166
assert(i == 0);
163167
queue.execute();
164168
assert(i == 1);
@@ -176,8 +180,12 @@ void test_proxy_async_with_callback(void) {
176180
}
177181
executor = std::this_thread::get_id();
178182
cond.notify_one();
183+
return (void*)1337;
179184
},
180-
[&]() { j++; });
185+
[&](void* result) {
186+
assert((intptr_t)result == 1337);
187+
j++;
188+
});
181189
std::unique_lock<std::mutex> lock(mutex);
182190
cond.wait(lock, [&]() { return i == 2; });
183191
assert(executor == looper.get_id());
@@ -199,8 +207,12 @@ void test_proxy_async_with_callback(void) {
199207
}
200208
executor = std::this_thread::get_id();
201209
cond.notify_one();
210+
return nullptr;
202211
},
203-
[&]() { j++; });
212+
[&](void* result) {
213+
assert(result == nullptr);
214+
j++;
215+
});
204216
std::unique_lock<std::mutex> lock(mutex);
205217
cond.wait(lock, [&]() { return i == 3; });
206218
assert(executor == returner.get_id());

0 commit comments

Comments
 (0)