1/* @title: Reader writer lock */
2#pragma once
3#include <compiler.h>
4#include <stdatomic.h>
5#include <stddef.h>
6#include <stdint.h>
7#include <sync/lock_chk_types.h>
8#include <thread/thread_types.h>
9
10/* rwlock: pointer sized shared reader writer lock
11 *
12 * note: the writer bit leads to two separate
13 * possible encodings for the rest of the bits in the lock word.
14 *
15 * ┌─────────────────────────┐
16 * Bits │ .... .... .... 3..0 │
17 * Use when w = 1 │ %%%% %%%% %%%% %ppw │
18 * Use when w = 0 │ RRRR RRRR RRRW appw │
19 * └─────────────────────────┘
20 *
21 *
22 * w - writer bit -> a writer holds the lock
23 * a - waiter bit -> threads are waiting on the lock
24 * W - writer want -> a writer wants the lock
25 * R - reader count -> used to store the number of readers
26 * p - prio. ceil. -> boosts threads to this ceiling
27 *
28 * %%%% - pointer to owner thread
29 *
30 */
31struct rwlock {
32 _Atomic(uintptr_t) lock_word;
33
34#ifdef DEBUG_LOCK_CHK
35 struct lock_chk_lock chk;
36#endif /* DEBUG_LOCK_CHK */
37};
38
39enum rwlock_acquire_type {
40 RWLOCK_ACQUIRE_READ = 0,
41 RWLOCK_ACQUIRE_WRITE = 1,
42};
43
44void rw_lock_internal(struct rwlock *lock, enum rwlock_acquire_type type,
45 uint8_t subclass, const struct lock_chk_site *site);
46void rw_unlock_internal(struct rwlock *lock, const struct lock_chk_site *site);
47void rwlock_init_chk_internal(struct rwlock *lock,
48 enum thread_prio_class ceiling,
49 const struct lock_chk_class *class,
50 enum lock_chk_flags flags);
51void rwlock_set_chk_flags(struct rwlock *lock, enum lock_chk_flags flags);
52void rwlock_reinit_chk(struct rwlock *lock, enum thread_prio_class ceiling,
53 const struct lock_chk_class *class,
54 enum lock_chk_flags flags);
55bool rwlock_locked(struct rwlock *lock, enum rwlock_acquire_type type);
56void rwlock_assert_held_internal(struct rwlock *lock,
57 enum rwlock_acquire_type type,
58 const struct lock_chk_site *site);
59void rwlock_assert_not_held_internal(struct rwlock *lock,
60 const struct lock_chk_site *site);
61
62#define RWLOCK_ASSERT_HELD(lock, type) \
63 rwlock_assert_held_internal((lock), (type), LOCK_CHK_SITE_HERE())
64#define RWLOCK_ASSERT_READ(lock) RWLOCK_ASSERT_HELD((lock), RWLOCK_ACQUIRE_READ)
65#define RWLOCK_ASSERT_WRITE(lock) \
66 RWLOCK_ASSERT_HELD((lock), RWLOCK_ACQUIRE_WRITE)
67#define RWLOCK_ASSERT_NOT_HELD(lock) \
68 rwlock_assert_not_held_internal((lock), LOCK_CHK_SITE_HERE())
69
70#define RWLOCK_PRIO_CEIL_SHIFT (1)
71
72#ifdef DEBUG_LOCK_CHK
73
74#define RWLOCK_INIT_CHK(ceil_, class_, flags_) \
75 ((struct rwlock) { \
76 .lock_word = ATOMIC_VAR_INIT((ceil_) << RWLOCK_PRIO_CEIL_SHIFT), \
77 .chk = LOCK_CHK_LOCK_VALUE_INIT((class_), (flags_)), \
78 })
79
80#define rwlock_init_chk(lock_, ceil_, class_, flags_) \
81 rwlock_init_chk_internal((lock_), (ceil_), (class_), (flags_))
82#define rwlock_init_auto_internal(lock_, ceil_, flags_) \
83 do { \
84 static const struct lock_chk_class __auto_class = { \
85 .name = #lock_, \
86 .file = __RELFILE__, \
87 .line = __LINE__, \
88 }; \
89 rwlock_init_chk_internal((lock_), (ceil_), &__auto_class, (flags_)); \
90 } while (0)
91
92#else /* !defined(DEBUG_LOCK_CHK) */
93
94#define RWLOCK_INIT_CHK(ceil_, class_, flags_) \
95 ((struct rwlock) { \
96 .lock_word = ATOMIC_VAR_INIT((ceil_) << RWLOCK_PRIO_CEIL_SHIFT), \
97 })
98
99#define rwlock_init_chk(lock_, ceil_, class_, flags_) \
100 rwlock_init_chk_internal((lock_), (ceil_), NULL, LOCK_UNCHKD)
101#define rwlock_init_auto_internal(lock_, ceil_, flags_) \
102 rwlock_init_chk_internal((lock_), (ceil_), NULL, LOCK_UNCHKD)
103
104#endif /* DEBUG_LOCK_CHK */
105
106#define RWLOCK_INIT(ceil_) RWLOCK_INIT_CHK((ceil_), NULL, LOCK_CHKD_FULL)
107#define RWLOCK_DEFINE(id, ceil_) struct rwlock id = RWLOCK_INIT(ceil_)
108#define RWLOCK_DEFINE_CHK(id, ceil_, class_, flags_) \
109 struct rwlock id = RWLOCK_INIT_CHK((ceil_), (class_), (flags_))
110
111#define rwlock_init_2(lock_, ceil_) \
112 rwlock_init_auto_internal((lock_), (ceil_), LOCK_CHKD_FULL)
113#define rwlock_init_3(lock_, ceil_, flags_) \
114 rwlock_init_auto_internal((lock_), (ceil_), (flags_))
115#define rwlock_init(...) \
116 _DISPATCH(rwlock_init, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
117
118#define rw_lock(lock_, type_) \
119 rw_lock_internal((lock_), (type_), 0, LOCK_CHK_SITE_HERE())
120#define rw_read_lock(lock_) \
121 rw_lock_internal((lock_), RWLOCK_ACQUIRE_READ, 0, LOCK_CHK_SITE_HERE())
122#define rw_write_lock(lock_) \
123 rw_lock_internal((lock_), RWLOCK_ACQUIRE_WRITE, 0, LOCK_CHK_SITE_HERE())
124#define rw_read_lock_subclass(lock_, subclass_) \
125 rw_lock_internal((lock_), RWLOCK_ACQUIRE_READ, (subclass_), \
126 LOCK_CHK_SITE_HERE())
127#define rw_write_lock_subclass(lock_, subclass_) \
128 rw_lock_internal((lock_), RWLOCK_ACQUIRE_WRITE, (subclass_), \
129 LOCK_CHK_SITE_HERE())
130#define rw_unlock(lock_) rw_unlock_internal((lock_), LOCK_CHK_SITE_HERE())
131