diff --git a/docs/agents/tasks/04-time-scheduler.md b/docs/agents/tasks/04-time-scheduler.md index ffc0273..6a4fa20 100644 --- a/docs/agents/tasks/04-time-scheduler.md +++ b/docs/agents/tasks/04-time-scheduler.md @@ -67,18 +67,19 @@ - 已建立 idle thread,所有普通线程 sleep/wait 时由 idle 承接 CPU。 - 已提供 `sched_sleep()`,线程可以睡眠指定 tick 数并被 timer 唤醒。 - 已提供 `wait_queue_init()`、`wait_queue_sleep()`、`wait_queue_wake_one()` 和 `wait_queue_wake_all()`。 +- 已提供 `wait_queue_wait()` 和 `wait_queue_wait_timeout()`,调用方可以等待条件成立,并在超时路径上获得失败返回值。 - 已建立单 CPU interrupt-safe lock 基础,当前 `spin_lock_irqsave()` 会保存并关闭中断,`spin_unlock_irqrestore()` 会恢复原中断状态。 - 已把 `kernel_thread_create()` 中的线程 id 分配和 run queue 入队纳入 interrupt-safe lock 保护。 - 已建立 `sched_irq_exit()`,timer IRQ 只设置 `need_resched`,trap 的 IRQ 返回边界统一消费调度请求。 - 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。 - 已把调度代码按职责拆分为 `core.c`、`thread.c`、`wait.c`、`idle.c` 和私有 `sched.h`,并把当前阶段自测/演示线程移到 `kernel/selftest/sched.c`。 -- `scripts/check.sh` 已验证 `timer initialized`、`timer tick=1/2/3`、`scheduler initialized`、`kernel thread selftest ok`、timer 驱动线程轮转、`sched_sleep()` 和 wait queue wakeup。 +- `scripts/check.sh` 已验证 `timer initialized`、`timer tick=1/2/3`、`scheduler initialized`、`kernel thread selftest ok`、timer 驱动线程轮转、`sched_sleep()`、wait queue wakeup、条件等待和超时等待。 后续扩展: - 把当前 `sched_irq_exit()` 继续收敛为更严格的 trap-frame aware interrupt-exit reschedule 模型,避免把普通线程栈切换入口长期当成完整抢占式切换。 - 继续扩大 interrupt-safe lock 覆盖范围,明确可睡眠路径和不可睡眠路径的锁规则。 -- 为 wait queue 增加条件等待、超时等待和状态检查。 +- 为 wait queue 增加更严格的状态检查,并把条件检查与 wakeup 边界纳入明确锁规则。 - 为线程退出增加更完整的生命周期状态、引用规则和最终释放约束。 下一阶段: diff --git a/include/tianole/sched.h b/include/tianole/sched.h index 5533e21..47742d0 100644 --- a/include/tianole/sched.h +++ b/include/tianole/sched.h @@ -5,6 +5,7 @@ #include typedef void (*kernel_thread_entry_t)(void *arg); +typedef int (*wait_condition_t)(void *arg); enum thread_state { THREAD_READY, @@ -44,6 +45,12 @@ void sched_yield(void); void sched_sleep(uint64_t ticks); void wait_queue_init(struct wait_queue *queue); void wait_queue_sleep(struct wait_queue *queue); +int wait_queue_wait( + struct wait_queue *queue, wait_condition_t condition, void *arg); +int wait_queue_wait_timeout(struct wait_queue *queue, + wait_condition_t condition, + void *arg, + uint64_t ticks); void wait_queue_wake_one(struct wait_queue *queue); void wait_queue_wake_all(struct wait_queue *queue); diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index 979e92f..00f18d6 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c @@ -1,4 +1,5 @@ #include +#include #include "sched.h" @@ -25,6 +26,35 @@ static void wait_queue_enqueue(struct wait_queue *queue, struct thread *thread) queue->tail = thread; } +static void wait_queue_remove(struct wait_queue *queue, struct thread *target) +{ + struct thread *prev = 0; + struct thread *thread; + + if (queue == 0 || target == 0) { + return; + } + + for (thread = queue->head; thread != 0; thread = thread->wait_next) { + if (thread == target) { + if (prev != 0) { + prev->wait_next = thread->wait_next; + } else { + queue->head = thread->wait_next; + } + + if (queue->tail == thread) { + queue->tail = prev; + } + + thread->wait_next = 0; + return; + } + + prev = thread; + } +} + void wait_queue_sleep(struct wait_queue *queue) { if (queue == 0 || current_thread == 0) { @@ -36,6 +66,58 @@ void wait_queue_sleep(struct wait_queue *queue) sched_yield(); } +int wait_queue_wait( + struct wait_queue *queue, wait_condition_t condition, void *arg) +{ + if (queue == 0 || condition == 0 || current_thread == 0) { + return -1; + } + + while (condition(arg) == 0) { + wait_queue_sleep(queue); + } + + return 0; +} + +int wait_queue_wait_timeout(struct wait_queue *queue, + wait_condition_t condition, + void *arg, + uint64_t ticks) +{ + uint64_t deadline; + + if (queue == 0 || condition == 0 || current_thread == 0) { + return -1; + } + + if (condition(arg) != 0) { + return 0; + } + + if (ticks == 0) { + return -1; + } + + deadline = timer_ticks() + ticks; + while (condition(arg) == 0) { + uint64_t now = timer_ticks(); + + if (now >= deadline) { + return -1; + } + + current_thread->wake_tick = deadline; + current_thread->state = THREAD_SLEEPING; + wait_queue_enqueue(queue, current_thread); + sched_yield(); + wait_queue_remove(queue, current_thread); + } + + current_thread->wake_tick = 0; + return 0; +} + void wait_queue_wake_one(struct wait_queue *queue) { struct thread *thread; @@ -51,7 +133,9 @@ void wait_queue_wake_one(struct wait_queue *queue) } thread->wait_next = 0; - if (thread->state == THREAD_WAITING) { + if (thread->state == THREAD_WAITING || + thread->state == THREAD_SLEEPING) { + thread->wake_tick = 0; thread->state = THREAD_READY; } } diff --git a/kernel/selftest/sched.c b/kernel/selftest/sched.c index a983a79..7cf9924 100644 --- a/kernel/selftest/sched.c +++ b/kernel/selftest/sched.c @@ -65,6 +65,16 @@ static void scheduler_demo_entry(void *arg) } static struct wait_queue demo_wait_queue; +static struct wait_queue condition_wait_queue; +static struct wait_queue timeout_wait_queue; +static int condition_ready; + +static int condition_is_ready(void *arg) +{ + int *ready = arg; + + return *ready != 0; +} static void wait_queue_demo_waiter(void *arg) { @@ -85,6 +95,45 @@ static void wait_queue_demo_waker(void *arg) wait_queue_wake_one(&demo_wait_queue); } +static void condition_wait_demo_waiter(void *arg) +{ + (void)arg; + + early_log_puts("condition waiter sleeping\n"); + if (wait_queue_wait(&condition_wait_queue, + condition_is_ready, + &condition_ready) != 0) { + panic("condition wait failed"); + } + early_log_puts("condition waiter woke\n"); +} + +static void condition_wait_demo_waker(void *arg) +{ + (void)arg; + + early_log_puts("condition waker sleeping\n"); + sched_sleep(5); + condition_ready = 1; + early_log_puts("condition waker wake_all\n"); + wait_queue_wake_all(&condition_wait_queue); +} + +static void timeout_wait_demo_waiter(void *arg) +{ + int never_ready = 0; + + (void)arg; + + early_log_puts("timeout waiter sleeping\n"); + if (wait_queue_wait_timeout( + &timeout_wait_queue, condition_is_ready, &never_ready, 3) != + -1) { + panic("timeout wait did not time out"); + } + early_log_puts("timeout waiter timed out\n"); +} + void sched_demo_start(void) { struct thread *first = kernel_thread_create( @@ -95,12 +144,23 @@ void sched_demo_start(void) kernel_thread_create("waiter", wait_queue_demo_waiter, 0); struct thread *waker = kernel_thread_create("waker", wait_queue_demo_waker, 0); + struct thread *condition_waiter = kernel_thread_create( + "condition-waiter", condition_wait_demo_waiter, 0); + struct thread *condition_waker = kernel_thread_create( + "condition-waker", condition_wait_demo_waker, 0); + struct thread *timeout_waiter = kernel_thread_create( + "timeout-waiter", timeout_wait_demo_waiter, 0); - if (first == 0 || second == 0 || waiter == 0 || waker == 0) { + if (first == 0 || second == 0 || waiter == 0 || waker == 0 || + condition_waiter == 0 || condition_waker == 0 || + timeout_waiter == 0) { panic("scheduler demo thread creation failed"); } wait_queue_init(&demo_wait_queue); + wait_queue_init(&condition_wait_queue); + wait_queue_init(&timeout_wait_queue); + condition_ready = 0; early_log_puts("scheduler starting\n"); sched_yield();