Files

3.2 KiB
Raw Permalink Blame History

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
05 predict addresses object vs handle/TCB vs task stack
510 explicit start constructor heap delta 0; start creates kernel objects
1016 C boundary annotate entry, name, words, context, priority, handle-out
1621 trampoline raw context equals object; static cast; private run()
2126 lifecycle ready/running/completed, handle cleared, self-delete
2630 GDB/exit stack local inside bounds; no move/vtable/destructor delete

Stable identity contract

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.