119 lines
4.8 KiB
Markdown
119 lines
4.8 KiB
Markdown
# K05 — C++ heap bridge, heap models and HeapStats
|
||
|
||
## Position
|
||
|
||
- Series: FreeRTOS C++
|
||
- Lesson: L04, card K05
|
||
- Duration: 30 minutes
|
||
- Runtime: real FreeRTOS V11.3.0 `heap_4.c` on Hazard3/RV32I
|
||
- C++ mode: freestanding C++17, no exceptions, RTTI or hosted `libstdc++`
|
||
- New surface: `HeapStats`, `HeapWatermark`, complete `new`/`delete` bridge
|
||
|
||
K03 proved one-owner RAII, while K05 now exposes the allocator policy and its
|
||
metrics. It does not introduce runtime resource selection; K06 will build that
|
||
on top of the explicit failure and alignment contract established here.
|
||
|
||
## Outcome
|
||
|
||
After 30 minutes the student can choose among `heap_1`…`heap_5` for a stated
|
||
memory topology/lifetime policy, enumerate all C++ allocation bridge forms,
|
||
distinguish total free bytes from largest free block, and prove that
|
||
minimum-ever free bytes retain history after current free returns to baseline.
|
||
|
||
## Lesson plan — 30 minutes
|
||
|
||
| Time | Mode | Evidence |
|
||
| --- | --- | --- |
|
||
| 0–5 | choose policy | justify heap scheme for three deployments |
|
||
| 5–9 | bridge | trace `new[] -> pvPortMalloc` and `delete[] -> vPortFree` |
|
||
| 9–14 | predict | draw A/B/C blocks and predicted stats after freeing A/C |
|
||
| 14–21 | Hazard3 | prove `request < total`, `request > largest`, failure |
|
||
| 21–26 | coalescence | free B; retry same request; inspect one free block |
|
||
| 26–30 | history/exit | baseline recovery versus minimum-ever history |
|
||
|
||
## Policy table
|
||
|
||
| Scheme | Free? | Coalesce? | Key deployment constraint |
|
||
| --- | --- | --- | --- |
|
||
| heap_1 | no | n/a | monotonic startup allocation only |
|
||
| heap_2 | yes | no | holes remain separate; fragmentation grows |
|
||
| heap_3 | libc | libc | linker/compiler must provide a suitable heap |
|
||
| heap_4 | yes | yes | one FreeRTOS-owned region |
|
||
| heap_5 | yes | yes | regions defined in increasing address order before allocation |
|
||
|
||
Student choices:
|
||
|
||
1. All objects allocated once at startup, never deleted: `heap_1` is a valid
|
||
simplest policy.
|
||
2. One freestanding RAM region, dynamic tasks/queues with deletion: `heap_4`.
|
||
3. Two disjoint RAM banks that must both feed the kernel allocator: `heap_5`.
|
||
|
||
`heap_3` is not treated as a portable freestanding default because it delegates
|
||
storage and behavior to the compiler C library and linker heap.
|
||
|
||
## Canonical experiment
|
||
|
||
```text
|
||
baseline: [ free ........................................ ]
|
||
allocate: [ A 2K ][ B 4K ][ C 2K ][ free ............... ]
|
||
free A/C: [ free ][ B live ][ free + trailing free ...... ]
|
||
total=12256, largest=10192, blocks=2
|
||
request 10193 -> FAIL (sum is enough, no individual block is)
|
||
free B: [ one coalesced free block ..................... ]
|
||
retry 10193 -> SUCCESS
|
||
free retry -> current free returns to baseline
|
||
minimum-ever remains at the historical low
|
||
```
|
||
|
||
Exact values may change with alignment/header configuration. Assessment uses
|
||
relations, except for the fixed requested payload sizes in the source.
|
||
|
||
## Stable evidence contract
|
||
|
||
| Evidence | Required relation |
|
||
| --- | --- |
|
||
| C++ addresses | three distinct, aligned addresses inside `ucHeap` |
|
||
| bridge counters | allocations=3, deallocations=3, live=0 |
|
||
| deallocation order | A, C, B |
|
||
| fragmented stats | free blocks >=2 and total > largest |
|
||
| failed request | largest < request < total; successful allocations unchanged |
|
||
| coalesced stats | free blocks=1; largest=current=baseline |
|
||
| retry | same request succeeds inside `ucHeap` |
|
||
| final stats | current=baseline, minimum-ever remains below current |
|
||
|
||
## Complete bridge contract
|
||
|
||
```cpp
|
||
void* operator new(size_t);
|
||
void* operator new[](size_t);
|
||
void operator delete(void*) noexcept;
|
||
void operator delete[](void*) noexcept;
|
||
void operator delete(void*, size_t) noexcept;
|
||
void operator delete[](void*, size_t) noexcept;
|
||
```
|
||
|
||
Ordinary `new` is fail-fast in this course profile because exceptions are off.
|
||
The recoverable experiment uses raw `pvPortMalloc` and checks `nullptr`.
|
||
|
||
## Misconceptions
|
||
|
||
1. Total free bytes do not imply one payload of that size can be allocated.
|
||
2. Returning to the current-free baseline does not erase the minimum-ever
|
||
watermark.
|
||
3. `heap_4` limits external fragmentation by coalescing; it cannot promise
|
||
absence of all fragmentation while live blocks separate holes.
|
||
4. A C++ bridge selects one heap domain; it does not merge five schemes.
|
||
5. `heap_3` inherits the C library/linker heap contract and is therefore not a
|
||
self-contained freestanding answer.
|
||
|
||
## Acceptance
|
||
|
||
- host test verifies field mapping and current/peak watermark calculations;
|
||
- ABI test finds all six allocation functions and no hosted C++ runtime;
|
||
- Hazard3 reaches all seven checkpoints and PASS;
|
||
- failure request lies strictly between largest block and total free bytes;
|
||
- retry succeeds only after B is freed and blocks coalesce;
|
||
- student chooses heap_1, heap_4 and heap_5 for the three stated scenarios and
|
||
justifies each constraint.
|
||
|