PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
rwlock.h
Go to the documentation of this file.
1#pragma once
2
3#include <kernel/cpu/cli.h>
5
6#include <stdatomic.h>
7#include <sys/defs.h>
8
9#ifndef NDEBUG
10#include <kernel/log/panic.h>
11#endif
12
13typedef struct thread thread_t;
14
15/**
16 * @brief Read-Write Ticket Lock
17 * @defgroup kernel_sync_rwlock Read-Write Ticket Lock
18 * @ingroup kernel_sync
19 *
20 * @{
21 */
22
23/**
24 * @brief Number of iterations before we consider a deadlock to have occurred in a rwlock operation.
25 * This is only used in debug builds.
26 */
27#define RWLOCK_DEADLOCK_ITERATIONS 10000000
28
29/**
30 * @brief Acquires a rwlock for reading for the reminder of the current scope.
31 *
32 * @param lock Pointer to the rwlock to acquire.
33 */
34#define RWLOCK_READ_SCOPE(lock) \
35 __attribute__((cleanup(rwlock_read_cleanup))) rwlock_t* CONCAT(rl, __COUNTER__) = (lock); \
36 rwlock_read_acquire((lock))
37
38/**
39 * @brief Acquires a rwlock for writing for the reminder of the current scope.
40 *
41 * @param lock Pointer to the rwlock to acquire.
42 */
43#define RWLOCK_WRITE_SCOPE(lock) \
44 __attribute__((cleanup(rwlock_write_cleanup))) rwlock_t* CONCAT(wl, __COUNTER__) = (lock); \
45 rwlock_write_acquire((lock))
46
47/**
48 * @brief Create a rwlock initializer.
49 *
50 * @return A rwlock_t initializer.
51 */
52#define RWLOCK_CREATE() \
53 (rwlock_t) \
54 { \
55 .readTicket = ATOMIC_VAR_INIT(0), .readServe = ATOMIC_VAR_INIT(0), .writeTicket = ATOMIC_VAR_INIT(0), \
56 .writeServe = ATOMIC_VAR_INIT(0), .activeReaders = ATOMIC_VAR_INIT(0) \
57 }
58
59/**
60 * @brief Read-Write Ticket Lock structure.
61 * @struct rwlock_t
62 *
63 * A Read-Write Ticket Lock allows one only writer or multiple readers to access a shared resource at the same time.
64 */
65typedef struct
66{
67 atomic_uint16_t readTicket;
68 atomic_uint16_t readServe;
69 atomic_uint16_t writeTicket;
70 atomic_uint16_t writeServe;
71 atomic_uint16_t activeReaders;
72} rwlock_t;
73
74/**
75 * @brief Initializes a rwlock.
76 *
77 * @param lock Pointer to the rwlock to initialize.
78 */
79static inline void rwlock_init(rwlock_t* lock)
80{
81 atomic_init(&lock->readTicket, 0);
82 atomic_init(&lock->readServe, 0);
83 atomic_init(&lock->writeTicket, 0);
84 atomic_init(&lock->writeServe, 0);
85 atomic_init(&lock->activeReaders, 0);
86}
87
88/**
89 * @brief Acquires a rwlock for reading, blocking until it is available.
90 *
91 * @param lock Pointer to the rwlock to acquire.
92 */
93static inline void rwlock_read_acquire(rwlock_t* lock)
94{
95 cli_push();
96
97#ifndef NDEBUG
98 uint64_t iterations = 0;
99#endif
100
102
103 while (atomic_load_explicit(&lock->readServe, memory_order_acquire) != ticket)
104 {
105 ASM("pause");
106#ifndef NDEBUG
107 if (++iterations >= RWLOCK_DEADLOCK_ITERATIONS)
108 {
109 panic(NULL, "Deadlock in rwlock_read_acquire detected");
110 }
111#endif
112 }
113
114 while (true)
115 {
117
120 {
121 break;
122 }
123
125
126 while (atomic_load_explicit(&lock->writeServe, memory_order_relaxed) !=
128 {
129#ifndef NDEBUG
130 if (++iterations >= RWLOCK_DEADLOCK_ITERATIONS)
131 {
132 panic(NULL, "Deadlock in rwlock_read_acquire detected");
133 }
134#endif
135 ASM("pause");
136 }
137 }
139}
140
141/**
142 * @brief Releases a rwlock from reading.
143 *
144 * @param lock Pointer to the rwlock to release.
145 */
147{
149
150 cli_pop();
151}
152
153/**
154 * @brief Acquires a rwlock for writing, blocking until it is available.
155 *
156 * @param lock Pointer to the rwlock to acquire.
157 */
159{
160 cli_push();
161
162#ifndef NDEBUG
163 uint64_t iterations = 0;
164#endif
165
167
168 while (atomic_load_explicit(&lock->writeServe, memory_order_acquire) != ticket)
169 {
170#ifndef NDEBUG
171 if (++iterations >= RWLOCK_DEADLOCK_ITERATIONS)
172 {
173 panic(NULL, "Deadlock in rwlock_write_acquire detected");
174 }
175#endif
176 ASM("pause");
177 }
178
179 while (atomic_load_explicit(&lock->activeReaders, memory_order_acquire) > 0)
180 {
181#ifndef NDEBUG
182 if (++iterations >= RWLOCK_DEADLOCK_ITERATIONS)
183 {
184 panic(NULL, "Deadlock in rwlock_write_acquire detected");
185 }
186#endif
187 ASM("pause");
188 }
189}
190
191/**
192 * @brief Releases a rwlock from writing.
193 *
194 * @param lock Pointer to the rwlock to release.
195 */
197{
199
200 cli_pop();
201}
202
203static inline void rwlock_read_cleanup(rwlock_t** lock)
204{
206}
207
208static inline void rwlock_write_cleanup(rwlock_t** lock)
209{
211}
212
213/** @} */
static void cli_pop(void)
Decrements the CLI depth, re-enabling interrupts if depth reaches zero and interrupts were enabled pr...
Definition cli.h:42
static void cli_push(void)
Increments the CLI depth, disabling interrupts if depth was zero.
Definition cli.h:24
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:292
static void rwlock_write_release(rwlock_t *lock)
Releases a rwlock from writing.
Definition rwlock.h:196
static void rwlock_init(rwlock_t *lock)
Initializes a rwlock.
Definition rwlock.h:79
static void rwlock_read_release(rwlock_t *lock)
Releases a rwlock from reading.
Definition rwlock.h:146
static void rwlock_write_cleanup(rwlock_t **lock)
Definition rwlock.h:208
static void rwlock_write_acquire(rwlock_t *lock)
Acquires a rwlock for writing, blocking until it is available.
Definition rwlock.h:158
#define RWLOCK_DEADLOCK_ITERATIONS
Number of iterations before we consider a deadlock to have occurred in a rwlock operation....
Definition rwlock.h:27
static void rwlock_read_cleanup(rwlock_t **lock)
Definition rwlock.h:203
static void rwlock_read_acquire(rwlock_t *lock)
Acquires a rwlock for reading, blocking until it is available.
Definition rwlock.h:93
#define ASM(...)
Inline assembly macro.
Definition defs.h:160
#define NULL
Pointer error value.
Definition NULL.h:25
static lock_t lock
Definition percpu.c:31
@ memory_order_seq_cst
Definition stdatomic.h:121
@ memory_order_release
Definition stdatomic.h:119
@ memory_order_relaxed
Definition stdatomic.h:116
@ memory_order_acquire
Definition stdatomic.h:118
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:259
#define atomic_load_explicit(object, order)
Definition stdatomic.h:264
#define atomic_init(obj, value)
Definition stdatomic.h:75
#define atomic_fetch_sub_explicit(object, operand, order)
Definition stdatomic.h:262
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINT16_TYPE__ uint16_t
Definition stdint.h:13
Read-Write Ticket Lock structure.
Definition rwlock.h:66
atomic_uint16_t writeTicket
Definition rwlock.h:69
atomic_uint16_t activeReaders
Definition rwlock.h:71
atomic_uint16_t readTicket
Definition rwlock.h:67
atomic_uint16_t writeServe
Definition rwlock.h:70
atomic_uint16_t readServe
Definition rwlock.h:68
Thread of execution structure.
Definition thread.h:61