ReviseAlgo Logo

Concurrency

Coroutines (C++20)

Build stackless asynchronous generators and tasks using co_await, co_yield, and promise structures in C++20.

Interview: How coroutines manage state without a stack, and the roles of promise_type and coroutine_handle.

Last Updated: June 13, 2026 10 min read

Introduced in C++20, Coroutines are functions that can suspend execution and resume later. Unlike standard functions, their state is allocated on the heap rather than the stack, allowing them to yield values lazily.

Keywords

Use co_await to suspend execution, co_yield to return values lazily, and co_return to finish.

promise_type

An internal interface used by the coroutine to communicate state changes back to the caller.

coroutine_handle

A handle used to control the execution of the coroutine (such as resuming or destroying it).

Stackless Design

C++20 coroutines are stackless. When a coroutine is suspended, its execution state (variables, parameters, and instruction pointer) is saved in a heap-allocated activation frame. This allows other tasks to run on the thread without allocating a thread stack for each coroutine.

Code Walkthrough

Implementing a lazy integer generator using C++20 coroutines.

#include <iostream>
#include <coroutine>

struct Generator { struct promise_type { int current_value;

Generator get_return_object() { return Generator{std::coroutine_handle<promise_type>::from_promise(*this)}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void unhandled_exception() { std::terminate(); } void return_void() {}

std::suspend_always yield_value(int value) { current_value = value; return {}; } };

std::coroutine_handle<promise_type> coro;

Generator(std::coroutine_handle<promise_type> h) : coro(h) {} ~Generator() { if (coro) coro.destroy(); }

bool next() { coro.resume(); return !coro.done(); }

int value() { return coro.promise().current_value; } };

Generator range(int start, int end) { for (int i = start; i <= end; ++i) { co_yield i; // Suspends coroutine and returns value } }

int main() { auto gen = range(1, 3); while (gen.next()) { std::cout << "Generated: " << gen.value() << std::endl; } return 0; }

Interview-Relevant Information

Q: What is the difference between stackful and stackless coroutines?
Answer: Stackful coroutines (like boost::fibers) allocate a call stack for each fiber, allowing them to suspend execution from nested functions. C++20 coroutines are stackless: they compile to a state machine, use a heap-allocated frame to store local variables, and can only suspend from the coroutine function itself.

Q: What is the function of promise_type in a coroutine?
Answer: promise_type acts as the controller interface for the coroutine. It defines the coroutine's behavior during execution steps, including what to do at startup (initial suspend), when returning values (yield or return), and how to handle exceptions.

Quick Checklist

Did you implement promise_type? Do you destroy the coroutine handle on generator exit? If yes, your coroutine design is clean.

Use Cases

Implementing lazy sequence evaluation or infinite range generators.

Writing lightweight, non-blocking asynchronous event loops in web servers.

Common Mistakes

Passing local variables by reference to a coroutine that suspended (which invalidates the reference on resume). Use pass-by-value instead.

Leaking memory by forgetting to call .destroy() on active coroutine handles.