diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-05-13 16:22:31 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-05-13 16:22:31 +0800 |
| commit | a3dad7adaa957f8872267ce837366cb9ec7a93a9 (patch) | |
| tree | beaefbd5b047dd09e010f7909ae421833b4bc36e /na/kernel/waitqueue.cc | |
| parent | e7314f76d9edfbee2f676fad90ae6a727403263f (diff) | |
| download | lume-a3dad7adaa957f8872267ce837366cb9ec7a93a9.tar.gz lume-a3dad7adaa957f8872267ce837366cb9ec7a93a9.tar.xz | |
feat: implement WaitQueue class for blocking synchronization
Diffstat (limited to 'na/kernel/waitqueue.cc')
| -rw-r--r-- | na/kernel/waitqueue.cc | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/na/kernel/waitqueue.cc b/na/kernel/waitqueue.cc new file mode 100644 index 0000000..9ee3f46 --- /dev/null +++ b/na/kernel/waitqueue.cc @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * WaitQueue — blocking synchronization implementation + * + * sleep(): current task → Sleeping, link into queue, schedule() + * wake_one(): dequeue head task → Runnable, sched_enqueue() + * + * The internal spinlock protects the wait list only. + * schedule() is called AFTER releasing the spinlock to avoid + * holding a lock across a context switch. + */ + +#include <lume/waitqueue.h> +#include <lume/task.h> +#include <lume/sched.h> +#include <lume/kprintf.h> +#include <arch/cpu.h> + +void WaitQueue::sleep(Spinlock* external_lock) +{ + TaskControlBlock* curr = current_task(); + if (!curr) { + if (external_lock) external_lock->release(); + return; + } + + { + LockGuard guard(lock_); + + /* Mark task as sleeping */ + curr->state = TaskState::Sleeping; + + /* Remove from RunQueue (sched_dequeue acquires its own lock) */ + /* We do NOT call sched_dequeue here because schedule() handles + * the put_prev_task logic when state != Running. */ + + /* Link into wait queue tail */ + list_node* node = &curr->run_link; + node->prev = head_.prev; + node->next = &head_; + head_.prev->next = node; + head_.prev = node; + } + + if (external_lock) { + external_lock->release(); + } + + /* Context switch away — will return here when woken */ + schedule(); +} + +bool WaitQueue::wake_one() +{ + LockGuard guard(lock_); + + if (head_.is_empty()) + return false; + + /* Dequeue the first (oldest) waiter */ + list_node* node = head_.next; + node->prev->next = node->next; + node->next->prev = node->prev; + node->init(); + + TaskControlBlock* task = + list_entry<TaskControlBlock, &TaskControlBlock::run_link>(node); + + task->state = TaskState::Runnable; + sched_enqueue(task); + + return true; +} + +int WaitQueue::wake_all() +{ + LockGuard guard(lock_); + + int count = 0; + while (!head_.is_empty()) { + list_node* node = head_.next; + node->prev->next = node->next; + node->next->prev = node->prev; + node->init(); + + TaskControlBlock* task = + list_entry<TaskControlBlock, &TaskControlBlock::run_link>(node); + + task->state = TaskState::Runnable; + sched_enqueue(task); + count++; + } + + return count; +} |
