Skip to main content

Coroutine Lifetimes and Hazards

advanced11 min readLesson 157 of 204

Frames are heap allocations. Parameters are copied or moved in. Lambdas with by-reference captures are the classic trap.

The frame owns more than you think

The frame stores: the coroutine's parameters (copied or moved at call time โ€” by-value parameters are moved into the frame; references stored as references still point outside and can dangle), all locals live across suspensions, and the promise. Frame allocation is normally a heap operator new โ€” unless the compiler proves the lifetime is scoped and elides it (HALO). Treat every coroutine call as an allocation unless measured otherwise.

The by-reference lambda trap

auto makeGen() {
    int state = 0;
    return [&state]() -> Generator<int> {   // WRONG: state dies with makeGen
        while (state < 3) co_yield state++;
    }();
}

The lambda captured state by reference; the generator outlives it. First resume reads a dead variable. Fix: capture by value ([state]) or pass state as a by-value coroutine parameter (parameters live inside the frame โ€” the safe home).

Predictable lifetime rules

  1. Coroutine parameters are safe inside the frame if passed by value/move.
  2. Anything referenced (parameters by &, captures by &) must provably outlive the generator โ€” same discipline as views.
  3. The generator object owns the handle; moving the generator moves ownership; the moved-from one must not be resumed or destroyed twice (std::exchange in the move, as in the lesson's code).
  4. Destroy or let RAII destroy before the referenced environment dies.

The graded exercise: repair exactly the dangling-capture generator, then predict which of several snippets dangle.

Now practice

Coroutine Lifetime ClinicMove-only ownership, single-consumption discipline, and the frame-allocation mental model.1 challenge ยท ยท ~16 min