sched: enforce thread state transitions
This commit is contained in:
parent
7aaea3a4d3
commit
b92453788b
@ -93,3 +93,10 @@
|
||||
- 不要直接返回 `-22` 这类负数字面量;使用 `-EINVAL` 这类符号化 errno,
|
||||
结构检查会拒绝裸负数返回。
|
||||
- errno 常量放在 `include/tianole/errno.h`,只增加当前内核实际使用的值。
|
||||
|
||||
## 调度状态
|
||||
|
||||
- 线程状态转换必须通过 `kernel/sched/sched.h` 中的 helper 完成。
|
||||
- 不要在调度实现或其他子系统里直接写 `thread->state = ...`;结构检查会拒绝
|
||||
这种写法。
|
||||
- 新增状态或转换规则时,必须同步更新状态转换 helper 和 scheduler selftest。
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/sched.h>
|
||||
#include <tianole/spinlock.h>
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user