Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

An awaiter in C++ is an object that implements a specific interface required by the co_await operator to control the suspension and resumption of a C++20 coroutine. It dictates whether a coroutine should suspend, executes logic immediately after the coroutine state is saved, and determines the evaluated result of the co_await expression upon resumption. To qualify as an awaiter, a type must expose three specific methods: await_ready, await_suspend, and await_resume.
#include <coroutine>

struct CustomAwaiter {
    // 1. Determines if suspension is necessary
    bool await_ready() const noexcept;

    // 2. Executes immediately after the coroutine state is saved
    // Return type can be void, bool, or std::coroutine_handle<P>
    void await_suspend(std::coroutine_handle<>) const noexcept;

    // 3. Produces the result of the co_await expression
    decltype(auto) await_resume() const noexcept;
};

The Awaiter Interface Mechanics

When the compiler encounters a co_await expression, it interacts with the awaiter object through a strict sequence of operations.

1. await_ready()

This method acts as an optimization checkpoint. It returns a bool indicating whether the operation is already complete.
  • If it returns true, the coroutine does not suspend. The compiler skips await_suspend and immediately calls await_resume.
  • If it returns false, the coroutine prepares for suspension by saving its local state and instruction pointer to the heap-allocated coroutine frame.

2. await_suspend(std::coroutine_handle<>)

This method is invoked after the coroutine has been suspended and its state is safely stored. The compiler passes a std::coroutine_handle representing the currently suspended coroutine. This is the critical juncture where the awaiter can schedule the coroutine for future resumption. The behavior of await_suspend is dictated by its return type:
  • void: Control is immediately returned to the caller or resumer of the current coroutine.
  • bool: If it returns true, control returns to the caller/resumer. If it returns false, the suspension is aborted, and the current coroutine is immediately resumed.
  • std::coroutine_handle<P>: The compiler performs a symmetric transfer. It suspends the current coroutine and resumes the coroutine associated with the returned handle via a tail call (or by returning the handle to the underlying coroutine machinery). This avoids a nested function call, preventing stack overflow during chained coroutine executions.

3. await_resume()

This method is called when the coroutine is resumed (or immediately if await_ready returned true). The return type of await_resume becomes the return type of the entire co_await expression. If the method returns void, the co_await expression evaluates to void.

Awaitable vs. Awaiter Resolution

In C++ terminology, an Awaitable is any type that supports the co_await operator, while an Awaiter is the concrete object implementing the three methods above. The compiler resolves an Awaitable into an Awaiter through a sequential transformation pipeline:
  1. Promise Transformation: If the enclosing coroutine’s promise type defines await_transform(expression), the compiler evaluates promise.await_transform(expression). If not, the original expression is used.
  2. Operator Overload: The compiler evaluates the result of step 1. If that result has an accessible operator co_await() (either as a member function or a non-member function found via Argument-Dependent Lookup), the operator is invoked. If no such operator exists, the result of step 1 is used directly.
  3. Direct Awaiter: The final result from step 2 is treated as the Awaiter and must implement the required await_ready, await_suspend, and await_resume methods.

Conceptual Compiler Expansion

The following pseudo-code illustrates how the compiler expands a co_await expression. Explicit pseudo-code markers (bracketed in < >) are used to represent context-switching assembly and control flow interruptions that cannot be expressed in standard C++.
// Conceptual representation of compiler-generated co_await expansion.
// Markers like <return-to-caller-or-resumer> represent compiler-injected 
// context switches, not standard C++ function returns.

template <typename Awaiter>
auto conceptual_co_await_expansion(Awaiter& awaiter, std::coroutine_handle<> current_handle) {
    if (!awaiter.await_ready()) {
        // <suspend-coroutine> 
        // Compiler saves instruction pointer and local state to the coroutine frame.
        
        using SuspendResult = decltype(awaiter.await_suspend(current_handle));
        
        if constexpr (std::is_void_v<SuspendResult>) {
            awaiter.await_suspend(current_handle);
            <return-to-caller-or-resumer>; 
        } 
        else if constexpr (std::is_same_v<SuspendResult, bool>) {
            if (awaiter.await_suspend(current_handle)) {
                <return-to-caller-or-resumer>;
            }
            // If false, suspension is aborted. Execution falls through to resume.
        } 
        else {
            // Symmetric transfer: returns std::coroutine_handle<P>
            auto next_coro = awaiter.await_suspend(current_handle);
            <tail-call-resume>(next_coro); // Resumes next_coro without consuming stack space
        }
        
        // <coroutine-resumption-point>
        // Execution is injected back here when the coroutine is explicitly resumed.
    }

    // Coroutine resumed (or never suspended), evaluate result
    return awaiter.await_resume();
}

Trivial Standard Awaiters

The <coroutine> header provides two trivial awaiters that demonstrate the minimal implementation of the interface, primarily used by promise types to control initial and final suspension points:
namespace std {
    struct suspend_always {
        constexpr bool await_ready() const noexcept { return false; }
        constexpr void await_suspend(coroutine_handle<>) const noexcept {}
        constexpr void await_resume() const noexcept {}
    };

    struct suspend_never {
        constexpr bool await_ready() const noexcept { return true; }
        constexpr void await_suspend(coroutine_handle<>) const noexcept {}
        constexpr void await_resume() const noexcept {}
    };
}
Master C++ with Deep Grasping Methodology!Learn More