Skip to content

Commit 8f7fa62

Browse files
committed
Experiment with atomic builtins
Seems to work!
1 parent 4c58245 commit 8f7fa62

File tree

7 files changed

+66
-56
lines changed

7 files changed

+66
-56
lines changed

system/lib/libc/musl/src/internal/pthread_impl.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,10 @@ hidden int __timedwait_cp(volatile int *, int, clockid_t, const struct timespec
183183
hidden void __wait(volatile int *, volatile int *, int, int);
184184
static inline void __wake(volatile void *addr, int cnt, int priv)
185185
{
186-
#ifdef __EMSCRIPTEN__
186+
#ifdef __EMSCRIPTEN_PTHREADS__
187+
(void)priv;
188+
__builtin_wasm_memory_atomic_notify((int *)addr, cnt < 0 ? -1 : cnt);
189+
#elif defined(__EMSCRIPTEN__)
187190
(void)priv;
188191
emscripten_futex_wake(addr, cnt < 0 ? INT_MAX : cnt);
189192
#else
@@ -196,21 +199,24 @@ static inline void __wake(volatile void *addr, int cnt, int priv)
196199

197200
static inline void __futexwait(volatile void *addr, int val, int priv)
198201
{
199-
#ifdef __EMSCRIPTEN__
202+
#ifdef __EMSCRIPTEN_PTHREADS__
200203
(void)priv;
201-
const int is_runtime_thread = emscripten_is_main_runtime_thread();
204+
int is_runtime_thread = emscripten_is_main_runtime_thread();
202205
if (is_runtime_thread) {
203206
int e;
204207
do {
205208
// Main runtime thread may need to run proxied calls, so sleep in very small slices to be responsive.
206-
e = emscripten_futex_wait(addr, val, 1);
209+
e = __builtin_wasm_memory_atomic_wait32((int *)addr, val, 1000000);
207210
// Assist other threads by executing proxied operations that are effectively singlethreaded.
208211
emscripten_main_thread_process_queued_calls();
209-
} while(e == -ETIMEDOUT);
212+
} while(e == 2/* ETIMEDOUT*/);
210213
} else {
211214
// Can wait in one go.
212-
emscripten_futex_wait(addr, val, INFINITY);
215+
__builtin_wasm_memory_atomic_wait32((int *)addr, val, -1);
213216
}
217+
#elif defined(__EMSCRIPTEN__)
218+
(void)priv;
219+
emscripten_futex_wait(addr, val, INFINITY);
214220
#else
215221
if (priv) priv = FUTEX_PRIVATE;
216222
__syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0) != -ENOSYS ||

system/lib/libc/musl/src/thread/__timedwait.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ int __timedwait_cp(volatile int *addr, int val,
5656
}
5757
#ifdef __EMSCRIPTEN__
5858
pthread_t self = __pthread_self();
59-
double msecsToSleep = top ? (top->tv_sec * 1000 + top->tv_nsec / 1000000.0) : INFINITY;
60-
const int is_runtime_thread = emscripten_is_main_runtime_thread();
59+
double msecsToSleep = top ? (top->tv_sec * 1000.0 + top->tv_nsec / 1000000.0) : INFINITY;
60+
int is_runtime_thread = emscripten_is_main_runtime_thread();
6161

6262
// Main runtime thread may need to run proxied calls, so sleep in very small slices to be responsive.
6363
const double maxMsecsToSleep = is_runtime_thread ? 1 : 100;
@@ -75,14 +75,20 @@ int __timedwait_cp(volatile int *addr, int val,
7575
return ECANCELED;
7676
}
7777
// Must wait in slices in case this thread is cancelled in between.
78-
r = -emscripten_futex_wait(addr, val, msecsToSleep > maxMsecsToSleep ? maxMsecsToSleep : msecsToSleep);
78+
r = __builtin_wasm_memory_atomic_wait32((int *)addr, val,
79+
(msecsToSleep > maxMsecsToSleep ? maxMsecsToSleep : msecsToSleep) * /*NSEC_PER_MSEC*/1000000);
80+
if (r == 2) r = ETIMEDOUT;
81+
if (r == 1) r = EWOULDBLOCK;
7982
// Assist other threads by executing proxied operations that are effectively singlethreaded.
8083
if (is_runtime_thread) emscripten_main_thread_process_queued_calls();
84+
8185
msecsToSleep = sleepUntilTime - emscripten_get_now();
82-
} while(r == ETIMEDOUT && msecsToSleep > 0);
86+
} while (r == ETIMEDOUT && msecsToSleep > 0);
8387
} else {
8488
// Can wait in one go.
85-
r = -emscripten_futex_wait(addr, val, msecsToSleep);
89+
r = __builtin_wasm_memory_atomic_wait32((int *)addr, val, top ? (msecsToSleep * /*NSEC_PER_MSEC*/1000000) : -1);
90+
if (r == 2) r = ETIMEDOUT;
91+
if (r == 1) r = EWOULDBLOCK;
8692
}
8793
#else
8894
r = -__futex4_cp(addr, FUTEX_WAIT|priv, val, top);

system/lib/libc/musl/src/thread/__wait.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ void __wait(volatile int *addr, volatile int *waiters, int val, int priv)
1313
if (waiters) a_inc(waiters);
1414
#ifdef __EMSCRIPTEN__
1515
pthread_t self = __pthread_self();
16-
const int is_runtime_thread = emscripten_is_main_runtime_thread();
16+
int is_runtime_thread = emscripten_is_main_runtime_thread();
1717

1818
// Main runtime thread may need to run proxied calls, so sleep in very small slices to be responsive.
19-
const double maxMsecsToSleep = is_runtime_thread ? 1 : 100;
19+
const int64_t maxNsecsToSleep = is_runtime_thread ? 1000000 : 100000000;
2020

2121
while (*addr==val) {
2222
if (self->cancelasync) {
@@ -27,13 +27,13 @@ void __wait(volatile int *addr, volatile int *waiters, int val, int priv)
2727
return;
2828
}
2929
// Must wait in slices in case this thread is cancelled in between.
30-
e = emscripten_futex_wait(addr, val, maxMsecsToSleep);
30+
e = __builtin_wasm_memory_atomic_wait32((int *)addr, val, maxNsecsToSleep);
3131
// Assist other threads by executing proxied operations that are effectively singlethreaded.
3232
if (is_runtime_thread) emscripten_main_thread_process_queued_calls();
33-
} while(e == -ETIMEDOUT);
33+
} while(e == /*ETIMEDOUT*/2);
3434
} else {
3535
// Can wait in one go.
36-
emscripten_futex_wait(addr, val, INFINITY);
36+
__builtin_wasm_memory_atomic_wait32((int *)addr, val, -1);
3737
}
3838
}
3939
#else

system/lib/libc/musl/src/thread/pthread_cond_timedwait.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ static inline void unlock_requeue(volatile int *l, volatile int *r, int w)
5555
// primitive is strictly not needed, since it is more like an optimization to avoid spuriously waking
5656
// all waiters, just to make them wait on another location immediately afterwards. Here we do exactly
5757
// that: wake every waiter.
58-
emscripten_futex_wake(l, 0x7FFFFFFF);
58+
__builtin_wasm_memory_atomic_notify((int *)l, -1);
5959
#else
6060
if (w) __wake(l, 1, 1);
6161
else __syscall(SYS_futex, l, FUTEX_REQUEUE|FUTEX_PRIVATE, 0, 1, r) != -ENOSYS

system/lib/libc/musl/src/time/nanosleep.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ int nanosleep(const struct timespec *req, struct timespec *rem)
1212
errno = EINVAL;
1313
return -1;
1414
}
15-
emscripten_thread_sleep(req->tv_sec * 1000.0 + req->tv_nsec / 1e6);
15+
emscripten_thread_sleep(req->tv_sec * 1000.0 + req->tv_nsec / 1000000.0);
1616
return 0;
1717
#else
1818
return __syscall_ret(-__clock_nanosleep(CLOCK_REALTIME, 0, req, rem));

system/lib/libc/musl/src/unistd/usleep.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
#define _GNU_SOURCE
22
#include <unistd.h>
33
#include <time.h>
4-
#ifdef __EMSCRTIPEN__
4+
#ifdef __EMSCRIPTEN__
55
#include <emscripten/threading.h>
66
#endif
77

88
int usleep(unsigned useconds)
99
{
10-
#ifdef __EMSCRTIPEN__
11-
emscripten_thread_sleep(usec / 1e3);
10+
#ifdef __EMSCRIPTEN__
11+
emscripten_thread_sleep(useconds / 1000.0);
1212
return 0;
1313
#else
1414
struct timespec tv = {

system/lib/pthread/library_pthread.c

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -112,33 +112,29 @@ void emscripten_thread_sleep(double msecs) {
112112
double now = emscripten_get_now();
113113
double target = now + msecs;
114114

115-
__pthread_testcancel(); // pthreads spec: sleep is a cancellation point, so must test if this
116-
// thread is cancelled during the sleep.
117-
emscripten_current_thread_process_queued_calls();
118-
119115
// If we have less than this many msecs left to wait, busy spin that instead.
120-
const double minimumTimeSliceToSleep = 0.1;
116+
const double minTimeSliceToSleep = 0.1;
121117

122-
// main thread may need to run proxied calls, so sleep in very small slices to be responsive.
118+
// Main browser thread may need to run proxied calls, so sleep in very small slices to be responsive.
123119
const double maxMsecsSliceToSleep = emscripten_is_main_browser_thread() ? 1 : 100;
124120

125121
emscripten_conditional_set_current_thread_status(
126122
EM_THREAD_STATUS_RUNNING, EM_THREAD_STATUS_SLEEPING);
127-
now = emscripten_get_now();
128-
while (now < target) {
123+
124+
double msecsToSleep;
125+
do {
129126
// Keep processing the main loop of the calling thread.
130127
__pthread_testcancel(); // pthreads spec: sleep is a cancellation point, so must test if this
131128
// thread is cancelled during the sleep.
132129
emscripten_current_thread_process_queued_calls();
133130

134-
now = emscripten_get_now();
135-
double msecsToSleep = target - now;
136-
if (msecsToSleep > maxMsecsSliceToSleep)
137-
msecsToSleep = maxMsecsSliceToSleep;
138-
if (msecsToSleep >= minimumTimeSliceToSleep)
139-
emscripten_futex_wait(&dummyZeroAddress, 0, msecsToSleep);
140-
now = emscripten_get_now();
141-
};
131+
msecsToSleep = target - emscripten_get_now();
132+
if (msecsToSleep < minTimeSliceToSleep)
133+
continue;
134+
135+
__builtin_wasm_memory_atomic_wait32((int *)&dummyZeroAddress, 0,
136+
(msecsToSleep > maxMsecsSliceToSleep ? maxMsecsSliceToSleep : msecsToSleep) * /*NSEC_PER_MSEC*/1000000);
137+
} while (msecsToSleep > 0);
142138

143139
emscripten_conditional_set_current_thread_status(
144140
EM_THREAD_STATUS_SLEEPING, EM_THREAD_STATUS_RUNNING);
@@ -336,7 +332,7 @@ static void _do_call(em_queued_call* q) {
336332
} else {
337333
// The caller owns this call object, it is listening to it and will free it up.
338334
q->operationDone = 1;
339-
emscripten_futex_wake(&q->operationDone, INT_MAX);
335+
__builtin_wasm_memory_atomic_notify((int *)&q->operationDone, -1);
340336
}
341337
}
342338

@@ -390,25 +386,27 @@ static CallQueue* GetOrAllocateQueue(
390386
return q;
391387
}
392388

389+
// TODO(kleisauke): All paths call this with timeoutMSecs == INFINITY, perhaps drop this param?
393390
EMSCRIPTEN_RESULT emscripten_wait_for_call_v(em_queued_call* call, double timeoutMSecs) {
391+
int done = emscripten_atomic_load_u32(&call->operationDone);
392+
if (done) return EMSCRIPTEN_RESULT_SUCCESS;
393+
394+
int waitIndefinitely = isinf(timeoutMSecs);
395+
396+
emscripten_set_current_thread_status(EM_THREAD_STATUS_WAITPROXY);
397+
398+
double timeoutUntilTime = emscripten_get_now() + timeoutMSecs;
394399
int r;
400+
do {
401+
r = __builtin_wasm_memory_atomic_wait32((int *)&call->operationDone, 0,
402+
waitIndefinitely ? -1 : (timeoutMSecs * /*NSEC_PER_MSEC*/1000000));
395403

396-
int done = emscripten_atomic_load_u32(&call->operationDone);
397-
if (!done) {
398-
double now = emscripten_get_now();
399-
double waitEndTime = now + timeoutMSecs;
400-
emscripten_set_current_thread_status(EM_THREAD_STATUS_WAITPROXY);
401-
while (!done && now < waitEndTime) {
402-
r = emscripten_futex_wait(&call->operationDone, 0, waitEndTime - now);
403-
done = emscripten_atomic_load_u32(&call->operationDone);
404-
now = emscripten_get_now();
405-
}
406-
emscripten_set_current_thread_status(EM_THREAD_STATUS_RUNNING);
407-
}
408-
if (done)
409-
return EMSCRIPTEN_RESULT_SUCCESS;
410-
else
411-
return EMSCRIPTEN_RESULT_TIMED_OUT;
404+
timeoutMSecs = timeoutUntilTime - emscripten_get_now();
405+
} while (r == /*ETIMEDOUT*/2 && timeoutMSecs > 0);
406+
407+
emscripten_set_current_thread_status(EM_THREAD_STATUS_RUNNING);
408+
409+
return r == 0 ? EMSCRIPTEN_RESULT_SUCCESS : EMSCRIPTEN_RESULT_TIMED_OUT;
412410
}
413411

414412
EMSCRIPTEN_RESULT emscripten_wait_for_call_i(
@@ -470,7 +468,7 @@ int _emscripten_do_dispatch_to_thread(
470468
// If queue of the main browser thread is full, then we wait. (never drop messages for the main
471469
// browser thread)
472470
if (target_thread == emscripten_main_browser_thread_id()) {
473-
emscripten_futex_wait((void*)&q->call_queue_head, head, INFINITY);
471+
__builtin_wasm_memory_atomic_wait32((int *)&q->call_queue_head, head, -1);
474472
pthread_mutex_lock(&call_queue_lock);
475473
head = emscripten_atomic_load_u32((void*)&q->call_queue_head);
476474
tail = emscripten_atomic_load_u32((void*)&q->call_queue_tail);
@@ -680,7 +678,7 @@ void emscripten_current_thread_process_queued_calls() {
680678
pthread_mutex_unlock(&call_queue_lock);
681679

682680
// If the queue was full and we had waiters pending to get to put data to queue, wake them up.
683-
emscripten_futex_wake((void*)&q->call_queue_head, 0x7FFFFFFF);
681+
__builtin_wasm_memory_atomic_notify((int *)&q->call_queue_head, -1);
684682

685683
if (emscripten_is_main_browser_thread())
686684
bool_main_thread_inside_nested_process_queued_calls = 0;

0 commit comments

Comments
 (0)