81 lines
3.2 KiB
Markdown
81 lines
3.2 KiB
Markdown
# 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.
|
||
|