From 624034acb52cff238daf252262bbe2b56a404ff0 Mon Sep 17 00:00:00 2001 From: Microindole Date: Tue, 12 May 2026 17:41:03 +0800 Subject: [PATCH] feat(sched): add observable kernel thread exit --- docs/agents/tasks/04-time-scheduler.md | 5 +-- include/tianole/sched.h | 1 + kernel/sched/sched.h | 1 + kernel/sched/thread.c | 42 ++++++++++++++++++++++---- kernel/selftest/sched.c | 21 ++++++++++++- scripts/checks/boot.sh | 12 ++++++++ 6 files changed, 73 insertions(+), 9 deletions(-) diff --git a/docs/agents/tasks/04-time-scheduler.md b/docs/agents/tasks/04-time-scheduler.md index 796a741..17ae08c 100644 --- a/docs/agents/tasks/04-time-scheduler.md +++ b/docs/agents/tasks/04-time-scheduler.md @@ -152,9 +152,10 @@ - 已把 `kernel_thread_create()` 中的线程 id 分配和 run queue 入队纳入 interrupt-safe lock 保护。 - 已建立 `sched_irq_exit()`,timer IRQ 只设置 `need_resched`,trap 的 IRQ 返回边界统一消费调度请求。 - 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。 +- 已建立统一 `kernel_thread_exit()`/`sched_thread_exit()`,线程入口返回和显式退出都会进入明确退出路径,再由调度安全边界回收非当前 DEAD 线程。 - 已在调度私有头中加入 thread state helper,调度核心、线程退出和 wait queue 路径不再直接散写主要状态转换。 - 已把调度代码按职责拆分为 `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、条件等待、超时等待、线程返回退出、显式退出和 DEAD 线程回收。 后续扩展: @@ -181,7 +182,7 @@ ### D. thread lifecycle - 为线程退出增加更完整的生命周期状态、引用规则和最终释放约束。 -- 补充“当前线程不能释放自身内核栈”的文档和自测。 +- 继续补充“当前线程不能释放自身内核栈”的更严格断言和未来 join/wait 语义。 - 为未来 `kthread_stop()`、join/wait 和进程退出保留接口空间。 - 回收路径需要覆盖等待队列残留、run queue 残留和 timer sleep 残留。 diff --git a/include/tianole/sched.h b/include/tianole/sched.h index bd8eb8f..5352312 100644 --- a/include/tianole/sched.h +++ b/include/tianole/sched.h @@ -41,6 +41,7 @@ struct thread { void sched_init(void); struct thread *kernel_thread_create( const char *name, kernel_thread_entry_t entry, void *arg); +void kernel_thread_exit(void) __attribute__((noreturn)); void sched_start(void) __attribute__((noreturn)); void sched_tick(uint64_t tick); void sched_irq_exit(void); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index c4eaf6f..c1784ac 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -74,6 +74,7 @@ static inline void thread_set_dead(struct thread *thread) void enqueue_thread(struct thread *thread); void sched_reap_dead_threads(void); +void sched_thread_exit(void) __attribute__((noreturn)); void sched_selftest(void); int sched_idle_create(void); void sched_demo_start(void) __attribute__((noreturn)); diff --git a/kernel/sched/thread.c b/kernel/sched/thread.c index 36e0be4..618c9de 100644 --- a/kernel/sched/thread.c +++ b/kernel/sched/thread.c @@ -98,6 +98,9 @@ struct thread *kernel_thread_create( static void release_thread(struct thread *thread) { + early_log_puts("thread reaped "); + early_log_puts(thread->name); + early_log_puts("\n"); kfree(thread->stack_base); kfree(thread); } @@ -106,7 +109,10 @@ void sched_reap_dead_threads(void) { struct thread *prev = 0; struct thread *thread = run_queue_head; + struct thread *reap_list = 0; + uint64_t flags; + spin_lock_irqsave(&scheduler_lock, &flags); while (thread != 0) { struct thread *next = thread->next; @@ -121,13 +127,41 @@ void sched_reap_dead_threads(void) run_queue_tail = prev; } - release_thread(thread); + thread->next = reap_list; + reap_list = thread; } else { prev = thread; } thread = next; } + spin_unlock_irqrestore(&scheduler_lock, flags); + + while (reap_list != 0) { + struct thread *next = reap_list->next; + + reap_list->next = 0; + release_thread(reap_list); + reap_list = next; + } +} + +void sched_thread_exit(void) +{ + if (current_thread == 0) { + panic("thread exit without current thread"); + } + + thread_set_dead(current_thread); + + for (;;) { + sched_yield(); + } +} + +void kernel_thread_exit(void) +{ + sched_thread_exit(); } static void thread_trampoline(void) @@ -139,9 +173,5 @@ static void thread_trampoline(void) } thread->entry(thread->arg); - thread_set_dead(thread); - - for (;;) { - sched_yield(); - } + kernel_thread_exit(); } diff --git a/kernel/selftest/sched.c b/kernel/selftest/sched.c index 7cf9924..5aeeab8 100644 --- a/kernel/selftest/sched.c +++ b/kernel/selftest/sched.c @@ -134,6 +134,21 @@ static void timeout_wait_demo_waiter(void *arg) early_log_puts("timeout waiter timed out\n"); } +static void return_exit_demo_thread(void *arg) +{ + (void)arg; + + early_log_puts("return exit thread returning\n"); +} + +static void explicit_exit_demo_thread(void *arg) +{ + (void)arg; + + early_log_puts("explicit exit thread exiting\n"); + kernel_thread_exit(); +} + void sched_demo_start(void) { struct thread *first = kernel_thread_create( @@ -150,10 +165,14 @@ void sched_demo_start(void) "condition-waker", condition_wait_demo_waker, 0); struct thread *timeout_waiter = kernel_thread_create( "timeout-waiter", timeout_wait_demo_waiter, 0); + struct thread *return_exit = + kernel_thread_create("return-exit", return_exit_demo_thread, 0); + struct thread *explicit_exit = kernel_thread_create( + "explicit-exit", explicit_exit_demo_thread, 0); if (first == 0 || second == 0 || waiter == 0 || waker == 0 || condition_waiter == 0 || condition_waker == 0 || - timeout_waiter == 0) { + timeout_waiter == 0 || return_exit == 0 || explicit_exit == 0) { panic("scheduler demo thread creation failed"); } diff --git a/scripts/checks/boot.sh b/scripts/checks/boot.sh index 87c88f6..c911f60 100755 --- a/scripts/checks/boot.sh +++ b/scripts/checks/boot.sh @@ -34,6 +34,12 @@ check_lines build/debug.log \ "condition waiter sleeping" \ "condition waker sleeping" \ "timeout waiter sleeping" \ + "return exit thread returning" \ + "explicit exit thread exiting" \ + "thread reaped worker-a" \ + "thread reaped worker-b" \ + "thread reaped return-exit" \ + "thread reaped explicit-exit" \ "preempt thread 1 step=2" \ "preempt thread 2 step=2" \ "timeout waiter timed out" \ @@ -71,6 +77,12 @@ check_lines build/serial.log \ "condition waiter sleeping" \ "condition waker sleeping" \ "timeout waiter sleeping" \ + "return exit thread returning" \ + "explicit exit thread exiting" \ + "thread reaped worker-a" \ + "thread reaped worker-b" \ + "thread reaped return-exit" \ + "thread reaped explicit-exit" \ "preempt thread 1 step=2" \ "preempt thread 2 step=2" \ "timeout waiter timed out" \