use syscall_arg_t and __scc macro for arguments to __alt_socketcall
[musl] / src / thread / __lock.c
index 5ba5dc5..60eece4 100644 (file)
@@ -1,32 +1,62 @@
 #include "pthread_impl.h"
 
-void __lock_2(volatile int *l)
-{
-       if (!__syscall(SYS_futex, l, FUTEX_LOCK_PI, 0, 0))
-               return;
-       int old, tid = __pthread_self()->tid|INT_MIN;
-       while ((old = a_cas(l, 0, tid))) {
-               a_cas(l, old, old|INT_MIN);
-               __syscall(SYS_futex, l, FUTEX_WAIT, old|INT_MIN, 0);
-       }
-}
+/* 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)
 {
-       if (a_cas(l, 0, __pthread_self()->tid)) __lock_2(l);
-}
-
-void __unlock_2(volatile int *l)
-{
-       if (__syscall(SYS_futex, l, FUTEX_UNLOCK_PI)) {
-               *l = 0;
-               __syscall(SYS_futex, l, FUTEX_WAKE, 1);
+       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)
 {
-       int old = *l;
-       if (!(old & INT_MIN) && a_cas(l, old, 0)==old) return;
-       __unlock_2(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);
+               }
+       }
 }