feat: add lab-rv32i-freertos-task-wrapper card
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# K07 — TaskHandle_t, explicit start and static trampoline
|
||||
|
||||
## Position
|
||||
|
||||
- Series: FreeRTOS C++
|
||||
- Lesson: L06, card K07
|
||||
- Duration: 30 minutes
|
||||
- One C++ owner object plus one raw verifier task
|
||||
- Dynamic FreeRTOS TCB/stack from real `heap_4.c`
|
||||
|
||||
K02 introduced CRTP in a larger first-task experiment. K07 revisits the task
|
||||
boundary after the memory/resource cards and demands a precise identity proof:
|
||||
object, TCB/handle and task stack are separate things with separate lifetimes.
|
||||
|
||||
## Outcome
|
||||
|
||||
After 30 minutes the student can annotate every argument of `xTaskCreate`,
|
||||
explain why construction and start are separate, trace `void*` back to a typed
|
||||
member body, and prove stable object address without virtual dispatch.
|
||||
|
||||
## Lesson plan — 30 minutes
|
||||
|
||||
| Time | Mode | Evidence |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | predict addresses | object vs handle/TCB vs task stack |
|
||||
| 5–10 | explicit start | constructor heap delta 0; start creates kernel objects |
|
||||
| 10–16 | C boundary | annotate entry, name, words, context, priority, handle-out |
|
||||
| 16–21 | trampoline | raw context equals object; static cast; private `run()` |
|
||||
| 21–26 | lifecycle | ready/running/completed, handle cleared, self-delete |
|
||||
| 26–30 | GDB/exit | stack local inside bounds; no move/vtable/destructor delete |
|
||||
|
||||
## Stable identity contract
|
||||
|
||||
```text
|
||||
object_before == object_after_start
|
||||
== xTaskCreate context
|
||||
== trampoline context
|
||||
== this in run
|
||||
== completed object
|
||||
|
||||
handle_after_start == current handle == TCB address
|
||||
handle_after_start != C++ object address
|
||||
local variable address lies in task stack bounds
|
||||
```
|
||||
|
||||
## Why explicit start
|
||||
|
||||
The derived object is not complete while its base constructor runs. Starting
|
||||
there could let the scheduler enter `run()` against an incompletely constructed
|
||||
object. `start()` is a separate state transition invoked only after the complete
|
||||
owner exists. Duplicate start is rejected without another `xTaskCreate` call.
|
||||
|
||||
## Wrapper contract
|
||||
|
||||
- constructor stores name, stack words and priority only;
|
||||
- `start()` moves `constructed -> starting -> ready` and calls the C API;
|
||||
- static trampoline sets `running`, invokes `Derived::run()`, then records
|
||||
`completed`, clears the handle and deletes the current kernel task;
|
||||
- copy and move are deleted because the context address must not change;
|
||||
- no destructor force-deletes a live task.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- host test proves no constructor create call, one explicit create, stable
|
||||
context, duplicate rejection and `start_failed` behavior;
|
||||
- ABI contains the static trampoline but no vtable/RTTI/hosted runtime;
|
||||
- Hazard3 shows object outside `ucHeap`, TCB in `ucHeap`, stack-local inside
|
||||
reported task stack bounds;
|
||||
- counter result is 500500, wrapper ends completed with null handle;
|
||||
- all identity equalities and checkpoint 12 PASS hold.
|
||||
|
||||
## Main traps
|
||||
|
||||
1. `TaskHandle_t` is not the C++ object pointer; it identifies the TCB.
|
||||
2. Stack depth is in `StackType_t` words, not necessarily bytes.
|
||||
3. Moving a started wrapper invalidates the kernel's stored context.
|
||||
4. Starting in a constructor exposes a partial object.
|
||||
5. Hiding forced deletion in a destructor does not unwind arbitrary task-local
|
||||
C++ objects and creates unsafe asynchronous lifetime coupling.
|
||||
|
||||
Reference in New Issue
Block a user