fix(sched): guard wait queue state transitions

This commit is contained in:
Microindole 2026-05-12 17:11:13 +08:00
parent 61775e86e9
commit f97c30d5d0
No known key found for this signature in database
GPG Key ID: 22FB34CD2133ACA1
3 changed files with 100 additions and 24 deletions

View File

@ -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 或未入队线程。

View File

@ -4,6 +4,8 @@
#include <stddef.h>
#include <stdint.h>
#include <tianole/spinlock.h>
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;
};

View File

@ -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;
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);
}