Skip to content

Commit 058a9ff

Browse files
authored
Split out housekeeping tasks into new emscripten_yield function. NFC (#16521)
We currently only do two thing in this function: 1. Check if we should die because some other thread crashed. 2. Process the work queue. However, I have plans to do other things such as keep loaded code in sync across threads.
1 parent e89ed30 commit 058a9ff

File tree

8 files changed

+66
-38
lines changed

8 files changed

+66
-38
lines changed

system/include/emscripten/threading.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ void emscripten_check_blocking_allowed(void);
289289
// Experimental API for syncing loaded code between pthreads.
290290
void _emscripten_thread_sync_code();
291291

292+
void _emscripten_yield();
293+
292294
#ifdef __cplusplus
293295
}
294296
#endif

system/lib/libc/musl/src/sched/sched_yield.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ int sched_yield()
99
{
1010
#if __EMSCRIPTEN__
1111
// SharedArrayBuffer and wasm threads do not support explicit yielding.
12-
// For now we at least process our event queue so that other threads who
13-
// are waiting on this one to perform actions can make progesss.
14-
emscripten_current_thread_process_queued_calls();
12+
// For now we at least call `emscripten_yield` which processes the event queue
13+
// (along with other essential tasks).
14+
_emscripten_yield();
1515
return 0;
1616
#else
1717
return syscall(SYS_sched_yield);

system/lib/pthread/emscripten_futex_wait.c

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
#include <errno.h>
99
#include <math.h>
1010
#include <emscripten/threading.h>
11-
#include <stdbool.h>
1211
#include <stdlib.h>
1312
#include <stdio.h>
1413
#include "atomic.h"
@@ -28,13 +27,13 @@ static int futex_wait_busy(volatile void *addr, uint32_t val, double timeout) {
2827
// nonzero, the contents of the address pointed by __emscripten_main_thread_futex
2928
// tell which address the main thread is simulating its wait on.
3029
// We need to be careful of recursion here: If we wait on a futex, and
31-
// then call _emscripten_main_thread_process_queued_calls() below, that
32-
// will call code that takes the proxying mutex - which can once more
33-
// reach this code in a nested call. To avoid interference between the
34-
// two (there is just a single __emscripten_main_thread_futex at a time), unmark
35-
// ourselves before calling the potentially-recursive call. See below for
36-
// how we handle the case of our futex being notified during the time in
37-
// between when we are not set as the value of __emscripten_main_thread_futex.
30+
// then call _emscripten_yield() below, that will call code that takes the
31+
// proxying mutex - which can once more reach this code in a nested call. To
32+
// avoid interference between the two (there is just a single
33+
// __emscripten_main_thread_futex at a time), unmark ourselves before calling
34+
// the potentially-recursive call. See below for how we handle the case of our
35+
// futex being notified during the time in between when we are not set as the
36+
// value of __emscripten_main_thread_futex.
3837
void* last_addr = a_cas_p(&_emscripten_main_thread_futex, 0, (void*)addr);
3938
// We must not have already been waiting.
4039
assert(last_addr == 0);
@@ -62,13 +61,13 @@ static int futex_wait_busy(volatile void *addr, uint32_t val, double timeout) {
6261
// We were told to stop waiting, so stop.
6362
break;
6463
}
65-
emscripten_main_thread_process_queued_calls();
64+
_emscripten_yield();
6665

6766
// Check the value, as if we were starting the futex all over again.
6867
// This handles the following case:
6968
//
7069
// * wait on futex A
71-
// * recurse into emscripten_main_thread_process_queued_calls(),
70+
// * recurse into _emscripten_yield(),
7271
// which waits on futex B. that sets the __emscripten_main_thread_futex address to
7372
// futex B, and there is no longer any mention of futex A.
7473
// * a worker is done with futex A. it checks __emscripten_main_thread_futex but does
@@ -83,7 +82,7 @@ static int futex_wait_busy(volatile void *addr, uint32_t val, double timeout) {
8382
// That case motivates the design here. Given that, checking the memory
8483
// address is also necessary for other reasons: we unset and re-set our
8584
// address in __emscripten_main_thread_futex around calls to
86-
// emscripten_main_thread_process_queued_calls(), and a worker could
85+
// _emscripten_yield(), and a worker could
8786
// attempt to wake us up right before/after such times.
8887
//
8988
// Note that checking the memory value of the futex is valid to do: we
@@ -92,11 +91,11 @@ static int futex_wait_busy(volatile void *addr, uint32_t val, double timeout) {
9291
// later time when there is no need to block. The only "odd" thing is
9392
// that we may have caused side effects in that "delay" time. But the
9493
// only side effects we can have are to call
95-
// emscripten_main_thread_process_queued_calls(). That is always ok to
94+
// _emscripten_yield(). That is always ok to
9695
// do on the main thread (it's why it is ok for us to call it in the
9796
// middle of this function, and elsewhere). So if we check the value
9897
// here and return, it's the same is if what happened on the main thread
99-
// was the same as calling emscripten_main_thread_process_queued_calls()
98+
// was the same as calling _emscripten_yield()
10099
// a few times before calling emscripten_futex_wait().
101100
if (__c11_atomic_load((_Atomic uintptr_t*)addr, __ATOMIC_SEQ_CST) != val) {
102101
return -EWOULDBLOCK;
@@ -109,32 +108,12 @@ static int futex_wait_busy(volatile void *addr, uint32_t val, double timeout) {
109108
return 0;
110109
}
111110

112-
static _Atomic bool thread_crashed = false;
113-
114-
void _emscripten_thread_crashed() {
115-
thread_crashed = true;
116-
}
117-
118111
int emscripten_futex_wait(volatile void *addr, uint32_t val, double max_wait_ms) {
119112
if ((((intptr_t)addr)&3) != 0) {
120113
return -EINVAL;
121114
}
122115

123-
if (emscripten_is_main_runtime_thread()) {
124-
// When a secondary thread crashes, we need to be able to interrupt the main
125-
// thread even if it's in a blocking/looping on a mutex. We want to avoid
126-
// using the normal proxying mechanism to send this message since it can
127-
// allocate (or otherwise itself crash) so use a low level atomic primitive
128-
// for this signal.
129-
if (thread_crashed) {
130-
// Return the event loop so we can handle the message from the crashed
131-
// thread.
132-
emscripten_unwind_to_js_event_loop();
133-
}
134-
135-
// Assist other threads by executing proxied operations that are effectively singlethreaded.
136-
emscripten_main_thread_process_queued_calls();
137-
}
116+
_emscripten_yield();
138117

139118
int ret;
140119
emscripten_conditional_set_current_thread_status(EM_THREAD_STATUS_RUNNING, EM_THREAD_STATUS_WAITFUTEX);

system/lib/pthread/emscripten_yield.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#include <stdbool.h>
2+
#include <sched.h>
3+
#include <threads.h>
4+
#include "syscall.h"
5+
6+
#include <emscripten/threading.h>
7+
8+
static _Atomic bool thread_crashed = false;
9+
10+
void _emscripten_thread_crashed() {
11+
thread_crashed = true;
12+
}
13+
14+
/*
15+
* Called whenever a thread performs a blocking action (or calls sched_yield).
16+
* This function takes care of running the event queue and other housekeeping
17+
* tasks.
18+
*/
19+
void _emscripten_yield() {
20+
int is_runtime_thread = emscripten_is_main_runtime_thread();
21+
22+
// When a secondary thread crashes, we need to be able to interrupt the main
23+
// thread even if it's in a blocking/looping on a mutex. We want to avoid
24+
// using the normal proxying mechanism to send this message since it can
25+
// allocate (or otherwise itself crash) so use a low level atomic primitive
26+
// for this signal.
27+
if (is_runtime_thread) {
28+
if (thread_crashed) {
29+
// Return the event loop so we can handle the message from the crashed
30+
// thread.
31+
emscripten_unwind_to_js_event_loop();
32+
}
33+
34+
// Assist other threads by executing proxied operations that are effectively
35+
// singlethreaded.
36+
emscripten_main_thread_process_queued_calls();
37+
}
38+
39+
// TODO(sbc): Do code synchronization between threads as part of yield
40+
//_emscripten_thread_sync_code();
41+
}

system/lib/pthread/library_pthread_stub.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ void emscripten_current_thread_process_queued_calls() {
4646
// nop
4747
}
4848

49+
void _emscripten_yield() {
50+
// nop
51+
}
52+
4953
int pthread_mutex_init(
5054
pthread_mutex_t* __restrict mutex, const pthread_mutexattr_t* __restrict attr) {
5155
return 0;

tests/other/metadce/minimal_main_Oz_USE_PTHREADS_PROXY_TO_PTHREAD.funcs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ $_emscripten_thread_crashed
1919
$_emscripten_thread_exit
2020
$_emscripten_thread_free_data
2121
$_emscripten_thread_init
22+
$_emscripten_yield
2223
$_main_thread
2324
$a_cas
2425
$a_cas_p.1
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
18576
1+
18582

tools/system_libs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,7 @@ def get_files(self):
852852
'emscripten_thread_state.S',
853853
'emscripten_futex_wait.c',
854854
'emscripten_futex_wake.c',
855+
'emscripten_yield.c',
855856
])
856857
else:
857858
ignore += ['thread']

0 commit comments

Comments
 (0)