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