diff --git a/docs/agents/code-style.md b/docs/agents/code-style.md index d23d84b..455ffd6 100644 --- a/docs/agents/code-style.md +++ b/docs/agents/code-style.md @@ -93,3 +93,10 @@ - 不要直接返回 `-22` 这类负数字面量;使用 `-EINVAL` 这类符号化 errno, 结构检查会拒绝裸负数返回。 - errno 常量放在 `include/tianole/errno.h`,只增加当前内核实际使用的值。 + +## 调度状态 + +- 线程状态转换必须通过 `kernel/sched/sched.h` 中的 helper 完成。 +- 不要在调度实现或其他子系统里直接写 `thread->state = ...`;结构检查会拒绝 + 这种写法。 +- 新增状态或转换规则时,必须同步更新状态转换 helper 和 scheduler selftest。 diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index c1784ac..6ca95bd 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -3,6 +3,7 @@ #include +#include #include #include @@ -42,34 +43,83 @@ static inline int thread_is_dead(const struct thread *thread) return thread != 0 && thread->state == THREAD_DEAD; } -static inline void thread_set_ready(struct thread *thread) +static inline int thread_state_transition_is_valid( + enum thread_state from, enum thread_state to) +{ + if (from == to) { + return 1; + } + + switch (from) { + case THREAD_READY: + return to == THREAD_RUNNING; + case THREAD_RUNNING: + return to == THREAD_READY || to == THREAD_SLEEPING || + to == THREAD_WAITING || to == THREAD_DEAD; + case THREAD_SLEEPING: + case THREAD_WAITING: + return to == THREAD_READY || to == THREAD_DEAD; + case THREAD_DEAD: + return 0; + default: + return 0; + } +} + +static inline void thread_validate_state_transition( + struct thread *thread, enum thread_state state) +{ + if (thread == 0) { + panic("null thread state transition"); + } + + if (!thread_state_transition_is_valid(thread->state, state)) { + panic("invalid thread state transition"); + } +} + +static inline void thread_set_state( + struct thread *thread, enum thread_state state) +{ + thread_validate_state_transition(thread, state); + thread->state = state; +} + +static inline void thread_init_ready(struct thread *thread) { thread->wake_tick = 0; thread->state = THREAD_READY; } +static inline void thread_set_ready(struct thread *thread) +{ + thread_set_state(thread, THREAD_READY); + thread->wake_tick = 0; +} + static inline void thread_set_running(struct thread *thread) { - thread->state = THREAD_RUNNING; + thread_set_state(thread, THREAD_RUNNING); } static inline void thread_set_sleeping( struct thread *thread, uint64_t wake_tick) { + thread_validate_state_transition(thread, THREAD_SLEEPING); thread->wake_tick = wake_tick; thread->state = THREAD_SLEEPING; } static inline void thread_set_waiting(struct thread *thread) { + thread_set_state(thread, THREAD_WAITING); thread->wake_tick = 0; - thread->state = THREAD_WAITING; } static inline void thread_set_dead(struct thread *thread) { + thread_set_state(thread, THREAD_DEAD); thread->wake_tick = 0; - thread->state = THREAD_DEAD; } void enqueue_thread(struct thread *thread); diff --git a/kernel/sched/thread.c b/kernel/sched/thread.c index 618c9de..c217a71 100644 --- a/kernel/sched/thread.c +++ b/kernel/sched/thread.c @@ -77,7 +77,7 @@ struct thread *kernel_thread_create( stack_top = (uintptr_t)thread->stack_base + KERNEL_STACK_SIZE; - thread_set_ready(thread); + thread_init_ready(thread); thread->entry = entry; thread->arg = arg; thread->stack_top = align_down_uintptr(stack_top, STACK_ALIGNMENT); diff --git a/kernel/selftest/sched.c b/kernel/selftest/sched.c index 53c88e0..0fcfc40 100644 --- a/kernel/selftest/sched.c +++ b/kernel/selftest/sched.c @@ -20,6 +20,35 @@ static int selftest_condition_false(void *arg) return 0; } +static void assert_thread_transition( + enum thread_state from, enum thread_state to, int expected) +{ + if (thread_state_transition_is_valid(from, to) != expected) { + panic("thread state transition selftest failed"); + } +} + +static void sched_state_machine_selftest(void) +{ + assert_thread_transition(THREAD_READY, THREAD_RUNNING, 1); + assert_thread_transition(THREAD_RUNNING, THREAD_READY, 1); + assert_thread_transition(THREAD_RUNNING, THREAD_SLEEPING, 1); + assert_thread_transition(THREAD_RUNNING, THREAD_WAITING, 1); + assert_thread_transition(THREAD_RUNNING, THREAD_DEAD, 1); + assert_thread_transition(THREAD_SLEEPING, THREAD_READY, 1); + assert_thread_transition(THREAD_WAITING, THREAD_READY, 1); + assert_thread_transition(THREAD_SLEEPING, THREAD_DEAD, 1); + assert_thread_transition(THREAD_WAITING, THREAD_DEAD, 1); + + assert_thread_transition(THREAD_READY, THREAD_SLEEPING, 0); + assert_thread_transition(THREAD_READY, THREAD_WAITING, 0); + assert_thread_transition(THREAD_READY, THREAD_DEAD, 0); + assert_thread_transition(THREAD_SLEEPING, THREAD_RUNNING, 0); + assert_thread_transition(THREAD_WAITING, THREAD_RUNNING, 0); + assert_thread_transition(THREAD_DEAD, THREAD_READY, 0); + assert_thread_transition(THREAD_DEAD, THREAD_RUNNING, 0); +} + void sched_selftest(void) { struct spinlock test_lock; @@ -30,6 +59,8 @@ void sched_selftest(void) kernel_thread_create("worker-b", thread_selftest_entry, 0); uint64_t flags; + sched_state_machine_selftest(); + test_lock.locked = 0; if (first == 0 || second == 0 || first == second) { diff --git a/scripts/tools/check_structure.py b/scripts/tools/check_structure.py index 1e02679..10db333 100644 --- a/scripts/tools/check_structure.py +++ b/scripts/tools/check_structure.py @@ -124,6 +124,25 @@ def check_no_bare_negative_errno(root: Path, files: list[Path]) -> list[str]: return errors +def check_scheduler_state_writes(root: Path, files: list[Path]) -> list[str]: + errors = [] + state_write_re = re.compile(r"->state\s*=") + allowed_path = Path("kernel/sched/sched.h") + + for path in files: + if path == allowed_path: + continue + + for line_no, line in enumerate(read_text(root, path).splitlines(), 1): + if state_write_re.search(line): + errors.append( + f"{path}:{line_no}: thread state writes must use " + "kernel/sched/sched.h helpers" + ) + + return errors + + def is_function_declaration_start(line: str) -> bool: stripped = line.strip() @@ -424,6 +443,7 @@ def main() -> int: errors = [] errors.extend(check_no_relative_parent_includes(root, source_files(files))) errors.extend(check_no_bare_negative_errno(root, source_files(files))) + errors.extend(check_scheduler_state_writes(root, source_files(files))) errors.extend(check_public_header_docs(root, public_headers(files))) errors.extend(check_selftests_are_centralized(c_files(files))) errors.extend(check_makefile_source_lists(root, files, all_mode))