Files
lab-rv32i-freertos-vector-raii/doc/card-plan.md
T

106 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# K03 — Vector V1: destructor, move, ownership and RAII
## Position in the series
- Series: FreeRTOS C++
- Lesson: L02, card K03
- Executables: exactly one
- Kernel: unchanged FreeRTOS V11.3.0 in C
- C++ mode: freestanding C++17, no exceptions, RTTI or hosted `libstdc++`
- Starting artifact: the deliberately incomplete `Vector V0` shown on the card
The current K02 card concentrates on `Task<Derived>` and does not require a
separate Vector exercise. K03 therefore carries its short V0 starting snippet
on page 1. This removes a hidden prerequisite without spending a second card
on the broken implementation.
## Outcome
After 45 minutes the student can state and prove the one-owner invariant,
explain why copy operations are deleted, implement the release-before-transfer
order of move assignment, and show in Hazard3 that normal scope exit returns
the FreeRTOS heap to its baseline.
## Canonical experiment
```text
heap baseline
-> source(4) owns A
-> destination(2) owns B
-> destination = move(source)
free B
destination takes A
source becomes {nullptr, 0}
-> final_owner(move(destination)) owns A
-> final_owner destructor frees A
-> two empty moved-from objects destruct without another free
-> heap baseline restored
```
## Lesson plan — 45 minutes
| Time | Mode | Student evidence |
| --- | --- | --- |
| 05 | diagnose V0 | identify leak, shallow-copy double-free risk and missing ownership rule |
| 511 | destructor | connect object scope to `delete[]` and then to `vPortFree()` |
| 1117 | copy vs move | justify deleted copy and the empty moved-from state |
| 1725 | move assignment | order `release -> take -> clear source`; handle occupied destination and self-move |
| 2538 | Hazard3/GDB | replay checkpoints 17 and follow buffer A, buffer B, counts and heap bytes |
| 3843 | exercise | complete the three state-changing parts of move assignment and test the invariant |
| 4345 | exit | explain one-owner, baseline recovery and the limit of RAII under forced task deletion |
## Stable evidence contract
| Point | Required observation |
| --- | --- |
| 1 | `g_initial_free` is the reference after heap initialization |
| 2 | `source.data() == A`, allocation count 1 |
| 3 | `destination.data() == B`, `A != B`, allocation count 2 |
| 4 | first freed address is B; destination has A; source is empty |
| 5 | final owner has the same A; destination is empty |
| 6 | A is the second freed address; current free equals baseline |
| 7 | allocations=2, deallocations=2, live=0, `pass=1` |
Numeric heap values are recorded but the assessment relies on relations:
```text
after_two_owners < initial
minimum_ever <= after_two_owners
after_scope == initial
```
## Student exercise
The destination already owns B. The learner must put these operations in the
only safe order and explain each invariant boundary:
```cpp
if (this != &other) {
release();
elements_ = other.elements_;
size_ = other.size_;
other.elements_ = nullptr;
other.size_ = 0;
}
```
Required reasoning:
1. Taking A before releasing B loses the only pointer to B and leaks it.
2. Releasing after taking A would free A, the buffer just transferred.
3. Not clearing the source creates two owners and a later double free.
4. Omitting the self-move guard releases the only buffer before reading it.
## Acceptance
- host test passes under ASan and UBSan;
- a copy attempt fails to compile because the operation is deleted;
- ELF contains no vtable, RTTI, exceptions, dynamic initializer or hosted C++
dependency;
- all six required scalar/array, sized/unsized allocation functions exist;
- Hazard3 reaches checkpoint 7 with `pass=1`;
- student connects `delete[] -> operator delete[] -> vPortFree()`;
- student states that RAII requires normal C++ lifetime completion and does not
promise cleanup after arbitrary task termination.