feat: add lab-rv32i-freertos-scheduler-states card

This commit is contained in:
user
2026-07-21 19:14:20 +02:00
commit 71ddd87301
66 changed files with 34187 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
# K04 — Scheduler states, priorities, time slicing and typed ticks
## Position in the series
- Series: FreeRTOS C++
- Lesson: L03, card K04
- Duration: 30 minutes
- Executables: exactly one
- Kernel: unchanged FreeRTOS V11.3.0 in C
- C++ mode: freestanding C++17, no exceptions, RTTI or hosted `libstdc++`
- New C++ surface: `Ticks`, `TickPoint`, non-owning `TaskRef`
K02 introduced task entry and lifecycle, while K03 established ownership and
RAII for a heap buffer. K04 deliberately does not build a large facade. It
first exposes the scheduler's raw evidence and adds only types that prevent a
duration, a tick point and an owning task object from being confused.
## Outcome
After 30 minutes the student can predict and verify `READY`, `RUNNING`,
`BLOCKED` and `DELETED`; distinguish a relative delay from a periodic wake
reference; explain time slicing for equal priorities; and use the order
`BEFORE -> HIGH -> AFTER` as proof of immediate priority preemption.
## Lesson plan — 30 minutes
| Time | Mode | Required evidence |
| --- | --- | --- |
| 04 | prediction | fill the first state/priority table without running code |
| 48 | typed time | distinguish `Ticks` from `TickPoint`; no assumption that tick means ms |
| 813 | time slicing | obtain `A,B,A` or `B,A,B` at equal priority 2 |
| 1319 | blocked state | show A=`eBlocked`, B=`eRunning`, elapsed exactly 3 ticks |
| 1925 | priority change | show B at priority 3 and marker order `1,2,3` |
| 2530 | interpretation | compare relative/periodic delay, state limits and stale `TaskRef` |
## Canonical experiment
```text
scheduler starts
-> startup probe: A READY, B READY
-> equal priority 2: A/B alternate on tick boundaries
-> A calls vTaskDelay(3)
-> A BLOCKED; B RUNNING
-> at start+3 A becomes READY and then RUNNING
-> A writes BEFORE=1 and raises B to priority 3
-> B preempts, writes HIGH=2, lowers itself to priority 2
-> A resumes inside/after vTaskPrioritySet and writes AFTER=3
-> workers delete themselves; verifier writes terminal snapshot and PASS
```
The test accepts either initial equal-priority order. It requires the
alternating relation `first == third && first != second`.
## Stable evidence contract
| Evidence | Required relation |
| --- | --- |
| `g_alternating_tasks[0..2]` | A,B,A or B,A,B |
| delay snapshot | A=`eBlocked`, B=`eRunning` |
| delay timestamps | `end - start == 3`, observation lies before end |
| priority snapshot | A=`eReady`, B=`eRunning`, B priority 3 |
| `g_priority_order` | exactly `1,2,3` |
| terminal snapshot | A/B recorded as `eDeleted`, handles no longer queried |
| `g_scheduler_states_pass` | 1 |
## Prediction table
The student completes state and priority before inspecting the executable:
| Event | A state / priority | B state / priority | Current task | Why? |
| --- | --- | --- | --- | --- |
| startup probe | | | | |
| A before delay | | | | |
| B while A delays | | | | |
| A after 3 ticks | | | | |
| before raise | | | | |
| B at p=3 | | | | |
| after API returns | | | | |
| verifier | | | | |
## Typed wrapper boundary
```cpp
freertos::Ticks duration{3};
const auto start = freertos::TickPoint::now();
vTaskDelay(duration.count());
const auto elapsed = freertos::TickPoint::now() - start;
freertos::TaskRef peer{handle}; // no ownership
peer.set_priority(3);
```
`TaskRef` is the size of one native handle and has no virtual dispatch. It
must not be used after the task is deleted. K04 uses raw scheduling calls on
purpose; a broad `Scheduler` facade is deferred until K09.
## Misconceptions to surface
1. Highest priority does not mean “always running”: a high-priority task can
be blocked and therefore ineligible.
2. `vTaskDelay(3)` does not promise three milliseconds unless the configured
tick rate makes that conversion true.
3. `vTaskDelay` is relative; repeated work plus relative delays can drift.
`xTaskDelayUntil` advances a periodic reference.
4. Equal priority does not establish a fixed first task; verify alternation,
not a guessed first ID.
5. A handle is not ownership. After deletion, `TaskRef` can dangle.
## Acceptance
- host test proves typed tick arithmetic, wrap-around and one-handle `TaskRef`;
- ELF contains no RTTI, vtable, exception/unwind or hosted C++ dependency;
- Hazard3 proves a three-entry alternating trace;
- the blocked snapshot and exactly-three-tick delay pass;
- priority markers are exactly `1,2,3` and B is priority 3 at marker 2;
- all eight snapshots match the intended state transition sequence;
- student explains why the startup snapshot is taken after scheduler start.