diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 13:54:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 13:54:38 +0000 |
commit | 8c1ab65c0f548d20b7f177bdb736daaf603340e1 (patch) | |
tree | df55b7e75bf43f2bf500845b105afe3ac3a5157e /libc-top-half/musl/src/thread/__lock.c | |
parent | Initial commit. (diff) | |
download | wasi-libc-upstream/0.0_git20221206.8b7148f.tar.xz wasi-libc-upstream/0.0_git20221206.8b7148f.zip |
Adding upstream version 0.0~git20221206.8b7148f.upstream/0.0_git20221206.8b7148f
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libc-top-half/musl/src/thread/__lock.c')
-rw-r--r-- | libc-top-half/musl/src/thread/__lock.c | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/libc-top-half/musl/src/thread/__lock.c b/libc-top-half/musl/src/thread/__lock.c new file mode 100644 index 0000000..60eece4 --- /dev/null +++ b/libc-top-half/musl/src/thread/__lock.c @@ -0,0 +1,62 @@ +#include "pthread_impl.h" + +/* This lock primitive combines a flag (in the sign bit) and a + * congestion count (= threads inside the critical section, CS) in a + * single int that is accessed through atomic operations. The states + * of the int for value x are: + * + * x == 0: unlocked and no thread inside the critical section + * + * x < 0: locked with a congestion of x-INT_MIN, including the thread + * that holds the lock + * + * x > 0: unlocked with a congestion of x + * + * or in an equivalent formulation x is the congestion count or'ed + * with INT_MIN as a lock flag. + */ + +void __lock(volatile int *l) +{ + int need_locks = libc.need_locks; + if (!need_locks) return; + /* fast path: INT_MIN for the lock, +1 for the congestion */ + int current = a_cas(l, 0, INT_MIN + 1); + if (need_locks < 0) libc.need_locks = 0; + if (!current) return; + /* A first spin loop, for medium congestion. */ + for (unsigned i = 0; i < 10; ++i) { + if (current < 0) current -= INT_MIN + 1; + // assertion: current >= 0 + int val = a_cas(l, current, INT_MIN + (current + 1)); + if (val == current) return; + current = val; + } + // Spinning failed, so mark ourselves as being inside the CS. + current = a_fetch_add(l, 1) + 1; + /* The main lock acquisition loop for heavy congestion. The only + * change to the value performed inside that loop is a successful + * lock via the CAS that acquires the lock. */ + for (;;) { + /* We can only go into wait, if we know that somebody holds the + * lock and will eventually wake us up, again. */ + if (current < 0) { + __futexwait(l, current, 1); + current -= INT_MIN + 1; + } + /* assertion: current > 0, the count includes us already. */ + int val = a_cas(l, current, INT_MIN + current); + if (val == current) return; + current = val; + } +} + +void __unlock(volatile int *l) +{ + /* Check l[0] to see if we are multi-threaded. */ + if (l[0] < 0) { + if (a_fetch_add(l, -(INT_MIN + 1)) != (INT_MIN + 1)) { + __wake(l, 1, 1); + } + } +} |