From f97c30d5d09e13ae5b504dd01a6793121744da39 Mon Sep 17 00:00:00 2001 From: Microindole Date: Tue, 12 May 2026 17:11:13 +0800 Subject: [PATCH] fix(sched): guard wait queue state transitions --- docs/agents/tasks/04-time-scheduler.md | 3 +- include/tianole/sched.h | 3 + kernel/sched/wait.c | 118 ++++++++++++++++++++----- 3 files changed, 100 insertions(+), 24 deletions(-) diff --git a/docs/agents/tasks/04-time-scheduler.md b/docs/agents/tasks/04-time-scheduler.md index 0b093de..d2d7c4e 100644 --- a/docs/agents/tasks/04-time-scheduler.md +++ b/docs/agents/tasks/04-time-scheduler.md @@ -147,6 +147,7 @@ - 已提供 `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()`,调用方可以等待条件成立,并在超时路径上获得失败返回值。 +- wait queue 已有内部 interrupt-safe lock,条件检查、等待入队和 wakeup 队列修改已收敛到同一同步边界,降低 lost wakeup 风险。 - 已建立单 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 返回边界统一消费调度请求。 @@ -165,7 +166,7 @@ ### B. wait queue 锁语义 - 为 wait queue 增加内部锁或要求调用方持有指定锁,并在接口命名中体现约束。 -- 把条件检查、等待入队和睡眠切换收敛成一个不会丢唤醒的模式。 +- 继续把条件所属数据的修改规则文档化;当前 wait queue 内部锁已经覆盖条件检查、等待入队和 wakeup 队列修改。 - 区分 `wake_one`、`wake_all`、timeout wakeup 和条件 wakeup 的状态处理。 - 检查 wakeup 是否可能唤醒 DEAD、RUNNING 或未入队线程。 diff --git a/include/tianole/sched.h b/include/tianole/sched.h index 47742d0..bd8eb8f 100644 --- a/include/tianole/sched.h +++ b/include/tianole/sched.h @@ -4,6 +4,8 @@ #include #include +#include + typedef void (*kernel_thread_entry_t)(void *arg); typedef int (*wait_condition_t)(void *arg); @@ -16,6 +18,7 @@ enum thread_state { }; struct wait_queue { + struct spinlock lock; struct thread *head; struct thread *tail; }; diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index 00f18d6..c3b3dee 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c @@ -9,11 +9,13 @@ void wait_queue_init(struct wait_queue *queue) return; } + queue->lock.locked = 0; queue->head = 0; queue->tail = 0; } -static void wait_queue_enqueue(struct wait_queue *queue, struct thread *thread) +static void wait_queue_enqueue_locked( + struct wait_queue *queue, struct thread *thread) { thread->wait_next = 0; @@ -26,7 +28,8 @@ 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) +static void wait_queue_remove_locked( + struct wait_queue *queue, struct thread *target) { struct thread *prev = 0; struct thread *thread; @@ -55,29 +58,66 @@ static void wait_queue_remove(struct wait_queue *queue, struct thread *target) } } +static void wait_queue_mark_ready_locked(struct thread *thread) +{ + if (thread->state == THREAD_WAITING || + thread->state == THREAD_SLEEPING) { + thread->wake_tick = 0; + thread->state = THREAD_READY; + } +} + void wait_queue_sleep(struct wait_queue *queue) { + uint64_t flags; + if (queue == 0 || current_thread == 0) { return; } - wait_queue_enqueue(queue, current_thread); + spin_lock_irqsave(&queue->lock, &flags); + wait_queue_enqueue_locked(queue, current_thread); current_thread->state = THREAD_WAITING; - sched_yield(); + spin_unlock_irqrestore(&queue->lock, flags); + + for (;;) { + sched_yield(); + + spin_lock_irqsave(&queue->lock, &flags); + if (current_thread->state != THREAD_WAITING) { + spin_unlock_irqrestore(&queue->lock, flags); + return; + } + spin_unlock_irqrestore(&queue->lock, flags); + } } int wait_queue_wait( struct wait_queue *queue, wait_condition_t condition, void *arg) { + uint64_t flags; + if (queue == 0 || condition == 0 || current_thread == 0) { return -1; } - while (condition(arg) == 0) { - wait_queue_sleep(queue); - } + for (;;) { + spin_lock_irqsave(&queue->lock, &flags); + if (condition(arg) != 0) { + spin_unlock_irqrestore(&queue->lock, flags); + return 0; + } - return 0; + wait_queue_enqueue_locked(queue, current_thread); + current_thread->state = THREAD_WAITING; + spin_unlock_irqrestore(&queue->lock, flags); + + sched_yield(); + + spin_lock_irqsave(&queue->lock, &flags); + wait_queue_remove_locked(queue, current_thread); + spin_unlock_irqrestore(&queue->lock, flags); + } } int wait_queue_wait_timeout(struct wait_queue *queue, @@ -86,6 +126,7 @@ int wait_queue_wait_timeout(struct wait_queue *queue, uint64_t ticks) { uint64_t deadline; + uint64_t flags; if (queue == 0 || condition == 0 || current_thread == 0) { return -1; @@ -100,29 +141,47 @@ int wait_queue_wait_timeout(struct wait_queue *queue, } deadline = timer_ticks() + ticks; - while (condition(arg) == 0) { + for (;;) { uint64_t now = timer_ticks(); + spin_lock_irqsave(&queue->lock, &flags); + if (condition(arg) != 0) { + current_thread->wake_tick = 0; + spin_unlock_irqrestore(&queue->lock, flags); + return 0; + } + if (now >= deadline) { + current_thread->wake_tick = 0; + spin_unlock_irqrestore(&queue->lock, flags); 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); - } + wait_queue_enqueue_locked(queue, current_thread); + spin_unlock_irqrestore(&queue->lock, flags); - current_thread->wake_tick = 0; - return 0; + sched_yield(); + + spin_lock_irqsave(&queue->lock, &flags); + wait_queue_remove_locked(queue, current_thread); + spin_unlock_irqrestore(&queue->lock, flags); + } } void wait_queue_wake_one(struct wait_queue *queue) { struct thread *thread; + uint64_t flags; - if (queue == 0 || queue->head == 0) { + if (queue == 0) { + return; + } + + spin_lock_irqsave(&queue->lock, &flags); + if (queue->head == 0) { + spin_unlock_irqrestore(&queue->lock, flags); return; } @@ -133,16 +192,29 @@ void wait_queue_wake_one(struct wait_queue *queue) } thread->wait_next = 0; - if (thread->state == THREAD_WAITING || - thread->state == THREAD_SLEEPING) { - thread->wake_tick = 0; - thread->state = THREAD_READY; - } + wait_queue_mark_ready_locked(thread); + spin_unlock_irqrestore(&queue->lock, flags); } void wait_queue_wake_all(struct wait_queue *queue) { - while (queue != 0 && queue->head != 0) { - wait_queue_wake_one(queue); + struct thread *thread; + uint64_t flags; + + if (queue == 0) { + return; } + + spin_lock_irqsave(&queue->lock, &flags); + while (queue->head != 0) { + thread = queue->head; + queue->head = thread->wait_next; + if (queue->head == 0) { + queue->tail = 0; + } + + thread->wait_next = 0; + wait_queue_mark_ready_locked(thread); + } + spin_unlock_irqrestore(&queue->lock, flags); }