You're viewing the readable version of this site. The interactive extras (search, diagrams, read-aloud) need JavaScript and a current browser. Enable JavaScript; if it is already enabled, update your browser.

Learn · Hidden Machines · Part II

budding

Await Stores the Rest of the Function

Coroutines, suspension frames, and liveness.

coroutines, async, compiler-lowering, memory, learn

You will lower an async function conceptually into a state machine, identify live-across-suspend values, and reason about frame ownership and destruction.

Put a bookmark where execution stopped

Consider a function that computes a request body, awaits a reply, then formats the reply using the original query. At suspension, what must survive? The next control location, the query, and anything else used after resumption.

Prediction — identify frame members.

Conceptually, lowering produces a carrier containing a program-counter tag and the live locals. Re-entry switches on the tag and continues at the corresponding block. Destroy releases the frame without pretending the suspended body ran to completion. This is an extended finite-state machine: finite control state plus stored data.

The representation exposes practical questions hidden by syntax. Who owns the frame? When may it be destroyed? Does a large buffer cross suspension because one later log statement refers to it? Does a borrowed view outlive its backing storage? Allocation claims must name whether the compiler can eliminate or embed the frame in the actual calling regime.

Where the model stops

Not every async abstraction has one portable lowering or allocation strategy. Compiler and ABI choices differ. The semantic claim is smaller: suspension requires persistent control state and live data somewhere. Do not infer heap allocation merely from the keyword, and do not retain a resource across await without making its lifetime visible.

Lessons

  • A suspend point turns the remainder of control flow into a resumable state.
  • Values live after suspension become frame data.
  • Completion and destruction are distinct terminal paths.
  • Source-level convenience does not erase ownership or allocation.

Practice

  1. Mark every variable live across each await in a two-await function.
  2. Distinguish coroutine state from the state of the remote request it awaits.
  3. Transfer the frame model to a generator paused between yielded values.

If a suspended function starts children, who owns their lifetime when its own scope exits?

References

  1. LLVM, “Coroutines in LLVM”.” — coroutine frames, suspend indices, continuation functions, and destruction.
  2. ECMAScript Language Specification, “Async Function Definitions”.” — language semantics of async evaluation.