%!TEX root = std.tex \rSec0[exec]{Execution control library} \rSec1[exec.general]{General} \pnum This Clause describes components supporting execution of function objects\iref{function.objects}. \pnum The following subclauses describe the requirements, concepts, and components for execution control primitives as summarized in \tref{exec.summary}. \begin{libsumtab}{Execution control library summary}{exec.summary} \ref{exec.sched} & Schedulers & \tcode{} \\ \ref{exec.recv} & Receivers & \\ \ref{exec.opstate} & Operation states & \\ \ref{exec.snd} & Senders & \\ \end{libsumtab} \pnum \tref{exec.pos} shows the types of customization point objects\iref{customization.point.object} used in the execution control library. \begin{floattable}{Types of customization point objects in the execution control library}{exec.pos}{lx{0.23\hsize}x{0.45\hsize}} \topline \lhdr{Customization point} & \chdr{Purpose} & \rhdr{Examples} \\ \lhdr{object type} & & \\ \capsep core & provide core execution functionality, and connection between core components & e.g., \tcode{connect}, \tcode{start} \\ completion functions & called by senders to announce the completion of the work (success, error, or cancellation) & \tcode{set_value}, \tcode{set_error}, \tcode{set_stopped} \\ senders & allow the specialization of the provided sender algorithms & \begin{itemize} \item sender factories (e.g., \tcode{schedule}, \tcode{just}, \tcode{read_env}) \item sender adaptors (e.g., \tcode{continues_on}, \tcode{then}, \tcode{let_value}) \item sender consumers (e.g., \tcode{sync_wait}) \end{itemize} \\ queries & allow querying different properties of objects & \begin{itemize} \item general queries (e.g., \tcode{get_allocator}, \tcode{get_stop_token}) \item environment queries (e.g., \tcode{get_scheduler}, \tcode{get_delegation_scheduler}) \item scheduler queries (e.g., \tcode{get_forward_progress_guarantee}) \item sender attribute queries (e.g., \tcode{get_completion_scheduler}) \end{itemize} \\ \end{floattable} \pnum This clause makes use of the following exposition-only entities. \pnum For a subexpression \tcode{expr}, let \tcode{\exposid{MANDATE-NOTHROW}(expr)} be expression-equivalent to \tcode{expr}. \mandates \tcode{noexcept(expr)} is \tcode{true}. \pnum \begin{codeblock} namespace std { template concept @\defexposconcept{movable-value}@ = // \expos @\libconcept{move_constructible}@> && @\libconcept{constructible_from}@, T> && (!is_array_v>); } \end{codeblock} \pnum For function types \tcode{F1} and \tcode{F2} denoting \tcode{R1(Args1...)} and \tcode{R2(Args2...)}, respectively, \tcode{\exposid{MATCHING-SIG}(F1, F2)} is \tcode{true} if and only if \tcode{\libconcept{same_as}} is \tcode{true}. \pnum For a subexpression \tcode{err}, let \tcode{Err} be \tcode{decltype((err))} and let \tcode{\exposid{AS-EXCEPT-PTR}(err)} be: \begin{itemize} \item \tcode{err} if \tcode{decay_t} denotes the type \tcode{exception_ptr}. \mandates \tcode{err != exception_ptr()} is \tcode{true}. \item Otherwise, \tcode{make_exception_ptr(system_error(err))} if \tcode{decay_t} denotes the type \tcode{error_code}. \item Otherwise, \tcode{make_exception_ptr(err)}. \end{itemize} \rSec1[exec.queryable]{Queries and queryables} \rSec2[exec.queryable.general]{General} \pnum A \defnadj{queryable}{object} is a read-only collection of key/value pair where each key is a customization point object known as a \defn{query object}. A \defn{query} is an invocation of a query object with a queryable object as its first argument and a (possibly empty) set of additional arguments. A query imposes syntactic and semantic requirements on its invocations. \pnum Let \tcode{q} be a query object, let \tcode{args} be a (possibly empty) pack of subexpressions, let \tcode{env} be a subexpression that refers to a queryable object \tcode{o} of type \tcode{O}, and let \tcode{cenv} be a subexpression referring to \tcode{o} such that \tcode{decltype((cenv))} is \tcode{const O\&}. The expression \tcode{q(env, args...)} is equal to\iref{concepts.equality} the expression \tcode{q(cenv, args...)}. \pnum The type of a query expression cannot be \tcode{void}. \pnum The expression \tcode{q(env, args...)} is equality-preserving\iref{concepts.equality} and does not modify the query object or the arguments. \pnum If the expression \tcode{env.query(q, args...)} is well-formed, then it is expression-equivalent to \tcode{q(env, args...)}. \pnum Unless otherwise specified, the result of a query is valid as long as the queryable object is valid. \rSec2[exec.queryable.concept]{\tcode{queryable} concept} \begin{codeblock} namespace std { template concept @\defexposconcept{queryable}@ = @\libconcept{destructible}@; // \expos } \end{codeblock} \pnum The exposition-only \exposconcept{queryable} concept specifies the constraints on the types of queryable objects. \pnum Let \tcode{env} be an object of type \tcode{Env}. The type \tcode{Env} models \exposconcept{queryable} if for each callable object \tcode{q} and a pack of subexpressions \tcode{args}, if \tcode{requires \{ q(env, args...) \}} is \tcode{true} then \tcode{q(env, args...)} meets any semantic requirements imposed by \tcode{q}. \rSec1[exec.async.ops]{Asynchronous operations} \pnum An \defnadj{execution}{resource} is a program entity that manages a (possibly dynamic) set of execution agents\iref{thread.req.lockable.general}, which it uses to execute parallel work on behalf of callers. \begin{example} The currently active thread, a system-provided thread pool, and uses of an API associated with an external hardware accelerator are all examples of execution resources. \end{example} Execution resources execute asynchronous operations. An execution resource is either valid or invalid. \pnum An \defnadj{asynchronous}{operation} is a distinct unit of program execution that \begin{itemize} \item is explicitly created; \item can be explicitly started once at most; \item once started, eventually completes exactly once with a (possibly empty) set of result datums and in exactly one of three \defnx{dispositions}{disposition}: success, failure, or cancellation; \begin{itemize} \item A successful completion, also known as a \defnadj{value}{completion}, can have an arbitrary number of result datums. \item A failure completion, also known as an \defnadj{error}{completion}, has a single result datum. \item A cancellation completion, also known as a \defnadj{stopped}{completion}, has no result datum. \end{itemize} An asynchronous operation's \defnadj{async}{result} is its disposition and its (possibly empty) set of result datums. \item can complete on a different execution resource than the execution resource on which it started; and \item can create and start other asynchronous operations called \defnadj{child}{operations}. A child operation is an asynchronous operation that is created by the parent operation and, if started, completes before the parent operation completes. A \defnadj{parent}{operation} is the asynchronous operation that created a particular child operation. \end{itemize} \begin{note} An asynchronous operation can execute synchronously; that is, it can complete during the execution of its start operation on the thread of execution that started it. \end{note} \pnum An asynchronous operation has associated state known as its \defnadj{operation}{state}. \pnum An asynchronous operation has an associated environment. An \defn{environment} is a queryable object\iref{exec.queryable} representing the execution-time properties of the operation's caller. The caller of an asynchronous operation is its parent operation or the function that created it. An asynchronous operation's operation state owns the operation's environment. \pnum An asynchronous operation has an associated receiver. A \defn{receiver} is an aggregation of three handlers for the three asynchronous completion dispositions: \begin{itemize} \item a value completion handler for a value completion, \item an error completion handler for an error completion, and \item a stopped completion handler for a stopped completion. \end{itemize} A receiver has an associated environment. An asynchronous operation's operation state owns the operation's receiver. The environment of an asynchronous operation is equal to its receiver's environment. \pnum For each completion disposition, there is a \defnadj{completion}{function}. A completion function is a customization point object\iref{customization.point.object} that accepts an asynchronous operation's receiver as the first argument and the result datums of the asynchronous operation as additional arguments. The value completion function invokes the receiver's value completion handler with the value result datums; likewise for the error completion function and the stopped completion function. A completion function has an associated type known as its \defnadj{completion}{tag} that is the unqualified type of the completion function. A valid invocation of a completion function is called a \defnadj{completion}{operation}. \pnum The \defn{lifetime of an asynchronous operation}, also known as the operation's \defn{async lifetime}, begins when its start operation begins executing and ends when its completion operation begins executing. If the lifetime of an asynchronous operation's associated operation state ends before the lifetime of the asynchronous operation, the behavior is undefined. After an asynchronous operation executes a completion operation, its associated operation state is invalid. Accessing any part of an invalid operation state is undefined behavior. \pnum An asynchronous operation shall not execute a completion operation before its start operation has begun executing. After its start operation has begun executing, exactly one completion operation shall execute. The lifetime of an asynchronous operation's operation state can end during the execution of the completion operation. \pnum A \defn{sender} is a factory for one or more asynchronous operations. \defnx{Connecting}{connect} a sender and a receiver creates an asynchronous operation. The asynchronous operation's associated receiver is equal to the receiver used to create it, and its associated environment is equal to the environment associated with the receiver used to create it. The lifetime of an asynchronous operation's associated operation state does not depend on the lifetimes of either the sender or the receiver from which it was created. A sender is started when it is connected to a receiver and the resulting asynchronous operation is started. A sender's async result is the async result of the asynchronous operation created by connecting it to a receiver. A sender sends its results by way of the asynchronous operation(s) it produces, and a receiver receives those results. A sender is either valid or invalid; it becomes invalid when its parent sender (see below) becomes invalid. \pnum A \defn{scheduler} is an abstraction of an execution resource with a uniform, generic interface for scheduling work onto that resource. It is a factory for senders whose asynchronous operations execute value completion operations on an execution agent belonging to the scheduler's associated execution resource. A \defn{schedule-expression} obtains such a sender from a scheduler. A \defn{schedule sender} is the result of a schedule expression. On success, an asynchronous operation produced by a schedule sender executes a value completion operation with an empty set of result datums. Multiple schedulers can refer to the same execution resource. A scheduler can be valid or invalid. A scheduler becomes invalid when the execution resource to which it refers becomes invalid, as do any schedule senders obtained from the scheduler, and any operation states obtained from those senders. \pnum An asynchronous operation has one or more associated completion schedulers for each of its possible dispositions. A \defn{completion scheduler} is a scheduler whose associated execution resource is used to execute a completion operation for an asynchronous operation. A value completion scheduler is a scheduler on which an asynchronous operation's value completion operation can execute. Likewise for error completion schedulers and stopped completion schedulers. \pnum A sender has an associated queryable object\iref{exec.queryable} known as its \defnx{attributes}{attribute} that describes various characteristics of the sender and of the asynchronous operation(s) it produces. For each disposition, there is a query object for reading the associated completion scheduler from a sender's attributes; i.e., a value completion scheduler query object for reading a sender's value completion scheduler, etc. If a completion scheduler query is well-formed, the returned completion scheduler is unique for that disposition for any asynchronous operation the sender creates. A schedule sender is required to have a value completion scheduler attribute whose value is equal to the scheduler that produced the schedule sender. \pnum A \defn{completion signature} is a function type that describes a completion operation. An asynchronous operation has a finite set of possible completion signatures corresponding to the completion operations that the asynchronous operation potentially evaluates\iref{basic.def.odr}. For a completion function \tcode{set}, receiver \tcode{rcvr}, and pack of arguments \tcode{args}, let \tcode{c} be the completion operation \tcode{set(rcvr, args...)}, and let \tcode{F} be the function type \tcode{decltype(auto(set))(decltype((args))...)}. A completion signature \tcode{Sig} is associated with \tcode{c} if and only if \tcode{\exposid{MATCHING-SIG}(Sig, F)} is \tcode{true}\iref{exec.general}). Together, a sender type and an environment type \tcode{Env} determine the set of completion signatures of an asynchronous operation that results from connecting the sender with a receiver that has an environment of type \tcode{Env}. The type of the receiver does not affect an asynchronous operation's completion signatures, only the type of the receiver's environment. \pnum A sender algorithm is a function that takes and/or returns a sender. There are three categories of sender algorithms: \begin{itemize} \item A \defn{sender factory} is a function that takes non-senders as arguments and that returns a sender. \item A \defn{sender adaptor} is a function that constructs and returns a parent sender from a set of one or more child senders and a (possibly empty) set of additional arguments. An asynchronous operation created by a parent sender is a parent operation to the child operations created by the child senders. \item A \defn{sender consumer} is a function that takes one or more senders and a (possibly empty) set of additional arguments, and whose return type is not the type of a sender. \end{itemize} \rSec1[execution.syn]{Header \tcode{} synopsis} \indexheader{execution}% \begin{codeblock} namespace std { // \ref{execpol.type}, execution policy type trait template struct is_execution_policy; template constexpr bool @\libglobal{is_execution_policy_v}@ = is_execution_policy::value; } namespace std::execution { // \ref{execpol.seq}, sequenced execution policy class sequenced_policy; // \ref{execpol.par}, parallel execution policy class parallel_policy; // \ref{execpol.parunseq}, parallel and unsequenced execution policy class parallel_unsequenced_policy; // \ref{execpol.unseq}, unsequenced execution policy class unsequenced_policy; // \ref{execpol.objects}, execution policy objects inline constexpr sequenced_policy seq{ @\unspec@ }; inline constexpr parallel_policy par{ @\unspec@ }; inline constexpr parallel_unsequenced_policy par_unseq{ @\unspec@ }; inline constexpr unsequenced_policy unseq{ @\unspec@ }; } namespace std { // \ref{exec.general}, helper concepts template concept @\exposconceptnc{movable-value}@ = @\seebelownc@; // \expos template concept @\defexposconceptnc{decays-to}@ = @\libconcept{same_as}@, To>; // \expos template concept @\defexposconceptnc{class-type}@ = @\exposconceptnc{decays-to}@ && is_class_v; // \expos // \ref{exec.queryable}, queryable objects template concept @\exposconceptnc{queryable}@ = @\seebelownc@; // \expos // \ref{exec.queries}, queries struct forwarding_query_t { @\unspec@ }; struct get_allocator_t { @\unspec@ }; struct get_stop_token_t { @\unspec@ }; inline constexpr forwarding_query_t forwarding_query{}; inline constexpr get_allocator_t get_allocator{}; inline constexpr get_stop_token_t get_stop_token{}; template using stop_token_of_t = remove_cvref_t()))>; template concept @\defexposconceptnc{forwarding-query}@ = forwarding_query(T{}); // \expos } namespace std::execution { // \ref{exec.queries}, queries struct get_domain_t { @\unspec@ }; struct get_scheduler_t { @\unspec@ }; struct get_delegation_scheduler_t { @\unspec@ }; struct get_forward_progress_guarantee_t { @\unspec@ }; template struct get_completion_scheduler_t { @\unspec@ }; inline constexpr get_domain_t get_domain{}; inline constexpr get_scheduler_t get_scheduler{}; inline constexpr get_delegation_scheduler_t get_delegation_scheduler{}; enum class forward_progress_guarantee; inline constexpr get_forward_progress_guarantee_t get_forward_progress_guarantee{}; template constexpr get_completion_scheduler_t get_completion_scheduler{}; struct empty_env {}; struct get_env_t { @\unspec@ }; inline constexpr get_env_t get_env{}; template using env_of_t = decltype(get_env(declval())); // \ref{exec.domain.default}, execution domains struct default_domain; // \ref{exec.sched}, schedulers struct scheduler_t {}; template concept @\libconcept{scheduler}@ = @\seebelow@; // \ref{exec.recv}, receivers struct receiver_t {}; template concept @\libconcept{receiver}@ = @\seebelow@; template concept @\libconcept{receiver_of}@ = @\seebelow@; struct set_value_t { @\unspec@ }; struct set_error_t { @\unspec@ }; struct set_stopped_t { @\unspec@ }; inline constexpr set_value_t set_value{}; inline constexpr set_error_t set_error{}; inline constexpr set_stopped_t set_stopped{}; // \ref{exec.opstate}, operation states struct operation_state_t {}; template concept @\libconcept{operation_state}@ = @\seebelow@; struct start_t; inline constexpr start_t start{}; // \ref{exec.snd}, senders struct sender_t {}; template concept @\libconcept{sender}@ = @\seebelow@; template concept @\libconcept{sender_in}@ = @\seebelow@; template concept @\libconcept{sender_to}@ = @\seebelow@; template struct @\exposidnc{type-list}@; // \expos // \ref{exec.getcomplsigs}, completion signatures struct get_completion_signatures_t; inline constexpr get_completion_signatures_t get_completion_signatures {}; template requires @\libconcept{sender_in}@ using completion_signatures_of_t = @\exposid{call-result-t}@; template using @\exposidnc{decayed-tuple}@ = tuple...>; // \expos template using @\exposidnc{variant-or-empty}@ = @\seebelownc@; // \expos template class Tuple = @\exposid{decayed-tuple}@, template class Variant = @\exposid{variant-or-empty}@> requires @\libconcept{sender_in}@ using value_types_of_t = @\seebelow@; template class Variant = @\exposid{variant-or-empty}@> requires @\libconcept{sender_in}@ using error_types_of_t = @\seebelow@; template requires @\libconcept{sender_in}@ constexpr bool sends_stopped = @\seebelow@; template using @\exposidnc{single-sender-value-type}@ = @\seebelownc@; // \expos template concept @\exposconcept{single-sender}@ = @\seebelow@; // \expos template<@\libconcept{sender}@ Sndr> using tag_of_t = @\seebelow@; // \ref{exec.snd.transform}, sender transformations template requires (sizeof...(Env) <= 1) constexpr @\libconcept{sender}@ decltype(auto) transform_sender( Domain dom, Sndr&& sndr, const Env&... env) noexcept(@\seebelow@); // \ref{exec.snd.transform.env}, environment transformations template constexpr @\exposconcept{queryable}@ decltype(auto) transform_env( Domain dom, Sndr&& sndr, Env&& env) noexcept; // \ref{exec.snd.apply}, sender algorithm application template constexpr decltype(auto) apply_sender( Domain dom, Tag, Sndr&& sndr, Args&&... args) noexcept(@\seebelow@); // \ref{exec.connect}, the connect sender algorithm struct connect_t; inline constexpr connect_t connect{}; template using connect_result_t = decltype(connect(declval(), declval())); // \ref{exec.factories}, sender factories struct just_t { @\unspec@ }; struct just_error_t { @\unspec@ }; struct just_stopped_t { @\unspec@ }; struct schedule_t { @\unspec@ }; inline constexpr just_t just{}; inline constexpr just_error_t just_error{}; inline constexpr just_stopped_t just_stopped{}; inline constexpr schedule_t schedule{}; inline constexpr @\unspec@ read_env{}; template<@\libconcept{scheduler}@ Sndr> using schedule_result_t = decltype(schedule(declval())); // \ref{exec.adapt}, sender adaptors template<@\exposconcept{class-type}@ D> struct sender_adaptor_closure { }; struct starts_on_t { @\unspec@ }; struct continues_on_t { @\unspec@ }; struct on_t { @\unspec@ }; struct schedule_from_t { @\unspec@ }; struct then_t { @\unspec@ }; struct upon_error_t { @\unspec@ }; struct upon_stopped_t { @\unspec@ }; struct let_value_t { @\unspec@ }; struct let_error_t { @\unspec@ }; struct let_stopped_t { @\unspec@ }; struct bulk_t { @\unspec@ }; struct split_t { @\unspec@ }; struct when_all_t { @\unspec@ }; struct when_all_with_variant_t { @\unspec@ }; struct into_variant_t { @\unspec@ }; struct stopped_as_optional_t { @\unspec@ }; struct stopped_as_error_t { @\unspec@ }; inline constexpr starts_on_t starts_on{}; inline constexpr continues_on_t continues_on{}; inline constexpr on_t on{}; inline constexpr schedule_from_t schedule_from{}; inline constexpr then_t then{}; inline constexpr upon_error_t upon_error{}; inline constexpr upon_stopped_t upon_stopped{}; inline constexpr let_value_t let_value{}; inline constexpr let_error_t let_error{}; inline constexpr let_stopped_t let_stopped{}; inline constexpr bulk_t bulk{}; inline constexpr split_t split{}; inline constexpr when_all_t when_all{}; inline constexpr when_all_with_variant_t when_all_with_variant{}; inline constexpr into_variant_t into_variant{}; inline constexpr stopped_as_optional_t stopped_as_optional{}; inline constexpr stopped_as_error_t stopped_as_error{}; // \ref{exec.util}, sender and receiver utilities // \ref{exec.util.cmplsig} template concept @\exposconceptnc{completion-signature}@ = @\seebelownc@; // \expos template<@\exposconcept{completion-signature}@... Fns> struct completion_signatures {}; template concept @\exposconceptnc{valid-completion-signatures}@ = @\seebelownc@; // \expos // \ref{exec.util.cmplsig.trans} template< @\exposconcept{valid-completion-signatures}@ InputSignatures, @\exposconcept{valid-completion-signatures}@ AdditionalSignatures = completion_signatures<>, template class SetValue = @\seebelow@, template class SetError = @\seebelow@, @\exposconcept{valid-completion-signatures}@ SetStopped = completion_signatures> using transform_completion_signatures = completion_signatures<@\seebelow@>; template< @\libconcept{sender}@ Sndr, class Env = empty_env, @\exposconcept{valid-completion-signatures}@ AdditionalSignatures = completion_signatures<>, template class SetValue = @\seebelow@, template class SetError = @\seebelow@, @\exposconcept{valid-completion-signatures}@ SetStopped = completion_signatures> requires @\libconcept{sender_in}@ using transform_completion_signatures_of = transform_completion_signatures< completion_signatures_of_t, AdditionalSignatures, SetValue, SetError, SetStopped>; // \ref{exec.run.loop}, run_loop class run_loop; } namespace std::this_thread { // \ref{exec.consumers}, consumers struct sync_wait_t { @\unspec@ }; struct sync_wait_with_variant_t { @\unspec@ }; inline constexpr sync_wait_t sync_wait{}; inline constexpr sync_wait_with_variant_t sync_wait_with_variant{}; } namespace std::execution { // \ref{exec.as.awaitable} struct as_awaitable_t { @\unspec@ }; inline constexpr as_awaitable_t as_awaitable{}; // \ref{exec.with.awaitable.senders} template<@\exposconcept{class-type}@ Promise> struct with_awaitable_senders; } \end{codeblock} \pnum The exposition-only type \tcode{\exposid{variant-or-empty}} is defined as follows: \begin{itemize} \item If \tcode{sizeof...(Ts)} is greater than zero, \tcode{\exposid{variant-or-empty}} denotes \tcode{variant} where \tcode{Us...} is the pack \tcode{decay_t...} with duplicate types removed. \item Otherwise, \tcode{\exposid{variant-or-empty}} denotes the exposition-only class type: \begin{codeblock} namespace std::execution { struct @\exposidnc{empty-variant}@ { // \expos @\exposidnc{empty-variant}@() = delete; }; } \end{codeblock} \end{itemize} \pnum For types \tcode{Sndr} and \tcode{Env}, \tcode{\exposid{single-sender-value-type}} is an alias for: \begin{itemize} \item \tcode{value_types_of_t} if that type is well-formed, \item Otherwise, \tcode{void} if \tcode{value_types_of_t} is \tcode{variant>} or \tcode{vari\-ant<>}, \item Otherwise, \tcode{value_types_of_t} if that type is well-formed, \item Otherwise, \tcode{\exposid{single-sender-value-type}} is ill-formed. \end{itemize} \pnum The exposition-only concept \exposconcept{single-sender} is defined as follows: \begin{codeblock} namespace std::execution { template concept @\defexposconcept{single-sender}@ = @\libconcept{sender_in}@ && requires { typename @\exposid{single-sender-value-type}@; }; } \end{codeblock} \rSec1[exec.queries]{Queries} \rSec2[exec.fwd.env]{\tcode{forwarding_query}} \pnum \tcode{forwarding_query} asks a query object whether it should be forwarded through queryable adaptors. \pnum The name \tcode{forwarding_query} denotes a query object. For some query object \tcode{q} of type \tcode{Q}, \tcode{forwarding_query(q)} is expression-equivalent to: \begin{itemize} \item \tcode{\exposid{MANDATE-NOTHROW}(q.query(forwarding_query))} if that expression is well-formed. \mandates The expression above has type \tcode{bool} and is a core constant expression if \tcode{q} is a core constant expression. \item Otherwise, \tcode{true} if \tcode{\libconcept{derived_from}} is \tcode{true}. \item Otherwise, \tcode{false}. \end{itemize} \rSec2[exec.get.allocator]{\tcode{get_allocator}} \pnum \tcode{get_allocator} asks a queryable object for its associated allocator. \pnum The name \tcode{get_allocator} denotes a query object. For a subexpression \tcode{env}, \tcode{get_allocator(env)} is expression-equivalent to \tcode{\exposid{MANDATE-NOTHROW}(as_const(env).query(get_allocator))}. \mandates If the expression above is well-formed, its type satisfies \exposconcept{simple-allocator}\iref{allocator.requirements.general}. \pnum \tcode{forwarding_query(get_allocator)} is a core constant expression and has value \tcode{true}. \rSec2[exec.get.stop.token]{\tcode{get_stop_token}} \pnum \tcode{get_stop_token} asks a queryable object for an associated stop token. \pnum The name \tcode{get_stop_token} denotes a query object. For a subexpression \tcode{env}, \tcode{get_stop_token(env)} is expression-equivalent to: \begin{itemize} \item \tcode{\exposid{MANDATE-NOTHROW}(as_const(env).query(get_stop_token))} if that expression is well-formed. \mandates The type of the expression above satisfies \libconcept{stoppable_token}. \item Otherwise, \tcode{never_stop_token\{\}}. \end{itemize} \pnum \tcode{forwarding_query(get_stop_token)} is a core constant expression and has value \tcode{true}. \rSec2[exec.get.env]{\tcode{execution::get_env}} \pnum \tcode{execution::get_env} is a customization point object. For a subexpression \tcode{o}, \tcode{execution::get_env(o)} is expression-equivalent to: \begin{itemize} \item \tcode{\exposid{MANDATE-NOTHROW}(as_const(o).get_env())} if that expression is well-formed. \mandates The type of the expression above satisfies \exposconcept{queryable}\iref{exec.queryable}. \item Otherwise, \tcode{empty_env\{\}}. \end{itemize} \pnum The value of \tcode{get_env(o)} shall be valid while \tcode{o} is valid. \pnum \begin{note} When passed a sender object, \tcode{get_env} returns the sender's associated attributes. When passed a receiver, \tcode{get_env} returns the receiver's associated execution environment. \end{note} \rSec2[exec.get.domain]{\tcode{execution::get_domain}} \pnum \tcode{get_domain} asks a queryable object for its associated execution domain tag. \pnum The name \tcode{get_domain} denotes a query object. For a subexpression \tcode{env}, \tcode{get_domain(env)} is expression-equivalent to \tcode{\exposid{MANDATE-NOTHROW}(as_const(env).query(get_domain))}. \pnum \tcode{forwarding_query(execution::get_domain)} is a core constant expression and has value \tcode{true}. \rSec2[exec.get.scheduler]{\tcode{execution::get_scheduler}} \pnum \tcode{get_scheduler} asks a queryable object for its associated scheduler. \pnum The name \tcode{get_scheduler} denotes a query object. For a subexpression \tcode{env}, \tcode{get_scheduler(env)} is expression-equivalent to \tcode{\exposid{MANDATE-NOTHROW}(as_const(env).query(get_scheduler))}. \mandates If the expression above is well-formed, its type satisfies \libconcept{scheduler}. \pnum \tcode{forwarding_query(execution::get_scheduler)} is a core constant expression and has value \tcode{true}. \rSec2[exec.get.delegation.scheduler]{\tcode{execution::get_delegation_scheduler}} \pnum \tcode{get_delegation_scheduler} asks a queryable object for a scheduler that can be used to delegate work to for the purpose of forward progress delegation\iref{intro.progress}. \pnum The name \tcode{get_delegation_scheduler} denotes a query object. For a subexpression \tcode{env}, \tcode{get_delegation_scheduler(env)} is expression-equivalent to \tcode{\exposid{MANDATE-NOTHROW}(as_const(env).query(get_delegation_scheduler))}. \mandates If the expression above is well-formed, its type satisfies \libconcept{scheduler}. \pnum \tcode{forwarding_query(execution::get_delegation_scheduler)} is a core constant expression and has value \tcode{true}. \rSec2[exec.get.fwd.progress]{\tcode{execution::get_forward_progress_guarantee}} \begin{codeblock} namespace std::execution { enum class forward_progress_guarantee { concurrent, parallel, weakly_parallel }; } \end{codeblock} \pnum \tcode{get_forward_progress_guarantee} asks a scheduler about the forward progress guarantee of execution agents created by that scheduler's associated execution resource\iref{intro.progress}. \pnum The name \tcode{get_forward_progress_guarantee} denotes a query object. For a subexpression \tcode{sch}, let \tcode{Sch} be \tcode{decltype((sch))}. If \tcode{Sch} does not satisfy \libconcept{scheduler}, \tcode{get_forward_progress_guarantee} is ill-formed. Otherwise, \tcode{get_forward_progress_guarantee(sch)} is expression-equivalent to: \begin{itemize} \item \tcode{\exposid{MANDATE-NOTHROW}(as_const(sch).query(get_forward_progress_guarantee))}, if that expression is well-formed. \mandates The type of the expression above is \tcode{forward_progress_guarantee}. \item Otherwise, \tcode{forward_progress_guarantee::weakly_parallel}. \end{itemize} \pnum If \tcode{get_forward_progress_guarantee(sch)} for some scheduler \tcode{sch} returns \tcode{forward_progress_guaran\-tee::concurrent}, all execution agents created by that scheduler's associated execution resource shall provide the concurrent forward progress guarantee. If it returns \tcode{forward_progress_guarantee::parallel}, all such execution agents shall provide at least the parallel forward progress guarantee. \rSec2[exec.get.compl.sched]{\tcode{execution::get_completion_scheduler}} \pnum \tcode{get_completion_scheduler<\exposid{completion-tag>}} obtains the completion scheduler associated with a completion tag from a sender's attributes. \pnum The name \tcode{get_completion_scheduler} denotes a query object template. For a subexpression \tcode{q}, the expression \tcode{get_completion_scheduler<\exposid{completion-tag}>(q)} is ill-formed if \exposid{completion-tag} is not one of \tcode{set_value_t}, \tcode{set_error_t}, or \tcode{set_stopped_t}. Otherwise, \tcode{get_completion_scheduler<\exposid{completion-tag}>(q)} is expression-equivalent to \begin{codeblock} @\exposid{MANDATE-NOTHROW}@(as_const(q).query(get_completion_scheduler<@\exposid{completion-tag}@>)) \end{codeblock} \mandates If the expression above is well-formed, its type satisfies \libconcept{scheduler}. \pnum Let \exposid{completion-fn} be a completion function\iref{exec.async.ops}; let \exposid{completion-tag} be the associated completion tag of \exposid{completion-fn}; let \tcode{args} be a pack of subexpressions; and let \tcode{sndr} be a subexpression such that \tcode{\libconcept{sender}} is \tcode{true} and \tcode{get_completion_scheduler<\exposid{completion-tag}>(get_env(sndr))} is well-formed and denotes a scheduler \tcode{sch}. If an asynchronous operation created by connecting \tcode{sndr} with a receiver \tcode{rcvr} causes the evaluation of \tcode{\exposid{completion-fn}(rcvr, args...)}, the behavior is undefined unless the evaluation happens on an execution agent that belongs to \tcode{sch}'s associated execution resource. \pnum The expression \tcode{forwarding_query(get_completion_scheduler<\exposid{completion-tag}>)} is a core constant expression and has value \tcode{true}. \rSec1[exec.sched]{Schedulers} \pnum The \libconcept{scheduler} concept defines the requirements of a scheduler type\iref{exec.async.ops}. \tcode{schedule} is a customization point object that accepts a scheduler. A valid invocation of \tcode{schedule} is a schedule-expression. \begin{codeblock} namespace std::execution { template concept @\deflibconcept{scheduler}@ = @\libconcept{derived_from}@::scheduler_concept, scheduler_t> && @\exposconcept{queryable}@ && requires(Sch&& sch) { { schedule(std::forward(sch)) } -> @\libconcept{sender}@; { auto(get_completion_scheduler( get_env(schedule(std::forward(sch))))) } -> @\libconcept{same_as}@>; } && @\libconcept{equality_comparable}@> && @\libconcept{copy_constructible}@>; } \end{codeblock} \pnum Let \tcode{Sch} be the type of a scheduler and let \tcode{Env} be the type of an execution environment for which \tcode{\libconcept{sender_in}, Env>} is satisfied. Then \tcode{\exposconcept{sender-in-of}, Env>} shall be modeled. \pnum None of a scheduler's copy constructor, destructor, equality comparison, or \tcode{swap} member functions shall exit via an exception. None of these member functions, nor a scheduler type's \tcode{schedule} function, shall introduce data races as a result of potentially concurrent\iref{intro.races} invocations of those functions from different threads. \pnum For any two values \tcode{sch1} and \tcode{sch2} of some scheduler type \tcode{Sch}, \tcode{sch1 == sch2} shall return \tcode{true} only if both \tcode{sch1} and \tcode{sch2} share the same associated execution resource. \pnum For a given scheduler expression \tcode{sch}, the expression \tcode{get_completion_scheduler(get_env(schedule(sch)))} shall compare equal to \tcode{sch}. \pnum For a given scheduler expression \tcode{sch}, if the expression \tcode{get_domain(sch)} is well-formed, then the expression \tcode{get_domain(get_env(schedule(sch)))} is also well-formed and has the same type. \pnum A scheduler type's destructor shall not block pending completion of any receivers connected to the sender objects returned from \tcode{schedule}. \begin{note} The ability to wait for completion of submitted function objects can be provided by the associated execution resource of the scheduler. \end{note} \rSec1[exec.recv]{Receivers} \rSec2[exec.recv.concepts]{Receiver concepts} \pnum A receiver represents the continuation of an asynchronous operation. The \libconcept{receiver} concept defines the requirements for a receiver type\iref{exec.async.ops}. The \libconcept{receiver_of} concept defines the requirements for a receiver type that is usable as the first argument of a set of completion operations corresponding to a set of completion signatures. The \tcode{get_env} customization point object is used to access a receiver's associated environment. \begin{codeblock} namespace std::execution { template concept @\deflibconcept{receiver}@ = @\libconcept{derived_from}@::receiver_concept, receiver_t> && requires(const remove_cvref_t& rcvr) { { get_env(rcvr) } -> @\exposconcept{queryable}@; } && @\libconcept{move_constructible}@> && // rvalues are movable, and @\libconcept{constructible_from}@, Rcvr>; // lvalues are copyable template concept @\defexposconcept{valid-completion-for}@ = requires (Signature* sig) { [](Tag(*)(Args...)) requires @\exposconcept{callable}@, Args...> {}(sig); }; template concept @\defexposconcept{has-completions}@ = requires (Completions* completions) { []<@\exposconcept{valid-completion-for}@...Sigs>(completion_signatures*) {}(completions); }; template concept @\deflibconcept{receiver_of}@ = @\libconcept{receiver}@ && @\exposconcept{has-completions}@; } \end{codeblock} \pnum Class types that are marked \tcode{final} do not model the \libconcept{receiver} concept. \pnum Let \tcode{rcvr} be a receiver and let \tcode{op_state} be an operation state associated with an asynchronous operation created by connecting \tcode{rcvr} with a sender. Let \tcode{token} be a stop token equal to \tcode{get_stop_token(get_env(rcvr))}. \tcode{token} shall remain valid for the duration of the asynchronous operation's lifetime\iref{exec.async.ops}. \begin{note} This means that, unless it knows about further guarantees provided by the type of \tcode{rcvr}, the implementation of \tcode{op_state} cannot use \tcode{token} after it executes a completion operation. This also implies that any stop callbacks registered on token must be destroyed before the invocation of the completion operation. \end{note} \rSec2[exec.set.value]{\tcode{execution::set_value}} \pnum \tcode{set_value} is a value completion function\iref{exec.async.ops}. Its associated completion tag is \tcode{set_value_t}. The expression \tcode{set_value(rcvr, vs...)} for a subexpression \tcode{rcvr} and pack of subexpressions \tcode{vs} is ill-formed if \tcode{rcvr} is an lvalue or an rvalue of const type. Otherwise, it is expression-equivalent to \tcode{\exposid{MANDATE-NOTHROW}(rcvr.set_value(vs...))}. \rSec2[exec.set.error]{\tcode{execution::set_error}} \pnum \tcode{set_error} is an error completion function\iref{exec.async.ops}. Its associated completion tag is \tcode{set_error_t}. The expression \tcode{set_error(rcvr, err)} for some \tcode{subexpressions} \tcode{rcvr} and \tcode{err} is ill-formed if \tcode{rcvr} is an lvalue or an rvalue of const type. Otherwise, it is expression-equivalent to \tcode{\exposid{MANDATE-NOTHROW}(rcvr.set_error(err))}. \rSec2[exec.set.stopped]{\tcode{execution::set_stopped}} \pnum \tcode{set_stopped} is a stopped completion function\iref{exec.async.ops}. Its associated completion tag is \tcode{set_stopped_t}. The expression \tcode{set_stopped(rcvr)} for a subexpression \tcode{rcvr} is ill-formed if \tcode{rcvr} is an lvalue or an rvalue of const type. Otherwise, it is expression-equivalent to \tcode{\exposid{MANDATE-NOTHROW}(rcvr.set_stopped())}. \rSec1[exec.opstate]{Operation states} \rSec2[exec.opstate.general]{General} \pnum The \libconcept{operation_state} concept defines the requirements of an operation state type\iref{exec.async.ops}. \begin{codeblock} namespace std::execution { template concept @\deflibconcept{operation_state}@ = @\libconcept{derived_from}@ && is_object_v && requires (O& o) { { start(o) } noexcept; }; } \end{codeblock} \pnum If an \libconcept{operation_state} object is destroyed during the lifetime of its asynchronous operation\iref{exec.async.ops}, the behavior is undefined. \begin{note} The \libconcept{operation_state} concept does not impose requirements on any operations other than destruction and \tcode{start}, including copy and move operations. Invoking any such operation on an object whose type models \libconcept{operation_state} can lead to undefined behavior. \end{note} \pnum The program is ill-formed if it performs a copy or move construction or assigment operation on an operation state object created by connecting a library-provided sender. \rSec2[exec.opstate.start]{\tcode{execution::start}} \pnum The name \tcode{start} denotes a customization point object that starts\iref{exec.async.ops} the asynchronous operation associated with the operation state object. For a subexpression \tcode{op}, the expression \tcode{start(op)} is ill-formed if \tcode{op} is an rvalue. Otherwise, it is expression-equivalent to \tcode{\exposid{MANDATE-NOTHROW}(op.start())}. \pnum If \tcode{op.start()} does not start\iref{exec.async.ops} the asynchronous operation associated with the operation state \tcode{op}, the behavior of calling \tcode{start(op)} is undefined. \rSec1[exec.snd]{Senders} \rSec2[exec.snd.general]{General} \pnum Subclauses \ref{exec.factories} and \ref{exec.adapt} define customizable algorithms that return senders. Each algorithm has a default implementation. Let \tcode{sndr} be the result of an invocation of such an algorithm or an object equal to the result\iref{concepts.equality}, and let \tcode{Sndr} be \tcode{decltype((sndr))}. Let \tcode{rcvr} be a receiver of type \tcode{Rcvr} with associated environment \tcode{env} of type \tcode{Env} such that \tcode{\libconcept{sender_to}} is \tcode{true}. For the default implementation of the algorithm that produced \tcode{sndr}, connecting \tcode{sndr} to \tcode{rcvr} and starting the resulting operation state\iref{exec.async.ops} necessarily results in the potential evaluation\iref{basic.def.odr} of a set of completion operations whose first argument is a subexpression equal to \tcode{rcvr}. Let \tcode{Sigs} be a pack of completion signatures corresponding to this set of completion operations. Then the type of the expression \tcode{get_completion_signatures(sndr, env)} is a specialization of the class template \tcode{completion_signatures}\iref{exec.util.cmplsig}, the set of whose template arguments is \tcode{Sigs}. If a user-provided implementation of the algorithm that produced \tcode{sndr} is selected instead of the default, any completion signature that is in the set of types denoted by \tcode{completion_signatures_of_t} and that is not part of \tcode{Sigs} shall correspond to error or stopped completion operations, unless otherwise specified. \rSec2[exec.snd.expos]{Exposition-only entities} \pnum Subclause \ref{exec.snd} makes use of the following exposition-only entities. \pnum For a queryable object \tcode{env}, \tcode{\exposid{FWD-ENV}(env)} is an expression whose type satisfies \exposconcept{queryable} such that for a query object \tcode{q} and a pack of subexpressions \tcode{as}, the expression \tcode{\exposid{FWD-ENV}(env).query(q, as...)} is ill-formed if \tcode{forwarding_query(q)} is \tcode{false}; otherwise, it is expression-equivalent to \tcode{env.query(q, as...)}. \pnum For a query object \tcode{q} and \tcode{a} subexpression \tcode{v}, \tcode{\exposid{MAKE-ENV}(q, v)} is an expression \tcode{env} whose type satisfies \exposconcept{queryable} such that the result of \tcode{env.query(q)} has a value equal to \tcode{v}\iref{concepts.equality}. Unless otherwise stated, the object to which \tcode{env.query(q)} refers remains valid while \tcode{env} remains valid. \pnum For two queryable objects \tcode{env1} and \tcode{env2}, a query object \tcode{q}, and a pack of subexpressions \tcode{as}, \tcode{\exposid{JOIN-ENV}(env1, env2)} is an expression \tcode{env3} whose type satisfies \exposconcept{queryable} such that \tcode{env3.query(q, as...)} is expression-equivalent to: \begin{itemize} \item \tcode{env1.query(q, as...)} if that expression is well-formed, \item otherwise, \tcode{env2.query(q, as...)} if that expression is well-formed, \item otherwise, \tcode{env3.query(q, as...)} is ill-formed. \end{itemize} \pnum The results of \exposid{FWD-ENV}, \exposid{MAKE-ENV}, and \exposid{JOIN-ENV} can be context-dependent; i.e., they can evaluate to expressions with different types and value categories in different contexts for the same arguments. \pnum For a scheduler \tcode{sch}, \tcode{\exposid{SCHED-ATTRS}(sch)} is an expression \tcode{o1} whose type satisfies \exposconcept{queryable} such that \tcode{o1.query(get_completion_scheduler)} is an expression with the same type and value as \tcode{sch} where \tcode{Tag} is one of \tcode{set_value_t} or \tcode{set_stopped_t}, and such that \tcode{o1.query(get_domain)} is expression-equivalent to \tcode{sch.query(get_domain)}. \tcode{\exposid{SCHED-ENV}(sch)} is an expression \tcode{o2} whose type satisfies \exposconcept{queryable} such that \tcode{o2.query(get_scheduler)} is a prvalue with the same type and value as \tcode{sch}, and such that \tcode{o2.query(get_domain)} is expression-equivalent to \tcode{sch.query(get_domain)}. \pnum For two subexpressions \tcode{rcvr} and \tcode{expr}, \tcode{\exposid{SET-VALUE}(rcvr, expr)} is expression-equivalent to \tcode{(expr, set_value(std::move(rcvr)))} if the type of \tcode{expr} is \tcode{void}; otherwise, \tcode{set_value(std::move(rcvr), expr)}. \tcode{\exposid{TRY-EVAL}(rcvr, expr)} is equivalent to: \begin{codeblock} try { expr; } catch(...) { set_error(std::move(rcvr), current_exception()); } \end{codeblock} if \tcode{expr} is potentially-throwing; otherwise, \tcode{expr}. \tcode{\exposid{TRY-SET-VALUE}(rcvr, expr)} is \begin{codeblock} @\exposid{TRY-EVAL}@(rcvr, @\exposid{SET-VALUE}@(rcvr, expr)) \end{codeblock} except that \tcode{rcvr} is evaluated only once. \begin{itemdecl} template constexpr auto @\exposid{completion-domain}@(const Sndr& sndr) noexcept; \end{itemdecl} \begin{itemdescr} \pnum \tcode{\exposid{COMPL-DOMAIN}(T)} is the type of the expression \tcode{get_domain(get_completion_scheduler(get_env(sndr)))}. \pnum \effects If all of the types \tcode{\exposid{COMPL-DOMAIN}(set_value_t)}, \tcode{\exposid{COMPL-DOMAIN}(set_error_t)}, and\linebreak \tcode{\exposid{COMPL-DOMAIN}(set_stopped_t)} are ill-formed, \tcode{completion-domain(sndr)} is a default-constructed prvalue of type \tcode{Default}. Otherwise, if they all share a common type\iref{meta.trans.other} (ignoring those types that are ill-formed), then \tcode{\exposid{completion-domain}(sndr)} is a default-constructed prvalue of that type. Otherwise, \tcode{\exposid{completion-domain}(sndr)} is ill-formed. \end{itemdescr} \begin{itemdecl} template constexpr decltype(auto) @\exposid{query-with-default}@( Tag, const Env& env, Default&& value) noexcept(@\seebelow@); \end{itemdecl} \begin{itemdescr} \pnum Let \tcode{e} be the expression \tcode{Tag()(env)} if that expression is well-formed; otherwise, it is \tcode{static_cast(std::forward(value))}. \pnum \returns \tcode{e}. \pnum \remarks The expression in the noexcept clause is \tcode{noexcept(e)}. \end{itemdescr} \begin{itemdecl} template constexpr auto @\exposid{get-domain-early}@(const Sndr& sndr) noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} return Domain(); \end{codeblock} where \tcode{Domain} is the decayed type of the first of the following expressions that is well-formed: \begin{itemize} \item \tcode{get_domain(get_env(sndr))} \item \tcode{\exposid{completion-domain}(sndr)} \item \tcode{default_domain()} \end{itemize} \end{itemdescr} \begin{itemdecl} template constexpr auto @\exposid{get-domain-late}@(const Sndr& sndr, const Env& env) noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{itemize} \item If \tcode{\exposconcept{sender-for}} is \tcode{true}, then \begin{codeblock} return Domain(); \end{codeblock} where \tcode{Domain} is the type of the following expression: \begin{codeblock} [] { auto [_, sch, _] = sndr; return @\exposid{query-or-default}@(get_domain, sch, default_domain()); }(); \end{codeblock} \begin{note} The \tcode{continues_on} algorithm works in tandem with \tcode{schedule_from}\iref{exec.schedule.from} to give scheduler authors a way to customize both how to transition onto (\tcode{continues_on}) and off of (\tcode{schedule_from}) a given execution context. Thus, \tcode{continues_on} ignores the domain of the predecessor and uses the domain of the destination scheduler to select a customization, a property that is unique to \tcode{continues_on}. That is why it is given special treatment here. \end{note} \item Otherwise, \begin{codeblock} return Domain(); \end{codeblock} where \tcode{Domain} is the first of the following expressions that is well-formed and whose type is not \tcode{void}: \begin{itemize} \item \tcode{get_domain(get_env(sndr))} \item \tcode{\exposid{completion-domain}(sndr)} \item \tcode{get_domain(env)} \item \tcode{get_domain(get_scheduler(env))} \item \tcode{default_domain()} \end{itemize} \end{itemize} \end{itemdescr} \pnum \begin{codeblock} template<@\exposconcept{callable}@ Fun> requires is_nothrow_move_constructible_v struct @\exposid{emplace-from}@ { Fun @\exposid{fun}@; // \expos using type = @\exposid{call-result-t}@; constexpr operator type() && noexcept(@\exposconcept{nothrow-callable}@) { return std::move(fun)(); } constexpr type operator()() && noexcept(@\exposconcept{nothrow-callable}@) { return std::move(fun)(); } }; \end{codeblock} \begin{note} \exposid{emplace-from} is used to emplace non-movable types into \tcode{tuple}, \tcode{optional}, \tcode{variant}, and similar types. \end{note} \pnum \begin{codeblock} struct @\exposid{on-stop-request}@ { inplace_stop_source& @\exposid{stop-src}@; // \expos void operator()() noexcept { @\exposid{stop-src}@.request_stop(); } }; \end{codeblock} \pnum \begin{codeblock} template struct @\exposid{product-type}@ { // \expos T@$_0$@ t@$_0$@; // \expos T@$_1$@ t@$_1$@; // \expos @...@ T@$_n$@ t@$_n$@; // \expos template constexpr decltype(auto) @\exposid{get}@(this Self&& self) noexcept; // \expos template constexpr decltype(auto) @\exposid{apply}@(this Self&& self, Fn&& fn) // \expos noexcept(@\seebelow@); }; \end{codeblock} \pnum \begin{note} \exposid{product-type} is presented here in pseudo-code form for the sake of exposition. It can be approximated in standard \Cpp{} with a tuple-like implementation that takes care to keep the type an aggregate that can be used as the initializer of a structured binding declaration. \end{note} \begin{note} An expression of type \exposid{product-type} is usable as the initializer of a structured binding declaration\iref{dcl.struct.bind}. \end{note} \begin{itemdecl} template constexpr decltype(auto) @\exposid{get}@(this Self&& self) noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} auto& [...ts] = self; return std::forward_like(ts...[I]); \end{codeblock} \end{itemdescr} \begin{codeblock} template constexpr decltype(auto) @\exposid{apply}@(this Self&& self, Fn&& fn) noexcept(@\seebelow@); \end{codeblock} \begin{itemdescr} \pnum \constraints The expression in the \tcode{return} statement below is well-formed. \pnum \effects Equivalent to: \begin{codeblock} auto& [...ts] = self; return std::forward(fn)(std::forward_like(ts)...); \end{codeblock} \pnum \remarks The expression in the \tcode{noexcept} clause is \tcode{true} if the \tcode{return} statement above is not potentially throwing; otherwise, \tcode{false}. \end{itemdescr} \begin{itemdecl} template constexpr auto @\exposid{make-sender}@(Tag tag, Data&& data, Child&&... child); \end{itemdecl} \begin{itemdescr} \pnum \mandates The following expressions are \tcode{true}: \begin{itemize} \item \tcode{\libconcept{semiregular}} \item \tcode{\exposconcept{movable-value}} \item \tcode{(\libconcept{sender} \&\&...)} \end{itemize} \pnum \returns A prvalue of type \tcode{\exposid{basic-sender}, decay_t...>} that has been direct-list-initialized with the forwarded arguments, where \exposid{basic-sender} is the following exposition-only class template except as noted below. \end{itemdescr} \begin{codeblock} namespace std::execution { template concept @\defexposconcept{completion-tag}@ = // \expos @\libconcept{same_as}@ || @\libconcept{same_as}@ || @\libconcept{same_as}@; template class T, class... Args> concept @\defexposconcept{valid-specialization}@ = // \expos requires { typename T; }; struct @\exposid{default-impls}@ { // \expos static constexpr auto @\exposid{get-attrs}@ = @\seebelow@; // \expos static constexpr auto @\exposid{get-env}@ = @\seebelow@; // \expos static constexpr auto @\exposid{get-state}@ = @\seebelow@; // \expos static constexpr auto @\exposid{start}@ = @\seebelow@; // \expos static constexpr auto @\exposid{complete}@ = @\seebelow@; // \expos }; template struct @\exposid{impls-for}@ : @\exposid{default-impls}@ {}; // \expos template // \expos using @\exposid{state-type}@ = decay_t<@\exposid{call-result-t}@< decltype(@\exposid{impls-for}@>::@\exposid{get-state}@), Sndr, Rcvr&>>; template // \expos using @\exposid{env-type}@ = @\exposid{call-result-t}@< decltype(@\exposid{impls-for}@>::@\exposid{get-env}@), Index, @\exposid{state-type}@&, const Rcvr&>; template using @\exposid{child-type}@ = decltype(declval().template @\exposid{get}@()); // \expos template using @\exposid{indices-for}@ = remove_reference_t::@\exposid{indices-for}@; // \expos template struct @\exposid{basic-state}@ { // \expos @\exposid{basic-state}@(Sndr&& sndr, Rcvr&& rcvr) noexcept(@\seebelow@) : @\exposid{rcvr}@(std::move(rcvr)) , @\exposid{state}@(@\exposid{impls-for}@>::@\exposid{get-state}@(std::forward(sndr), @\exposid{rcvr}@)) { } Rcvr @\exposid{rcvr}@; // \expos @\exposid{state-type}@ @\exposid{state}@; // \expos }; template requires @\exposid{valid-specialization}@<@\exposid{env-type}@, Index, Sndr, Rcvr> struct @\exposid{basic-receiver}@ { // \expos using receiver_concept = receiver_t; using @\exposid{tag-t}@ = tag_of_t; // \expos using @\exposid{state-t}@ = @\exposid{state-type}@; // \expos static constexpr const auto& @\exposid{complete}@ = @\exposid{impls-for}@::@\exposid{complete}@; // \expos template requires @\exposconcept{callable}@ void set_value(Args&&... args) && noexcept { @\exposid{complete}@(Index(), op->@\exposid{state}@, op->@\exposid{rcvr}@, set_value_t(), std::forward(args)...); } template requires @\exposconcept{callable}@ void set_error(Error&& err) && noexcept { @\exposid{complete}@(Index(), op->@\exposid{state}@, op->@\exposid{rcvr}@, set_error_t(), std::forward(err)); } void set_stopped() && noexcept requires @\exposconcept{callable}@ { @\exposid{complete}@(Index(), op->@\exposid{state}@, op->@\exposid{rcvr}@, set_stopped_t()); } auto get_env() const noexcept -> @\exposid{env-type}@ { return @\exposid{impls-for}@::@\exposid{get-env}@(Index(), op->@\exposid{state}@, op->@\exposid{rcvr}@); } @\exposid{basic-state}@* @\exposid{op}@; // \expos }; constexpr auto @\exposid{connect-all}@ = @\seebelow@; // \expos template using @\exposid{connect-all-result}@ = @\exposid{call-result-t}@< // \expos decltype(@\exposid{connect-all}@), @\exposid{basic-state}@*, Sndr, @\exposid{indices-for}@>; template requires @\exposconcept{valid-specialization}@<@\exposid{state-type}@, Sndr, Rcvr> && @\exposconcept{valid-specialization}@<@\exposid{connect-all-result}@, Sndr, Rcvr> struct @\exposid{basic-operation}@ : @\exposid{basic-state}@ { // \expos using operation_state_concept = operation_state_t; using @\exposid{tag-t}@ = tag_of_t; // \expos @\exposid{connect-all-result}@ @\exposid{inner-ops}@; // \expos @\exposid{basic-operation}@(Sndr&& sndr, Rcvr&& rcvr) noexcept(@\seebelow@) // \expos : @\exposid{basic-state}@(std::forward(sndr), std::move(rcvr)), @\exposid{inner-ops}@(@\exposid{connect-all}@(this, std::forward(sndr), @\exposid{indices-for}@())) {} void start() & noexcept { auto& [...ops] = @\exposid{inner-ops}@; @\exposid{impls-for}@::@\exposid{start}@(this->@\exposid{state}@, this->@\exposid{rcvr}@, ops...); } }; template using @\exposid{completion-signatures-for}@ = @\seebelow@; // \expos template struct @\exposid{basic-sender}@ : @\exposid{product-type}@ { // \expos using sender_concept = sender_t; using @\exposid{indices-for}@ = index_sequence_for; // \expos decltype(auto) get_env() const noexcept { auto& [_, data, ...child] = *this; return @\exposid{impls-for}@::@\exposid{get-attrs}@(data, child...); } template<@\exposconcept{decays-to}@<@\exposid{basic-sender}@> Self, receiver Rcvr> auto connect(this Self&& self, Rcvr rcvr) noexcept(@\seebelow@) -> @\exposid{basic-operation}@ { return {std::forward(self), std::move(rcvr)}; } template<@\exposconcept{decays-to}@<@\exposid{basic-sender}@> Self, class Env> auto get_completion_signatures(this Self&& self, Env&& env) noexcept -> @\exposid{completion-signatures-for}@ { return {}; } }; } \end{codeblock} \pnum The default template argument for the \tcode{Data} template parameter denotes an unspecified empty trivially copyable class type that models \libconcept{semiregular}. \pnum It is unspecified whether a specialization of \exposid{basic-sender} is an aggregate. \pnum An expression of type \exposid{basic-sender} is usable as the initializer of a structured binding declaration\iref{dcl.struct.bind}. \pnum The expression in the \tcode{noexcept} clause of the constructor of \exposid{basic-state} is: \begin{codeblock} is_nothrow_move_constructible_v && @\exposconcept{nothrow-callable}@>::@\exposid{get-state}@), Sndr, Rcvr&> \end{codeblock} \pnum The object \exposid{connect-all} is initialized with a callable object equivalent to the following lambda: \begin{itemdecl} []( @\exposid{basic-state}@* op, Sndr&& sndr, index_sequence) noexcept(@\seebelow@) -> decltype(auto) { auto& [_, data, ...child] = sndr; return @\exposid{product-type}@{connect( std::forward_like(child), @\exposid{basic-receiver}@>{op})...}; } \end{itemdecl} \begin{itemdescr} \pnum \constraints The expression in the \tcode{return} statement is well-formed. \pnum \remarks The expression in the \tcode{noexcept} clause is \tcode{true} if the \tcode{return} statement is not potentially throwing; otherwise, \tcode{false}. \end{itemdescr} \pnum The expression in the \tcode{noexcept} clause of the constructor of \exposid{basic-operation} is: \begin{codeblock} is_nothrow_constructible_v<@\exposid{basic-state}@, Self, Rcvr> && noexcept(@\exposid{connect-all}@(this, std::forward(sndr), @\exposid{indices-for}@())) \end{codeblock} \pnum The expression in the \tcode{noexcept} clause of the \tcode{connect} member function of \exposid{basic-sender} is: \begin{codeblock} is_nothrow_constructible_v<@\exposid{basic-operation}@, Self, Rcvr> \end{codeblock} \pnum The member \tcode{\exposid{default-impls}::\exposid{get-attrs}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [](const auto&, const auto&... child) noexcept -> decltype(auto) { if constexpr (sizeof...(child) == 1) return (@\exposid{FWD-ENV}@(get_env(child)), ...); else return empty_env(); } \end{codeblock} \pnum The member \tcode{\exposid{default-impls}::\exposid{get-env}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [](auto, auto&, const auto& rcvr) noexcept -> decltype(auto) { return @\exposid{FWD-ENV}@(get_env(rcvr)); } \end{codeblock} \pnum The member \tcode{\exposid{default-impls}::\exposid{get-state}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [](Sndr&& sndr, Rcvr& rcvr) noexcept -> decltype(auto) { auto& [_, data, ...child] = sndr; return std::forward_like(data); } \end{codeblock} \pnum The member \tcode{\exposid{default-impls}::\exposid{start}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [](auto&, auto&, auto&... ops) noexcept -> void { (execution::start(ops), ...); } \end{codeblock} \pnum The member \tcode{\exposid{default-impls}::\exposid{complete}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} []( Index, auto& state, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void requires @\exposconcept{callable}@ { static_assert(Index::value == 0); Tag()(std::move(rcvr), std::forward(args)...); } \end{codeblock} \pnum For a subexpression \tcode{sndr} let \tcode{Sndr} be \tcode{decltype((sndr))}. Let \tcode{rcvr} be a receiver with an associated environment of type \tcode{Env} such that \tcode{\libconcept{sender_in}} is \tcode{true}. \tcode{\exposid{completion-signatures-for}} denotes a specialization of \tcode{completion_signatures}, the set of whose template arguments correspond to the set of completion operations that are potentially evaluated as a result of starting\iref{exec.async.ops} the operation state that results from connecting \tcode{sndr} and \tcode{rcvr}. When \tcode{\libconcept{sender_in}} is \tcode{false}, the type denoted by \tcode{\exposid{completion-signatures-for}}, if any, is not a specialization of \tcode{completion_signatures}. \recommended When \tcode{\libconcept{sender_in}} is \tcode{false}, implementations are encouraged to use the type denoted by \tcode{\exposid{completion-signatures-for}} to communicate to users why. \begin{itemdecl} template<@\libconcept{sender}@ Sndr, @\exposconcept{queryable}@ Env> constexpr auto @\exposid{write-env}@(Sndr&& sndr, Env&& env); // \expos \end{itemdecl} \begin{itemdescr} \pnum \exposid{write-env} is an exposition-only sender adaptor that, when connected with a receiver \tcode{rcvr}, connects the adapted sender with a receiver whose execution environment is the result of joining the \exposconcept{queryable} argument \tcode{env} to the result of \tcode{get_env(rcvr)}. \pnum Let \exposid{write-env-t} be an exposition-only empty class type. \pnum \returns \begin{codeblock} @\exposid{make-sender}@(@\exposid{write-env-t}@(), std::forward(env), std::forward(sndr)) \end{codeblock} \pnum \remarks The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \exposid{write-env-t} as follows: \begin{codeblock} template<> struct @\exposid{impls-for}@<@\exposid{write-env-t}@> : @\exposid{default-impls}@ { static constexpr auto @\exposid{get-env}@ = [](auto, const auto& state, const auto& rcvr) noexcept { return @\exposid{JOIN-ENV}@(state, get_env(rcvr)); }; }; \end{codeblock} \end{itemdescr} \rSec2[exec.snd.concepts]{Sender concepts} \pnum The \libconcept{sender} concept defines the requirements for a sender type\iref{exec.async.ops}. The \libconcept{sender_in} concept defines the requirements for a sender type that can create asynchronous operations given an associated environment type. The \libconcept{sender_to} concept defines the requirements for a sender type that can connect with a specific receiver type. The \tcode{get_env} customization point object is used to access a sender's associated attributes. The connect customization point object is used to connect\iref{exec.async.ops} a sender and a receiver to produce an operation state. \begin{codeblock} namespace std::execution { template concept @\exposconcept{valid-completion-signatures}@ = @\seebelow@; // \expos template concept @\defexposconcept{is-sender}@ = // \expos derived_from; template concept @\defexposconcept{enable-sender}@ = // \expos @\exposconcept{is-sender}@ || @\exposconcept{is-awaitable}@>; // \ref{exec.awaitable} template concept @\deflibconcept{sender}@ = bool(@\exposconcept{enable-sender}@>) && requires (const remove_cvref_t& sndr) { { get_env(sndr) } -> @\exposconcept{queryable}@; } && @\libconcept{move_constructible}@> && @\libconcept{constructible_from}@, Sndr>; template concept @\deflibconcept{sender_in}@ = @\libconcept{sender}@ && @\exposconcept{queryable}@ && requires (Sndr&& sndr, Env&& env) { { get_completion_signatures(std::forward(sndr), std::forward(env)) } -> @\exposconcept{valid-completion-signatures}@; }; template concept @\deflibconcept{sender_to}@ = @\libconcept{sender_in}@> && @\libconcept{receiver_of}@>> && requires (Sndr&& sndr, Rcvr&& rcvr) { connect(std::forward(sndr), std::forward(rcvr)); }; } \end{codeblock} \pnum Given a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype((sndr))} and let \tcode{rcvr} be a receiver with an associated environment whose type is \tcode{Env}. A completion operation is a \defnadj{permissible}{completion} for \tcode{Sndr} and \tcode{Env} if its completion signature appears in the argument list of the specialization of \tcode{completion_signatures} denoted by \tcode{completion_signatures_of_t}. \tcode{Sndr} and \tcode{Env} model \tcode{\libconcept{sender_in}} if all the completion operations that are potentially evaluated by connecting \tcode{sndr} to \tcode{rcvr} and starting the resulting operation state are permissible completions for \tcode{Sndr} and \tcode{Env}. \pnum A type models the exposition-only concept \defexposconcept{valid-completion-signatures} if it denotes a specialization of the \tcode{completion_signatures} class template. \pnum The exposition-only concepts \exposconcept{sender-of} and \exposconcept{sender-in-of} define the requirements for a sender type that completes with a given unique set of value result types. \begin{codeblock} namespace std::execution { template using @\exposid{value-signature}@ = set_value_t(As...); // \expos template concept @\defexposconcept{sender-in-of}@ = @\libconcept{sender_in}@ && @\exposid{MATCHING-SIG}@( // see \ref{exec.general} set_value_t(Values...), value_types_of_t); template concept @\defexposconcept{sender-of}@ = @\exposconcept{sender-in-of}@; } \end{codeblock} \pnum Let \tcode{sndr} be an expression such that \tcode{decltype((sndr))} is \tcode{Sndr}. The type \tcode{tag_of_t} is as follows: \begin{itemize} \item If the declaration \begin{codeblock} auto&& [tag, data, ...children] = sndr; \end{codeblock} would be well-formed, \tcode{tag_of_t} is an alias for \tcode{decltype(auto(tag))}. \item Otherwise, \tcode{tag_of_t} is ill-formed. \end{itemize} \pnum Let \exposconcept{sender-for} be an exposition-only concept defined as follows: \begin{codeblock} namespace std::execution { template concept @\defexposconcept{sender-for}@ = @\libconcept{sender}@ && @\libconcept{same_as}@, Tag>; } \end{codeblock} \pnum For a type \tcode{T}, \tcode{\exposid{SET-VALUE-SIG}(T)} denotes the type \tcode{set_value_t()} if \tcode{T} is \cv{} \tcode{void}; otherwise, it denotes the type \tcode{set_value_t(T)}. \pnum Library-provided sender types \begin{itemize} \item always expose an overload of a member \tcode{connect} that accepts an rvalue sender and \item only expose an overload of a member \tcode{connect} that accepts an lvalue sender if they model \libconcept{copy_constructible}. \end{itemize} \rSec2[exec.awaitable]{Awaitable helpers} \pnum The sender concepts recognize awaitables as senders. For \ref{exec}, an \defn{awaitable} is an expression that would be well-formed as the operand of a \tcode{co_await} expression within a given context. \pnum For a subexpression \tcode{c}, let \tcode{\exposid{GET-AWAITER}(c, p)} be expression-equivalent to the series of transformations and conversions applied to \tcode{c} as the operand of an \grammarterm{await-expression} in a coroutine, resulting in lvalue \tcode{e} as described by \ref{expr.await}, where \tcode{p} is an lvalue referring to the coroutine's promise, which has type \tcode{Promise}. \begin{note} This includes the invocation of the promise type's \tcode{await_transform} member if any, the invocation of the \tcode{operator co_await} picked by overload resolution if any, and any necessary implicit conversions and materializations. \end{note} \pnum Let \exposconcept{is-awaitable} be the following exposition-only concept: \begin{codeblock} namespace std { template concept @\exposconcept{await-suspend-result}@ = @\seebelow@; // \expos template concept @\defexposconcept{is-awaiter}@ = // \expos requires (A& a, coroutine_handle h) { a.await_ready() ? 1 : 0; { a.await_suspend(h) } -> @\exposconcept{await-suspend-result}@; a.await_resume(); }; template concept @\defexposconcept{is-awaitable}@ = // \expos requires (C (*fc)() noexcept, Promise& p) { { @\exposid{GET-AWAITER}@(fc(), p) } -> @\exposconcept{is-awaiter}@; }; } \end{codeblock} \tcode{\defexposconcept{await-suspend-result}} is \tcode{true} if and only if one of the following is \tcode{true}: \begin{itemize} \item \tcode{T} is \tcode{void}, or \item \tcode{T} is \tcode{bool}, or \item \tcode{T} is a specialization of \tcode{coroutine_handle}. \end{itemize} \pnum For a subexpression \tcode{c} such that \tcode{decltype((c))} is type \tcode{C}, and an lvalue \tcode{p} of type \tcode{Promise}, \tcode{\exposid{await-result-\newline type}} denotes the type \tcode{decltype(\exposid{GET-AWAITER}(c, p).await_resume())}. \pnum Let \exposid{with-await-transform} be the exposition-only class template: \begin{codeblock} namespace std::execution { template concept @\defexposconcept{has-as-awaitable}@ = // \expos requires (T&& t, Promise& p) { { std::forward(t).as_awaitable(p) } -> @\exposconcept{is-awaitable}@; }; template struct @\exposid{with-await-transform}@ { // \expos template T&& await_transform(T&& value) noexcept { return std::forward(value); } template<@\exposconcept{has-as-awaitable}@ T> decltype(auto) await_transform(T&& value) noexcept(noexcept(std::forward(value).as_awaitable(declval()))) { return std::forward(value).as_awaitable(static_cast(*this)); } }; } \end{codeblock} \pnum Let \exposid{env-promise} be the exposition-only class template: \begin{codeblock} namespace std::execution { template struct @\exposid{env-promise}@ : @\exposid{with-await-transform}@<@\exposid{env-promise}@> { // \expos @\unspec@ get_return_object() noexcept; @\unspec@ initial_suspend() noexcept; @\unspec@ final_suspend() noexcept; void unhandled_exception() noexcept; void return_void() noexcept; coroutine_handle<> unhandled_stopped() noexcept; const Env& get_env() const noexcept; }; } \end{codeblock} \begin{note} Specializations of \exposid{env-promise} are used only for the purpose of type computation; its members need not be defined. \end{note} \rSec2[exec.domain.default]{\tcode{execution::default_domain}} \pnum \begin{codeblock} namespace std::execution { struct default_domain { template<@\libconcept{sender}@ Sndr, @\exposconcept{queryable}@... Env> requires (sizeof...(Env) <= 1) static constexpr @\libconcept{sender}@ decltype(auto) transform_sender(Sndr&& sndr, const Env&... env) noexcept(@\seebelow@); template<@\libconcept{sender}@ Sndr, @\exposconcept{queryable}@ Env> static constexpr @\exposconcept{queryable}@ decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept; template static constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args) noexcept(@\seebelow@); }; } \end{codeblock} \begin{itemdecl} template<@\libconcept{sender}@ Sndr, @\exposconcept{queryable}@... Env> requires (sizeof...(Env) <= 1) constexpr @\libconcept{sender}@ decltype(auto) transform_sender(Sndr&& sndr, const Env&... env) noexcept(@\seebelow@); \end{itemdecl} \begin{itemdescr} \pnum Let \tcode{e} be the expression \begin{codeblock} tag_of_t().transform_sender(std::forward(sndr), env...) \end{codeblock} if that expression is well-formed; otherwise, \tcode{std::forward(sndr)}. \pnum \returns \tcode{e}. \pnum \remarks The exception specification is equivalent to \tcode{noexcept(e)}. \end{itemdescr} \begin{itemdecl} template<@\libconcept{sender}@ Sndr, @\exposconcept{queryable}@ Env> constexpr @\exposconcept{queryable}@ decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept; \end{itemdecl} \begin{itemdescr} \pnum Let \tcode{e} be the expression \begin{codeblock} tag_of_t().transform_env(std::forward(sndr), std::forward(env)) \end{codeblock} if that expression is well-formed; otherwise, \tcode{static_cast(std::forward(env))}. \pnum \mandates \tcode{noexcept(e)} is \tcode{true}. \pnum \returns \tcode{e}. \end{itemdescr} \begin{itemdecl} template constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args) noexcept(@\seebelow@); \end{itemdecl} \begin{itemdescr} \pnum Let \tcode{e} be the expression \begin{codeblock} Tag().apply_sender(std::forward(sndr), std::forward(args)...) \end{codeblock} \pnum \constraints \tcode{e} is a well-formed expression. \pnum \returns \tcode{e}. \pnum \remarks The exception specification is equivalent to \tcode{noexcept(e)}. \end{itemdescr} \rSec2[exec.snd.transform]{\tcode{execution::transform_sender}} \begin{itemdecl} namespace std::execution { template requires (sizeof...(Env) <= 1) constexpr @\libconcept{sender}@ decltype(auto) transform_sender(Domain dom, Sndr&& sndr, const Env&... env) noexcept(@\seebelow@); } \end{itemdecl} \begin{itemdescr} \pnum Let \exposid{transformed-sndr} be the expression \begin{codeblock} dom.transform_sender(std::forward(sndr), env...) \end{codeblock} if that expression is well-formed; otherwise, \begin{codeblock} default_domain().transform_sender(std::forward(sndr), env...) \end{codeblock} Let \exposid{final-sndr} be the expression \exposid{transformed-sndr} if \exposid{transformed-sndr} and \exposid{sndr} have the same type ignoring cv-qualifiers; otherwise, it is the expression \tcode{transform_sender(dom, transformed-sndr, env...)}. \pnum \returns \exposid{final-sndr}. \pnum \remarks The exception specification is equivalent to \tcode{noexcept(\exposid{final-sndr})}. \end{itemdescr} \rSec2[exec.snd.transform.env]{\tcode{execution::transform_env}} \begin{itemdecl} namespace std::execution { template constexpr @\exposconcept{queryable}@ decltype(auto) transform_env(Domain dom, Sndr&& sndr, Env&& env) noexcept; } \end{itemdecl} \begin{itemdescr} \pnum Let \tcode{e} be the expression \begin{codeblock} dom.transform_env(std::forward(sndr), std::forward(env)) \end{codeblock} if that expression is well-formed; otherwise, \begin{codeblock} default_domain().transform_env(std::forward(sndr), std::forward(env)) \end{codeblock} \pnum \mandates \tcode{noexcept(e)} is \tcode{true}. \pnum \returns \tcode{e}. \end{itemdescr} \rSec2[exec.snd.apply]{\tcode{execution::apply_sender}} \begin{itemdecl} namespace std::execution { template constexpr decltype(auto) apply_sender(Domain dom, Tag, Sndr&& sndr, Args&&... args) noexcept(@\seebelow@); } \end{itemdecl} \begin{itemdescr} \pnum Let $e$ be the expression \begin{codeblock} dom.apply_sender(Tag(), std::forward(sndr), std::forward(args)...) \end{codeblock} if that expression is well-formed; otherwise, \begin{codeblock} default_domain().apply_sender(Tag(), std::forward(sndr), std::forward(args)...) \end{codeblock} \pnum \constraints The expression $e$ is well-formed. \pnum \returns $e$. \pnum \remarks The exception specification is equivalent to \tcode{noexcept($e$)}. \end{itemdescr} \rSec2[exec.getcomplsigs]{\tcode{execution::get_completion_signatures}} \pnum \tcode{get_completion_signatures} is a customization point object. Let \tcode{sndr} be an expression such that \tcode{decltype((sndr))} is \tcode{Sndr}, and let \tcode{env} be an expression such that \tcode{decltype((env))} is \tcode{Env}. Let \tcode{new_sndr} be the expression \tcode{transform_sender(decltype(\exposid{get-domain-late}(sndr, env)){}, sndr, env)}, and let \tcode{NewSndr} be \tcode{decltype((new_sndr))}. Then \tcode{get_completion_signatures(sndr, env)} is expression-equivalent to \tcode{(void(sndr), void(env), CS())} except that \tcode{void(sndr)} and \tcode{void(env)} are indeterminately sequenced, where \tcode{CS} is: \begin{itemize} \item \tcode{decltype(new_sndr.get_completion_signatures(env))} if that type is well-formed, \item Otherwise, \tcode{remove_cvref_t::completion_signatures} if that type is well-formed, \item Otherwise, if \tcode{\exposconcept{is-awaitable}>} is \tcode{true}, then: \begin{codeblock} completion_signatures< @\exposid{SET-VALUE-SIG}@(@\exposid{await-result-type}@>), // \iref{exec.snd.concepts} set_error_t(exception_ptr), set_stopped_t()> \end{codeblock} \item Otherwise, \tcode{CS} is ill-formed. \end{itemize} \pnum Let \tcode{rcvr} be an rvalue whose type \tcode{Rcvr} models \libconcept{receiver}, and let \tcode{Sndr} be the type of a sender such that \tcode{\libconcept{sender_in}>} is \tcode{true}. Let \tcode{Sigs...} be the template arguments of the \tcode{completion_signatures} specialization named by \tcode{completion_signatures_of_t>}. Let \tcode{CSO} be a completion function. If sender \tcode{Sndr} or its operation state cause the expression \tcode{CSO(rcvr, args...)} to be potentially evaluated\iref{basic.def.odr} then there shall be a signature \tcode{Sig} in \tcode{Sigs...} such that \begin{codeblock} @\exposid{MATCHING-SIG}@(@\exposid{decayed-typeof}@(decltype(args)...), Sig) \end{codeblock} is \tcode{true}\iref{exec.general}. \rSec2[exec.connect]{\tcode{execution::connect}} \pnum \tcode{connect} connects\iref{exec.async.ops} a sender with a receiver. \pnum The name \tcode{connect} denotes a customization point object. For subexpressions \tcode{sndr} and \tcode{rcvr}, let \tcode{Sndr} be \tcode{decltype((sndr))} and \tcode{Rcvr} be \tcode{decltype((rcvr))}, let \tcode{new_sndr} be the expression \begin{codeblock} transform_sender(decltype(@\exposid{get-domain-late}@(sndr, get_env(rcvr))){}, sndr, get_env(rcvr)) \end{codeblock} and let \tcode{DS} and \tcode{DR} be \tcode{decay_t} and \tcode{decay_t}, respectively. \pnum Let \exposid{connect-awaitable-promise} be the following exposition-only class: \begin{codeblock} namespace std::execution { struct @\exposid{connect-awaitable-promise}@ : @\exposid{with-await-transform}@<@\exposid{connect-awaitable-promise}@> { @\exposid{connect-awaitable-promise}@(DS&, DR& rcvr) noexcept : @\exposid{rcvr}@(rcvr) {} suspend_always initial_suspend() noexcept { return {}; } [[noreturn]] suspend_always final_suspend() noexcept { terminate(); } [[noreturn]] void unhandled_exception() noexcept { terminate(); } [[noreturn]] void return_void() noexcept { terminate(); } coroutine_handle<> unhandled_stopped() noexcept { set_stopped(std::move(@\exposid{rcvr}@)); return noop_coroutine(); } @\exposid{operation-state-task}@ get_return_object() noexcept { return @\exposid{operation-state-task}@{ coroutine_handle<@\exposid{connect-awaitable-promise}@>::from_promise(*this)}; } env_of_t get_env() const noexcept { return execution::get_env(@\exposid{rcvr}@); } private: DR& @\exposid{rcvr}@; // \expos }; } \end{codeblock} \pnum Let \exposid{operation-state-task} be the following exposition-only class: \begin{codeblock} namespace std::execution { struct @\exposid{operation-state-task}@ { using operation_state_concept = operation_state_t; using promise_type = @\exposid{connect-awaitable-promise}@; explicit @\exposid{operation-state-task}@(coroutine_handle<> h) noexcept : coro(h) {} @\exposid{operation-state-task}@(@\exposid{operation-state-task}@&& o) noexcept : @\exposid{coro}@(exchange(o.@\exposid{coro}@, {})) {} ~@\exposid{operation-state-task}@() { if (@\exposid{coro}@) @\exposid{coro}@.destroy(); } void start() & noexcept { @\exposid{coro}@.resume(); } private: coroutine_handle<> @\exposid{coro}@; // \expos }; } \end{codeblock} \pnum Let \tcode{V} name the type \tcode{\exposid{await-result-type}}, let \tcode{Sigs} name the type \begin{codeblock} completion_signatures< @\exposid{SET-VALUE-SIG}@(V), // see \iref{exec.snd.concepts} set_error_t(exception_ptr), set_stopped_t()> \end{codeblock} and let \exposid{connect-awaitable} be an exposition-only coroutine defined as follows: \begin{codeblock} namespace std::execution { template auto @\exposid{suspend-complete}@(Fun fun, Ts&&... as) noexcept { // \expos auto fn = [&, fun]() noexcept { fun(std::forward(as)...); }; struct awaiter { decltype(@\exposid{fn}@) @\exposid{fn}@; // \expos static constexpr bool await_ready() noexcept { return false; } void await_suspend(coroutine_handle<>) noexcept { @\exposid{fn}@(); } [[noreturn]] void await_resume() noexcept { unreachable(); } }; return awaiter{@\exposid{fn}@}; } @\exposid{operation-state-task}@ @\exposid{connect-awaitable}@(DS sndr, DR rcvr) requires @\libconcept{receiver_of}@ { exception_ptr ep; try { if constexpr (@\libconcept{same_as}@) { co_await std::move(sndr); co_await @\exposid{suspend-complete}@(set_value, std::move(rcvr)); } else { co_await @\exposid{suspend-complete}@(set_value, std::move(rcvr), co_await std::move(sndr)); } } catch(...) { ep = current_exception(); } co_await @\exposid{suspend-complete}@(set_error, std::move(rcvr), std::move(ep)); } } \end{codeblock} \pnum The expression \tcode{connect(sndr, rcvr)} is expression-equivalent to: \begin{itemize} \item \tcode{new_sndr.connect(rcvr)} if that expression is well-formed. \mandates The type of the expression above satisfies \libconcept{operation_state}. \item Otherwise, \tcode{\exposid{connect-awaitable}(new_sndr, rcvr)}. \end{itemize} \mandates \tcode{\libconcept{sender} \&\& \libconcept{receiver}} is \tcode{true}. \rSec2[exec.factories]{Sender factories} \rSec3[exec.schedule]{\tcode{execution::schedule}} \pnum \tcode{schedule} obtains a schedule sender\iref{exec.async.ops} from a scheduler. \pnum The name \tcode{schedule} denotes a customization point object. For a subexpression \tcode{sch}, the expression \tcode{schedule(sch)} is expression-equivalent to \tcode{sch.schedule()}. \pnum \mandates The type of \tcode{sch.schedule()} satisfies \libconcept{sender}. \pnum If the expression \begin{codeblock} get_completion_scheduler(get_env(sch.schedule())) == sch \end{codeblock} is ill-formed or evaluates to \tcode{false}, the behavior of calling \tcode{schedule(sch)} is undefined. \rSec3[exec.just]{\tcode{execution::just}, \tcode{execution::just_error}, \tcode{execution::just_stopped}} \pnum \tcode{just}, \tcode{just_error}, and \tcode{just_stopped} are sender factories whose asynchronous operations complete synchronously in their start operation with a value completion operation, an error completion operation, or a stopped completion operation, respectively. \pnum The names \tcode{just}, \tcode{just_error}, and \tcode{just_stopped} denote customization point objects. Let \exposid{just-cpo} be one of \tcode{just}, \tcode{just_error}, or \tcode{just_stopped}. For a pack of subexpressions \tcode{ts}, let \tcode{Ts} be the pack of types \tcode{decltype((ts))}. The expression \tcode{\exposid{just-cpo}(ts...)} is ill-formed if \begin{itemize} \item \tcode{(\exposconcept{movable-value} \&\&...)} is \tcode{false}, or \item \exposid{just-cpo} is \tcode{just_error} and \tcode{sizeof...(ts) == 1} is \tcode{false}, or \item \exposid{just-cpo} is \tcode{just_stopped} and \tcode{sizeof...(ts) == 0} is \tcode{false}. \end{itemize} Otherwise, it is expression-equivalent to \tcode{\exposid{make-sender}(\exposid{just-cpo}, \exposid{product-type}\{ts...\})}. For \tcode{just}, \tcode{just_error}, and \tcode{just_stopped}, let \exposid{set-cpo} be \tcode{set_value}, \tcode{set_error}, and \tcode{set_stopped}, respectively. The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \exposid{just-cpo} as follows: \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@<@\exposid{decayed-typeof}@<@\exposid{just-cpo}@>> : @\exposid{default-impls}@ { static constexpr auto @\exposid{start}@ = [](auto& state, auto& rcvr) noexcept -> void { auto& [...ts] = state; @\exposid{set-cpo}@(std::move(rcvr), std::move(ts)...); }; }; } \end{codeblock} \rSec3[exec.read.env]{\tcode{execution::read_env}} \pnum \tcode{read_env} is a sender factory for a sender whose asynchronous operation completes synchronously in its start operation with a value completion result equal to a value read from the receiver's associated environment. \pnum \tcode{read_env} is a customization point object. For some query object \tcode{q}, the expression \tcode{read_env(q)} is expression-equivalent to \tcode{\exposid{make-sender}(read_env, q)}. \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \tcode{read_env} as follows: \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@<@\exposid{decayed-typeof}@> : @\exposid{default-impls}@ { static constexpr auto start = [](auto query, auto& rcvr) noexcept -> void { @\exposid{TRY-SET-VALUE}@(rcvr, query(get_env(rcvr))); }; }; } \end{codeblock} \rSec2[exec.adapt]{Sender adaptors} \rSec3[exec.adapt.general]{General} \pnum Subclause \ref{exec.adapt} specifies a set of sender adaptors. \pnum The bitwise inclusive \logop{or} operator is overloaded for the purpose of creating sender chains. The adaptors also support function call syntax with equivalent semantics. \pnum Unless otherwise specified: \begin{itemize} \item A sender adaptor is prohibited from causing observable effects, apart from moving and copying its arguments, before the returned sender is connected with a receiver using \tcode{connect}, and \tcode{start} is called on the resulting operation state. \item A parent sender\iref{exec.async.ops} with a single child sender \tcode{sndr} has an associated attribute object equal to \tcode{\exposid{FWD-ENV}(get_env(sndr))}\iref{exec.fwd.env}. \item A parent sender with more than one child sender has an associated attributes object equal to \tcode{empty_env\{\}}. \item When a parent sender is connected to a receiver \tcode{rcvr}, any receiver used to connect a child sender has an associated environment equal to \tcode{\exposid{FWD-ENV}(get_env(rcvr))}. \item These requirements apply to any function that is selected by the implementation of the sender adaptor. \end{itemize} \pnum If a sender returned from a sender adaptor specified in \ref{exec.adapt} is specified to include \tcode{set_error_t(Err)} among its set of completion signatures where \tcode{decay_t} denotes the type \tcode{exception_ptr}, but the implementation does not potentially evaluate an error completion operation with an \tcode{exception_ptr} argument, the implementation is allowed to omit the \tcode{exception_ptr} error completion signature from the set. \rSec3[exec.adapt.obj]{Closure objects} \pnum A \defnadj{pipeable}{sender adaptor closure object} is a function object that accepts one or more \libconcept{sender} arguments and returns a \libconcept{sender}. For a pipeable sender adaptor closure object \tcode{c} and an expression \tcode{sndr} such that \tcode{decltype((sndr))} models \libconcept{sender}, the following expressions are equivalent and yield a \libconcept{sender}: \begin{codeblock} c(sndr) sndr | c \end{codeblock} Given an additional pipeable sender adaptor closure object \tcode{d}, the expression \tcode{c | d} produces another pipeable sender adaptor closure object \tcode{e}: \tcode{e} is a perfect forwarding call wrapper\iref{func.require} with the following properties: \begin{itemize} \item Its target object is an object \tcode{d2} of type \tcode{decltype(auto(d))} direct-non-list-initialized with \tcode{d}. \item It has one bound argument entity, an object \tcode{c2} of type \tcode{decltype(auto(c))} direct-non-list-initialized with \tcode{c}. \item Its call pattern is \tcode{d2(c2(arg))}, where arg is the argument used in a function call expression of \tcode{e}. \end{itemize} The expression \tcode{c | d} is well-formed if and only if the initializations of the state entities\iref{func.def} of \tcode{e} are all well-formed. \pnum An object \tcode{t} of type \tcode{T} is a pipeable sender adaptor closure object if \tcode{T} models \tcode{\libconcept{derived_from}>}, \tcode{T} has no other base classes of type \tcode{sender_adaptor_closure} for any other type \tcode{U}, and \tcode{T} does not satisfy \libconcept{sender}. \pnum The template parameter \tcode{D} for \tcode{sender_adaptor_closure} can be an incomplete type. Before any expression of type \cv{} \tcode{D} appears as an operand to the \tcode{|} operator, \tcode{D} shall be complete and model \tcode{\libconcept{derived_from}>}. The behavior of an expression involving an object of type \cv{} \tcode{D} as an operand to the \tcode{|} operator is undefined if overload resolution selects a program-defined \tcode{operator|} function. \pnum A \defnadj{pipeable}{sender adaptor object} is a customization point object that accepts a \libconcept{sender} as its first argument and returns a \libconcept{sender}. If a pipeable sender adaptor object accepts only one argument, then it is a pipeable sender adaptor closure object. \pnum If a pipeable sender adaptor object adaptor accepts more than one argument, then let \tcode{sndr} be an expression such that \tcode{decltype((sndr))} models \libconcept{sender}, let \tcode{args...} be arguments such that \tcode{adaptor(sndr, args...)} is a well-formed expression as specified below, and let \tcode{BoundArgs} be a pack that denotes \tcode{decltype(auto(args))...}. The expression \tcode{adaptor(args...)} produces a pipeable sender adaptor closure object \tcode{f} that is a perfect forwarding call wrapper with the following properties: \begin{itemize} \item Its target object is a copy of adaptor. \item Its bound argument entities \tcode{bound_args} consist of objects of types \tcode{BoundArgs...} direct-non-list-initialized with \tcode{std::forward(args)...}, respectively. \item Its call pattern is \tcode{adaptor(rcvr, bound_args...)}, where \tcode{rcvr} is the argument used in a function call expression of \tcode{f}. \end{itemize} The expression \tcode{adaptor(args...)} is well-formed if and only if the initializations of the bound argument entities of the result, as specified above, are all well-formed. \rSec3[exec.starts.on]{\tcode{execution::starts_on}} \pnum \tcode{starts_on} adapts an input sender into a sender that will start on an execution agent belonging to a particular scheduler's associated execution resource. \pnum The name \tcode{starts_on} denotes a customization point object. For subexpressions \tcode{sch} and \tcode{sndr}, if \tcode{decltype((\newline sch))} does not satisfy \libconcept{scheduler}, or \tcode{decltype((sndr))} does not satisfy \libconcept{sender}, \tcode{starts_on(sch, sndr)} is ill-formed. \pnum Otherwise, the expression \tcode{starts_on(sch, sndr)} is expression-equivalent to: \begin{codeblock} transform_sender( @\exposid{query-or-default}@(get_domain, sch, default_domain()), @\exposid{make-sender}@(starts_on, sch, sndr)) \end{codeblock} except that \tcode{sch} is evaluated only once. \pnum Let \tcode{out_sndr} and \tcode{env} be subexpressions such that \tcode{OutSndr} is \tcode{decltype((out_sndr))}. If \tcode{\exposconcept{sender-for}} is \tcode{false}, then the expressions \tcode{starts_on.transform_env(out_sndr, env)} and\linebreak \tcode{starts_on.transform_sender(out_sndr, env)} are ill-formed; otherwise \begin{itemize} \item \tcode{starts_on.transform_env(out_sndr, env)} is equivalent to: \begin{codeblock} auto&& [_, sch, _] = out_sndr; return @\exposid{JOIN-ENV}@(@\exposid{SCHED-ENV}@(sch), @\exposid{FWD-ENV}@(env)); \end{codeblock} \item \tcode{starts_on.transform_sender(out_sndr, env)} is equivalent to: \begin{codeblock} auto&& [_, sch, sndr] = out_sndr; return let_value( schedule(sch), [sndr = std::forward_like(sndr)]() mutable noexcept(is_nothrow_move_constructible_v>) { return std::move(sndr); }); \end{codeblock} \end{itemize} \pnum Let \tcode{out_sndr} be a subexpression denoting a sender returned from \tcode{starts_on(sch, sndr)} or one equal to such, and let \tcode{OutSndr} be the type \tcode{decltype((out_sndr))}. Let \tcode{out_rcvr} be a subexpression denoting a receiver that has an environment of type \tcode{Env} such that \tcode{\libconcept{sender_in}} is \tcode{true}. Let \tcode{op} be an lvalue referring to the operation state that results from connecting \tcode{out_sndr} with \tcode{out_rcvr}. Calling \tcode{start(op)} shall start \tcode{sndr} on an execution agent of the associated execution resource of \tcode{sch}. If scheduling onto \tcode{sch} fails, an error completion on \tcode{out_rcvr} shall be executed on an unspecified execution agent. \rSec3[exec.continues.on]{\tcode{execution::continues_on}} \pnum \tcode{continues_on} adapts a sender into one that completes on the specified scheduler. \pnum The name \tcode{continues_on} denotes a pipeable sender adaptor object. For subexpressions \tcode{sch} and \tcode{sndr}, if \tcode{decltype((sch))} does not satisfy \libconcept{scheduler}, or \tcode{decltype((sndr))} does not satisfy \libconcept{sender}, \tcode{continues_on(sndr, sch)} is ill-formed. \pnum Otherwise, the expression \tcode{continues_on(sndr, sch)} is expression-equivalent to: \begin{codeblock} transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(continues_on, sch, sndr)) \end{codeblock} except that \tcode{sndr} is evaluated only once. \pnum The exposition-only class template \exposid{impls-for} is specialized for \tcode{continues_on_t} as follows: \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@ : @\exposid{default-impls}@ { static constexpr auto @\exposid{get-attrs}@ = [](const auto& data, const auto& child) noexcept -> decltype(auto) { return @\exposid{JOIN-ENV}@(@\exposid{SCHED-ATTRS}@(data), @\exposid{FWD-ENV}@(get_env(child))); }; }; } \end{codeblock} \pnum Let \tcode{sndr} and \tcode{env} be subexpressions such that \tcode{Sndr} is \tcode{decltype((sndr))}. If \tcode{\exposconcept{sender-for}} is \tcode{false}, then the expression \tcode{continues_on.transform_sender(sndr, env)} is ill-formed; otherwise, it is equal to: \begin{codeblock} auto [_, data, child] = sndr; return schedule_from(std::move(data), std::move(child)); \end{codeblock} \begin{note} This causes the \tcode{continues_on(sndr, sch)} sender to become \tcode{schedule_from(sch, sndr)} when it is connected with a receiver whose execution domain does not customize \tcode{continues_on}. \end{note} \pnum Let \tcode{out_sndr} be a subexpression denoting a sender returned from \tcode{continues_on(sndr, sch)} or one equal to such, and let \tcode{OutSndr} be the type \tcode{decltype((out_sndr))}. Let \tcode{out_rcvr} be a subexpression denoting a receiver that has an environment of type \tcode{Env} such that \tcode{\libconcept{sender_in}} is \tcode{true}. Let \tcode{op} be an lvalue referring to the operation state that results from connecting \tcode{out_sndr} with \tcode{out_rcvr}. Calling \tcode{start(op)} shall start \tcode{sndr} on the current execution agent and execute completion operations on \tcode{out_rcvr} on an execution agent of the execution resource associated with \tcode{sch}. If scheduling onto \tcode{sch} fails, an error completion on \tcode{out_rcvr} shall be executed on an unspecified execution agent. \rSec3[exec.schedule.from]{\tcode{execution::schedule_from}} \pnum \tcode{schedule_from} schedules work dependent on the completion of a sender onto a scheduler's associated execution resource. \begin{note} \tcode{schedule_from} is not meant to be used in user code; it is used in the implementation of \tcode{continues_on}. \end{note} \pnum The name \tcode{schedule_from} denotes a customization point object. For some subexpressions \tcode{sch} and \tcode{sndr}, let \tcode{Sch} be \tcode{decltype((sch))} and \tcode{Sndr} be \tcode{decltype((sndr))}. If \tcode{Sch} does not satisfy \libconcept{scheduler}, or \tcode{Sndr} does not satisfy \libconcept{sender}, \tcode{schedule_from(sch, sndr)} is ill-formed. \pnum Otherwise, the expression \tcode{schedule_from(sch, sndr)} is expression-equivalent to: \begin{codeblock} transform_sender( @\exposid{query-or-default}@(get_domain, sch, default_domain()), @\exposid{make-sender}@(schedule_from, sch, sndr)) \end{codeblock} except that \tcode{sch} is evaluated only once. \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \tcode{schedule_from_t} as follows: \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@ : @\exposid{default-impls}@ { static constexpr auto @\exposid{get-attrs}@ = @\seebelow@; static constexpr auto @\exposid{get-state}@ = @\seebelow@; static constexpr auto @\exposid{complete}@ = @\seebelow@; }; } \end{codeblock} \pnum The member \tcode{\exposid{impls-for}::\exposid{get-attrs}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [](const auto& data, const auto& child) noexcept -> decltype(auto) { return @\exposid{JOIN-ENV}@(@\exposid{SCHED-ATTRS}@(data), @\exposid{FWD-ENV}@(get_env(child))); } \end{codeblock} \pnum The member \tcode{\exposid{impls-for}::\exposid{get-state}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [](Sndr&& sndr, Rcvr& rcvr) noexcept(@\seebelow@) requires @\libconcept{sender_in}@<@\exposid{child-type}@, env_of_t> { auto& [_, sch, child] = sndr; using sched_t = decltype(auto(sch)); using variant_t = @\seebelow@; using receiver_t = @\seebelow@; using operation_t = connect_result_t, receiver_t>; constexpr bool nothrow = noexcept(connect(schedule(sch), receiver_t{nullptr})); struct @\exposid{state-type}@ { Rcvr& @\exposid{rcvr}@; // \expos variant_t @\exposid{async-result}@; // \expos operation_t @\exposid{op-state}@; // \expos explicit @\exposid{state-type}@(sched_t sch, Rcvr& rcvr) noexcept(nothrow) : @\exposid{rcvr}@(rcvr), @\exposid{op-state}@(connect(schedule(sch), receiver_t{this})) {} }; return @\exposid{state-type}@{sch, rcvr}; } \end{codeblock} \pnum Objects of the local class \exposid{state-type} can be used to initialize a structured binding. \pnum Let \tcode{Sigs} be a pack of the arguments to the \tcode{completion_signatures} specialization named by \tcode{completion_signatures_of_t, env_of_t>}. Let \exposid{as-tuple} be an alias template that transforms a completion signature \tcode{Tag(Args...)} into the tuple specialization \tcode{\exposid{decayed-tuple}}. Then \tcode{variant_t} denotes the type \tcode{variant...>}, except with duplicate types removed. \pnum \tcode{receiver_t} is an alias for the following exposition-only class: \begin{codeblock} namespace std::execution { struct @\exposid{receiver-type}@ { using receiver_concept = receiver_t; @\exposid{state-type}@* @\exposid{state}@; // \expos void set_value() && noexcept { visit( [this](Tuple& result) noexcept -> void { if constexpr (!@\libconcept{same_as}@) { auto& [tag, ...args] = result; tag(std::move(@\exposid{state}@->@\exposid{rcvr}@), std::move(args)...); } }, @\exposid{state}@->@\exposid{async-result}@); } template void set_error(Error&& err) && noexcept { execution::set_error(std::move(@\exposid{state}@->@\exposid{rcvr}@), std::forward(err)); } void set_stopped() && noexcept { execution::set_stopped(std::move(@\exposid{state}@->@\exposid{rcvr}@)); } decltype(auto) get_env() const noexcept { return @\exposid{FWD-ENV}@(execution::get_env(@\exposid{state}@->@\exposid{rcvr}@)); } }; } \end{codeblock} \pnum The expression in the \tcode{noexcept} clause of the lambda is \tcode{true} if the construction of the returned \exposid{state-type} object is not potentially throwing; otherwise, \tcode{false}. \pnum The member \tcode{\exposid{impls-for}::\exposid{complete}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [](auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void { using result_t = @\exposid{decayed-tuple}@; constexpr bool nothrow = is_nothrow_constructible_v; @\exposid{TRY-EVAL}@(rcvr, [&]() noexcept(nothrow) { state.@\exposid{async-result}@.template emplace(Tag(), std::forward(args)...); }()); if (state.@\exposid{async-result}@.valueless_by_exception()) return; if (state.@\exposid{async-result}@.index() == 0) return; start(state.@\exposid{op-state}@); }; \end{codeblock} \pnum Let \tcode{out_sndr} be a subexpression denoting a sender returned from \tcode{schedule_from(sch, sndr)} or one equal to such, and let \tcode{OutSndr} be the type \tcode{decltype((out_sndr))}. Let \tcode{out_rcvr} be a subexpression denoting a receiver that has an environment of type \tcode{Env} such that \tcode{\libconcept{sender_in}} is \tcode{true}. Let \tcode{op} be an lvalue referring to the operation state that results from connecting \tcode{out_sndr} with \tcode{out_rcvr}. Calling \tcode{start(op)} shall start \tcode{sndr} on the current execution agent and execute completion operations on \tcode{out_rcvr} on an execution agent of the execution resource associated with \tcode{sch}. If scheduling onto \tcode{sch} fails, an error completion on \tcode{out_rcvr} shall be executed on an unspecified execution agent. \rSec3[exec.on]{\tcode{execution::on}} \pnum The \tcode{on} sender adaptor has two forms: \begin{itemize} \item \tcode{on(sch, sndr)}, which starts a sender \tcode{sndr} on an execution agent belonging to a scheduler \tcode{sch}'s associated execution resource and that, upon \tcode{sndr}'s completion, transfers execution back to the execution resource on which the \tcode{on} sender was started. \item \tcode{on(sndr, sch, closure)}, which upon completion of a sender \tcode{sndr}, transfers execution to an execution agent belonging to a scheduler \tcode{sch}'s associated execution resource, then executes a sender adaptor closure \tcode{closure} with the async results of the sender, and that then transfers execution back to the execution resource on which \tcode{sndr} completed. \end{itemize} \pnum The name \tcode{on} denotes a pipeable sender adaptor object. For subexpressions \tcode{sch} and \tcode{sndr}, \tcode{on(sch, sndr)} is ill-formed if any of the following is true: \begin{itemize} \item \tcode{decltype((sch))} does not satisfy \libconcept{scheduler}, or \item \tcode{decltype((sndr))} does not satisfy \libconcept{sender} and \tcode{sndr} is not a pipeable sender adaptor closure object\iref{exec.adapt.obj}, or \item \tcode{decltype((sndr))} satisfies \libconcept{sender} and \tcode{sndr }is also a pipeable sender adaptor closure object. \end{itemize} \pnum Otherwise, if \tcode{decltype((sndr))} satisfies \libconcept{sender}, the expression \tcode{on(sch, sndr)} is expression-equivalent to: \begin{codeblock} transform_sender( @\exposid{query-or-default}@(get_domain, sch, default_domain()), @\exposid{make-sender}@(on, sch, sndr)) \end{codeblock} except that \tcode{sch} is evaluated only once. \pnum For subexpressions \tcode{sndr}, \tcode{sch}, and \tcode{closure}, if \begin{itemize} \item \tcode{decltype((sch))} does not satisfy \libconcept{scheduler}, or \item \tcode{decltype((sndr))} does not satisfy \libconcept{sender}, or \item \tcode{closure} is not a pipeable sender adaptor closure object\iref{exec.adapt.obj}, \end{itemize} the expression \tcode{on(sndr, sch, closure)} is ill-formed; otherwise, it is expression-equivalent to: \begin{codeblock} transform_sender( @\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(on, @\exposid{product-type}@{sch, closure}, sndr)) \end{codeblock} except that \tcode{sndr} is evaluated only once. \pnum Let \tcode{out_sndr} and \tcode{env} be subexpressions, let \tcode{OutSndr} be \tcode{decltype((out_sndr))}, and let \tcode{Env} be \tcode{decltype((\linebreak env))}. If \tcode{\exposconcept{sender-for}} is \tcode{false}, then the expressions \tcode{on.transform_env(out_sndr, env)} and \tcode{on.transform_sender(out_sndr, env)} are ill-formed. \pnum Otherwise: Let \exposid{not-a-scheduler} be an unspecified empty class type, and let \exposid{not-a-sender} be the exposition-only type: \begin{codeblock} struct @\exposid{not-a-sender}@ { using sender_concept = sender_t; auto get_completion_signatures(auto&&) const { return @\seebelow@; } }; \end{codeblock} where the member function \tcode{get_completion_signatures} returns an object of a type that is not a specialization of the \tcode{completion_signatures} class template. \pnum The expression \tcode{on.transform_env(out_sndr, env)} has effects equivalent to: \begin{codeblock} auto&& [_, data, _] = out_sndr; if constexpr (@\libconcept{scheduler}@) { return @\exposid{JOIN-ENV}@(@\exposid{SCHED-ENV}@(std::forward_like(data)), @\exposid{FWD-ENV}@(std::forward(env))); } else { return std::forward(env); } \end{codeblock} \pnum The expression \tcode{on.transform_sender(out_sndr, env)} has effects equivalent to: \begin{codeblock} auto&& [_, data, child] = out_sndr; if constexpr (@\libconcept{scheduler}@) { auto orig_sch = @\exposid{query-with-default}@(get_scheduler, env, @\exposid{not-a-scheduler}@()); if constexpr (@\libconcept{same_as}@) { return @\exposid{not-a-sender}@{}; } else { return continues_on( starts_on(std::forward_like(data), std::forward_like(child)), std::move(orig_sch)); } } else { auto& [sch, closure] = data; auto orig_sch = @\exposid{query-with-default}@( get_completion_scheduler, get_env(child), @\exposid{query-with-default}@(get_scheduler, env, @\exposid{not-a-scheduler}@())); if constexpr (@\libconcept{same_as}@) { return @\exposid{not-a-sender}@{}; } else { return @\exposid{write-env}@( continues_on( std::forward_like(closure)( continues_on( @\exposid{write-env}@(std::forward_like(child), @\exposid{SCHED-ENV}@(orig_sch)), sch)), orig_sch), @\exposid{SCHED-ENV}@(sch)); } } \end{codeblock} \pnum \recommended Implementations should use the return type of \tcode{\exposid{not-a-sender}::get_completion_signatures} to inform users that their usage of \tcode{on} is incorrect because there is no available scheduler onto which to restore execution. \pnum Let \tcode{out_sndr} be a subexpression denoting a sender returned from \tcode{on(sch, sndr)} or one equal to such, and let \tcode{OutSndr} be the type \tcode{decltype((out_sndr))}. Let \tcode{out_rcvr} be a subexpression denoting a receiver that has an environment of type \tcode{Env} such that \tcode{\libconcept{sender_in}} is \tcode{true}. Let \tcode{op} be an lvalue referring to the operation state that results from connecting \tcode{out_sndr} with \tcode{out_rcvr}. Calling \tcode{start(op)} shall \begin{itemize} \item remember the current scheduler, \tcode{get_scheduler(get_env(rcvr))}; \item start \tcode{sndr} on an execution agent belonging to sch's associated execution resource; \item upon \tcode{sndr}'s completion, transfer execution back to the execution resource associated with the scheduler remembered in step 1; and \item forward \tcode{sndr}'s async result to \tcode{out_rcvr}. \end{itemize} If any scheduling operation fails, an error completion on \tcode{out_rcvr} shall be executed on an unspecified execution agent. \pnum Let \tcode{out_sndr} be a subexpression denoting a sender returned from \tcode{on(sndr, sch, closure)} or one equal to such, and let \tcode{OutSndr} be the type \tcode{decltype((out_sndr))}. Let \tcode{out_rcvr} be a subexpression denoting a receiver that has an environment of type \tcode{Env} such that \tcode{\libconcept{sender_in}} is \tcode{true}. Let \tcode{op} be an lvalue referring to the operation state that results from connecting \tcode{out_sndr} with \tcode{out_rcvr}. Calling \tcode{start(op)} shall \begin{itemize} \item remember the current scheduler, which is the first of the following expressions that is well-formed: \begin{itemize} \item \tcode{get_completion_scheduler(get_env(sndr))} \item \tcode{get_scheduler(get_env(rcvr))}; \end{itemize} \item start \tcode{sndr} on the current execution agent; \item upon \tcode{sndr}'s completion, transfer execution to an agent owned by sch's associated execution resource; \item forward \tcode{sndr}'s async result as if by connecting and starting a sender \tcode{closure(S)}, where \tcode{S} is a sender that completes synchronously with \tcode{sndr}'s async result; and \item upon completion of the operation started in the previous step, transfer execution back to the execution resource associated with the scheduler remembered in step 1 and forward the operation's async result to \tcode{out_rcvr}. \end{itemize} If any scheduling operation fails, an error completion on \tcode{out_rcvr} shall be executed on an unspecified execution agent. \rSec3[exec.then]{\tcode{execution::then}, \tcode{execution::upon_error}, \tcode{execution::upon_stopped}} \pnum \tcode{then} attaches an invocable as a continuation for an input sender's value completion operation. \tcode{upon_error} and \tcode{upon_stopped} do the same for the error and stopped completion operations, respectively, sending the result of the invocable as a value completion. \pnum The names \tcode{then}, \tcode{upon_error}, and \tcode{upon_stopped} denote pipeable sender adaptor objects. Let the expression \exposid{then-cpo} be one of \tcode{then}, \tcode{upon_error}, or \tcode{upon_stopped}. For subexpressions \tcode{sndr} and \tcode{f}, if \tcode{decltype((sndr))} does not satisfy \libconcept{sender}, or \tcode{decltype((f))} does not satisfy \exposconcept{movable-value}, \tcode{\exposid{then-cpo}(\linebreak sndr, f) }is ill-formed. \pnum Otherwise, the expression \tcode{\exposid{then-cpo}(sndr, f)} is expression-equivalent to: \begin{codeblock} transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(@\exposid{then-cpo}@, f, sndr)) \end{codeblock} except that \tcode{sndr} is evaluated only once. \pnum For \tcode{then}, \tcode{upon_error}, and \tcode{upon_stopped}, let \exposid{set-cpo} be \tcode{set_value}, \tcode{set_error}, and \tcode{set_stopped}, respectively. The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \exposid{then-cpo} as follows: \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@<@\exposid{decayed-typeof}@<@\exposid{then-cpo}@>> : @\exposid{default-impls}@ { static constexpr auto @\exposid{complete}@ = [] (auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void { if constexpr (@\libconcept{same_as}@>) { @\exposid{TRY-SET-VALUE}@(rcvr, invoke(std::move(fn), std::forward(args)...)); } else { Tag()(std::move(rcvr), std::forward(args)...); } }; }; } \end{codeblock} \pnum The expression \tcode{\exposid{then-cpo}(sndr, f)} has undefined behavior unless it returns a sender\tcode{out_sndr} that \begin{itemize} \item invokes \tcode{f} or a copy of such with the value, error, or stopped result datums of \tcode{sndr} for \tcode{then}, \tcode{upon_error}, and \tcode{upon_stopped}, respectively, using the result value of \tcode{f} as \tcode{out_sndr}'s value completion, and \item forwards all other completion operations unchanged. \end{itemize} \rSec3[exec.let]{\tcode{execution::let_value}, \tcode{execution::let_error}, \tcode{execution::let_stopped}} \pnum \tcode{let_value}, \tcode{let_error}, and \tcode{let_stopped} transform a sender's value, error, and stopped completions, respectively, into a new child asynchronous operation by passing the sender's result datums to a user-specified callable, which returns a new sender that is connected and started. \pnum For \tcode{let_value}, \tcode{let_error}, and \tcode{let_stopped}, let \exposid{set-cpo} be \tcode{set_value}, \tcode{set_error}, and \tcode{set_stopped}, respectively. Let the expression \exposid{let-cpo} be one of \tcode{let_value}, \tcode{let_error}, or \tcode{let_stopped}. For a subexpression \tcode{sndr}, let \tcode{\exposid{let-env}(sndr)} be expression-equivalent to the first well-formed expression below: \begin{itemize} \item \tcode{SCHED-ENV(get_completion_scheduler<\exposid{decayed-typeof}<\exposid{set-cpo}>>(get_env(sndr)))} \item \tcode{\exposid{MAKE-ENV}(get_domain, get_domain(get_env(sndr)))} \item \tcode{(void(sndr), empty_env\{\})} \end{itemize} \pnum The names \tcode{let_value}, \tcode{let_error}, and \tcode{let_stopped} denote pipeable sender adaptor objects. For subexpressions \tcode{sndr} and \tcode{f}, let \tcode{F} be the decayed type of \tcode{f}. If \tcode{decltype((sndr))} does not satisfy \libconcept{sender} or if \tcode{decltype((f))} does not satisfy \exposconcept{movable-value}, the expression \tcode{\exposid{let-cpo}(sndr, f)} is ill-formed. If \tcode{F} does not satisfy \libconcept{invocable}, the expression \tcode{let_stopped(sndr, f)} is ill-formed. \pnum Otherwise, the expression \tcode{\exposid{let-cpo}(sndr, f)} is expression-equivalent to: \begin{codeblock} transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(@\exposid{let-cpo}@, f, sndr)) \end{codeblock} except that \tcode{sndr} is evaluated only once. \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \exposid{let-cpo} as follows: \begin{codeblock} namespace std::execution { template void @\exposid{let-bind}@(State& state, Rcvr& rcvr, Args&&... args); // \expos template<> struct @\exposid{impls-for}@<@\exposid{decayed-typeof}@<@\exposid{let-cpo}@>> : @\exposid{default-impls}@ { static constexpr auto @\exposid{get-state}@ = @\seebelow@; static constexpr auto @\exposid{complete}@ = @\seebelow@; }; } \end{codeblock} \pnum Let \exposid{receiver2} denote the following exposition-only class template: \begin{codeblock} namespace std::execution { template struct @\exposid{receiver2}@ { using receiver_concept = receiver_t; template void set_value(Args&&... args) && noexcept { execution::set_value(std::move(@\exposid{rcvr}@), std::forward(args)...); } template void set_error(Error&& err) && noexcept { execution::set_error(std::move(@\exposid{rcvr}@), std::forward(err)); } void set_stopped() && noexcept { execution::set_stopped(std::move(@\exposid{rcvr}@)); } decltype(auto) get_env() const noexcept { return @\exposid{JOIN-ENV}@(@\exposid{env}@, @\exposid{FWD-ENV}@(execution::get_env(@\exposid{rcvr}@))); } Rcvr& @\exposid{rcvr}@; // \expos Env @\exposid{env}@; // \expos }; } \end{codeblock} \pnum \tcode{\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{get-state}} is initialized with a callable object equivalent to the following: \begin{codeblock} [](Sndr&& sndr, Rcvr& rcvr) requires @\seebelow@ { auto& [_, fn, child] = sndr; using fn_t = decay_t; using env_t = decltype(@\exposid{let-env}@(child)); using args_variant_t = @\seebelow@; using ops2_variant_t = @\seebelow@; struct @\exposid{state-type}@ { fn_t @\exposid{fn}@; // \expos env_t @\exposid{env}@; // \expos args_variant_t @\exposid{args}@; // \expos ops2_variant_t @\exposid{ops2}@; // \expos }; return @\exposid{state-type}@{std::forward_like(fn), @\exposid{let-env}@(child), {}, {}}; } \end{codeblock} \pnum Let \tcode{Sigs} be a pack of the arguments to the \tcode{completion_signatures} specialization named by \tcode{completion_signatures_of_t<\exposid{child-type}, env_of_t>}. Let \tcode{LetSigs} be a pack of those types in \tcode{Sigs} with a return type of \tcode{\exposid{decayed-typeof}<\exposid{set-cpo}>}. Let \exposid{as-tuple} be an alias template such that \tcode{\exposid{as-tuple}<\linebreak Tag(Args...)>} denotes the type \tcode{\exposid{decayed-tuple}}. Then \tcode{args_variant_t} denotes the type \tcode{variant...>} except with duplicate types removed. \pnum Given a type \tcode{Tag} and a pack \tcode{Args}, let \exposid{as-sndr2} be an alias template such that \tcode{\exposid{as-sndr2}} denotes the type \tcode{\exposid{call-result-t}\&...>}. Then \tcode{ops2_variant_t} denotes the type \begin{codeblock} variant, @\exposid{receiver2}@>...> \end{codeblock} except with duplicate types removed. \pnum The \grammarterm{requires-clause} constraining the above lambda is satisfied if and only if the types \tcode{args_variant_t} and \tcode{ops2_variant_t} are well-formed. \pnum The exposition-only function template \exposid{let-bind} has effects equivalent to: \begin{codeblock} using args_t = @\exposid{decayed-tuple}@; auto mkop2 = [&] { return connect( apply(std::move(state.fn), state.args.template emplace(std::forward(args)...)), @\exposid{receiver2}@{rcvr, std::move(state.env)}); }; start(state.ops2.template emplace(@\exposid{emplace-from}@{mkop2})); \end{codeblock} \pnum \tcode{\exposid{impls-for}<\exposid{decayed-typeof}>::\exposid{complete}} is initialized with a callable object equivalent to the following: \begin{codeblock} [] (auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void { if constexpr (@\libconcept{same_as}@>) { @\exposid{TRY-EVAL}@(rcvr, @\exposid{let-bind}@(state, rcvr, std::forward(args)...)); } else { Tag()(std::move(rcvr), std::forward(args)...); } } \end{codeblock} \pnum Let \tcode{sndr} and \tcode{env} be subexpressions, and let \tcode{Sndr} be \tcode{decltype((sndr))}. If \tcode{\exposconcept{sender-for}>} is \tcode{false}, then the expression \tcode{\exposid{let-cpo}.transform_env(sndr, env)} is ill-formed. Otherwise, it is equal to \tcode{\exposid{JOIN-ENV}(\exposid{let-env}(sndr), \exposid{FWD-ENV}(env))}. \pnum Let the subexpression \tcode{out_sndr} denote the result of the invocation \tcode{\exposid{let-cpo}(sndr, f)} or an object equal to such, and let the subexpression \tcode{rcvr} denote a receiver such that the expression \tcode{connect(out_sndr, rcvr)} is well-formed. The expression \tcode{connect(out_sndr, rcvr)} has undefined behavior unless it creates an asynchronous operation\iref{exec.async.ops} that, when started: \begin{itemize} \item invokes \tcode{f} when \exposid{set-cpo} is called with \tcode{sndr}'s result datums, \item makes its completion dependent on the completion of a sender returned by \tcode{f}, and \item propagates the other completion operations sent by \tcode{sndr}. \end{itemize} \rSec3[exec.bulk]{\tcode{execution::bulk}} \pnum \tcode{bulk} runs a task repeatedly for every index in an index space. The name \tcode{bulk} denotes a pipeable sender adaptor object. For subexpressions \tcode{sndr}, \tcode{shape}, and \tcode{f}, let \tcode{Shape} be \tcode{decltype(auto(shape))}. If \begin{itemize} \item \tcode{decltype((sndr))} does not satisfy \libconcept{sender}, or \item \tcode{Shape} does not satisfy \libconcept{integral}, or \item \tcode{decltype((f))} does not satisfy \exposconcept{movable-value}, \end{itemize} \tcode{bulk(sndr, shape, f)} is ill-formed. \pnum Otherwise, the expression \tcode{bulk(sndr, shape, f)} is expression-equivalent to: \begin{codeblock} transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(bulk, @\exposid{product-type}@{shape, f}, sndr)) \end{codeblock} except that \tcode{sndr} is evaluated only once. \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \tcode{bulk_t} as follows: \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@ : @\exposid{default-impls}@ { static constexpr auto @\exposid{complete}@ = @\seebelow@; }; } \end{codeblock} \pnum The member \tcode{\exposid{impls-for}::\exposid{complete}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [] (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void requires @\seebelow@ { if constexpr (@\libconcept{same_as}@) { auto& [shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), args...)); @\exposid{TRY-EVAL}@(rcvr, [&]() noexcept(nothrow) { for (decltype(auto(shape)) i = 0; i < shape; ++i) { f(auto(i), args...); } Tag()(std::move(rcvr), std::forward(args)...); }()); } else { Tag()(std::move(rcvr), std::forward(args)...); } } \end{codeblock} \pnum The expression in the \grammarterm{requires-clause} of the lambda above is \tcode{true} if and only if \tcode{Tag} denotes a type other than \tcode{set_value_t} or if the expression \tcode{f(auto(shape), args...)} is well-formed. \pnum Let the subexpression \tcode{out_sndr} denote the result of the invocation \tcode{bulk(sndr, shape, f)} or an object equal to such, and let the subexpression \tcode{rcvr} denote a receiver such that the expression \tcode{connect(out_sndr, rcvr)} is well-formed. The expression \tcode{connect(out_sndr, rcvr)} has undefined behavior unless it creates an asynchronous operation\iref{exec.async.ops} that, when started, \begin{itemize} \item on a value completion operation, invokes \tcode{f(i, args...)} for every \tcode{i} of type \tcode{Shape} from \tcode{0} to \tcode{shape}, where \tcode{args} is a pack of lvalue subexpressions referring to the value completion result datums of the input sender, and \item propagates all completion operations sent by \tcode{sndr}. \end{itemize} \rSec3[exec.split]{\tcode{execution::split}} \pnum \tcode{split} adapts an arbitrary sender into a sender that can be connected multiple times. \pnum Let \exposid{split-env} be the type of an environment such that, given an instance \tcode{env}, the expression \tcode{get_stop_token(env)} is well-formed and has type \tcode{inplace_stop_token.} \pnum The name \tcode{split} denotes a pipeable sender adaptor object. For a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype((sndr))}. If \tcode{\libconcept{sender_in}} is \tcode{false}, \tcode{split(sndr)} is ill-formed. \pnum Otherwise, the expression \tcode{split(sndr)} is expression-equivalent to: \begin{codeblock} transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(split, {}, sndr)) \end{codeblock} except that \tcode{sndr} is evaluated only once. \begin{note} The default implementation of \tcode{transform_sender} will have the effect of connecting the sender to a receiver. It will return a sender with a different tag type. \end{note} \pnum Let \exposid{local-state} denote the following exposition-only class template: \begin{codeblock} namespace std::execution { struct @\exposid{local-state-base}@ { // \expos virtual ~@\exposid{local-state-base}@() = default; virtual void @\exposid{notify}@() noexcept = 0; // \expos }; template struct @\exposid{local-state}@ : @\exposid{local-state-base}@ { // \expos using @\exposid{on-stop-callback}@ = // \expos stop_callback_for_t>, @\exposid{on-stop-request}@>; @\exposid{local-state}@(Sndr&& sndr, Rcvr& rcvr) noexcept; ~@\exposid{local-state}@(); void @\exposid{notify}@() noexcept override; private: optional<@\exposid{on-stop-callback}@> @\exposid{on_stop}@; // \expos @\exposid{shared-state}@* @\exposid{sh_state}@; // \expos Rcvr* @\exposid{rcvr}@; // \expos }; } \end{codeblock} \begin{itemdecl} @\exposid{local-state}@(Sndr&& sndr, Rcvr& rcvr) noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} auto& [_, data, _] = sndr; this->@\exposid{sh_state}@ = data.sh_state.get(); this->@\exposid{sh_state}@->@\exposid{inc-ref}@(); this->@\exposid{rcvr}@ = addressof(rcvr); \end{codeblock} \end{itemdescr} \begin{itemdecl} ~@\exposid{local-state}@(); \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} sh_state->@\exposid{dec-ref}@(); \end{codeblock} \end{itemdescr} \begin{itemdecl} void @\exposid{notify}@() noexcept override; \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} @\exposid{on_stop}@.reset(); visit( [this](const auto& tupl) noexcept -> void { apply( [this](auto tag, const auto&... args) noexcept -> void { tag(std::move(*@\exposid{rcvr}@), args...); }, tupl); }, @\exposid{sh_state}@->result); \end{codeblock} \end{itemdescr} \pnum Let \exposid{split-receiver} denote the following exposition-only class template: \begin{codeblock} namespace std::execution { template struct @\exposid{split-receiver}@ { // \expos using receiver_concept = receiver_t; template void @\exposid{complete}@(Tag, Args&&... args) noexcept { // \expos using tuple_t = @\exposid{decayed-tuple}@; try { @\exposid{sh_state}@->result.template emplace(Tag(), std::forward(args)...); } catch (...) { using tuple_t = tuple; @\exposid{sh_state}@->result.template emplace(set_error, current_exception()); } @\exposid{sh_state}@->notify(); } template void set_value(Args&&... args) && noexcept { @\exposid{complete}@(execution::set_value, std::forward(args)...); } template void set_error(Error&& err) && noexcept { @\exposid{complete}@(execution::set_error, std::forward(err)); } void set_stopped() && noexcept { @\exposid{complete}@(execution::set_stopped); } struct @\exposid{env}@ { // \expos @\exposid{shared-state}@* @\exposid{sh-state}@; // \expos inplace_stop_token query(get_stop_token_t) const noexcept { return @\exposid{sh-state}@->stop_src.get_token(); } }; @\exposid{env}@ get_env() const noexcept { return @\exposid{env}@{@\exposid{sh_state}@}; } @\exposid{shared-state}@* @\exposid{sh_state}@; // \expos }; } \end{codeblock} \pnum Let \exposid{shared-state} denote the following exposition-only class template: \begin{codeblock} namespace std::execution { template struct @\exposid{shared-state}@ { using @\exposid{variant-type}@ = @\seebelow@; // \expos using @\exposid{state-list-type}@ = @\seebelow@; // \expos explicit @\exposid{shared-state}@(Sndr&& sndr); void @\exposid{start-op}@() noexcept; // \expos void @\exposid{notify}@() noexcept; // \expos void @\exposid{inc-ref}@() noexcept; // \expos void @\exposid{dec-ref}@() noexcept; // \expos inplace_stop_source @\exposid{stop_src}@{}; // \expos @\exposid{variant-type}@ @\exposid{result}@{}; // \expos @\exposid{state-list-type}@ @\exposid{waiting_states}@; // \expos atomic @\exposid{completed}@{false}; // \expos atomic @\exposid{ref_count}@{1}; // \expos connect_result_t> @\exposid{op_state}@; // \expos }; } \end{codeblock} \pnum Let \tcode{Sigs} be a pack of the arguments to the \tcode{completion_signatures} specialization named by \tcode{completion_signatures_of_t}. For type \tcode{Tag} and pack \tcode{Args}, let \exposid{as-tuple} be an alias template such that \tcode{\exposid{as-tuple}} denotes the type \tcode{\exposid{decayed-tuple}}. Then \exposid{variant-type} denotes the type \begin{codeblock} variant, tuple, @\exposid{as-tuple}@...> \end{codeblock} but with duplicate types removed. \pnum Let \exposid{state-list-type} be a type that stores a list of pointers to \exposid{local-state-base} objects and that permits atomic insertion. \begin{itemdecl} explicit @\exposid{shared-state}@(Sndr&& sndr); \end{itemdecl} \begin{itemdescr} \pnum \effects Initializes \exposid{op_state} with the result of \tcode{connect(std::forward(sndr), \exposid{split-re\-ceiver}\{this\})}. \pnum \ensures \exposid{waiting_states} is empty, and \exposid{completed} is \tcode{false}. \end{itemdescr} \begin{itemdecl} void @\exposid{start-op}@() noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Evaluates \tcode{\exposid{inc-ref}()}. If \tcode{stop_src.stop_requested()} is \tcode{true}, evaluates \tcode{\exposid{notify}()}; otherwise, evaluates \tcode{start(\exposid{op_state})}. \end{itemdescr} \begin{itemdecl} void @\exposid{notify}@() noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Atomically does the following: \begin{itemize} \item Sets \tcode{completed} to \tcode{true}, and \item Exchanges \tcode{waiting_states} with an empty list, storing the old value in a local \tcode{prior_states}. \end{itemize} Then, for each pointer \tcode{p} in \tcode{prior_states}, evaluates \tcode{p->\exposid{notify}()}. Finally, evaluates \tcode{\exposid{dec-ref}()}. \end{itemdescr} \begin{itemdecl} void @\exposid{inc-ref}@() noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Increments \exposid{ref_count}. \end{itemdescr} \begin{itemdecl} void @\exposid{dec-ref}@() noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Decrements \exposid{ref_count}. If the new value of \exposid{ref_count} is \tcode{0}, calls \tcode{delete this}. \pnum \sync If an evaluation of \tcode{\exposid{dec-ref}()} does not decrement the \tcode{ref_count} to \tcode{0} then synchronizes with the evaluation of \tcode{dec-ref()} that decrements \tcode{ref_count} to \tcode{0}. \end{itemdescr} \pnum Let \exposid{split-impl-tag} be an empty exposition-only class type. Given an expression \tcode{sndr}, the expression \tcode{split.transform_sender(sndr)} is equivalent to: \begin{codeblock} auto&& [tag, _, child] = sndr; auto* sh_state = new @\exposid{shared-state}@{std::forward_like(child)}; return @\exposid{make-sender}@(@\exposid{split-impl-tag}@(), @\exposid{shared-wrapper}@{sh_state, tag}); \end{codeblock} where \exposid{shared-wrapper} is an exposition-only class that manages the reference count of the \exposid{shared-state} object pointed to by sh_state. \exposid{shared-wrapper} models \libconcept{copyable} with move operations nulling out the moved-from object, copy operations incrementing the reference count by calling \tcode{sh_state->\exposid{inc-ref}()}, and assignment operations performing a copy-and-swap operation. The destructor has no effect if sh_state is null; otherwise, it decrements the reference count by evaluating \tcode{sh_state->\exposid{dec-ref}()}. \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \exposid{split-impl-tag} as follows: \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@<@\exposid{split-impl-tag}@> : @\exposid{default-impls}@ { static constexpr auto @\exposid{get-state}@ = @\seebelow@; static constexpr auto @\exposid{start}@ = @\seebelow@; }; } \end{codeblock} \pnum The member \tcode{\exposid{impls-for}<\exposid{split-impl-tag}>::\exposid{get-state}} is initialized with a callable object equivalent to the following lambda expression: \begin{codeblock} [](Sndr&& sndr, auto& rcvr) noexcept { return @\exposid{local-state}@{std::forward(sndr), rcvr}; } \end{codeblock} \pnum The member \tcode{\exposid{impls-for}<\exposid{split-impl-tag}>::\exposid{start}} is initialized with a callable object that has a function call operator equivalent to the following: \begin{codeblock} template void operator()(@\exposid{local-state}@& state, Rcvr& rcvr) const noexcept; \end{codeblock} \effects If \tcode{state.\exposid{sh_state}->\exposid{completed}} is \tcode{true}, evaluates \tcode{state.\exposid{notify}()} and returns. Otherwise, does the following in order: \begin{itemize} \item Evaluates \begin{codeblock} state.@\exposid{on_stop}@.emplace( get_stop_token(get_env(rcvr)), @\exposid{on-stop-request}@{state.@\exposid{sh_state}@->@\exposid{stop_src}@}); \end{codeblock} \item Then atomically does the following: \begin{itemize} \item Reads the value \tcode{c} of \tcode{state.\exposid{sh_state}->\exposid{completed}}, and \item Inserts \tcode{addressof(state)} into \tcode{state.\exposid{sh_state}->\exposid{waiting_states}} if \tcode{c} is \tcode{false}. \end{itemize} \item If \tcode{c} is \tcode{true}, calls \tcode{state.\exposid{notify}()} and returns. \item Otherwise, if \tcode{addressof(state)} is the first item added to \tcode{state.\exposid{sh_state}->\exposid{waiting_states}}, evaluates \tcode{state.\exposid{sh_state}->\exposid{start-op}()}. \end{itemize} \rSec3[exec.when.all]{\tcode{execution::when_all}} \pnum \tcode{when_all} and \tcode{when_all_with_variant} both adapt multiple input senders into a sender that completes when all input senders have completed. \tcode{when_all} only accepts senders with a single value completion signature and on success concatenates all the input senders' value result datums into its own value completion operation. \tcode{when_all_with_variant(sndrs...)} is semantically equivalent to w\tcode{hen_all(into_variant(sndrs)...)}, where \tcode{sndrs} is a pack of subexpressions whose types model \libconcept{sender}. \pnum The names \tcode{when_all} and \tcode{when_all_with_variant} denote customization point objects. Let \tcode{sndrs} be a pack of subexpressions, let \tcode{Sndrs} be a pack of the types \tcode{decltype((sndrs))...}, and let \tcode{CD} be the type \tcode{common_type_t}. The expressions \tcode{when_all(sndrs...)} and \tcode{when_all_with_variant(sndrs...)} are ill-formed if any of the following is \tcode{true}: \begin{itemize} \item \tcode{sizeof...(sndrs)} is \tcode{0}, or \item \tcode{(\libconcept{sender} \&\& ...)} is \tcode{false}, or \item \tcode{CD} is ill-formed. \end{itemize} \pnum The expression \tcode{when_all(sndrs...)} is expression-equivalent to: \begin{codeblock} transform_sender(CD(), @\exposid{make-sender}@(when_all, {}, sndrs...)) \end{codeblock} \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \tcode{when_all_t} as follows: \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@ : @\exposid{default-impls}@ { static constexpr auto @\exposid{get-attrs}@ = @\seebelow@; static constexpr auto @\exposid{get-env}@ = @\seebelow@; static constexpr auto @\exposid{get-state}@ = @\seebelow@; static constexpr auto @\exposid{start}@ = @\seebelow@; static constexpr auto @\exposid{complete}@ = @\seebelow@; }; } \end{codeblock} \pnum The member \tcode{\exposid{impls-for}::\exposid{get-attrs}} is initialized with a callable object equivalent to the following lambda expression: \begin{codeblock} [](auto&&, auto&&... child) noexcept { if constexpr (@\libconcept{same_as}@) { return empty_env(); } else { return @\exposid{MAKE-ENV}@(get_domain, CD()); } } \end{codeblock} \pnum The member \tcode{\exposid{impls-for}::\exposid{get-env}} is initialized with a callable object equivalent to the following lambda expression: \begin{codeblock} [](auto&&, State& state, const Receiver& rcvr) noexcept { return @\exposid{JOIN-ENV}@( @\exposid{MAKE-ENV}@(get_stop_token, state.@\exposid{stop_src}@.get_token()), get_env(rcvr)); } \end{codeblock} \pnum The member \tcode{\exposid{impls-for}::\exposid{get-state}} is initialized with a callable object equivalent to the following lambda expression: \begin{codeblock} [](Sndr&& sndr, Rcvr& rcvr) noexcept(@$e$@) -> decltype(@$e$@) { return @$e$@; } \end{codeblock} where $e$ is the expression \begin{codeblock} std::forward(sndr).apply(@\exposid{make-state}@()) \end{codeblock} and where \exposid{make-state} is the following exposition-only class template: \begin{codeblock} template concept @\defexposconcept{max-1-sender-in}@ = @\libconcept{sender_in}@ && // \expos (tuple_size_v> <= 1); enum class @\exposid{disposition}@ { @\exposid{started}@, @\exposid{error}@, @\exposid{stopped}@ }; // \expos template struct @\exposid{make-state}@ { template<@\exposconcept{max-1-sender-in}@>... Sndrs> auto operator()(auto, auto, Sndrs&&... sndrs) const { using values_tuple = @\seebelow@; using errors_variant = @\seebelow@; using stop_callback = stop_callback_for_t>, @\exposid{on-stop-request}@>; struct @\exposid{state-type}@ { void @\exposid{arrive}@(Rcvr& rcvr) noexcept { // \expos if (0 == --count) { @\exposid{complete}@(rcvr); } } void @\exposid{complete}@(Rcvr& rcvr) noexcept; // \expos atomic @\exposid{count}@{sizeof...(sndrs)}; // \expos inplace_stop_source @\exposid{stop_src}@{}; // \expos atomic<@\exposid{disposition}@> disp{@\exposidnc{disposition}@::@\exposidnc{started}@}; // \expos errors_variant @\exposid{errors}@{}; // \expos values_tuple @\exposid{values}@{}; // \expos optional @\exposid{on_stop}@{nullopt}; // \expos }; return @\exposid{state-type}@{}; } }; \end{codeblock} \pnum Let \exposid{copy-fail} be \tcode{exception_ptr} if decay-copying any of the child senders' result datums can potentially throw; otherwise, \exposid{none-such}, where \exposid{none-such} is an unspecified empty class type. \pnum The alias \tcode{values_tuple} denotes the type \begin{codeblock} tuple, @\exposid{decayed-tuple}@, optional>...> \end{codeblock} if that type is well-formed; otherwise, \tcode{tuple<>}. \pnum The alias \tcode{errors_variant} denotes the type \tcode{variant<\exposid{none-such}, \exposid{copy-fail}, Es...>} with duplicate types removed, where \tcode{Es} is the pack of the decayed types of all the child senders' possible error result datums. \pnum The member \tcode{void \exposid{state-type}::\exposid{complete}(Rcvr\& rcvr) noexcept} behaves as follows: \begin{itemize} \item If \tcode{disp} is equal to \tcode{\exposid{disposition}::\exposid{started}}, evaluates: \begin{codeblock} auto tie = [](tuple& t) noexcept { return tuple(t); }; auto set = [&](auto&... t) noexcept { set_value(std::move(rcvr), std::move(t)...); }; @\exposid{on_stop}@.reset(); apply( [&](auto&... opts) noexcept { apply(set, tuple_cat(tie(*opts)...)); }, values); \end{codeblock} \item Otherwise, if \tcode{disp} is equal to \tcode{\exposid{disposition}::\exposid{error}}, evaluates: \begin{codeblock} @\exposid{on_stop}@.reset(); visit( [&](Error& error) noexcept { if constexpr (!@\libconcept{same_as}@) { set_error(std::move(rcvr), std::move(error)); } }, errors); \end{codeblock} \item Otherwise, evaluates: \begin{codeblock} @\exposid{on_stop}@.reset(); set_stopped(std::move(rcvr)); \end{codeblock} \end{itemize} \pnum The member \tcode{\exposid{impls-for}::\exposid{start}} is initialized with a callable object equivalent to the following lambda expression: \begin{codeblock} []( State& state, Rcvr& rcvr, Ops&... ops) noexcept -> void { state.@\exposid{on_stop}@.emplace( get_stop_token(get_env(rcvr)), @\exposid{on-stop-request}@{state.@\exposid{stop_src}@}); if (state.@\exposid{stop_src}@.stop_requested()) { state.@\exposid{on_stop.}@reset(); set_stopped(std::move(rcvr)); } else { (start(ops), ...); } } \end{codeblock} \pnum The member \exposid{\tcode{impls-for}::\exposid{complete}} is initialized with a callable object equivalent to the following lambda expression: \begin{codeblock} []( this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void { if constexpr (@\libconcept{same_as}@) { if (@\exposid{disposition}@::@\exposid{error}@ != state.disp.exchange(@\exposid{disposition}@::@\exposid{error}@)) { state.@\exposid{stop_src}@.request_stop(); @\exposid{TRY-EMPLACE-ERROR}@(state.errors, std::forward(args)...); } } else if constexpr (@\libconcept{same_as}@) { auto expected = @\exposid{disposition}@::@\exposid{started}@; if (state.disp.compare_exchange_strong(expected, @\exposid{disposition}@::@\exposid{stopped}@)) { state.@\exposid{stop_src}@.request_stop(); } } else if constexpr (!@\libconcept{same_as}@>) { if (state.disp == @\exposid{disposition}@::@\exposid{started}@) { auto& opt = get(state.values); @\exposid{TRY-EMPLACE-VALUE}@(complete, opt, std::forward(args)...); } } state.@\exposid{arrive}@(rcvr); } \end{codeblock} where \tcode{\exposid{TRY-EMPLACE-ERROR}(v, e)}, for subexpressions \tcode{v} and \tcode{e}, is equivalent to: \begin{codeblock} try { v.template emplace(e); } catch (...) { v.template emplace(current_exception()); } \end{codeblock} if the expression \tcode{decltype(auto(e))(e)} is potentially throwing; otherwise, \tcode{v.template emplace(e)}; and where \tcode{\exposid{TRY-EMPLACE-VALUE}(c, o, as...)}, for subexpressions \tcode{c}, \tcode{o}, and pack of subexpressions \tcode{as}, is equivalent to: \begin{codeblock} try { o.emplace(as...); } catch (...) { c(Index(), state, rcvr, set_error, current_exception()); return; } \end{codeblock} if the expression \tcode{\exposid{decayed-tuple}\{as...\}} is potentially throwing; otherwise, \tcode{o.emplace(\linebreak as...)}. \pnum The expression \tcode{when_all_with_variant(sndrs...)} is expression-equivalent to: \begin{codeblock} transform_sender(CD(), @\exposid{make-sender}@(when_all_with_variant, {}, sndrs...)); \end{codeblock} \pnum Given subexpressions \tcode{sndr} and \tcode{env}, if \tcode{\exposconcept{sender-for}} is \tcode{false}, then the expression \tcode{when_all_with_variant.transform_sender(sndr, env)} is ill-formed; otherwise, it is equivalent to: \begin{codeblock} auto&& [_, _, ...child] = sndr; return when_all(into_variant(std::forward_like(child))...); \end{codeblock} \begin{note} This causes the \tcode{when_all_with_variant(sndrs...)} sender to become \tcode{when_all(into_variant(sndrs)...)} when it is connected with a receiver whose execution domain does not customize \tcode{when_all_with_variant}. \end{note} \rSec3[exec.into.variant]{\tcode{execution::into_variant}} \pnum \tcode{into_variant} adapts a sender with multiple value completion signatures into a sender with just one value completion signature consisting of a \tcode{variant} of \tcode{tuple}s. \pnum The name \tcode{into_variant} denotes a pipeable sender adaptor object. For a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype((sndr))}. If \tcode{Sndr} does not satisfy \libconcept{sender}, \tcode{into_variant(sndr)} is ill-formed. \pnum Otherwise, the expression \tcode{into_variant(sndr)} is expression-equivalent to: \begin{codeblock} transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(into_variant, {}, sndr)) \end{codeblock} except that \tcode{sndr} is only evaluated once. \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \tcode{into_variant} as follows: \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@ : @\exposid{default-impls}@ { static constexpr auto @\exposid{get-state}@ = @\seebelow@; static constexpr auto @\exposid{complete}@ = @\seebelow@; }; } \end{codeblock} \pnum The member \tcode{\exposid{impls-for}::\exposid{get-state}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [](Sndr&& sndr, Rcvr& rcvr) noexcept -> type_identity, env_of_t>> { return {}; } \end{codeblock} \pnum The member \tcode{\exposid{impls-for}::\exposid{complete}} is initialized with a callable object equivalent to the following lambda: \begin{codeblock} []( auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void { if constexpr (@\libconcept{same_as}@) { using variant_type = typename State::type; @\exposid{TRY-SET-VALUE}@(rcvr, variant_type(@\exposid{decayed-tuple}@{std::forward(args)...})); } else { Tag()(std::move(rcvr), std::forward(args)...); } } \end{codeblock} \rSec3[exec.stopped.opt]{\tcode{execution::stopped_as_optional}} \pnum \tcode{stopped_as_optional} maps a sender's stopped completion operation into a value completion operation as a disengaged \tcode{optional}. The sender's value completion operation is also converted into an \tcode{optional}. The result is a sender that never completes with stopped, reporting cancellation by completing with a disengaged \tcode{optional}. \pnum The name \tcode{stopped_as_optional} denotes a pipeable sender adaptor object. For a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype((sndr))}. The expression \tcode{stopped_as_optional(sndr)} is expression-equivalent to: \begin{codeblock} transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(stopped_as_optional, {}, sndr)) \end{codeblock} except that \tcode{sndr} is only evaluated once. \pnum Let \tcode{sndr} and \tcode{env} be subexpressions such that \tcode{Sndr} is \tcode{decltype((sndr))} and \tcode{Env} is \tcode{decltype((env))}. If \tcode{\exposconcept{sender-for}} is \tcode{false}, or if the type \tcode{\exposid{single-sender-value-type}} is ill-formed or \tcode{void}, then the expression \tcode{stopped_as_optional.transform_sender(sndr, env)} is ill-formed; otherwise, it is equivalent to: \begin{codeblock} auto&& [_, _, child] = sndr; using V = @\exposid{single-sender-value-type}@; return let_stopped( then(std::forward_like(child), [](Ts&&... ts) noexcept(is_nothrow_constructible_v) { return optional(in_place, std::forward(ts)...); }), []() noexcept { return just(optional()); }); \end{codeblock} \rSec3[exec.stopped.err]{\tcode{execution::stopped_as_error}} \pnum \tcode{stopped_as_error} maps an input sender's stopped completion operation into an error completion operation as a custom error type. The result is a sender that never completes with stopped, reporting cancellation by completing with an error. \pnum The name \tcode{stopped_as_error} denotes a pipeable sender adaptor object. For some subexpressions \tcode{sndr} and \tcode{err}, let \tcode{Sndr} be \tcode{decltype((sndr))} and let \tcode{Err} be \tcode{decltype((err))}. If the type \tcode{Sndr} does not satisfy \libconcept{sender} or if the type \tcode{Err} does not satisfy \exposconcept{movable-value}, \tcode{stopped_as_error(sndr, err)} is ill-formed. Otherwise, the expression \tcode{stopped_as_error(sndr, err)} is expression-equivalent to: \begin{codeblock} transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(stopped_as_error, err, sndr)) \end{codeblock} except that \tcode{sndr} is only evaluated once. \pnum Let \tcode{sndr} and \tcode{env} be subexpressions such that \tcode{Sndr} is \tcode{decltype((sndr))} and \tcode{Env} is \tcode{decltype((env))}. If \tcode{\exposconcept{sender-for}} is \tcode{false}, then the expression \tcode{stopped_as_error.transform_sender(sndr, env)} is ill-formed; otherwise, it is equivalent to: \begin{codeblock} auto&& [_, err, child] = sndr; using E = decltype(auto(err)); return let_stopped( std::forward_like(child), [err = std::forward_like(err)]() mutable noexcept(is_nothrow_move_constructible_v) { return just_error(std::move(err)); }); \end{codeblock} \rSec2[exec.consumers]{Sender consumers} \rSec3[exec.sync.wait]{\tcode{this_thread::sync_wait}} \pnum \tcode{this_thread::sync_wait} and \tcode{this_thread::sync_wait_with_variant} are used to block the current thread of execution until the specified sender completes and to return its async result. \tcode{sync_wait} mandates that the input sender has exactly one value completion signature. \pnum Let \exposid{sync-wait-env} be the following exposition-only class type: \begin{codeblock} namespace std::this_thread { struct @\exposid{sync-wait-env}@ { execution::run_loop* @\exposid{loop}@; // \expos auto query(execution::get_scheduler_t) const noexcept { return @\exposid{loop}@->get_scheduler(); } auto query(execution::get_delegation_scheduler_t) const noexcept { return @\exposid{loop}@->get_scheduler(); } }; } \end{codeblock} \pnum Let \exposid{sync-wait-result-type} and \exposid{sync-wait-with-variant-result-type} be exposition-only alias templates defined as follows: \begin{codeblock} namespace std::this_thread { template Sndr> using @\exposid{sync-wait-result-type}@ = optional>; template Sndr> using @\exposid{sync-wait-with-variant-result-type}@ = optional>; } \end{codeblock} \pnum The name \tcode{this_thread::sync_wait} denotes a customization point object. For a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype((sndr))}. If \tcode{\libconcept{sender_in}} is \tcode{false}, the expression \tcode{this_thread::sync_wait(sndr)} is ill-formed. Otherwise, it is expression-equivalent to the following, except that \tcode{sndr} is evaluated only once: \begin{codeblock} apply_sender(@\exposid{get-domain-early}@(sndr), sync_wait, sndr) \end{codeblock} \mandates \begin{itemize} \item The type \tcode{\exposid{sync-wait-result-type}} is well-formed. \item \tcode{\libconcept{same_as}>} is \tcode{true}, where $e$ is the apply_sender expression above. \end{itemize} \pnum Let \exposid{sync-wait-state} and \exposid{sync-wait-receiver} be the following exposition-only class templates: \begin{codeblock} namespace std::this_thread { template struct @\exposid{sync-wait-state}@ { // \expos execution::run_loop @\exposid{loop}@; // \expos exception_ptr @\exposid{error}@; // \expos @\exposid{sync-wait-result-type}@ @\exposidnc{result}@; // \expos }; template struct @\exposid{sync-wait-receiver}@ { // \expos using receiver_concept = execution::receiver_t; @\exposidnc{sync-wait-state}@* @\exposid{state}@; // \expos template void set_value(Args&&... args) && noexcept; template void set_error(Error&& err) && noexcept; void set_stopped() && noexcept; @\exposid{sync-wait-env}@ get_env() const noexcept { return {&@\exposid{state}@->@\exposid{loop}@}; } }; } \end{codeblock} \begin{itemdecl} template void set_value(Args&&... args) && noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} try { @\exposid{state}@->@\exposid{result}@.emplace(std::forward(args)...); } catch (...) { @\exposid{state}@->@\exposid{error}@ = current_exception(); } @\exposid{state}@->@\exposid{loop}@.finish(); \end{codeblock} \end{itemdescr} \begin{itemdecl} template void set_error(Error&& err) && noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} @\exposid{state}@->@\exposid{error}@ = @\exposid{AS-EXCEPT-PTR}@(std::forward(err)); // see \ref{exec.general} @\exposid{state}@->@\exposid{loop}@.finish(); \end{codeblock} \end{itemdescr} \begin{itemdecl} void set_stopped() && noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to \tcode{\exposid{state}->\exposid{loop}.finish()}. \end{itemdescr} \pnum For a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype((sndr))}. If \tcode{\libconcept{sender_to}>} is \tcode{false}, the expression \tcode{sync_wait.apply_sender(sndr)} is ill-formed; otherwise, it is equivalent to: \begin{codeblock} @\exposid{sync-wait-state}@ state; auto op = connect(sndr, @\exposid{sync-wait-receiver}@{&state}); start(op); state.@\exposid{loop}@.run(); if (state.@\exposid{error}@) { rethrow_exception(std::move(state.@\exposid{error}@)); } return std::move(state.@\exposid{result}@); \end{codeblock} \pnum The behavior of \tcode{this_thread::sync_wait(sndr)} is undefined unless: \begin{itemize} \item It blocks the current thread of execution\iref{defns.block} with forward progress guarantee delegation\iref{intro.progress} until the specified sender completes. \begin{note} The default implementation of \tcode{sync_wait} achieves forward progress guarantee delegation by providing a \tcode{run_loop} scheduler via the \tcode{get_delegation_scheduler} query on the \exposid{sync-wait-receiver}'s environment. The \tcode{run_loop} is driven by the current thread of execution. \end{note} \item It returns the specified sender's async results as follows: \begin{itemize} \item For a value completion, the result datums are returned in a \tcode{tuple} in an engaged \tcode{optional} object. \item For an error completion, an exception is thrown. \item For a stopped completion, a disengaged \tcode{optional} object is returned. \end{itemize} \end{itemize} \rSec3[exec.sync.wait.var]{\tcode{this_thread::sync_wait_with_variant}} \pnum The name \tcode{this_thread::sync_wait_with_variant} denotes a customization point object. For a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype(into_variant(sndr))}. If \tcode{\libconcept{sender_in}} is \tcode{false}, \tcode{this_thread::sync_wait_with_variant(sndr)} is ill-formed. Otherwise, it is expression-equivalent to the following, except \tcode{sndr} is evaluated only once: \begin{codeblock} apply_sender(@\exposid{get-domain-early}@(sndr), sync_wait_with_variant, sndr) \end{codeblock} \mandates \begin{itemize} \item The type \tcode{\exposid{sync-wait-with-variant-result-type}} is well-formed. \item \tcode{\libconcept{same_as}>} is \tcode{true}, where $e$ is the \tcode{ap\-ply_sender} expression above. \end{itemize} \pnum If \tcode{\exposconcept{callable}} is \tcode{false}, the expression \tcode{sync_wait_with_variant.apply_sender(\linebreak sndr)} is ill-formed. Otherwise, it is equivalent to: \begin{codeblock} using result_type = @\exposid{sync-wait-with-variant-result-type}@; if (auto opt_value = sync_wait(into_variant(sndr))) { return result_type(std::move(get<0>(*opt_value))); } return result_type(nullopt); \end{codeblock} \pnum The behavior of \tcode{this_thread::sync_wait_with_variant(sndr)} is undefined unless: \begin{itemize} \item It blocks the current thread of execution\iref{defns.block} with forward progress guarantee delegation\iref{intro.progress} until the specified sender completes. \begin{note} The default implementation of \tcode{sync_wait_with_variant} achieves forward progress guarantee delegation by relying on the forward progress guarantee delegation provided by \tcode{sync_wait}. \end{note} \item It returns the specified sender's async results as follows: \begin{itemize} \item For a value completion, the result datums are returned in an engaged \tcode{optional} object that contains a \tcode{variant} of \tcode{tuple}s. \item For an error completion, an exception is thrown. \item For a stopped completion, a disengaged \tcode{optional} object is returned. \end{itemize} \end{itemize} \rSec1[exec.util]{Sender/receiver utilities} \rSec2[exec.util.cmplsig]{\tcode{execution::completion_signatures}} \pnum \tcode{completion_signatures} is a type that encodes a set of completion signatures\iref{exec.async.ops}. \pnum \begin{example} \begin{codeblock} struct my_sender { using sender_concept = sender_t; using completion_signatures = execution::completion_signatures< set_value_t(), set_value_t(int, float), set_error_t(exception_ptr), set_error_t(error_code), set_stopped_t()>; }; \end{codeblock} Declares \tcode{my_sender} to be a sender that can complete by calling one of the following for a receiver expression \tcode{rcvr}: \begin{itemize} \item \tcode{set_value(rcvr)} \item \tcode{set_value(rcvr, int\{...\}, float\{...\})} \item \tcode{set_error(rcvr, exception_ptr\{...\})} \item \tcode{set_error(rcvr, error_code\{...\})} \item \tcode{set_stopped(rcvr)} \end{itemize} \end{example} \pnum This subclause makes use of the following exposition-only entities: \begin{codeblock} template concept @\defexposconcept{completion-signature}@ = @\seebelow@; \end{codeblock} \pnum A type \tcode{Fn} satisfies \exposconcept{completion-signature} if and only if it is a function type with one of the following forms: \begin{itemize} \item \tcode{set_value_t(Vs...)}, where \tcode{Vs} is a pack of object or reference types. \item \tcode{set_error_t(Err)}, where \tcode{Err} is an object or reference type. \item \tcode{set_stopped_t()} \end{itemize} \pnum \begin{codeblock} template struct @\exposid{indirect-meta-apply}@ { template class T, class... As> using @\exposid{meta-apply}@ = T; // \expos }; template concept @\defexposconcept{always-true}@ = true; // \expos template class Tuple, template class Variant> using @\exposid{gather-signatures}@ = @\seebelow@; \end{codeblock} \pnum Let \tcode{Fns} be a pack of the arguments of the \tcode{completion_signatures} specialization named by \tcode{Completions}, let \tcode{TagFns} be a pack of the function types in \tcode{Fns} whose return types are \tcode{Tag}, and let $\tcode{Ts}_n$ be a pack of the function argument types in the $n$-th type in \tcode{TagFns}. Then, given two variadic templates \tcode{Tuple} and \tcode{Variant}, the type \tcode{\exposid{gather-signatures}} names the type \begin{codeblock} @\exposid{META-APPLY}@(Variant, @\exposid{META-APPLY}@(Tuple, Ts@$_0$@...), @\itcorr[1]\exposid{META-APPLY}@(Tuple, Ts@$_1$@...), @\itcorr[1]\ldots@, @\itcorr[1]\exposid{META-APPLY}@(Tuple, Ts@$_{m-1}$@...)) \end{codeblock} where $m$ is the size of the pack \tcode{TagFns} and \tcode{META-APPLY(T, As...)} is equivalent to: \begin{codeblock} typename @\exposid{indirect-meta-apply}@<@\exposid{always-true}@>::template @\exposid{meta-apply}@ \end{codeblock} \pnum \begin{note} The purpose of \exposid{META-APPLY} is to make it valid to use non-variadic templates as \tcode{Variant} and \tcode{Tuple} arguments to \exposid{gather-signatures}. \end{note} \pnum \begin{codeblock} namespace std::execution { template<@\exposconcept{completion-signature}@... Fns> struct completion_signatures {}; template class Tuple = @\exposid{decayed-tuple}@, template class Variant = @\exposid{variant-or-empty}@> requires @\libconcept{sender_in}@ using value_types_of_t = @\exposid{gather-signatures}@, Tuple, Variant>; template class Variant = @\exposid{variant-or-empty}@> requires @\libconcept{sender_in}@ using error_types_of_t = @\exposid{gather-signatures}@, type_identity_t, Variant>; template requires @\libconcept{sender_in}@ constexpr bool sends_stopped = !@\libconcept{same_as}@<@\exposid{type-list}@<>, @\exposid{gather-signatures}@, @\exposid{type-list}@, @\exposid{type-list}@>>; } \end{codeblock} \rSec2[exec.util.cmplsig.trans]{\tcode{execution::transform_completion_signatures}} \pnum \tcode{transform_completion_signatures} is an alias template used to transform one set of completion signatures into another. It takes a set of completion signatures and several other template arguments that apply modifications to each completion signature in the set to generate a new specialization of \tcode{completion_signature}s. \pnum \begin{example} Given a sender \tcode{Sndr} and an environment \tcode{Env}, adapt the completion signatures of \tcode{Sndr} by lvalue-ref qualifying the values, adding an additional \tcode{exception_ptr} error completion if it is not already there, and leaving the other completion signatures alone. \begin{codeblock} template using my_set_value_t = completion_signatures< set_value_t(add_lvalue_reference_t...)>; using my_completion_signatures = transform_completion_signatures< completion_signatures_of_t, completion_signatures, my_set_value_t>; \end{codeblock} \end{example} \pnum This subclause makes use of the following exposition-only entities: \begin{codeblock} template using @\exposid{default-set-value}@ = completion_signatures; template using @\exposid{default-set-error}@ = completion_signatures; \end{codeblock} \pnum \begin{codeblock} namespace std::execution { template<@\exposconcept{valid-completion-signatures}@ InputSignatures, @\exposconcept{valid-completion-signatures}@ AdditionalSignatures = completion_signatures<>, template class SetValue = @\exposid{default-set-value}@, template class SetError = @\exposid{default-set-error}@, @\exposconcept{valid-completion-signatures}@ SetStopped = completion_signatures> using transform_completion_signatures = completion_signatures<@\seebelow@>; } \end{codeblock} \pnum \tcode{SetValue} shall name an alias template such that for any pack of types \tcode{As}, the type \tcode{SetValue} is either ill-formed or else \tcode{\exposconcept{valid-completion-signatures}>} is satisfied. \tcode{SetError} shall name an alias template such that for any type \tcode{Err}, \tcode{SetError} is either ill-formed or else \tcode{\exposconcept{valid-completion-signatures}>} is satisfied. \pnum Let \tcode{Vs} be a pack of the types in the \exposid{type-list} named by \tcode{\exposid{gather-signatures}}. \pnum Let \tcode{Es} be a pack of the types in the \exposid{type-list} named by \tcode{\exposid{gather-signatures}}, where \exposid{error-list} is an alias template such that \tcode{\exposid{error-list}<\linebreak Ts...>} is \tcode{\exposid{type-list}...>}. \pnum Let \tcode{Ss} name the type \tcode{completion_signatures<>} if \tcode{\exposid{gather-signatures}} is an alias for the type \tcode{\exposid{type-list}<>}; otherwise, \tcode{SetStopped}. \pnum If any of the above types are ill-formed, then \begin{codeblock} transform_completion_signatures \end{codeblock} is ill-formed. Otherwise, \begin{codeblock} transform_completion_signatures \end{codeblock} is the type \tcode{completion_signatures} where \tcode{Sigs...} is the unique set of types in all the template arguments of all the \tcode{completion_signatures} specializations in the set \tcode{AdditionalSignatures}, \tcode{Vs...}, \tcode{Es...}, \tcode{Ss}. \rSec1[exec.ctx]{Execution contexts} \rSec2[exec.run.loop]{\tcode{execution::run_loop}} \rSec3[exec.run.loop.general]{General} \pnum A \tcode{run_loop} is an execution resource on which work can be scheduled. It maintains a thread-safe first-in-first-out queue of work. Its \tcode{run} member function removes elements from the queue and executes them in a loop on the thread of execution that calls \tcode{run}. \pnum A \tcode{run_loop} instance has an associated \defn{count} that corresponds to the number of work items that are in its queue. Additionally, a \tcode{run_loop} instance has an associated state that can be one of \defn{starting}, \defn{running}, or \defn{finishing}. \pnum Concurrent invocations of the member functions of \tcode{run_loop} other than \tcode{run} and its destructor do not introduce data races. The member functions \exposid{pop-front}, \exposid{push-back}, and \tcode{finish} execute atomically. \pnum \recommended Implementations should use an intrusive queue of operation states to hold the work units to make scheduling allocation-free. \begin{codeblock} namespace std::execution { class run_loop { // \ref{exec.run.loop.types}, associated types class @\exposid{run-loop-scheduler}@; // \expos class @\exposid{run-loop-sender}@; // \expos struct @\exposid{run-loop-opstate-base}@ { // \expos virtual void @\exposid{execute}@() = 0; // \expos run_loop* @\exposid{loop}@; // \expos run-loop-opstate-base* @\exposid{next}@; // \expos }; template using @\exposid{run-loop-opstate}@ = @\unspec@; // \expos // \ref{exec.run.loop.members}, member functions @\exposid{run-loop-opstate-base}@* @\exposid{pop-front}@(); // \expos void @\exposid{push-back}@(@\exposid{run-loop-opstate-base}@*); // \expos public: // \ref{exec.run.loop.ctor}, constructor and destructor run_loop() noexcept; run_loop(run_loop&&) = delete; ~run_loop(); // \ref{exec.run.loop.members}, member functions @\exposid{run-loop-scheduler}@ get_scheduler(); void run(); void finish(); }; } \end{codeblock} \rSec3[exec.run.loop.types]{Associated types} \begin{itemdecl} class @\exposid{run-loop-scheduler}@; \end{itemdecl} \pnum \exposid{run-loop-scheduler} is an unspecified type that models \libconcept{scheduler}. \pnum Instances of \exposid{run-loop-scheduler} remain valid until the end of the lifetime of the \tcode{run_loop} instance from which they were obtained. \pnum Two instances of \exposid{run-loop-scheduler} compare equal if and only if they were obtained from the same \tcode{run_loop} instance. \pnum Let \exposid{sch} be an expression of type \exposid{run-loop-scheduler}. The expression \tcode{schedule(\exposid{sch})} has type \exposid{run-loop-\newline sender} and is not potentially-throwing if \exposid{sch} is not potentially-throwing. \begin{itemdecl} class @\exposid{run-loop-sender}@; \end{itemdecl} \pnum \exposid{run-loop-sender} is an exposition-only type that satisfies \libconcept{sender}. For any type \tcode{Env}, \tcode{completion_signatures_of_t<\exposid{run-loop-sender}, Env>} is \begin{codeblock} completion_signatures \end{codeblock} \pnum An instance of \exposid{run-loop-sender} remains valid until the end of the lifetime of its associated \tcode{run_loop} instance. \pnum Let \exposid{sndr} be an expression of type \exposid{run-loop-sender}, let \exposid{rcvr} be an expression such that \tcode{\libconcept{receiver_of}} is \tcode{true} where \tcode{CS} is the \tcode{completion_signatures} specialization above. Let \tcode{C} be either \tcode{set_value_t} or \tcode{set_stopped_t}. Then: \begin{itemize} \item The expression \tcode{connect(\exposid{sndr}, \exposid{rcvr})} has type \tcode{\exposid{run-loop-opstate}>} and is potentially-throwing if and only if \tcode{(void(\exposid{sndr}), auto(\exposid{rcvr}))} is potentially-throwing. \item The expression \tcode{get_completion_scheduler(get_env(\exposid{sndr}))} is potentially-throwing if and only if \exposid{sndr} is potentially-throwing, has type \exposid{run-loop-scheduler}, and compares equal to the \exposid{run-loop-\newline scheduler} instance from which \exposid{sndr} was obtained. \end{itemize} \begin{itemdecl} template struct @\exposid{run-loop-opstate}@; \end{itemdecl} \pnum \tcode{\exposid{run-loop-opstate}} inherits privately and unambiguously from \exposid{run-loop-opstate-base}. \pnum Let $o$ be a non-const lvalue of type \tcode{\exposid{run-loop-opstate}}, and let \tcode{REC($o$)} be a non-const lvalue reference to an instance of type \tcode{Rcvr} that was initialized with the expression \exposid{rcvr} passed to the invocation of connect that returned $o$. Then: \begin{itemize} \item The object to which \tcode{\exposid{REC}($o$)} refers remains valid for the lifetime of the object to which $o$ refers. \item The type \tcode{\exposid{run-loop-opstate}} overrides \tcode{\exposid{run-loop-opstate-base}::\exposid{execute}()} such that \tcode{$o$.\exposid{exe\-cute}()} is equivalent to: \begin{codeblock} if (get_stop_token(@\exposid{REC}@(@$o$@)).stop_requested()) { set_stopped(std::move(@\exposid{REC}@(@$o$@))); } else { set_value(std::move(@\exposid{REC}@(@$o$@))); } \end{codeblock} \item The expression \tcode{start($o$)} is equivalent to: \begin{codeblock} try { @$o$@.@\exposid{loop}@->@\exposid{push-back}@(addressof(@$o$@)); } catch(...) { set_error(std::move(@\exposid{REC}@(@$o$@)), current_exception()); } \end{codeblock} \end{itemize} \rSec3[exec.run.loop.ctor]{Constructor and destructor} \begin{itemdecl} run_loop() noexcept; \end{itemdecl} \begin{itemdescr} \pnum \ensures \exposid{count} is \tcode{0} and \exposid{state} is \exposid{starting}. \end{itemdescr} \begin{itemdecl} ~run_loop(); \end{itemdecl} \begin{itemdescr} \pnum \effects If \exposid{count} is not \tcode{0} or if \exposid{state} is \exposid{running}, invokes \tcode{terminate}\iref{except.terminate}. Otherwise, has no effects. \end{itemdescr} \rSec3[exec.run.loop.members]{Member functions} \begin{itemdecl} @\exposid{run-loop-opstate-base}@* @\exposid{pop-front}@(); \end{itemdecl} \begin{itemdescr} \pnum \effects Blocks\iref{defns.block} until one of the following conditions is \tcode{true}: \begin{itemize} \item \exposid{count} is \tcode{0} and \exposid{state} is \exposid{finishing}, in which case \exposid{pop-front} returns \tcode{nullptr}; or \item \exposid{count} is greater than \tcode{0}, in which case an item is removed from the front of the queue, \exposid{count} is decremented by \tcode{1}, and the removed item is returned. \end{itemize} \end{itemdescr} \begin{itemdecl} void @\exposid{push-back}@(@\exposid{run-loop-opstate-base}@* item); \end{itemdecl} \begin{itemdescr} \pnum \effects Adds \tcode{item} to the back of the queue and increments \exposid{count} by \tcode{1}. \pnum \sync This operation synchronizes with the \exposid{pop-front} operation that obtains \tcode{item}. \end{itemdescr} \begin{itemdecl} @\exposid{run-loop-scheduler}@ get_scheduler(); \end{itemdecl} \begin{itemdescr} \pnum \returns An instance of \exposid{run-loop-scheduler} that can be used to schedule work onto this \tcode{run_loop} instance. \end{itemdescr} \begin{itemdecl} void run(); \end{itemdecl} \begin{itemdescr} \pnum \expects \exposid{state} is \exposid{starting}. \pnum \effects Sets the \exposid{state} to \exposid{running}. Then, equivalent to: \begin{codeblock} while (auto* op = @\exposid{pop-front}@()) { op->@\exposid{execute}@(); } \end{codeblock} \pnum \remarks When \exposid{state} changes, it does so without introducing data races. \end{itemdescr} \begin{itemdecl} void finish(); \end{itemdecl} \begin{itemdescr} \pnum \effects Changes \exposid{state} to \exposid{finishing}. \pnum \sync \tcode{finish} synchronizes with the \exposid{pop-front} operation that returns \tcode{nullptr}. \end{itemdescr} \rSec1[exec.coro.util]{Coroutine utilities} \rSec2[exec.as.awaitable]{\tcode{execution::as_awaitable}} \pnum \tcode{as_awaitable} transforms an object into one that is awaitable within a particular coroutine. Subclause \ref{exec.coro.util} makes use of the following exposition-only entities: \begin{codeblock} namespace std::execution { template concept @\defexposconcept{awaitable-sender}@ = @\exposconcept{single-sender}@> && @\libconcept{sender_to}@ && // \seebelow requires (Promise& p) { { p.unhandled_stopped() } -> @\libconcept{convertible_to}@>; }; template class @\exposidnc{sender-awaitable}@; // \expos } \end{codeblock} \pnum The type \tcode{\exposid{sender-awaitable}} is equivalent to: \begin{codeblock} namespace std::execution { template class @\exposidnc{sender-awaitable}@ { struct @\exposidnc{unit}@ {}; // \expos using @\exposidnc{value-type}@ = // \expos @\exposidnc{single-sender-value-type}@>; using @\exposidnc{result-type }@= // \expos conditional_t, unit, @\exposid{value-type}@>; struct @\exposidnc{awaitable-receiver}@; // \expos variant @\exposidnc{result}@{}; // \expos connect_result_t @\exposidnc{state}@; // \expos public: @\exposid{sender-awaitable}@(Sndr&& sndr, Promise& p); static constexpr bool await_ready() noexcept { return false; } void await_suspend(coroutine_handle) noexcept { start(@\exposid{state}@); } @\exposid{value-type}@ await_resume(); }; } \end{codeblock} \pnum \exposid{awaitable-receiver} is equivalent to: \begin{codeblock} struct @\exposid{awaitable-receiver}@ { using receiver_concept = receiver_t; variant* @\exposidnc{result-ptr}@; // \expos coroutine_handle @\exposidnc{continuation}@; // \expos // \seebelow }; \end{codeblock} \pnum Let \tcode{rcvr} be an rvalue expression of type \exposid{awaitable-receiver}, let \tcode{crcvr} be a const lvalue that refers to \tcode{rcvr}, let \tcode{vs} be a pack of subexpressions, and let \tcode{err} be an expression of type \tcode{Err}. Then: \begin{itemize} \item If \tcode{\libconcept{constructible_from}<\exposid{result-type}, decltype((vs))...>} is satisfied, the expression \tcode{set_value(\newline rcvr, vs...)} is equivalent to: \begin{codeblock} try { rcvr.@\exposid{result-ptr}@->template emplace<1>(vs...); } catch(...) { rcvr.@\exposid{result-ptr}@->template emplace<2>(current_exception()); } rcvr.@\exposid{continuation}@.resume(); \end{codeblock} Otherwise, \tcode{set_value(rcvr, vs...)} is ill-formed. \item The expression \tcode{set_error(rcvr, err)} is equivalent to: \begin{codeblock} rcvr.@\exposid{result-ptr}@->template emplace<2>(@\exposid{AS-EXCEPT-PTR}@(err)); // see \ref{exec.general} rcvr.@\exposid{continuation}@.resume(); \end{codeblock} \item The expression \tcode{set_stopped(rcvr)} is equivalent to: \begin{codeblock} static_cast>(rcvr.@\exposid{continuation}@.promise().unhandled_stopped()).resume(); \end{codeblock} \item For any expression \tcode{tag} whose type satisfies \exposconcept{forwarding-query} and for any pack of subexpressions \tcode{as}, \tcode{get_env(crcvr).query(tag, as...)} is expression-equivalent to: \begin{codeblock} tag(get_env(as_const(crcvr.@\exposid{continuation}@.promise())), as...) \end{codeblock} \end{itemize} \begin{itemdecl} @\exposid{sender-awaitable}@(Sndr&& sndr, Promise& p); \end{itemdecl} \begin{itemdescr} \pnum \effects Initializes \exposid{state} with \begin{codeblock} connect(std::forward(sndr), @\exposid{awaitable-receiver}@{addressof(result), coroutine_handle::from_promise(p)}) \end{codeblock} \end{itemdescr} \begin{itemdecl} @\exposid{value-type}@ await_resume(); \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} if (@\exposid{result}@.index() == 2) rethrow_exception(get<2>(@\exposid{result}@)); if constexpr (!is_void_v<@\exposid{value-type}@>) return std::forward<@\exposid{value-type}@>(get<1>(@\exposid{result}@)); \end{codeblock} \end{itemdescr} \pnum \tcode{as_awaitable} is a customization point object. For subexpressions \tcode{expr} and \tcode{p} where \tcode{p} is an lvalue, \tcode{Expr} names the type \tcode{decltype((expr))} and \tcode{Promise} names the type \tcode{decay_t}, \tcode{as_awaitable(expr, p)} is expression-equivalent to, except that the evaluations of \tcode{expr} and \tcode{p} are indeterminately sequenced: \begin{itemize} \item \tcode{expr.as_awaitable(p)} if that expression is well-formed. \mandates \tcode{\exposconcept{is-awaitable}} is \tcode{true}, where \tcode{A} is the type of the expression above. \item Otherwise, \tcode{(void(p), expr)} if \tcode{\exposconcept{is-awaitable}} is \tcode{true}, where \tcode{U} is an unspecified class type that is not \tcode{Promise} and that lacks a member named \tcode{await_transform}. \expects \tcode{\exposconcept{is-awaitable}} is \tcode{true} and the expression \tcode{co_await expr} in a coroutine with promise type \tcode{U} is expression-equivalent to the same expression in a coroutine with promise type \tcode{Promise}. \item Otherwise, \tcode{\exposid{sender-awaitable}\{expr, p\}} if \tcode{\exposconcept{awaitable-sender}} is \tcode{true}. \item Otherwise, \tcode{(void(p), expr)}. \end{itemize} \rSec2[exec.with.awaitable.senders]{\tcode{execution::with_awaitable_senders}} \pnum \tcode{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 \tcode{unhandled_stopped} such that if a sender completes by calling \tcode{set_stopped}, it is treated as if an uncatchable "stopped" exception were thrown from the \grammarterm{await-expression}. \begin{note} The coroutine is never resumed, and the \tcode{unhandled_stopped} of the coroutine caller's promise type is called. \end{note} \begin{codeblock} namespace std::execution { template<@\exposconcept{class-type}@ Promise> struct with_awaitable_senders { template requires (!@\libconcept{same_as}@) void set_continuation(coroutine_handle h) noexcept; coroutine_handle<> continuation() const noexcept { return @\exposid{continuation}@; } coroutine_handle<> unhandled_stopped() noexcept { return @\exposid{stopped-handler}@(@\exposid{continuation}@.address()); } template @\seebelow@ await_transform(Value&& value); private: [[noreturn]] static coroutine_handle<> @\exposid{default-unhandled-stopped}@(void*) noexcept { // \expos terminate(); } coroutine_handle<> @\exposid{continuation}@{}; // \expos coroutine_handle<> (*@\exposid{stopped-handler}@)(void*) noexcept = // \expos &@\exposid{default-unhandled-stopped}@; }; } \end{codeblock} \begin{itemdecl} template requires (!@\libconcept{same_as}@) void set_continuation(coroutine_handle h) noexcept; \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} @\exposid{continuation}@ = h; if constexpr ( requires(OtherPromise& other) { other.unhandled_stopped(); } ) { @\exposid{stopped-handler}@ = [](void* p) noexcept -> coroutine_handle<> { return coroutine_handle::from_address(p) .promise().unhandled_stopped(); }; } else { @\exposid{stopped-handler}@ = &@\exposid{default-unhandled-stopped}@; } \end{codeblock} \end{itemdescr} \begin{itemdecl} template @\exposid{call-result-t}@ await_transform(Value&& value); \end{itemdecl} \begin{itemdescr} \pnum \effects Equivalent to: \begin{codeblock} return as_awaitable(std::forward(value), static_cast(*this)); \end{codeblock} \end{itemdescr}