| #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); |
| } |
| } |
| } |