1#include <crypto/prng.h>
2#include <sync/mutex.h>
3
4enum 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
20static inline uintptr_t mutex_make_lock_word(struct thread *owner) {
21 return ((uintptr_t) owner) | MUTEX_HELD_BIT;
22}
23
24static inline uintptr_t mutex_make_unlocked_word(void) {
25 return 0;
26}
27
28static 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
51static 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