Skip to content

Commit 84a2ff9

Browse files
Opt-in busywait mode for futexes
1 parent f1c557c commit 84a2ff9

File tree

12 files changed

+187
-5
lines changed

12 files changed

+187
-5
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ LIBC_TOP_HALF_MUSL_SOURCES += \
369369
thread/sem_trywait.c \
370370
thread/sem_wait.c \
371371
thread/wasm32/wasi_thread_start.s \
372+
thread/wasm32/__wasilibc_busywait.c \
372373
)
373374
endif
374375
ifeq ($(THREAD_MODEL), single)

expected/wasm32-wasip1-threads/defined-symbols.txt

+4
Original file line numberDiff line numberDiff line change
@@ -362,13 +362,16 @@ __wasilibc_cwd_lock
362362
__wasilibc_cwd_unlock
363363
__wasilibc_deinitialize_environ
364364
__wasilibc_dttoif
365+
__wasilibc_enable_futex_busywait_on_current_thread
365366
__wasilibc_ensure_environ
366367
__wasilibc_environ
367368
__wasilibc_fd_renumber
368369
__wasilibc_find_abspath
369370
__wasilibc_find_relpath
370371
__wasilibc_find_relpath_alloc
371372
__wasilibc_futex_wait
373+
__wasilibc_futex_wait_atomic_wait
374+
__wasilibc_futex_wait_maybe_busy
372375
__wasilibc_get_environ
373376
__wasilibc_iftodt
374377
__wasilibc_initialize_environ
@@ -400,6 +403,7 @@ __wasilibc_rmdirat
400403
__wasilibc_stat
401404
__wasilibc_tell
402405
__wasilibc_unlinkat
406+
__wasilibc_use_busy_futex
403407
__wasilibc_utimens
404408
__wasm_call_dtors
405409
__wcscoll_l

expected/wasm32-wasip1-threads/include-all.c

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@
167167
#include <utime.h>
168168
#include <values.h>
169169
#include <wasi/api.h>
170+
#include <wasi/libc-busywait.h>
170171
#include <wasi/libc-environ.h>
171172
#include <wasi/libc-find-relpath.h>
172173
#include <wasi/libc-nocwd.h>

expected/wasm32-wasip1-threads/predefined-macros.txt

+1
Original file line numberDiff line numberDiff line change
@@ -3091,6 +3091,7 @@
30913091
#define __va_copy(d,s) __builtin_va_copy(d,s)
30923092
#define __wasi__ 1
30933093
#define __wasi_api_h
3094+
#define __wasi_libc_busywait_h
30943095
#define __wasi_libc_environ_h
30953096
#define __wasi_libc_find_relpath_h
30963097
#define __wasi_libc_h

libc-bottom-half/headers/public/wasi/libc.h

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ int __wasilibc_rename_oldat(int olddirfd, const char *oldpath, const char *newpa
6464
int __wasilibc_rename_newat(const char *oldpath, int newdirfd, const char *newpath)
6565
__attribute__((__warn_unused_result__));
6666

67+
/// Enable busywait in futex on current thread.
68+
void __wasilibc_enable_futex_busywait_on_current_thread(void);
69+
6770
#ifdef __cplusplus
6871
}
6972
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef __wasi_libc_busywait_h
2+
#define __wasi_libc_busywait_h
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
/// Enable busywait in futex on current thread.
9+
void __wasilibc_enable_futex_busywait_on_current_thread(void);
10+
11+
#ifdef __cplusplus
12+
}
13+
#endif
14+
15+
#endif

libc-top-half/musl/src/thread/__wait.c

+16-5
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,18 @@
44
#endif
55

66
#ifndef __wasilibc_unmodified_upstream
7+
8+
weak int __wasilibc_futex_wait_maybe_busy(volatile void *addr, int op, int val, int64_t max_wait_ns);
9+
710
// Use WebAssembly's `wait` instruction to implement a futex. Note that `op` is
811
// unused but retained as a parameter to match the original signature of the
912
// syscall and that, for `max_wait_ns`, -1 (or any negative number) means wait
1013
// indefinitely.
1114
//
1215
// Adapted from Emscripten: see
1316
// https://github.com/emscripten-core/emscripten/blob/058a9fff/system/lib/pthread/emscripten_futex_wait.c#L111-L150.
14-
int __wasilibc_futex_wait(volatile void *addr, int op, int val, int64_t max_wait_ns)
17+
int __wasilibc_futex_wait_atomic_wait(volatile void *addr, int op, int val, int64_t max_wait_ns)
1518
{
16-
if ((((intptr_t)addr) & 3) != 0) {
17-
return -EINVAL;
18-
}
19-
2019
int ret = __builtin_wasm_memory_atomic_wait32((int *)addr, val, max_wait_ns);
2120

2221
// memory.atomic.wait32 returns:
@@ -32,6 +31,18 @@ int __wasilibc_futex_wait(volatile void *addr, int op, int val, int64_t max_wait
3231
assert(ret == 0);
3332
return 0;
3433
}
34+
35+
int __wasilibc_futex_wait(volatile void *addr, int op, int val, int64_t max_wait_ns)
36+
{
37+
if ((((intptr_t)addr) & 3) != 0) {
38+
return -EINVAL;
39+
}
40+
41+
if (__wasilibc_futex_wait_maybe_busy) {
42+
return __wasilibc_futex_wait_maybe_busy(addr, op, val, max_wait_ns);
43+
}
44+
return __wasilibc_futex_wait_atomic_wait(addr, op, val, max_wait_ns);
45+
}
3546
#endif
3647

3748
void __wait(volatile int *addr, volatile int *waiters, int val, int priv)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include <stdint.h>
2+
#include <time.h>
3+
#include <errno.h>
4+
5+
#define DEFINE_GLOBAL_GETTER(name, core_type, c_type) \
6+
static inline c_type name##_get(void) { \
7+
c_type val; \
8+
__asm__( \
9+
".globaltype " #name ", " #core_type "\n" \
10+
"global.get " #name "\n" \
11+
"local.set %0\n" \
12+
: "=r"(val)); \
13+
return val; \
14+
}
15+
#define DEFINE_GLOBAL_SETTER(name, core_type, c_type) \
16+
static inline void name##_set(c_type val) { \
17+
__asm__( \
18+
".globaltype " #name ", " #core_type "\n" \
19+
"local.get %0\n" \
20+
"global.set " #name "\n" \
21+
: : "r"(val)); \
22+
}
23+
24+
#define DEFINE_RW_GLOBAL(name, core_type, c_type) \
25+
__asm__( \
26+
".globaltype " #name ", " #core_type "\n" \
27+
".global " #name "\n" \
28+
#name ":\n" \
29+
); \
30+
DEFINE_GLOBAL_GETTER(name, core_type, c_type) \
31+
DEFINE_GLOBAL_SETTER(name, core_type, c_type)
32+
33+
DEFINE_RW_GLOBAL(__wasilibc_use_busy_futex, i32, int32_t)
34+
35+
void __wasilibc_enable_futex_busywait_on_current_thread(void)
36+
{
37+
__wasilibc_use_busy_futex_set(1);
38+
}
39+
40+
int __wasilibc_futex_wait_atomic_wait(volatile void *addr, int op, int val, int64_t max_wait_ns);
41+
42+
int __wasilibc_futex_wait_maybe_busy(volatile void *addr, int op, int val, int64_t max_wait_ns)
43+
{
44+
// PLEASE NOTE THAT WE CANNOT CALL LIBC FUNCTIONS THAT USE FUTEXES HERE
45+
46+
if (!__wasilibc_use_busy_futex_get()) {
47+
return __wasilibc_futex_wait_atomic_wait(addr, op, val, max_wait_ns);
48+
}
49+
50+
struct timespec start;
51+
int r = clock_gettime(CLOCK_REALTIME, &start);
52+
53+
// If we can't get the current time, we can't wait with a timeout.
54+
if (r) return r;
55+
56+
while (1) {
57+
// Check timeout if it's a positive value
58+
if (max_wait_ns >= 0) {
59+
struct timespec now;
60+
r = clock_gettime(CLOCK_REALTIME, &now);
61+
if (r) return r;
62+
63+
int64_t elapsed_ns = (now.tv_sec - start.tv_sec) * 1000000000 + now.tv_nsec - start.tv_nsec;
64+
if (elapsed_ns >= max_wait_ns) {
65+
return -ETIMEDOUT;
66+
}
67+
}
68+
69+
if (__c11_atomic_load((_Atomic int *)addr, __ATOMIC_SEQ_CST) != val) {
70+
break;
71+
}
72+
}
73+
74+
return 0;
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! filter.py(TARGET_TRIPLE): wasm32-wasip1-threads
2+
//! add-flags.py(CFLAGS): -I.
3+
//! add-flags.py(LDFLAGS): -Wl,--import-memory,--export-memory,--shared-memory,--max-memory=1073741824
4+
//! add-flags.py(RUN): --wasi threads
5+
#include "build/download/libc-test/src/functional/pthread_cond.c"
6+
7+
#include <wasi/libc-busywait.h>
8+
9+
__attribute__((constructor))
10+
void __wasilibc_enable_busywait(void)
11+
{
12+
__wasilibc_enable_futex_busywait_on_current_thread();
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! filter.py(TARGET_TRIPLE): wasm32-wasip1-threads
2+
//! add-flags.py(CFLAGS): -I.
3+
//! add-flags.py(LDFLAGS): -Wl,--import-memory,--export-memory,--shared-memory,--max-memory=1073741824
4+
//! add-flags.py(RUN): --wasi threads
5+
#include "build/download/libc-test/src/functional/pthread_mutex.c"
6+
7+
#include <wasi/libc-busywait.h>
8+
9+
__attribute__((constructor))
10+
void __wasilibc_enable_busywait(void)
11+
{
12+
__wasilibc_enable_futex_busywait_on_current_thread();
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! filter.py(TARGET_TRIPLE): wasm32-wasip1-threads
2+
//! add-flags.py(CFLAGS): -I.
3+
//! add-flags.py(LDFLAGS): -Wl,--import-memory,--export-memory,--shared-memory,--max-memory=1073741824
4+
//! add-flags.py(RUN): --wasi threads
5+
#include "build/download/libc-test/src/functional/pthread_tsd.c"
6+
7+
#include <wasi/libc-busywait.h>
8+
9+
__attribute__((constructor))
10+
void __wasilibc_enable_busywait(void)
11+
{
12+
__wasilibc_enable_futex_busywait_on_current_thread();
13+
}

test/src/misc/busywait.c

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//! filter.py(TARGET_TRIPLE): wasm32-wasip1-threads
2+
//! add-flags.py(CFLAGS): -pthread
3+
//! add-flags.py(LDFLAGS): -pthread
4+
//! add-flags.py(RUN): --wasi threads
5+
6+
#include <assert.h>
7+
#include <pthread.h>
8+
#include <time.h>
9+
#include <errno.h>
10+
#include <wasi/libc-busywait.h>
11+
12+
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
13+
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
14+
15+
int main() {
16+
struct timespec ts;
17+
int ret;
18+
19+
clock_gettime(CLOCK_REALTIME, &ts);
20+
ts.tv_sec += 1;
21+
22+
__wasilibc_enable_futex_busywait_on_current_thread();
23+
24+
pthread_mutex_lock(&mutex);
25+
26+
ret = pthread_cond_timedwait(&cond, &mutex, &ts);
27+
28+
assert(ret == ETIMEDOUT);
29+
30+
pthread_mutex_unlock(&mutex);
31+
return 0;
32+
}

0 commit comments

Comments
 (0)