| 1 | /* @title: Mutex */ |
| 2 | #pragma once |
| 3 | #include <compiler/core.h> |
| 4 | #include <sch/irql.h> |
| 5 | #include <stdbool.h> |
| 6 | #include <stdint.h> |
| 7 | #include <sync/lock_chk_types.h> |
| 8 | #include <tsa.h> |
| 9 | |
| 10 | /* mutex: pointer sized mutex |
| 11 | * |
| 12 | * ┌─────────────────────────┐ |
| 13 | * Bits │ .... .... .... 3..0 │ |
| 14 | * Use │ %%%% %%%% %%%% %rrh │ |
| 15 | * └─────────────────────────┘ |
| 16 | * |
| 17 | * h - "held" - is the lock held? |
| 18 | * |
| 19 | * r - reserved for future use |
| 20 | * |
| 21 | * %%%% - pointer to owner thread |
| 22 | * |
| 23 | */ |
| 24 | |
| 25 | struct TSA_CAPABILITY("mutex" ) mutex { |
| 26 | atomic_uintptr_t lock_word; |
| 27 | |
| 28 | #ifdef DEBUG_LOCK_CHK |
| 29 | struct lock_chk_lock chk; |
| 30 | #endif /* DEBUG_LOCK_CHK */ |
| 31 | }; |
| 32 | |
| 33 | void mutex_init_chk_full(struct mutex *mtx, const struct lock_chk_class *class, |
| 34 | enum lock_chk_flags flags); |
| 35 | void mutex_set_chk_flags(struct mutex *mtx, enum lock_chk_flags flags); |
| 36 | void mutex_reinit_chk(struct mutex *mtx, const struct lock_chk_class *class, |
| 37 | enum lock_chk_flags flags); |
| 38 | void mutex_unlock_full(struct mutex *mutex, const struct lock_chk_site *site) |
| 39 | TSA_RELEASES(mutex); |
| 40 | |
| 41 | void mutex_lock_full(struct mutex *mutex, const struct lock_chk_site *site) |
| 42 | TSA_ACQUIRES(mutex) TSA_MAY_SLEEP; |
| 43 | |
| 44 | void mutex_lock_subclass_full(struct mutex *mutex, enum lock_subclass subclass, |
| 45 | const struct lock_chk_site *site) |
| 46 | TSA_ACQUIRES(mutex) TSA_MAY_SLEEP; |
| 47 | |
| 48 | bool mutex_locked(struct mutex *mtx); |
| 49 | struct thread *mutex_read_owner(struct mutex *mtx); |
| 50 | void mutex_assert_held_full(struct mutex *mtx, const struct lock_chk_site *site) |
| 51 | TSA_ASSERT_CAPABILITY(mtx); |
| 52 | |
| 53 | void mutex_assert_not_held_full(struct mutex *mtx, |
| 54 | const struct lock_chk_site *site); |
| 55 | |
| 56 | #ifdef DEBUG_LOCK_CHK |
| 57 | |
| 58 | #define MUTEX_INIT_CHK(class_, flags_) \ |
| 59 | ((struct mutex) { \ |
| 60 | .lock_word = ATOMIC_VAR_INIT(0), \ |
| 61 | .chk = LOCK_CHK_LOCK_VALUE_INIT((class_), (flags_)), \ |
| 62 | }) |
| 63 | |
| 64 | #define mutex_init_chk(mtx_, class_, flags_) \ |
| 65 | mutex_init_chk_full((mtx_), (class_), (flags_)) |
| 66 | #define mutex_init_auto_internal(mtx_, flags_) \ |
| 67 | do { \ |
| 68 | static const struct lock_chk_class __auto_class = { \ |
| 69 | .name = #mtx_, \ |
| 70 | .file = __RELFILE__, \ |
| 71 | .line = __LINE__, \ |
| 72 | }; \ |
| 73 | mutex_init_chk_full((mtx_), &__auto_class, (flags_)); \ |
| 74 | } while (0) |
| 75 | |
| 76 | #else /* !defined(DEBUG_LOCK_CHK) */ |
| 77 | |
| 78 | #define MUTEX_INIT_CHK(class_, flags_) \ |
| 79 | ((struct mutex) {.lock_word = ATOMIC_VAR_INIT(0)}) |
| 80 | |
| 81 | #define mutex_init_chk(mtx_, class_, flags_) \ |
| 82 | mutex_init_chk_full((mtx_), NULL, LOCK_UNCHKD) |
| 83 | #define mutex_init_auto_internal(mtx_, flags_) \ |
| 84 | mutex_init_chk_full((mtx_), NULL, LOCK_UNCHKD) |
| 85 | |
| 86 | #endif /* DEBUG_LOCK_CHK */ |
| 87 | |
| 88 | #define MUTEX_INIT MUTEX_INIT_CHK(NULL, LOCK_CHKD_FULL) |
| 89 | #define MUTEX_DEFINE(id) struct mutex id = MUTEX_INIT |
| 90 | #define MUTEX_DEFINE_CHK(id, class_, flags_) \ |
| 91 | struct mutex id = MUTEX_INIT_CHK((class_), (flags_)) |
| 92 | |
| 93 | #define mutex_init_1(mtx_) mutex_init_2(mtx_, LOCK_CHKD_FULL) |
| 94 | #define mutex_init_2(mtx_, flags_) mutex_init_auto_internal((mtx_), (flags_)) |
| 95 | #define mutex_init(...) PP_CALL(mutex_init, __VA_ARGS__) |
| 96 | |
| 97 | #define mutex_lock(mutex_) mutex_lock_full((mutex_), LOCK_CHK_SITE_HERE()) |
| 98 | #define mutex_lock_subclass(mutex_, subclass_) \ |
| 99 | mutex_lock_subclass_full((mutex_), (subclass_), LOCK_CHK_SITE_HERE()) |
| 100 | #define mutex_unlock(mutex_) mutex_unlock_full((mutex_), LOCK_CHK_SITE_HERE()) |
| 101 | |
| 102 | #define MUTEX_ASSERT_HELD(m) mutex_assert_held_full((m), LOCK_CHK_SITE_HERE()) |
| 103 | #define MUTEX_ASSERT_NOT_HELD(m) \ |
| 104 | mutex_assert_not_held_full((m), LOCK_CHK_SITE_HERE()) |
| 105 | |
| 106 | static inline cc_always_inline |
| 107 | cc_unused void mutex_guard_exit(struct mutex **m) TSA_NO_ANALYSIS { |
| 108 | if (*m) |
| 109 | mutex_unlock(*m); |
| 110 | } |
| 111 | |
| 112 | static inline cc_always_inline cc_unused void |
| 113 | mutex_guard_assume(struct mutex *m) TSA_ASSERT_CAPABILITY(m) { |
| 114 | cc_ignore(m); |
| 115 | } |
| 116 | |
| 117 | static inline cc_always_inline cc_unused struct mutex * |
| 118 | mutex_guard_enter(struct mutex *m) TSA_MAY_SLEEP TSA_NO_ANALYSIS { |
| 119 | mutex_lock(m); |
| 120 | return m; |
| 121 | } |
| 122 | |
| 123 | static inline cc_always_inline cc_unused struct mutex * |
| 124 | mutex_guard_subclass_enter(struct mutex *m, enum lock_subclass subclass) |
| 125 | TSA_MAY_SLEEP TSA_NO_ANALYSIS { |
| 126 | mutex_lock_subclass(m, subclass); |
| 127 | return m; |
| 128 | } |
| 129 | |
| 130 | #define mutex_guard(m_) \ |
| 131 | cc_cleanup(mutex_guard_exit) struct mutex *PP_CONCAT(mtx_guard_, \ |
| 132 | __COUNTER__) = \ |
| 133 | (mutex_guard_assume(m_), mutex_guard_enter(m_)) |
| 134 | |
| 135 | #define mutex_guard_subclass(m_, subclass_) \ |
| 136 | cc_cleanup(mutex_guard_exit) struct mutex *PP_CONCAT(mtx_guard_, \ |
| 137 | __COUNTER__) = \ |
| 138 | (mutex_guard_assume(m_), \ |
| 139 | mutex_guard_subclass_enter((m_), (subclass_))) |
| 140 | |