| 1 | #include <crypto/prng.h> |
| 2 | #include <sync/mutex.h> |
| 3 | |
| 4 | enum mutex_bits : uintptr_t { |
| 5 | MUTEX_HELD_BIT = 1, |
| 6 | }; |
| 7 | |
| 8 | #define MUTEX_META_BITS (MUTEX_HELD_BIT) |
| 9 | |
| 10 | #define MUTEX_READ_LOCK_WORD(__mtx) \ |
| 11 | (atomic_load_explicit(&((struct mutex *) (__mtx))->lock_word, \ |
| 12 | memory_order_acquire)) |
| 13 | #define MUTEX_BACKOFF_DEFAULT 4 |
| 14 | #define MUTEX_BACKOFF_MAX 32768 |
| 15 | #define MUTEX_BACKOFF_SHIFT 1 |
| 16 | #define MUTEX_BACKOFF_JITTER_PCT 15 /* 15% variation of base backoff */ |
| 17 | #define MUTEX_UNLOCK_WAKE_THREAD_COUNT(__m) \ |
| 18 | 1 /* turnstile_get_waiter_count(__m) */ |
| 19 | |
| 20 | static inline uintptr_t mutex_make_lock_word(struct thread *owner) { |
| 21 | return ((uintptr_t) owner) | MUTEX_HELD_BIT; |
| 22 | } |
| 23 | |
| 24 | static inline uintptr_t mutex_make_unlocked_word(void) { |
| 25 | return 0; |
| 26 | } |
| 27 | |
| 28 | static inline bool mutex_try_lock(struct mutex *mtx, struct thread *self) { |
| 29 | uintptr_t old = atomic_load_explicit(&mtx->lock_word, memory_order_acquire); |
| 30 | uintptr_t newval = mutex_make_lock_word(owner: self); |
| 31 | |
| 32 | while (true) { |
| 33 | /* held: no can do! */ |
| 34 | if (old & MUTEX_HELD_BIT) |
| 35 | return false; |
| 36 | |
| 37 | /* We want to preserve other bits */ |
| 38 | |
| 39 | if (atomic_compare_exchange_weak_explicit( |
| 40 | &mtx->lock_word, |
| 41 | &old, /* If CAS fails, 'old' is updated to current value */ |
| 42 | newval, memory_order_acquire, memory_order_relaxed)) { |
| 43 | return true; |
| 44 | } |
| 45 | |
| 46 | /* CAS failed. `old` now holds the current word. */ |
| 47 | /* Loop again, but if someone has set held, give up. */ |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | static inline void mutex_lock_word_unlock(struct mutex *mtx) { |
| 52 | atomic_store_explicit(&mtx->lock_word, mutex_make_unlocked_word(), |
| 53 | memory_order_release); |
| 54 | } |
| 55 | |