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.

std::coroutine_handle is a non-owning, trivially copyable handle to a compiler-generated coroutine state (the coroutine frame). It provides the low-level interface required to resume execution of a suspended coroutine, query its execution status, access its associated promise object, or explicitly destroy its state. Defined in the <coroutine> header, the handle acts essentially as a type-safe wrapper around a void* pointer pointing to the coroutine frame. While conceptually dynamically allocated, C++20 explicitly supports Heap Allocation Elision (HALO). This means the compiler is permitted to optimize the coroutine state to be allocated on the stack or inlined into the caller’s frame, rather than strictly relying on heap allocation.

Template Structure and Specializations

The handle is implemented as a class template with void as the default template argument (template <typename Promise = void>), alongside an explicit specialization for void (coroutine_handle<void>). There is no inheritance relationship between the primary template and the explicit specialization.
namespace std {
    // Explicit specialization for the type-erased handle
    template <>
    struct coroutine_handle<void> {
        constexpr coroutine_handle() noexcept;
        constexpr coroutine_handle(nullptr_t) noexcept;
        coroutine_handle& operator=(nullptr_t) noexcept;
        
        // State observation
        constexpr explicit operator bool() const noexcept;
        bool done() const;
        
        // Execution control
        void operator()() const;
        void resume() const;
        
        // Lifecycle
        void destroy() const;
        
        // Interoperability
        constexpr void* address() const noexcept;
        static constexpr coroutine_handle from_address(void* addr);
    };

    // Primary template for the strongly-typed handle
    template <typename Promise>
    struct coroutine_handle {
        constexpr coroutine_handle() noexcept;
        constexpr coroutine_handle(nullptr_t) noexcept;
        coroutine_handle& operator=(nullptr_t) noexcept;

        // Implicit conversion to type-erased handle
        constexpr operator coroutine_handle<void>() const noexcept;
        
        // State observation
        constexpr explicit operator bool() const noexcept;
        bool done() const;
        
        // Execution control
        void operator()() const;
        void resume() const;
        
        // Lifecycle
        void destroy() const;
        
        // Interoperability
        constexpr void* address() const noexcept;
        static constexpr coroutine_handle from_address(void* addr);

        // Promise interaction
        Promise& promise() const;
        static coroutine_handle from_promise(Promise& p);
    };

    // Comparison operators
    constexpr bool operator==(coroutine_handle<void> x, coroutine_handle<void> y) noexcept;
    constexpr strong_ordering operator<=>(coroutine_handle<void> x, coroutine_handle<void> y) noexcept;

    // Hash support
    template <class P> struct hash<coroutine_handle<P>>;
}

1. std::coroutine_handle<void> (Type-Erased Handle)

Often written as std::coroutine_handle<>, this explicit specialization knows nothing about the coroutine’s return type or promise object. It is used when the caller only needs to control the execution flow (resume or destroy) without interacting with the data yielded or returned by the coroutine.

2. std::coroutine_handle<Promise> (Strongly-Typed Handle)

The primary template is bound to a specific Promise type, allowing bidirectional data transfer between the caller and the coroutine frame via the promise object. It independently declares all the execution control and state observation methods found in the void specialization, alongside promise-specific methods and a conversion operator.

Core Mechanics and Member Functions

Execution Control

  • resume() / operator()(): Resumes the execution of a suspended coroutine. The call blocks until the coroutine suspends again or runs to completion. Invoking resume() on a coroutine that is already executing, or one that has completed (done() == true), results in undefined behavior.

State Observation

  • done(): Returns true if the coroutine is suspended at its final suspension point, and false if suspended at any other point. Crucially, calling done() on a coroutine that is currently executing is Undefined Behavior. This method can only be safely invoked when the coroutine is known to be in a suspended state.
  • operator bool(): Returns true if the handle points to a valid coroutine frame (i.e., the internal pointer is not nullptr). It does not indicate whether the coroutine has finished executing.

Promise Interaction (Typed Handle Only)

  • promise(): Returns a reference to the promise object stored inside the coroutine frame.
  • from_promise(Promise& p): A static factory function that reconstructs the coroutine_handle from a reference to the promise object. This relies on the compiler’s deterministic memory layout of the coroutine frame.

Memory and Lifecycle Management

  • destroy(): Explicitly destroys the coroutine frame, invoking the destructors of all in-scope local variables and the promise object, and deallocates the memory. Like done(), calling destroy() on a currently executing coroutine is undefined behavior.
Crucial Lifecycle Semantics: std::coroutine_handle does not implement RAII. It is a raw handle. If a handle goes out of scope, the underlying coroutine frame is not automatically destroyed, leading to a memory leak. Conversely, if a coroutine reaches the end of its execution and its promise type’s final_suspend returns std::suspend_never, the frame destroys itself automatically, leaving any existing coroutine_handle instances dangling.

Interoperability

  • address(): Extracts the underlying void* pointer. Useful for passing the coroutine through C-style APIs or storing it in type-erased contexts.
  • from_address(void* addr): Reconstructs a coroutine_handle from a raw pointer previously obtained via address(). This function is intentionally not noexcept because it possesses a narrow contract: passing an invalid or unaligned address results in undefined behavior.

Identity and Comparison

The <coroutine> header defines namespace-scope comparison operators (operator== and operator<=>) and specializes std::hash for the handle. These operate directly on the underlying memory addresses, allowing handles to be checked for identity or used as keys in associative containers (e.g., std::unordered_map or std::set). Because strongly-typed handles implicitly convert to coroutine_handle<void>, these operators seamlessly support all handle types.

Type Conversion

Because the primary template provides an implicit conversion operator (operator coroutine_handle<void>()), implicit type conversion is supported. You can pass a strongly-typed handle to any function expecting a type-erased handle. Converting back from a type-erased handle to a strongly-typed handle requires explicit reconstruction using the underlying memory address.
std::coroutine_handle<MyPromise> typed_handle = /* ... */;

// Implicit type conversion to type-erased handle
std::coroutine_handle<> erased_handle = typed_handle; 

// Explicit reconstruction requires using the raw address
std::coroutine_handle<MyPromise> restored_handle = 
    std::coroutine_handle<MyPromise>::from_address(erased_handle.address());
Master C++ with Deep Grasping Methodology!Learn More