]> git.proxmox.com Git - wasi-libc.git/blob - libc-top-half/musl/src/thread/__lock.c
WASI libc prototype implementation.
[wasi-libc.git] / libc-top-half / musl / src / thread / __lock.c
1 #include "pthread_impl.h"
2
3 /* This lock primitive combines a flag (in the sign bit) and a
4 * congestion count (= threads inside the critical section, CS) in a
5 * single int that is accessed through atomic operations. The states
6 * of the int for value x are:
7 *
8 * x == 0: unlocked and no thread inside the critical section
9 *
10 * x < 0: locked with a congestion of x-INT_MIN, including the thread
11 * that holds the lock
12 *
13 * x > 0: unlocked with a congestion of x
14 *
15 * or in an equivalent formulation x is the congestion count or'ed
16 * with INT_MIN as a lock flag.
17 */
18
19 void __lock(volatile int *l)
20 {
21 if (!libc.threads_minus_1) return;
22 /* fast path: INT_MIN for the lock, +1 for the congestion */
23 int current = a_cas(l, 0, INT_MIN + 1);
24 if (!current) return;
25 /* A first spin loop, for medium congestion. */
26 for (unsigned i = 0; i < 10; ++i) {
27 if (current < 0) current -= INT_MIN + 1;
28 // assertion: current >= 0
29 int val = a_cas(l, current, INT_MIN + (current + 1));
30 if (val == current) return;
31 current = val;
32 }
33 // Spinning failed, so mark ourselves as being inside the CS.
34 current = a_fetch_add(l, 1) + 1;
35 /* The main lock acquisition loop for heavy congestion. The only
36 * change to the value performed inside that loop is a successful
37 * lock via the CAS that acquires the lock. */
38 for (;;) {
39 /* We can only go into wait, if we know that somebody holds the
40 * lock and will eventually wake us up, again. */
41 if (current < 0) {
42 __futexwait(l, current, 1);
43 current -= INT_MIN + 1;
44 }
45 /* assertion: current > 0, the count includes us already. */
46 int val = a_cas(l, current, INT_MIN + current);
47 if (val == current) return;
48 current = val;
49 }
50 }
51
52 void __unlock(volatile int *l)
53 {
54 /* Check l[0] to see if we are multi-threaded. */
55 if (l[0] < 0) {
56 if (a_fetch_add(l, -(INT_MIN + 1)) != (INT_MIN + 1)) {
57 __wake(l, 1, 1);
58 }
59 }
60 }