fix(sched): guard wait queue state transitions
This commit is contained in:
parent
61775e86e9
commit
f97c30d5d0
@ -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 或未入队线程。
|
||||
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user