feat(sched): add condition and timeout wait queues

This commit is contained in:
Microindole 2026-05-10 12:18:45 +08:00
parent edd6e5f2d1
commit 288d55fb60
No known key found for this signature in database
GPG Key ID: 22FB34CD2133ACA1
4 changed files with 156 additions and 4 deletions

View File

@ -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 边界纳入明确锁规则
- 为线程退出增加更完整的生命周期状态、引用规则和最终释放约束。
下一阶段:

View File

@ -5,6 +5,7 @@
#include <stdint.h>
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);

View File

@ -1,4 +1,5 @@
#include <tianole/sched.h>
#include <tianole/timer.h>
#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;
}
}

View File

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