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
25struct 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
33void mutex_init_chk_full(struct mutex *mtx, const struct lock_chk_class *class,
34 enum lock_chk_flags flags);
35void mutex_set_chk_flags(struct mutex *mtx, enum lock_chk_flags flags);
36void mutex_reinit_chk(struct mutex *mtx, const struct lock_chk_class *class,
37 enum lock_chk_flags flags);
38void mutex_unlock_full(struct mutex *mutex, const struct lock_chk_site *site)
39 TSA_RELEASES(mutex);
40
41void mutex_lock_full(struct mutex *mutex, const struct lock_chk_site *site)
42 TSA_ACQUIRES(mutex) TSA_MAY_SLEEP;
43
44void 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
48bool mutex_locked(struct mutex *mtx);
49struct thread *mutex_read_owner(struct mutex *mtx);
50void mutex_assert_held_full(struct mutex *mtx, const struct lock_chk_site *site)
51 TSA_ASSERT_CAPABILITY(mtx);
52
53void 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
106static inline cc_always_inline
107cc_unused void mutex_guard_exit(struct mutex **m) TSA_NO_ANALYSIS {
108 if (*m)
109 mutex_unlock(*m);
110}
111
112static inline cc_always_inline cc_unused void
113mutex_guard_assume(struct mutex *m) TSA_ASSERT_CAPABILITY(m) {
114 cc_ignore(m);
115}
116
117static inline cc_always_inline cc_unused struct mutex *
118mutex_guard_enter(struct mutex *m) TSA_MAY_SLEEP TSA_NO_ANALYSIS {
119 mutex_lock(m);
120 return m;
121}
122
123static inline cc_always_inline cc_unused struct mutex *
124mutex_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