fix(sched): guard wait queue state transitions
This commit is contained in:
parent
61775e86e9
commit
f97c30d5d0
@ -147,6 +147,7 @@
|
|||||||
- 已提供 `sched_sleep()`,线程可以睡眠指定 tick 数并被 timer 唤醒。
|
- 已提供 `sched_sleep()`,线程可以睡眠指定 tick 数并被 timer 唤醒。
|
||||||
- 已提供 `wait_queue_init()`、`wait_queue_sleep()`、`wait_queue_wake_one()` 和 `wait_queue_wake_all()`。
|
- 已提供 `wait_queue_init()`、`wait_queue_sleep()`、`wait_queue_wake_one()` 和 `wait_queue_wake_all()`。
|
||||||
- 已提供 `wait_queue_wait()` 和 `wait_queue_wait_timeout()`,调用方可以等待条件成立,并在超时路径上获得失败返回值。
|
- 已提供 `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()` 会恢复原中断状态。
|
- 已建立单 CPU interrupt-safe lock 基础,当前 `spin_lock_irqsave()` 会保存并关闭中断,`spin_unlock_irqrestore()` 会恢复原中断状态。
|
||||||
- 已把 `kernel_thread_create()` 中的线程 id 分配和 run queue 入队纳入 interrupt-safe lock 保护。
|
- 已把 `kernel_thread_create()` 中的线程 id 分配和 run queue 入队纳入 interrupt-safe lock 保护。
|
||||||
- 已建立 `sched_irq_exit()`,timer IRQ 只设置 `need_resched`,trap 的 IRQ 返回边界统一消费调度请求。
|
- 已建立 `sched_irq_exit()`,timer IRQ 只设置 `need_resched`,trap 的 IRQ 返回边界统一消费调度请求。
|
||||||
@ -165,7 +166,7 @@
|
|||||||
### B. wait queue 锁语义
|
### B. wait queue 锁语义
|
||||||
|
|
||||||
- 为 wait queue 增加内部锁或要求调用方持有指定锁,并在接口命名中体现约束。
|
- 为 wait queue 增加内部锁或要求调用方持有指定锁,并在接口命名中体现约束。
|
||||||
- 把条件检查、等待入队和睡眠切换收敛成一个不会丢唤醒的模式。
|
- 继续把条件所属数据的修改规则文档化;当前 wait queue 内部锁已经覆盖条件检查、等待入队和 wakeup 队列修改。
|
||||||
- 区分 `wake_one`、`wake_all`、timeout wakeup 和条件 wakeup 的状态处理。
|
- 区分 `wake_one`、`wake_all`、timeout wakeup 和条件 wakeup 的状态处理。
|
||||||
- 检查 wakeup 是否可能唤醒 DEAD、RUNNING 或未入队线程。
|
- 检查 wakeup 是否可能唤醒 DEAD、RUNNING 或未入队线程。
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tianole/spinlock.h>
|
||||||
|
|
||||||
typedef void (*kernel_thread_entry_t)(void *arg);
|
typedef void (*kernel_thread_entry_t)(void *arg);
|
||||||
typedef int (*wait_condition_t)(void *arg);
|
typedef int (*wait_condition_t)(void *arg);
|
||||||
|
|
||||||
@ -16,6 +18,7 @@ enum thread_state {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct wait_queue {
|
struct wait_queue {
|
||||||
|
struct spinlock lock;
|
||||||
struct thread *head;
|
struct thread *head;
|
||||||
struct thread *tail;
|
struct thread *tail;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,11 +9,13 @@ void wait_queue_init(struct wait_queue *queue)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queue->lock.locked = 0;
|
||||||
queue->head = 0;
|
queue->head = 0;
|
||||||
queue->tail = 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;
|
thread->wait_next = 0;
|
||||||
|
|
||||||
@ -26,7 +28,8 @@ static void wait_queue_enqueue(struct wait_queue *queue, struct thread *thread)
|
|||||||
queue->tail = 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 *prev = 0;
|
||||||
struct thread *thread;
|
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)
|
void wait_queue_sleep(struct wait_queue *queue)
|
||||||
{
|
{
|
||||||
|
uint64_t flags;
|
||||||
|
|
||||||
if (queue == 0 || current_thread == 0) {
|
if (queue == 0 || current_thread == 0) {
|
||||||
return;
|
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;
|
current_thread->state = THREAD_WAITING;
|
||||||
|
spin_unlock_irqrestore(&queue->lock, flags);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
sched_yield();
|
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(
|
int wait_queue_wait(
|
||||||
struct wait_queue *queue, wait_condition_t condition, void *arg)
|
struct wait_queue *queue, wait_condition_t condition, void *arg)
|
||||||
{
|
{
|
||||||
|
uint64_t flags;
|
||||||
|
|
||||||
if (queue == 0 || condition == 0 || current_thread == 0) {
|
if (queue == 0 || condition == 0 || current_thread == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (condition(arg) == 0) {
|
for (;;) {
|
||||||
wait_queue_sleep(queue);
|
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,
|
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 ticks)
|
||||||
{
|
{
|
||||||
uint64_t deadline;
|
uint64_t deadline;
|
||||||
|
uint64_t flags;
|
||||||
|
|
||||||
if (queue == 0 || condition == 0 || current_thread == 0) {
|
if (queue == 0 || condition == 0 || current_thread == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -100,29 +141,47 @@ int wait_queue_wait_timeout(struct wait_queue *queue,
|
|||||||
}
|
}
|
||||||
|
|
||||||
deadline = timer_ticks() + ticks;
|
deadline = timer_ticks() + ticks;
|
||||||
while (condition(arg) == 0) {
|
for (;;) {
|
||||||
uint64_t now = timer_ticks();
|
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) {
|
if (now >= deadline) {
|
||||||
|
current_thread->wake_tick = 0;
|
||||||
|
spin_unlock_irqrestore(&queue->lock, flags);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_thread->wake_tick = deadline;
|
current_thread->wake_tick = deadline;
|
||||||
current_thread->state = THREAD_SLEEPING;
|
current_thread->state = THREAD_SLEEPING;
|
||||||
wait_queue_enqueue(queue, current_thread);
|
wait_queue_enqueue_locked(queue, current_thread);
|
||||||
sched_yield();
|
spin_unlock_irqrestore(&queue->lock, flags);
|
||||||
wait_queue_remove(queue, current_thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
current_thread->wake_tick = 0;
|
sched_yield();
|
||||||
return 0;
|
|
||||||
|
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)
|
void wait_queue_wake_one(struct wait_queue *queue)
|
||||||
{
|
{
|
||||||
struct thread *thread;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,16 +192,29 @@ void wait_queue_wake_one(struct wait_queue *queue)
|
|||||||
}
|
}
|
||||||
|
|
||||||
thread->wait_next = 0;
|
thread->wait_next = 0;
|
||||||
if (thread->state == THREAD_WAITING ||
|
wait_queue_mark_ready_locked(thread);
|
||||||
thread->state == THREAD_SLEEPING) {
|
spin_unlock_irqrestore(&queue->lock, flags);
|
||||||
thread->wake_tick = 0;
|
|
||||||
thread->state = THREAD_READY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_queue_wake_all(struct wait_queue *queue)
|
void wait_queue_wake_all(struct wait_queue *queue)
|
||||||
{
|
{
|
||||||
while (queue != 0 && queue->head != 0) {
|
struct thread *thread;
|
||||||
wait_queue_wake_one(queue);
|
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