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