| 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 | */ |
| 31 | struct 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 | |
| 39 | enum rwlock_acquire_type { |
| 40 | RWLOCK_ACQUIRE_READ = 0, |
| 41 | RWLOCK_ACQUIRE_WRITE = 1, |
| 42 | }; |
| 43 | |
| 44 | void rw_lock_internal(struct rwlock *lock, enum rwlock_acquire_type type, |
| 45 | uint8_t subclass, const struct lock_chk_site *site); |
| 46 | void rw_unlock_internal(struct rwlock *lock, const struct lock_chk_site *site); |
| 47 | void 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); |
| 51 | void rwlock_set_chk_flags(struct rwlock *lock, enum lock_chk_flags flags); |
| 52 | void rwlock_reinit_chk(struct rwlock *lock, enum thread_prio_class ceiling, |
| 53 | const struct lock_chk_class *class, |
| 54 | enum lock_chk_flags flags); |
| 55 | bool rwlock_locked(struct rwlock *lock, enum rwlock_acquire_type type); |
| 56 | void rwlock_assert_held_internal(struct rwlock *lock, |
| 57 | enum rwlock_acquire_type type, |
| 58 | const struct lock_chk_site *site); |
| 59 | void 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 | |