Skip to content

Add synchronous dispatch to thread. #13315

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

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -562,3 +562,4 @@ a license to everyone to use it as detailed in LICENSE.)
* Érico Porto <ericoporto2008@gmail.com>
* Albert Vaca Cintora <albertvaka@gmail.com>
* Zhi An Ng <zhin@google.com> (copyright owned by Google, Inc.)
* Luigi F. Cruz <luigifcruz@gmail.com>
4 changes: 4 additions & 0 deletions system/include/emscripten/threading.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ int _emscripten_call_on_thread(int force_async, pthread_t target_thread, EM_FUNC
// but may be simpler to reason about in some cases.
#define emscripten_dispatch_to_thread_async(target_thread, sig, func_ptr, satellite, ...) _emscripten_call_on_thread(1, (target_thread), (sig), (void*)(func_ptr), (satellite),##__VA_ARGS__)

// Similar to emscripten_dispatch_to_thread, but always runs the
// function synchronously, even if on the same thread.
#define emscripten_dispatch_to_thread_sync(target_thread, sig, func_ptr, satellite, ...) _emscripten_call_on_thread(2, (target_thread), (sig), (void*)(func_ptr), (satellite),##__VA_ARGS__)

// Returns 1 if the current thread is the thread that hosts the Emscripten runtime.
int emscripten_is_main_runtime_thread(void);

Expand Down
23 changes: 19 additions & 4 deletions system/lib/pthread/library_pthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ pthread_t emscripten_main_browser_thread_id() {
return main_browser_thread_id_;
}

int _emscripten_do_dispatch_to_thread(pthread_t target_thread, em_queued_call* call) {
int _emscripten_do_dispatch_to_thread_global(pthread_t target_thread, em_queued_call* call, int drop) {
assert(call);

// #if PTHREADS_DEBUG // TODO: Create a debug version of pthreads library
Expand Down Expand Up @@ -492,7 +492,7 @@ int _emscripten_do_dispatch_to_thread(pthread_t target_thread, em_queued_call* c

// If queue of the main browser thread is full, then we wait. (never drop messages for the main
// browser thread)
if (target_thread == emscripten_main_browser_thread_id()) {
if (target_thread == emscripten_main_browser_thread_id() || drop == 0) {
emscripten_futex_wait((void*)&q->call_queue_head, head, INFINITY);
pthread_mutex_lock(&call_queue_lock);
head = q->call_queue_head;
Expand Down Expand Up @@ -529,6 +529,11 @@ int _emscripten_do_dispatch_to_thread(pthread_t target_thread, em_queued_call* c
return 0;
}

int _emscripten_do_dispatch_to_thread(
pthread_t target_thread, em_queued_call* call) {
return _emscripten_do_dispatch_to_thread_global(target_thread, call, 1);
}

void emscripten_async_run_in_main_thread(em_queued_call* call) {
_emscripten_do_dispatch_to_thread(emscripten_main_browser_thread_id(), call);
}
Expand Down Expand Up @@ -859,8 +864,11 @@ em_queued_call* emscripten_async_waitable_run_in_main_runtime_thread_(
}

int _emscripten_call_on_thread(
int forceAsync,
int dispatchMode,
pthread_t targetThread, EM_FUNC_SIGNATURE sig, void* func_ptr, void* satellite, ...) {
// dispatchMode==0 Synchronous if in the same thread_id, otherwise asynchronous.
// dispatchMode==1 Force asynchronous call even if in the same thread_id.
// dispatchMode==2 Always force a synchronous call.
int numArguments = EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(sig);
em_queued_call* q = em_queued_call_malloc();
assert(q);
Expand Down Expand Up @@ -905,13 +913,20 @@ int _emscripten_call_on_thread(
q->calleeDelete = 1;
// The called function will not be async if we are on the same thread; force
// async if the user asked for that.
if (forceAsync) {
if (dispatchMode == 1) {
EM_ASM({
setTimeout(function() {
__emscripten_do_dispatch_to_thread($0, $1);
}, 0);
}, targetThread, q);
return 0;
} else if (dispatchMode == 2) {
q->calleeDelete = 0;
_emscripten_do_dispatch_to_thread_global(targetThread, q, 0);
emscripten_wait_for_call_v(q, INFINITY);
int res = q->returnValue.i;
em_queued_call_free(q);
return res;
} else {
return _emscripten_do_dispatch_to_thread(targetThread, q);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ $__wasm_init_memory
$_do_call
$_emscripten_call_on_thread
$_emscripten_do_dispatch_to_thread
$_emscripten_do_dispatch_to_thread_global
$_emscripten_thread_init
$_main_thread
$a_cas
Expand All @@ -32,6 +33,7 @@ $emscripten_stack_set_limits
$emscripten_sync_run_in_main_thread
$emscripten_sync_run_in_main_thread
$emscripten_tls_init
$emscripten_wait_for_call_v
$free_tls
$init_mparams
$sbrk
Expand Down
39 changes: 39 additions & 0 deletions tests/pthread/call_sync.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2020 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 <stdio.h>
#include <assert.h>
#include <stdbool.h>
#include <pthread.h>
#include <emscripten.h>
#include <emscripten/threading.h>

static _Atomic bool started = false;

void *new_thread(void* ctx) {
started = true;
emscripten_exit_with_live_runtime();
return NULL;
}

int magic_number() {
assert(emscripten_main_browser_thread_id() != pthread_self());
return 42;
}

int main() {
pthread_t worker;
assert(pthread_create(&worker, NULL, new_thread, NULL) == 0);
while (!started)
emscripten_sleep(100);

assert(emscripten_dispatch_to_thread_sync(worker, EM_FUNC_SIG_I, &magic_number, NULL) == 42);

#ifdef REPORT_RESULT
REPORT_RESULT(1);
#endif
}
4 changes: 4 additions & 0 deletions tests/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4011,6 +4011,10 @@ def test_pthread_run_on_main_thread_flood(self):
def test_pthread_call_async(self):
self.btest(test_file('pthread', 'call_async.c'), expected='1', args=['-s', 'USE_PTHREADS'])

@requires_threads
def test_pthread_call_sync(self):
self.btest(path_from_root('tests', 'pthread', 'call_sync.c'), expected='1', args=['-s', 'ASYNCIFY', '-s', 'USE_PTHREADS'])

# Test that it is possible to synchronously call a JavaScript function on the main thread and get a return value back.
@requires_threads
def test_pthread_call_sync_on_main_thread(self):
Expand Down