boost::capy::Executor

Concept for types that schedule coroutine execution.

Synopsis

template<class E>
concept Executor = std::is_nothrow_copy_constructible_v<E> &&
    std::is_nothrow_move_constructible_v<E> &&
    requires(E& e, E const& ce, E const& ce2, std::coroutine_handle<> h) {
        { ce == ce2 } noexcept ‐> std::convertible_to<bool>;
        { ce.context() } noexcept;
        requires std::is_lvalue_reference_v<decltype(ce.context())> &&
            std::derived_from<
                std::remove_reference_t<decltype(ce.context())>,
                execution_context>;
        { ce.on_work_started() } noexcept;
        { ce.on_work_finished() } noexcept;

        { ce.dispatch(h) } ‐> std::same_as<std::coroutine_handle<>>;
        { ce.post(h) };
    };

Description

An executor embodies a set of rules for determining how and where coroutines are executed. It provides operations to submit work and to track outstanding work for graceful shutdown.

Ordinary users writing coroutine tasks do not interact with dispatch and post directly. These operations are used by authors of coroutine machinery ‐‐ promise_type implementations, awaitables, await_transform ‐‐ to implement asynchronous algorithms such as when_all, when_any, async_mutex, channels, and similar primitives.

Syntactic Requirements

  • `E` must be nothrow copy and move constructible

  • `e1 == e2` must return a type convertible to `bool`, `noexcept`

  • `e.context()` must return an lvalue reference to a type derived from `execution_context`, `noexcept`

  • `e.on_work_started()` must be valid and `noexcept`

  • `e.on_work_finished()` must be valid and `noexcept`

  • `e.dispatch(h)` must return `std::coroutine_handle<>`

  • `e.post(h)` must be valid

Semantic Requirements

The context operation returns the owning context:

  • Returns a reference to the execution context that created this executor

  • The context outlives all executors created from it

The on_work_started and on_work_finished operations track work:

  • Calls must be paired; each `on_work_started` must have a matching `on_work_finished`

  • The context uses this count to determine when shutdown is complete

  • These are not intended for direct use by callers. They are public so that work guards can invoke them. This enables user‐defined guards with additional tracking behaviors, without the library needing to grant friendship to types it cannot anticipate

The dispatch operation returns a handle for symmetric transfer:

Every coroutine resumption must go through either symmetric transfer or the scheduler queue ‐‐ never through an inline resume() or dispatch() that creates a frame below the resumed coroutine.

  • If the executor determines it is safe to resume inline (e.g., already on the correct thread), returns `h` for the caller to use in symmetric transfer

  • Otherwise, posts the coroutine for later execution and returns `std::noop_coroutine()`

  • The caller is responsible for using the returned handle appropriately: returning it from `await_suspend` for symmetric transfer, or calling `.resume()` if at the event loop pump level

A conforming implementation might look like:

std::coroutine_handle<> dispatch(
    std::coroutine_handle<> h ) const
{
    if( ctx_.is_running_on_this_thread() )
        return h;              // symmetric transfer
    post( h );
    return std::noop_coroutine();
}

The post operation queues for later execution:

  • Never blocks the caller

  • The coroutine executes on the executor's associated context

Executor Validity

An executor becomes invalid when the first call to ctx.shutdown() returns. Calling dispatch, post, on_work_started, or on_work_finished on an invalid executor is undefined behavior. Copy, comparison, and context() remain valid until the context is destroyed.

Thread Safety

Distinct objects: Safe. Shared objects: Safe for copy, comparison, and context().

Conforming Signatures

class E
{
public:
    execution_context& context() const noexcept;

    void on_work_started() const noexcept;
    void on_work_finished() const noexcept;

    std::coroutine_handle<> dispatch(
        std::coroutine_handle<> h ) const;
    void post( std::coroutine_handle<> h ) const;

    bool operator==( E const& ) const noexcept;
};

Template Parameters

Name Description

E

The executor type.

See Also

ExecutionContext, execution_context

Created with MrDocs