1/* @title: Reader writer lock */
2#pragma once
3#include <stdatomic.h>
4#include <stddef.h>
5#include <stdint.h>
6#include <thread/thread_types.h>
7
8/* rwlock: pointer sized shared reader writer lock
9 *
10 * note: the writer bit leads to two separate
11 * possible encodings for the rest of the bits in the lock word.
12 *
13 * ┌─────────────────────────┐
14 * Bits │ .... .... .... 3..0 │
15 * Use when w = 1 │ %%%% %%%% %%%% %ppw │
16 * Use when w = 0 │ RRRR RRRR RRRW appw │
17 * └─────────────────────────┘
18 *
19 *
20 * w - writer bit -> a writer holds the lock
21 * a - waiter bit -> threads are waiting on the lock
22 * W - writer want -> a writer wants the lock
23 * R - reader count -> used to store the number of readers
24 * p - prio. ceil. -> boosts threads to this ceiling
25 *
26 * %%%% - pointer to owner thread
27 *
28 */
29struct rwlock {
30 _Atomic(uintptr_t) lock_word;
31};
32
33enum rwlock_acquire_type {
34 RWLOCK_ACQUIRE_READ = 0,
35 RWLOCK_ACQUIRE_WRITE = 1,
36};
37
38void rwlock_lock(struct rwlock *lock, enum rwlock_acquire_type type);
39void rwlock_unlock(struct rwlock *lock);
40void rwlock_init(struct rwlock *lock, enum thread_prio_class ceiling);
41bool rwlock_held(struct rwlock *lock, enum rwlock_acquire_type type);
42
43#define RWLOCK_ASSERT_HELD(lock, type) \
44 kassert(rwlock_held((lock), (type)), "rwlock not held")
45#define RWLOCK_ASSERT_READ(lock) RWLOCK_ASSERT_HELD((lock), RWLOCK_ACQUIRE_READ)
46#define RWLOCK_ASSERT_WRITE(lock) \
47 RWLOCK_ASSERT_HELD((lock), RWLOCK_ACQUIRE_WRITE)
48
49#define RWLOCK_PRIO_CEIL_SHIFT (1)
50#define RWLOCK_INIT(ceil) {((ceil) << RWLOCK_PRIO_CEIL_SHIFT)}
51
52static inline void rwlock_read_lock(struct rwlock *lock) {
53 rwlock_lock(lock, type: RWLOCK_ACQUIRE_READ);
54}
55
56static inline void rwlock_write_lock(struct rwlock *lock) {
57 rwlock_lock(lock, type: RWLOCK_ACQUIRE_WRITE);
58}
59