boost::capy::when_any

Wait for the first awaitable to complete (range overload).

Synopsis

Declared in <boost/capy/when_any.hpp>

template<IoAwaitableRange R>
requires (!std::is_void_v<detail::awaitable_result_t<std::ranges::range_value_t<R>>>)
[[nodiscard]]
task<std::pair<std::size_t, /* implementation-defined */>>
when_any(R&& awaitables);

Description

Races a range of awaitables with the same result type. Accepts any sized input range of IoAwaitable types, enabling use with arrays, spans, or custom containers.

Suspends

The calling coroutine suspends when co_await is invoked. All awaitables in the range are launched concurrently and execute in parallel. The coroutine resumes only after all awaitables have completed, even though the winner is determined by the first to finish.

Completion Conditions

  • Winner is determined when the first awaitable completes (success or exception)

  • Only one task can claim winner status via atomic compare‐exchange

  • Once a winner exists, stop is requested for all remaining siblings

  • Parent coroutine resumes only after all siblings acknowledge completion

  • The winner's index and result are returned; if the winner threw, the exception is rethrown

Cancellation Semantics

Cancellation is supported via stop_token propagated through the IoAwaitable protocol:

  • Each child awaitable receives a stop_token derived from a shared stop_source

  • When the parent's stop token is activated, the stop is forwarded to all children

  • When a winner is determined, stop_source_.request_stop() is called immediately

  • Siblings must handle cancellation gracefully and complete before parent resumes

  • Stop requests are cooperative; tasks must check and respond to them

Concurrency/Overlap

All awaitables are launched concurrently before any can complete. The launcher iterates through the range, starting each task on the caller's executor. Tasks may execute in parallel on multi‐threaded executors or interleave on single‐threaded executors. There is no guaranteed ordering of task completion.

Notable Error Conditions

  • Empty range: throws std::invalid_argument immediately (not via co_return)

  • Winner exception: if the winning task threw, that exception is rethrown

  • Non‐winner exceptions: silently discarded (only winner's result matters)

  • Cancellation: tasks may complete via cancellation without throwing

Example

task<void> example() {
    std::array<task<Response>, 3> requests = {
        fetch_from_server(0),
        fetch_from_server(1),
        fetch_from_server(2)
    };

    auto [index, response] = co_await when_any(std::move(requests));
}

Example with Vector

task<Response> fetch_fastest(std::vector<Server> const& servers) {
    std::vector<task<Response>> requests;
    for (auto const& server : servers)
        requests.push_back(fetch_from(server));

    auto [index, response] = co_await when_any(std::move(requests));
    co_return response;
}

Remarks

Elements are moved from the range; for lvalue ranges, the original container will have moved‐from elements after this call. The range is moved onto the coroutine frame to ensure lifetime safety. Unlike the variadic overload, no variant wrapper is needed since all tasks share the same return type.

Exceptions

Name

Thrown on

std::invalid_argument

if range is empty (thrown before coroutine suspends).

Rethrows

the winner's exception if the winning task threw an exception.

Return Value

A task yielding a pair of (winner_index, result).

Template Parameters

Name Description

R

Range type satisfying IoAwaitableRange.

Parameters

Name Description

awaitables

Range of awaitables to race concurrently (must not be empty).

See Also

when_any, IoAwaitableRange

Created with MrDocs