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.

A suspend point in C++ is a specific location within a C++20 coroutine where execution is temporarily halted, yielding control back to the caller or resumer. At this point, the compiler ensures that the coroutine’s execution state—including local variables, temporaries, and the instruction pointer—is preserved in a coroutine frame (typically dynamically allocated, though subject to Coroutine Heap Allocation Elision Optimization, or HALO). This state preservation allows execution to be resumed from the exact same location at a later time. Suspend points are explicitly or implicitly triggered by three keywords:
  • co_await expr: Evaluates an awaitable expression, potentially suspending the coroutine while waiting for a computation.
  • co_yield expr: Suspends execution and yields a value to the caller. This is syntactic sugar for co_await promise.yield_value(expr).
  • co_return expr: Evaluates the return expression, invokes promise.return_value(expr) (or promise.return_void()), destroys local variables in reverse order of creation, and subsequently transitions execution to the implicit final suspend point.

The Mechanics of Suspension

When the compiler encounters a suspend point via co_await, it evaluates an Awaitable expression. The compiler converts this Awaitable into an Awaiter object (e.g., via operator co_await() or the promise type’s await_transform()). The Awaiter must implement three specific methods that dictate the exact behavior of the suspension:
  1. await_ready(): Returns a bool. If true, the coroutine does not suspend, avoiding the overhead of context switching.
  2. await_suspend(std::coroutine_handle<PromiseType>): Called immediately after the coroutine is considered suspended and its state is saved, but before control is returned to the caller. It receives a strongly-typed handle to the suspended coroutine (though the awaiter may choose to accept a type-erased std::coroutine_handle<void> via implicit conversion). Because the coroutine is already suspended at this stage, another thread can resume (and potentially destroy) the coroutine while await_suspend is still executing.
  3. await_resume(): Called to obtain the result of the co_await expression. If the coroutine suspended, this is called immediately after resumption. If await_ready() returned true, it is called immediately without any suspension occurring.

Compiler Transformation

To visualize the mechanics of a suspend point, consider the following conceptual transformation performed by the C++ compiler when it encounters a co_await expression:
// Original syntax
auto result = co_await awaitable_expr;
// Conceptual compiler transformation
auto&& awaiter = get_awaiter(awaitable_expr);

if (!awaiter.await_ready()) {
    // 1. Save local variables and instruction pointer to the coroutine frame.
    // <--- ACTUAL SUSPEND POINT --->
    // The coroutine is officially suspended here.
    
    // 2. Construct a std::coroutine_handle pointing to the current frame.
    // PromiseType represents the specific promise type of the current coroutine.
    std::coroutine_handle<PromiseType> handle = /* compiler-generated handle */;
    
    // 3. Invoke await_suspend
    using suspend_result_t = decltype(awaiter.await_suspend(handle));
    
    if constexpr (std::is_void_v<suspend_result_t>) {
        awaiter.await_suspend(handle);
        // Control yields back to the caller/resumer.
        return; 
    } 
    else if constexpr (std::is_same_v<suspend_result_t, bool>) {
        if (awaiter.await_suspend(handle)) {
            // Control yields back to the caller/resumer.
            return; 
        }
        // If false, the coroutine was successfully suspended, but is now 
        // instructed to immediately resume. Execution falls through.
    }
    else { 
        // suspend_result_t is a std::coroutine_handle (Symmetric Transfer)
        // await_suspend returns another coroutine handle to resume next.
        auto next_coro = awaiter.await_suspend(handle);
        // The compiler performs a tail-call to next_coro.resume(), 
        // preventing stack overflow during chained resumptions.
        return next_coro.resume(); // Conceptual representation
    }
    
    // <--- RESUMPTION POINT --->
    // Execution continues here when handle.resume() is called by the resumer,
    // or if await_suspend returned a boolean `false`.
}

// 4. Extract the result
// This executes whether the coroutine suspended or bypassed suspension.
auto result = awaiter.await_resume();

Implicit Suspend Points

In addition to explicit keywords within the coroutine body, the C++ coroutine specification mandates two implicit suspend points managed by the coroutine’s Promise type:
  1. Initial Suspend Point: Occurs immediately after the coroutine frame is allocated, arguments are copied, and the promise object is constructed, but before the user-authored coroutine body begins executing. It is executed as co_await promise.initial_suspend().
  2. Final Suspend Point: Occurs after the coroutine body has finished executing (via co_return or falling off the end) and local variables have been destroyed. It is executed as co_await promise.final_suspend(). Suspending at the final suspend point keeps the coroutine frame alive so the caller can extract the final result or observe the completion state.
Master C++ with Deep Grasping Methodology!Learn More