PatchworkOS  da8a090
A non-POSIX operating system.
Loading...
Searching...
No Matches
wait.h
Go to the documentation of this file.
1#pragma once
2
3#include <kernel/cpu/regs.h>
4#include <kernel/sync/lock.h>
5
6#include <errno.h>
7#include <sys/list.h>
8#include <sys/proc.h>
9
10typedef struct thread thread_t;
11typedef struct cpu cpu_t;
12
13typedef struct wait_entry wait_entry_t;
14typedef struct wait_queue wait_queue_t;
15typedef struct wait_client wait_client_t;
16typedef struct wait wait_t;
17
18/**
19 * @brief Wait queue implementation.
20 * @defgroup kernel_sched_wait Waiting subsystem
21 * @ingroup kernel_sched
22 *
23 * The waiting subsystem provides threads with the ability to suspend their execution until a certain condition is met
24 * and/or a timeout occurs.
25 *
26 * The common usage pattern is to call `WAIT_BLOCK()` to check for a specified condition, when that condition is
27 * modified the subsystem utilizing the wait queue is expected to call `wait_unblock()` to wake up a specified number of
28 * waiting threads, causing them to re-evaluate the condition. If the condition is still not met the thread will go back
29 * to sleep, otherwise it will continue executing.
30 *
31 * @note Generally its preferred to use the `WAIT_BLOCK*` macros instead of directly calling the functions provided by
32 * this subsystem.
33 *
34 * @{
35 */
36
37/**
38 * @brief Used to indicate that the wait should unblock all waiting threads.
39 */
40#define WAIT_ALL UINT64_MAX
41
42/**
43 * @brief Blocks until the condition is true, will test the condition on every wakeup.
44 *
45 * @return On success, `0`. On error, `ERR` and `errno` is set to:
46 * - Check `wait_block_commit()`.
47 */
48#define WAIT_BLOCK(queue, condition) \
49 ({ \
50 assert(rflags_read() & RFLAGS_INTERRUPT_ENABLE); \
51 uint64_t result = 0; \
52 while (!(condition) && result == 0) \
53 { \
54 wait_queue_t* temp = queue; \
55 if (wait_block_prepare(&temp, 1, CLOCKS_NEVER) == ERR) \
56 { \
57 result = ERR; \
58 break; \
59 } \
60 result = wait_block_commit(); \
61 } \
62 result; \
63 })
64
65/**
66 * @brief Blocks until the condition is true, condition will be tested on every wakeup. Reaching the timeout will always
67 * unblock.
68 *
69 * @return On success, `0`. On error, `ERR` and `errno` is set to:
70 * - Check `wait_block_commit()`.
71 */
72#define WAIT_BLOCK_TIMEOUT(queue, condition, timeout) \
73 ({ \
74 assert(rflags_read() & RFLAGS_INTERRUPT_ENABLE); \
75 uint64_t result = 0; \
76 clock_t uptime = sys_time_uptime(); \
77 clock_t deadline = CLOCKS_DEADLINE(timeout, uptime); \
78 while (!(condition) && result == 0) \
79 { \
80 if (deadline <= uptime) \
81 { \
82 errno = ETIMEDOUT; \
83 result = ERR; \
84 break; \
85 } \
86 clock_t remaining = CLOCKS_REMAINING(deadline, uptime); \
87 wait_queue_t* temp = queue; \
88 if (wait_block_prepare(&temp, 1, remaining) == ERR) \
89 { \
90 result = ERR; \
91 break; \
92 } \
93 result = wait_block_commit(); \
94 uptime = sys_time_uptime(); \
95 } \
96 result; \
97 })
98
99/**
100 * @brief Blocks until the condition is true, condition will be tested on every wakeup. Will release the lock before
101 * blocking and acquire it again after waking up.
102 *
103 * @return On success, `0`. On error, `ERR` and `errno` is set to:
104 * - Check `wait_block_commit()`.
105 */
106#define WAIT_BLOCK_LOCK(queue, lock, condition) \
107 ({ \
108 assert(!(rflags_read() & RFLAGS_INTERRUPT_ENABLE)); \
109 uint64_t result = 0; \
110 while (!(condition) && result == 0) \
111 { \
112 wait_queue_t* temp = queue; \
113 if (wait_block_prepare(&temp, 1, CLOCKS_NEVER) == ERR) \
114 { \
115 result = ERR; \
116 break; \
117 } \
118 lock_release(lock); \
119 result = wait_block_commit(); \
120 assert(rflags_read() & RFLAGS_INTERRUPT_ENABLE); \
121 lock_acquire(lock); \
122 } \
123 result; \
124 })
125
126/**
127 * @brief Blocks until the condition is true, condition will be tested on every wakeup. Will release the lock before
128 * blocking and acquire it again after waking up. Reaching the timeout will always unblock.
129 *
130 * @return On success, `0`. On error, `ERR` and `errno` is set to:
131 * - Check `wait_block_commit()`.
132 */
133#define WAIT_BLOCK_LOCK_TIMEOUT(queue, lock, condition, timeout) \
134 ({ \
135 uint64_t result = 0; \
136 clock_t uptime = sys_time_uptime(); \
137 clock_t deadline = CLOCKS_DEADLINE(timeout, uptime); \
138 while (!(condition) && result == ERR) \
139 { \
140 if (deadline <= uptime) \
141 { \
142 errno = ETIMEDOUT; \
143 result = ERR; \
144 break; \
145 } \
146 clock_t remaining = CLOCKS_REMAINING(deadline, uptime); \
147 wait_queue_t* temp = queue; \
148 if (wait_block_prepare(&temp, 1, remaining) == ERR) \
149 { \
150 result = ERR; \
151 break; \
152 } \
153 lock_release(lock); \
154 result = wait_block_commit(); \
155 assert(rflags_read() & RFLAGS_INTERRUPT_ENABLE); \
156 lock_acquire(lock); \
157 uptime = sys_time_uptime(); \
158 } \
159 result; \
160 })
161
162/**
163 * @brief Represents a thread waiting on a wait queue.
164 * @struct wait_entry_t
165 *
166 * Since each thread can wait on multiple wait queues simultaneously, each wait queue the thread is waiting on
167 * will have its own wait entry.
168 */
169typedef struct wait_entry
170{
171 list_entry_t queueEntry; ///< Used in wait_queue_t->entries.
172 list_entry_t threadEntry; ///< Used in wait_client_t->entries.
173 thread_t* thread; ///< The thread that is waiting.
174 wait_queue_t* queue; ///< The wait queue the thread is waiting on.
176
177/**
178 * @brief The primitive that threads block on.
179 * @struct wait_queue_t
180 */
181typedef struct wait_queue
182{
184 list_t entries; ///< List of wait entries for threads waiting on this queue.
186
187/**
188 * @brief Represents a thread in the waiting subsystem.
189 * @struct wait_client_t
190 *
191 * Each thread stores all wait queues it is currently waiting on in here to allow blocking on multiple wait queues,
192 * since if one queue unblocks the thread must be removed from all other queues as well.
193 */
194typedef struct wait_client
195{
197 list_t entries; ///< List of wait entries, one for each wait queue the thread is waiting on.
198 errno_t err; ///< Error number set when unblocking the thread, `EOK` for no error.
199 clock_t deadline; ///< Deadline for timeout, `CLOCKS_NEVER` for no timeout.
200 wait_t* owner; ///< The wait cpu context of the cpu the thread is blocked on.
202
203/**
204 * @brief Represents one instance of the waiting subsystem for a CPU.
205 * @struct wait_t
206 */
207typedef struct wait
208{
209 list_t blockedThreads; ///< List of blocked threads, sorted by deadline.
211} wait_t;
212
213/**
214 * @brief Create a wait queue initializer.
215 *
216 * @param name The name of the wait queue variable.
217 * @return The wait queue initializer.
218 */
219#define WAIT_QUEUE_CREATE(name) {.lock = LOCK_CREATE(), .entries = LIST_CREATE(name.entries)}
220
221/**
222 * @brief Initialize wait queue.
223 *
224 * @param queue The wait queue to initialize.
225 */
226void wait_queue_init(wait_queue_t* queue);
227
228/**
229 * @brief Deinitialize wait queue.
230 *
231 * @param queue The wait queue to deinitialize.
232 */
234
235/**
236 * @brief Initialize a threads wait client.
237 *
238 * @param client The wait client to initialize.
239 */
240void wait_client_init(wait_client_t* client);
241
242/**
243 * @brief Initialize an instance of the waiting subsystem.
244 *
245 * @param wait The instance to initialize.
246 */
247void wait_init(wait_t* wait);
248
249/**
250 * @brief Check for timeouts and unblock threads as needed.
251 *
252 * Will be called by the `interrupt_handler()`.
253 *
254 * @param frame The interrupt frame.
255 * @param self The current CPU.
256 */
258
259/**
260 * @brief Prepare to block the currently running thread.
261 *
262 * Needed to handle race conditions when a thread is unblocked prematurely. The following sequence is used:
263 * - Call `wait_block_prepare()` to add the currently running thread to the provided wait queues and disable interrupts.
264 * - Check if the condition to block is still valid.
265 * - (The condition might change here, thus causing a race condition, leading to premature unblocking.)
266 * - If the condition was evaluated as not valid, call `wait_block_cancel()`.
267 * - If the condition was evaluated as valid, call `wait_block_commit()` to block the thread. If the thread was
268 * unblocked prematurely this function will return immediately.
269 *
270 * Will reenable interrupts on failure.
271 *
272 * @param waitQueues Array of wait queues to add the thread to.
273 * @param amount Number of wait queues to add the thread to.
274 * @param timeout Timeout.
275 * @return On success, `0`. On failure, returns `ERR` and `errno` is set to:
276 * - `EINVAL`: Invalid arguments.
277 * - `ENOMEM`: Out of memory.
278 */
279uint64_t wait_block_prepare(wait_queue_t** waitQueues, uint64_t amount, clock_t timeout);
280
281/**
282 * @brief Cancels blocking of the currently running thread.
283 *
284 * Should be called after `wait_block_prepare()` has been called if the condition to block is no longer valid.
285 *
286 * Will reenable interrupts.
287 */
288void wait_block_cancel(void);
289
290/**
291 * @brief Block the currently running thread.
292 *
293 * Should be called after `wait_block_prepare()`. If the thread was unblocked prematurely this function will return
294 * immediately.
295 *
296 * Will reenable interrupts.
297 *
298 * @return On success, `0`. On failure, `ERR` and `errno` is set to:
299 * - `ETIMEDOUT`: The thread timed out.
300 * - `EINTR`: The thread was interrupted by a note.
301 * - Other error codes as set by the subsystem utilizing the wait queue.
302 */
304
305/**
306 * @brief Finalize blocking of a thread.
307 *
308 * When `wait_block_commit()` is called the scheduler will be invoked, the scheduler will then call this function to
309 * finalize the blocking of the thread.
310 *
311 * Its possible that during the gap between `wait_block_commit()` and this function being called the thread was
312 * prematurely unblocked, in that case this function will return false and the scheduler will resume the thread
313 * immediately.
314 *
315 * @param frame The interrupt frame.
316 * @param self The CPU the thread is being blocked on.
317 * @param thread The thread to block.
318 * @param uptime The current uptime.
319 * @return `true` if the thread was blocked, `false` if the thread was prematurely unblocked.
320 */
322
323/**
324 * @brief Unblock a specific thread.
325 *
326 * Unblocks the provided thread, removing it from all wait queues it is waiting on.
327 *
328 * The thread must be in the `THREAD_UNBLOCKING` state when this function is called.
329 *
330 * @param thread The thread to unblock.
331 * @param err The errno value to set for the thread or `EOK` for no error.
332 */
333void wait_unblock_thread(thread_t* thread, errno_t err);
334
335/**
336 * @brief Unblock threads waiting on a wait queue.
337 *
338 * @param queue The wait queue to unblock threads from.
339 * @param amount The number of threads to unblock or `WAIT_ALL` to unblock all threads.
340 * @param err The errno value to set for the unblocked threads or `EOK` for no error.
341 * @return The number of threads that were unblocked.
342 */
344
345/** @} */
int errno_t
Definition errno_t.h:4
uint64_t wait_block_prepare(wait_queue_t **waitQueues, uint64_t amount, clock_t timeout)
Prepare to block the currently running thread.
Definition wait.c:103
uint64_t wait_unblock(wait_queue_t *queue, uint64_t amount, errno_t err)
Unblock threads waiting on a wait queue.
Definition wait.c:296
uint64_t wait_block_commit(void)
Block the currently running thread.
Definition wait.c:199
void wait_init(wait_t *wait)
Initialize an instance of the waiting subsystem.
Definition wait.c:63
bool wait_block_finalize(interrupt_frame_t *frame, cpu_t *self, thread_t *thread, clock_t uptime)
Finalize blocking of a thread.
Definition wait.c:231
void wait_block_cancel(void)
Cancels blocking of the currently running thread.
Definition wait.c:178
void wait_queue_deinit(wait_queue_t *queue)
Deinitialize wait queue.
Definition wait.c:44
void wait_check_timeouts(interrupt_frame_t *frame, cpu_t *self)
Check for timeouts and unblock threads as needed.
Definition wait.c:69
void wait_queue_init(wait_queue_t *queue)
Initialize wait queue.
Definition wait.c:38
void wait_client_init(wait_client_t *client)
Initialize a threads wait client.
Definition wait.c:54
void wait_unblock_thread(thread_t *thread, errno_t err)
Unblock a specific thread.
Definition wait.c:284
clock_t uptime(void)
System call for retreving the time since boot.
Definition uptime.c:6
__UINT64_TYPE__ clock_t
A nanosecond time.
Definition clock_t.h:13
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
CPU structure.
Definition cpu.h:122
Trap Frame Structure.
Definition interrupt.h:143
A entry in a doubly linked list.
Definition list.h:36
A doubly linked list.
Definition list.h:49
A simple ticket lock implementation.
Definition lock.h:43
Thread of execution structure.
Definition thread.h:63
Represents a thread in the waiting subsystem.
Definition wait.h:195
list_entry_t entry
Definition wait.h:196
list_t entries
List of wait entries, one for each wait queue the thread is waiting on.
Definition wait.h:197
wait_t * owner
The wait cpu context of the cpu the thread is blocked on.
Definition wait.h:200
clock_t deadline
Deadline for timeout, CLOCKS_NEVER for no timeout.
Definition wait.h:199
errno_t err
Error number set when unblocking the thread, EOK for no error.
Definition wait.h:198
Represents a thread waiting on a wait queue.
Definition wait.h:170
thread_t * thread
The thread that is waiting.
Definition wait.h:173
wait_queue_t * queue
The wait queue the thread is waiting on.
Definition wait.h:174
list_entry_t queueEntry
Used in wait_queue_t->entries.
Definition wait.h:171
list_entry_t threadEntry
Used in wait_client_t->entries.
Definition wait.h:172
The primitive that threads block on.
Definition wait.h:182
lock_t lock
Definition wait.h:183
list_t entries
List of wait entries for threads waiting on this queue.
Definition wait.h:184
Represents one instance of the waiting subsystem for a CPU.
Definition wait.h:208
lock_t lock
Definition wait.h:210
list_t blockedThreads
List of blocked threads, sorted by deadline.
Definition wait.h:209