summaryrefslogtreecommitdiffstats
path: root/docs/adr/0003-preemptive-kernel-tasks.md
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-01 16:19:45 +0200
committersalmonik <salmonik@tuta.io>2026-08-01 16:19:45 +0200
commitce43cb9554d443f12af886ee035a73bf9b322ac5 (patch)
tree4611a08b2596ab49c25793f20e126b66fea26169 /docs/adr/0003-preemptive-kernel-tasks.md
parentrsnix: formatting (diff)
downloadrs-dos-ce43cb9554d443f12af886ee035a73bf9b322ac5.tar.gz
rs-dos-ce43cb9554d443f12af886ee035a73bf9b322ac5.zip
docs: ADR 0003 (preemptive kernel tasks) + task glossary terms
- ADR 0003: preemptive round-robin kernel tasks on the 100 Hz tick, shared address space, guard pages + page-fault handler, graceful task panics, safe-builtin spawn surface, only-shell-touches-I/O invariant - CONTEXT.md: new 'Task' glossary entry; 'Paging strategy' now says guard pages exist on task stacks
Diffstat (limited to 'docs/adr/0003-preemptive-kernel-tasks.md')
-rw-r--r--docs/adr/0003-preemptive-kernel-tasks.md97
1 files changed, 97 insertions, 0 deletions
diff --git a/docs/adr/0003-preemptive-kernel-tasks.md b/docs/adr/0003-preemptive-kernel-tasks.md
new file mode 100644
index 0000000..ac38027
--- /dev/null
+++ b/docs/adr/0003-preemptive-kernel-tasks.md
@@ -0,0 +1,97 @@
1# ADR 0003 — Preemptive Kernel Tasks
2
3## Status
4
5Accepted.
6
7## Context
8
9The kernel is single-threaded and polling-based: a shell loop drives
10everything, drivers poll (AHCI has no interrupts), and the only
11preemption source is a 100 Hz PIT tick whose ISR currently just counts
12and acknowledges. The interrupt path already saves a complete register
13frame (`isr_common` pushes all GPRs + the iretq frame onto the
14interrupted stack), so switching tasks is "return a different stack
15pointer from the ISR dispatch" — a small asm change, not new machinery.
16
17The destination is a windowed GUI running multiple applications at
18once, which requires concurrent execution and, eventually, per-task
19input/output channels. The immediate goal is the foundation: a
20preemptive multitasking core that the GUI will be built on.
21
22Two constraints shape the design. First, all kernel driver state is
23single-user by construction: one shared AHCI DMA buffer plus a global
24"active disk" selector, a per-volume cached FAT with a global active
25volume, the VFS node table, and the shell's tty/vt terminal. Two tasks
26doing disk or filesystem work concurrently would corrupt each other.
27Second, there is no address-space separation and no user/kernel
28boundary — the kernel is one identity-mapped space (TempleOS-style), so
29"tasks" (not processes) sharing every kernel facility is the honest
30model.
31
32## Decision
33
34Preemptive, round-robin **kernel tasks** in the one shared address
35space, preempted on the 100 Hz PIT tick:
36
37- **Scheduler** — a fixed 16-slot static task table (the timer ISR
38 never allocates), round-robin across Ready tasks, one class, 10 ms
39 quantum. States: `ready`, `sleeping` (wake-at timestamp from
40 `uptime_ms`), `blocked`, `done`. The shell is task 0; a kernel idle
41 task (`hlt` loop) is the always-ready fallback. Reaping is lazy:
42 Done tasks' stacks are freed and slots reused at the next `spawn` (and
43 `jobs` sweeps).
44- **Context switch** — the timer ISR saves the running task's frame on
45 its own stack, stores the frame pointer in the task's slot, picks the
46 next Ready task, and returns its frame pointer; the asm moves `rsp`
47 there and iretq's. Yield and sleep use a software interrupt (vector
48 0x40) through the same machinery.
49- **Stacks** — each task gets 8 KiB from the page allocator plus one
50 **guard page** mapped below it (the paging code already maps/unmaps).
51 A new page-fault handler (vector 14) reads CR2; a fault inside a
52 task's guard page is a "stack overflow" task panic, any other fault
53 halts the kernel with the address.
54- **Spawn surface** — `sched::spawn(entry, name)` is general (any plain
55 `fn`), but the shell only offers safe builtins: `bg <name>`,
56 `jobs`, `sleep <ms>`, `kill <id>`, `demo`. First-slice tasks are
57 headless: they may sleep, yield, and log to serial/klog — nothing
58 else. Invariant: **only the shell task touches the disk,
59 filesystem, and terminal**; the scheduler does not enforce this (a
60 general `spawn` cannot), it is a documented boundary.
61- **Line atomicity** — a scheduler lock (preempt-disable counter, "no
62 switching while held") is held by `klog::append_fmt` and
63 `serial::write_str` so lines from different tasks never interleave
64 mid-line. The timer ISR skips switching while the lock is held.
65- **Graceful task panic** — the panic handler becomes task-aware: if
66 the scheduler is live and the panic is in a task (id ≥ 1), it logs
67 `sched: task N "name" panicked: <msg>`, marks the task Done, clears
68 the scheduler lock, and switches away via vector 0x40 — the shell
69 keeps running and `jobs` shows the corpse. Panics in ISR context,
70 before the scheduler exists, or in the shell task itself still halt.
71- **Shell surface** — `bg <name>` spawns a builtin (`spinner`,
72 `ticker`, `counter`, plus `boom` which panics on purpose), `jobs`
73 lists id/name/state, `sleep <ms>` blocks the shell task (background
74 tasks keep running — the visible proof of preemption), `kill <id>`
75 marks a task Done, `demo` spawns a showcase set at different sleep
76 rates.
77
78Explicitly **not** in this ADR: backgrounding arbitrary shell commands
79(`cmd &`), per-task I/O channels, task isolation, priorities, and the
80GUI itself. The scheduler's blocking/wake primitives are designed so
81the GUI's per-task windows and input queues can be built on them later.
82
83## Consequences
84
85- Concurrent disk/fs/tty/vt access remains unsupported: any second task
86 doing I/O needs per-task DMA buffers and real mutexes, which is
87 future work (the GUI-era step).
88- Task stacks overflow into a clean, labeled task death instead of
89 silent corruption; the glossary's "no guard pages" claim now reads
90 "except task stacks".
91- A buggy background task cannot take the kernel down (panic or stack
92 overflow), which the GUI will depend on; a buggy shell still can.
93- The serial log becomes interleaved across tasks — expected, and the
94 scheduler lock keeps it line-clean.
95- klog timestamps make task activity (`[ssss.mmm]`) traceable alongside
96 boot messages; rescan events, panic messages, and task logs all share
97 the ring.