summaryrefslogtreecommitdiffstats
path: root/libc-top-half/musl/src/thread/__wait.c
blob: 7ffa9872d954e748225bf26ac5e6d9a83bfc8fe1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "pthread_impl.h"
#ifndef __wasilibc_unmodified_upstream
#include "assert.h"
#endif

#ifndef __wasilibc_unmodified_upstream
// Use WebAssembly's `wait` instruction to implement a futex. Note that `op` is
// unused but retained as a parameter to match the original signature of the
// syscall and that, for `max_wait_ns`, -1 (or any negative number) means wait
// indefinitely.
//
// Adapted from Emscripten: see
// https://github.com/emscripten-core/emscripten/blob/058a9fff/system/lib/pthread/emscripten_futex_wait.c#L111-L150.
int __wasilibc_futex_wait(volatile void *addr, int op, int val, int64_t max_wait_ns)
{
    if ((((intptr_t)addr) & 3) != 0) {
        return -EINVAL;
    }

    int ret = __builtin_wasm_memory_atomic_wait32((int *)addr, val, max_wait_ns);

    // memory.atomic.wait32 returns:
    //   0 => "ok", woken by another agent.
    //   1 => "not-equal", loaded value != expected value
    //   2 => "timed-out", the timeout expired
    if (ret == 1) {
        return -EWOULDBLOCK;
    }
    if (ret == 2) {
        return -ETIMEDOUT;
    }
    assert(ret == 0);
    return 0;
}
#endif

void __wait(volatile int *addr, volatile int *waiters, int val, int priv)
{
	int spins=100;
	if (priv) priv = FUTEX_PRIVATE;
	while (spins-- && (!waiters || !*waiters)) {
		if (*addr==val) a_spin();
		else return;
	}
	if (waiters) a_inc(waiters);
	while (*addr==val) {
#ifdef __wasilibc_unmodified_upstream
		__syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0) != -ENOSYS
		|| __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0);
#else
		__wasilibc_futex_wait(addr, FUTEX_WAIT, val, -1);
#endif
	}
	if (waiters) a_dec(waiters);
}