# 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 | | --- | --- | --- | | 0–4 | prediction | fill the first state/priority table without running code | | 4–8 | typed time | distinguish `Ticks` from `TickPoint`; no assumption that tick means ms | | 8–13 | time slicing | obtain `A,B,A` or `B,A,B` at equal priority 2 | | 13–19 | blocked state | show A=`eBlocked`, B=`eRunning`, elapsed exactly 3 ticks | | 19–25 | priority change | show B at priority 3 and marker order `1,2,3` | | 25–30 | 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.