34 Execution control library [exec]

34.12 Coroutine utilities [exec.coro.util]

34.12.1 execution​::​as_awaitable [exec.as.awaitable]

as_awaitable transforms an object into one that is awaitable within a particular coroutine.
Subclause [exec.coro.util] makes use of the following exposition-only entities: namespace std::execution { template<class Sndr, class Promise> concept awaitable-sender = single-sender<Sndr, env_of_t<Promise>> && sender_to<Sndr, awaitable-receiver> && // see below requires (Promise& p) { { p.unhandled_stopped() } -> convertible_to<coroutine_handle<>>; }; template<class Sndr, class Promise> class sender-awaitable; // exposition only }
The type sender-awaitable<Sndr, Promise> is equivalent to:
namespace std::execution { template<class Sndr, class Promise> class sender-awaitable { struct unit {}; // exposition only using value-type = // exposition only single-sender-value-type<Sndr, env_of_t<Promise>>; using result-type = // exposition only conditional_t<is_void_v<value-type>, unit, value-type>; struct awaitable-receiver; // exposition only variant<monostate, result-type, exception_ptr> result{}; // exposition only connect_result_t<Sndr, awaitable-receiver> state; // exposition only public: sender-awaitable(Sndr&& sndr, Promise& p); static constexpr bool await_ready() noexcept { return false; } void await_suspend(coroutine_handle<Promise>) noexcept { start(state); } value-type await_resume(); }; }
awaitable-receiver is equivalent to: struct awaitable-receiver { using receiver_concept = receiver_t; variant<monostate, result-type, exception_ptr>* result-ptr; // exposition only coroutine_handle<Promise> continuation; // exposition only // see below };
Let rcvr be an rvalue expression of type awaitable-receiver, let crcvr be a const lvalue that refers to rcvr, let vs be a pack of subexpressions, and let err be an expression of type Err.
Then:
  • If constructible_from<result-type, decltype((vs))...> is satisfied, the expression set_value(
    rcvr, vs...)
    is equivalent to: try { rcvr.result-ptr->template emplace<1>(vs...); } catch(...) { rcvr.result-ptr->template emplace<2>(current_exception()); } rcvr.continuation.resume();
    Otherwise, set_value(rcvr, vs...) is ill-formed.
  • The expression set_error(rcvr, err) is equivalent to: rcvr.result-ptr->template emplace<2>(AS-EXCEPT-PTR(err)); // see [exec.general] rcvr.continuation.resume();
  • The expression set_stopped(rcvr) is equivalent to: static_cast<coroutine_handle<>>(rcvr.continuation.promise().unhandled_stopped()).resume();
  • For any expression tag whose type satisfies forwarding-query and for any pack of subexpressions as, get_env(crcvr).query(tag, as...) is expression-equivalent to: tag(get_env(as_const(crcvr.continuation.promise())), as...)
sender-awaitable(Sndr&& sndr, Promise& p);
Effects: Initializes state with connect(std::forward<Sndr>(sndr), awaitable-receiver{addressof(result), coroutine_handle<Promise>::from_promise(p)})
value-type await_resume();
Effects: Equivalent to: if (result.index() == 2) rethrow_exception(get<2>(result)); if constexpr (!is_void_v<value-type>) return std::forward<value-type>(get<1>(result));
as_awaitable is a customization point object.
For subexpressions expr and p where p is an lvalue, Expr names the type decltype((expr)) and Promise names the type decay_t<decltype((p))>, as_awaitable(expr, p) is expression-equivalent to, except that the evaluations of expr and p are indeterminately sequenced:
  • expr.as_awaitable(p) if that expression is well-formed.
    Mandates: is-awaitable<A, Promise> is true, where A is the type of the expression above.
  • Otherwise, (void(p), expr) if is-awaitable<Expr, U> is true, where U is an unspecified class type that is not Promise and that lacks a member named await_transform.
    Preconditions: is-awaitable<Expr, Promise> is true and the expression co_await expr in a coroutine with promise type U is expression-equivalent to the same expression in a coroutine with promise type Promise.
  • Otherwise, sender-awaitable{expr, p} if awaitable-sender<Expr, Promise> is true.
  • Otherwise, (void(p), expr).

34.12.2 execution​::​with_awaitable_senders [exec.with.awaitable.senders]

with_awaitable_senders, when used as the base class of a coroutine promise type, makes senders awaitable in that coroutine type.
In addition, it provides a default implementation of unhandled_stopped such that if a sender completes by calling set_stopped, it is treated as if an uncatchable "stopped" exception were thrown from the await-expression.
[Note 1: 
The coroutine is never resumed, and the unhandled_stopped of the coroutine caller's promise type is called.
— end note]
namespace std::execution { template<class-type Promise> struct with_awaitable_senders { template<class OtherPromise> requires (!same_as<OtherPromise, void>) void set_continuation(coroutine_handle<OtherPromise> h) noexcept; coroutine_handle<> continuation() const noexcept { return continuation; } coroutine_handle<> unhandled_stopped() noexcept { return stopped-handler(continuation.address()); } template<class Value> see below await_transform(Value&& value); private: [[noreturn]] static coroutine_handle<> default-unhandled-stopped(void*) noexcept { // exposition only terminate(); } coroutine_handle<> continuation{}; // exposition only coroutine_handle<> (*stopped-handler)(void*) noexcept = // exposition only &default-unhandled-stopped; }; }
template<class OtherPromise> requires (!same_as<OtherPromise, void>) void set_continuation(coroutine_handle<OtherPromise> h) noexcept;
Effects: Equivalent to: continuation = h; if constexpr ( requires(OtherPromise& other) { other.unhandled_stopped(); } ) { stopped-handler = [](void* p) noexcept -> coroutine_handle<> { return coroutine_handle<OtherPromise>::from_address(p) .promise().unhandled_stopped(); }; } else { stopped-handler = &default-unhandled-stopped; }
template<class Value> call-result-t<as_awaitable_t, Value, Promise&> await_transform(Value&& value);
Effects: Equivalent to: return as_awaitable(std::forward<Value>(value), static_cast<Promise&>(*this));