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