PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
wait.c
Go to the documentation of this file.
1#include <kernel/cpu/irq.h>
2#include <kernel/mem/cache.h>
3#include <kernel/sched/wait.h>
4
5#include <kernel/cpu/cpu.h>
6#include <kernel/cpu/ipi.h>
7#include <kernel/log/log.h>
8#include <kernel/log/panic.h>
10#include <kernel/sched/sched.h>
11#include <kernel/sched/thread.h>
12#include <kernel/sched/timer.h>
13#include <kernel/sync/lock.h>
14
15#include <assert.h>
16#include <errno.h>
17#include <stdatomic.h>
18#include <stdlib.h>
19#include <sys/list.h>
20
23
24PERCPU_DEFINE_CTOR(static wait_t, pcpu_wait)
25{
26 wait_t* wait = SELF_PTR(pcpu_wait);
27
29 lock_init(&wait->lock);
30}
31
32static void wait_remove_wait_entries(thread_t* thread, errno_t err)
33{
34 assert(atomic_load(&thread->state) == THREAD_UNBLOCKING);
35
36 thread->wait.err = err;
37
38 wait_entry_t* temp;
39 wait_entry_t* entry;
40 LIST_FOR_EACH_SAFE(entry, temp, &thread->wait.entries, threadEntry)
41 {
42 lock_acquire(&entry->queue->lock);
43 list_remove(&entry->queueEntry);
44 lock_release(&entry->queue->lock);
45
46 list_remove(&entry->threadEntry); // Belongs to thread, no lock needed.
47 cache_free(entry);
48 }
49}
50
52{
53 lock_init(&queue->lock);
54 list_init(&queue->entries);
55}
56
58{
59 LOCK_SCOPE(&queue->lock);
60
61 if (!list_is_empty(&queue->entries))
62 {
63 panic(NULL, "Wait queue with pending threads freed");
64 }
65}
66
68{
69 list_entry_init(&client->entry);
70 list_init(&client->entries);
71 client->err = EOK;
72 client->deadline = 0;
73 client->owner = NULL;
74}
75
77{
78 UNUSED(frame);
79
80 wait_t* wait = SELF_PTR(pcpu_wait);
81 LOCK_SCOPE(&wait->lock);
82
83 while (1)
84 {
85 thread_t* thread = CONTAINER_OF_SAFE(list_first(&wait->blockedThreads), thread_t, wait.entry);
86 if (thread == NULL)
87 {
88 return;
89 }
90
92 if (thread->wait.deadline > uptime)
93 {
94 timer_set(uptime, thread->wait.deadline);
95 return;
96 }
97
98 list_remove(&thread->wait.entry);
99
101 if (!atomic_compare_exchange_strong(&thread->state, &expected, THREAD_UNBLOCKING)) // Already unblocking.
102 {
103 continue;
104 }
105
107 sched_submit(thread);
108 }
109}
110
112{
113 if (waitQueues == NULL || amount == 0)
114 {
115 errno = EINVAL;
116 return ERR;
117 }
118
119 cli_push();
120
122 assert(thread != NULL);
123
124 for (uint64_t i = 0; i < amount; i++)
125 {
126 lock_acquire(&waitQueues[i]->lock);
127 }
128
129 for (uint64_t i = 0; i < amount; i++)
130 {
132 if (entry == NULL)
133 {
134 while (1)
135 {
136 wait_entry_t* other =
138 if (other == NULL)
139 {
140 break;
141 }
142 cache_free(other);
143 }
144
145 for (uint64_t j = 0; j < amount; j++)
146 {
147 lock_release(&waitQueues[j]->lock);
148 }
149
150 cli_pop();
151 errno = ENOMEM;
152 return ERR;
153 }
156 entry->queue = waitQueues[i];
157 entry->thread = thread;
158
159 list_push_back(&thread->wait.entries, &entry->threadEntry);
160 list_push_back(&entry->queue->entries, &entry->queueEntry);
161 }
162
163 thread->wait.err = EOK;
164 thread->wait.deadline = CLOCKS_DEADLINE(timeout, clock_uptime());
165 thread->wait.owner = NULL;
166
167 for (uint64_t i = 0; i < amount; i++)
168 {
169 lock_release(&waitQueues[i]->lock);
170 }
171
172 thread_state_t expected = THREAD_ACTIVE;
173 if (!atomic_compare_exchange_strong(&thread->state, &expected, THREAD_PRE_BLOCK))
174 {
175 if (expected != THREAD_UNBLOCKING)
176 {
177 panic(NULL, "Thread in invalid state in wait_block_prepare() state=%d", expected);
178 }
179 // We wait until the wait_block_commit() or wait_block_cancel() to actually do the early unblock.
180 }
181
182 // Return without enabling interrupts, they will be enabled in wait_block_commit() or wait_block_cancel().
183 return 0;
184}
185
187{
189
191 assert(thread != NULL);
192
193 thread_state_t state = atomic_exchange(&thread->state, THREAD_UNBLOCKING);
194
195 // State might already be unblocking if the thread unblocked prematurely.
196 assert(state == THREAD_PRE_BLOCK || state == THREAD_UNBLOCKING);
197
199
200 thread_state_t newState = atomic_exchange(&thread->state, THREAD_ACTIVE);
201 assert(newState == THREAD_UNBLOCKING); // Make sure state did not change.
202
203 cli_pop(); // Release cpu from wait_block_prepare().
205}
206
208{
210 assert(thread != NULL);
211
213
214 thread_state_t state = atomic_load(&thread->state);
215 switch (state)
216 {
219 atomic_store(&thread->state, THREAD_ACTIVE);
220 cli_pop(); // Release cpu from wait_block_prepare().
221 break;
222 case THREAD_PRE_BLOCK:
223 cli_pop(); // Release cpu from wait_block_prepare().
224 ipi_invoke();
225 break;
226 default:
227 panic(NULL, "Invalid thread state %d in wait_block_commit()", state);
228 }
229
231
232 if (thread->wait.err != EOK)
233 {
234 errno = thread->wait.err;
235 return ERR;
236 }
237 return 0;
238}
239
241{
242 UNUSED(frame);
243
244 wait_t* wait = SELF_PTR(pcpu_wait);
245
246 thread->wait.owner = wait;
247 LOCK_SCOPE(&wait->lock);
248
249 thread_t* lastThread = (CONTAINER_OF(list_last(&wait->blockedThreads), thread_t, wait.entry));
250
251 // Sort blocked threads by deadline
252 if (thread->wait.deadline == CLOCKS_NEVER || list_is_empty(&wait->blockedThreads) ||
253 lastThread->wait.deadline <= thread->wait.deadline)
254 {
255 list_push_back(&wait->blockedThreads, &thread->wait.entry);
256 }
257 else
258 {
259 thread_t* other;
260 LIST_FOR_EACH(other, &wait->blockedThreads, wait.entry)
261 {
262 if (other->wait.deadline > thread->wait.deadline)
263 {
264 list_prepend(&other->wait.entry, &thread->wait.entry);
265 break;
266 }
267 }
268 }
269
271 if (!atomic_compare_exchange_strong(&thread->state, &expected, THREAD_BLOCKED)) // Prematurely unblocked
272 {
273 list_remove(&thread->wait.entry);
275 atomic_store(&thread->state, THREAD_ACTIVE);
276 return false;
277 }
278
279 if (thread_is_note_pending(thread))
280 {
282 if (atomic_compare_exchange_strong(&thread->state, &expected, THREAD_UNBLOCKING))
283 {
284 list_remove(&thread->wait.entry);
286 atomic_store(&thread->state, THREAD_ACTIVE);
287 return false;
288 }
289 }
290
291 timer_set(uptime, thread->wait.deadline);
292 return true;
293}
294
296{
297 assert(atomic_load(&thread->state) == THREAD_UNBLOCKING);
298
299 lock_acquire(&thread->wait.owner->lock);
300 list_remove(&thread->wait.entry);
301 wait_remove_wait_entries(thread, err);
302 lock_release(&thread->wait.owner->lock);
303
304 sched_submit(thread);
305}
306
308{
309 uint64_t amountUnblocked = 0;
310
311 const uint64_t threadsPerBatch = 64;
312 while (1)
313 {
314 // For consistent lock ordering we first remove from the wait queue, release the wait queues lock and then
315 // acquire the threads cpu lock. Such that every time we acquire the locks its, cpu lock -> wait queue lock.
316
317 thread_t* threads[threadsPerBatch];
318 uint64_t toUnblock = amount < threadsPerBatch ? amount : threadsPerBatch;
319
320 lock_acquire(&queue->lock);
321
322 wait_entry_t* temp;
323 wait_entry_t* waitEntry;
324 uint64_t collected = 0;
325 LIST_FOR_EACH_SAFE(waitEntry, temp, &queue->entries, queueEntry)
326 {
327 if (collected == toUnblock)
328 {
329 break;
330 }
331
332 thread_t* thread = waitEntry->thread;
333
334 if (atomic_exchange(&thread->state, THREAD_UNBLOCKING) == THREAD_BLOCKED)
335 {
336 list_remove(&waitEntry->queueEntry);
337 list_remove(&waitEntry->threadEntry);
338 cache_free(waitEntry);
339 threads[collected] = thread;
340 collected++;
341 }
342 }
343
344 lock_release(&queue->lock);
345
346 if (collected == 0)
347 {
348 break;
349 }
350
351 for (uint64_t i = 0; i < collected; i++)
352 {
353 lock_acquire(&threads[i]->wait.owner->lock);
354 list_remove(&threads[i]->wait.entry);
355 wait_remove_wait_entries(threads[i], err);
356 lock_release(&threads[i]->wait.owner->lock);
357
358 sched_submit(threads[i]);
359 amountUnblocked++;
360 }
361 }
362
363 return amountUnblocked;
364}
#define assert(expression)
Definition assert.h:29
#define CLOCKS_NEVER
Definition clock_t.h:18
int errno_t
Definition errno_t.h:4
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
void ipi_invoke(void)
Invoke a IPI interrupt on the current CPU.
Definition ipi.c:269
#define SELF_PTR(ptr)
Macro to get a pointer to a percpu variable on the current CPU.
Definition percpu.h:93
#define PERCPU_DEFINE_CTOR(type, name)
Macro to define a percpu variable with a constructor.
Definition percpu.h:130
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:292
void cache_free(void *obj)
Free an object back to its cache.
Definition cache.c:205
#define CACHE_LINE
Cache line size in bytes.
Definition cache.h:75
void * cache_alloc(cache_t *cache)
Allocate an object from the cache.
Definition cache.c:109
#define CACHE_CREATE(_cache, _name, _size, _alignment, _ctor, _dtor)
Macro to create a cache initializer.
Definition cache.h:148
clock_t clock_uptime(void)
Retrieve the time in nanoseconds since boot.
Definition clock.c:99
bool thread_is_note_pending(thread_t *thread)
Check if a thread has a note pending.
Definition thread.c:171
static thread_t * thread_current_unsafe(void)
Retrieves the currently running thread without disabling interrupts.
Definition thread.h:137
thread_state_t
Thread state enum.
Definition thread.h:35
@ THREAD_UNBLOCKING
Has started unblocking, used to prevent the same thread being unblocked multiple times.
Definition thread.h:40
@ THREAD_PRE_BLOCK
Has started the process of blocking but has not yet been given to a owner cpu.
Definition thread.h:38
@ THREAD_BLOCKED
Is blocking and waiting in one or multiple wait queues.
Definition thread.h:39
@ THREAD_ACTIVE
Is either running or ready to run.
Definition thread.h:37
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
void sched_submit(thread_t *thread)
Submits a thread to the scheduler.
Definition sched.c:378
static void lock_init(lock_t *lock)
Initializes a lock.
Definition lock.h:79
#define LOCK_SCOPE(lock)
Acquires a lock for the reminder of the current scope.
Definition lock.h:58
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:175
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:96
void timer_set(clock_t now, clock_t deadline)
Schedule a one-shot timer interrupt on the current CPU.
Definition timer.c:139
#define EINVAL
Invalid argument.
Definition errno.h:142
#define EINTR
Interrupted system call.
Definition errno.h:52
#define ETIMEDOUT
Connection timed out.
Definition errno.h:577
#define ENOMEM
Out of memory.
Definition errno.h:92
#define errno
Error number variable.
Definition errno.h:27
#define EOK
No error.
Definition errno.h:32
#define UNUSED(x)
Mark a variable as unused.
Definition defs.h:96
#define LIST_FOR_EACH(elem, list, member)
Iterates over a list.
Definition list.h:58
static void list_remove(list_entry_t *entry)
Removes a list entry from its current list.
Definition list.h:290
static list_entry_t * list_first(list_t *list)
Gets the first entry in the list without removing it.
Definition list.h:406
static list_entry_t * list_last(list_t *list)
Gets the last entry in the list without removing it.
Definition list.h:423
static void list_push_back(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list.
Definition list.h:322
static void list_prepend(list_entry_t *head, list_entry_t *entry)
Prepends an entry to the list.
Definition list.h:280
#define LIST_FOR_EACH_SAFE(elem, temp, list, member)
Safely iterates over a list, allowing for element removal during iteration.
Definition list.h:73
static bool list_is_empty(list_t *list)
Checks if a list is empty.
Definition list.h:210
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:173
static list_entry_t * list_pop_front(list_t *list)
Pops the first entry from the list.
Definition list.h:366
static void list_init(list_t *list)
Initializes a list.
Definition list.h:185
clock_t uptime(void)
System call for retreving the time since boot.
Definition uptime.c:6
#define NULL
Pointer error value.
Definition NULL.h:25
#define ERR
Integer error value.
Definition ERR.h:17
#define CONTAINER_OF(ptr, type, member)
Container of macro.
#define CONTAINER_OF_SAFE(ptr, type, member)
Safe container of macro.
#define CLOCKS_DEADLINE(timeout, uptime)
Safely calculate deadline from timeout.
Definition clock_t.h:47
__UINT64_TYPE__ clock_t
A nanosecond time.
Definition clock_t.h:13
static lock_t lock
Definition percpu.c:31
#define RFLAGS_INTERRUPT_ENABLE
Definition regs.h:34
static uint64_t rflags_read(void)
Definition regs.h:80
#define atomic_store(object, desired)
Definition stdatomic.h:289
#define atomic_compare_exchange_strong(object, expected, desired)
Definition stdatomic.h:278
#define atomic_exchange(object, desired)
Definition stdatomic.h:282
#define atomic_load(object)
Definition stdatomic.h:288
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
Cache structure.
Definition cache.h:123
Trap Frame Structure.
Definition interrupt.h:195
Thread of execution structure.
Definition thread.h:61
wait_client_t wait
Definition thread.h:76
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
static void wait_remove_wait_entries(thread_t *thread, errno_t err)
Definition wait.c:32
static cache_t waitEntryCache
Definition wait.c:21