feat(sched): add condition and timeout wait queues
This commit is contained in:
parent
edd6e5f2d1
commit
288d55fb60
@ -67,18 +67,19 @@
|
|||||||
- 已建立 idle thread,所有普通线程 sleep/wait 时由 idle 承接 CPU。
|
- 已建立 idle thread,所有普通线程 sleep/wait 时由 idle 承接 CPU。
|
||||||
- 已提供 `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()`,调用方可以等待条件成立,并在超时路径上获得失败返回值。
|
||||||
- 已建立单 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 返回边界统一消费调度请求。
|
||||||
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
|
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
|
||||||
- 已把调度代码按职责拆分为 `core.c`、`thread.c`、`wait.c`、`idle.c` 和私有 `sched.h`,并把当前阶段自测/演示线程移到 `kernel/selftest/sched.c`。
|
- 已把调度代码按职责拆分为 `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 模型,避免把普通线程栈切换入口长期当成完整抢占式切换。
|
- 把当前 `sched_irq_exit()` 继续收敛为更严格的 trap-frame aware interrupt-exit reschedule 模型,避免把普通线程栈切换入口长期当成完整抢占式切换。
|
||||||
- 继续扩大 interrupt-safe lock 覆盖范围,明确可睡眠路径和不可睡眠路径的锁规则。
|
- 继续扩大 interrupt-safe lock 覆盖范围,明确可睡眠路径和不可睡眠路径的锁规则。
|
||||||
- 为 wait queue 增加条件等待、超时等待和状态检查。
|
- 为 wait queue 增加更严格的状态检查,并把条件检查与 wakeup 边界纳入明确锁规则。
|
||||||
- 为线程退出增加更完整的生命周期状态、引用规则和最终释放约束。
|
- 为线程退出增加更完整的生命周期状态、引用规则和最终释放约束。
|
||||||
|
|
||||||
下一阶段:
|
下一阶段:
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef void (*kernel_thread_entry_t)(void *arg);
|
typedef void (*kernel_thread_entry_t)(void *arg);
|
||||||
|
typedef int (*wait_condition_t)(void *arg);
|
||||||
|
|
||||||
enum thread_state {
|
enum thread_state {
|
||||||
THREAD_READY,
|
THREAD_READY,
|
||||||
@ -44,6 +45,12 @@ void sched_yield(void);
|
|||||||
void sched_sleep(uint64_t ticks);
|
void sched_sleep(uint64_t ticks);
|
||||||
void wait_queue_init(struct wait_queue *queue);
|
void wait_queue_init(struct wait_queue *queue);
|
||||||
void wait_queue_sleep(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_one(struct wait_queue *queue);
|
||||||
void wait_queue_wake_all(struct wait_queue *queue);
|
void wait_queue_wake_all(struct wait_queue *queue);
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#include <tianole/sched.h>
|
#include <tianole/sched.h>
|
||||||
|
#include <tianole/timer.h>
|
||||||
|
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
|
|
||||||
@ -25,6 +26,35 @@ 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)
|
||||||
|
{
|
||||||
|
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)
|
void wait_queue_sleep(struct wait_queue *queue)
|
||||||
{
|
{
|
||||||
if (queue == 0 || current_thread == 0) {
|
if (queue == 0 || current_thread == 0) {
|
||||||
@ -36,6 +66,58 @@ void wait_queue_sleep(struct wait_queue *queue)
|
|||||||
sched_yield();
|
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)
|
void wait_queue_wake_one(struct wait_queue *queue)
|
||||||
{
|
{
|
||||||
struct thread *thread;
|
struct thread *thread;
|
||||||
@ -51,7 +133,9 @@ void wait_queue_wake_one(struct wait_queue *queue)
|
|||||||
}
|
}
|
||||||
|
|
||||||
thread->wait_next = 0;
|
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;
|
thread->state = THREAD_READY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,6 +65,16 @@ static void scheduler_demo_entry(void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct wait_queue demo_wait_queue;
|
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)
|
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);
|
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)
|
void sched_demo_start(void)
|
||||||
{
|
{
|
||||||
struct thread *first = kernel_thread_create(
|
struct thread *first = kernel_thread_create(
|
||||||
@ -95,12 +144,23 @@ void sched_demo_start(void)
|
|||||||
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
|
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
|
||||||
struct thread *waker =
|
struct thread *waker =
|
||||||
kernel_thread_create("waker", wait_queue_demo_waker, 0);
|
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");
|
panic("scheduler demo thread creation failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_queue_init(&demo_wait_queue);
|
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");
|
early_log_puts("scheduler starting\n");
|
||||||
sched_yield();
|
sched_yield();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user