Coroutine

Stackless vs Stackful Coroutine

Two implementation forms for coroutines, differing in how they preserve execution state across a suspend.

Why does it matter?

The implementation form dictates what the coroutine can suspend from. A stackless coroutine can only suspend at the top level of its main routine — you can’t suspend() from inside a helper function it calls. A stackful coroutine can suspend anywhere in its call tree, which is what makes it suitable for things like recursive tree iteration.

Stackless

Uses the caller’s stack and a fixed-sized local state (usually a struct / compiled state machine).

  • Cheap — no separate stack allocation
  • Can only suspend at top-level points in the coroutine body (the compiler must know where to save state)
  • C++20 co_await, Python generators, Rust async fn are all stackless

Stackful

Uses a separate stack plus a fixed-sized local state (as part of the coroutine object).

  • Heavier — every coroutine instance allocates its own stack
  • Can suspend from anywhere, including deeply nested helper calls (see the Btree::Iterator tree-walk in Coroutine)
  • uC++ _Coroutine, Go goroutines, Lua coroutines are stackful

Why uC++ is stackful

uC++‘s tree-traversal iterator calls walk() recursively and suspend()s from inside a leaf — that’s impossible with stackless because the recursive frames live on the active call stack. Stackful pays the per-instance stack cost to buy that flexibility.