33 Execution control library [exec]
33.9.11.2 execution::just, execution::just_error, execution::just_stopped [exec.just]
33.9.12.9 execution::then, execution::upon_error, execution::upon_stopped [exec.then]
33.9.12.10 execution::let_value, execution::let_error, execution::let_stopped [exec.let]
33.9.12.11 execution::bulk, execution::bulk_chunked, and execution::bulk_unchunked [exec.bulk]
This Clause describes components
supporting execution of function objects (
[function.objects])
.The following subclauses describe
the requirements, concepts, and components
for execution control primitives as summarized in Table 
157.Table 
158 — Types of customization point objects in the execution control library 
[tab:exec.pos]|  |  |  | 
|  |  |  | 
|  | provide core execution functionality, and connection between core components |  | 
|  | called by senders to announce the completion of the work (success, error, or cancellation) |  | 
|  | allow the specialization of the provided sender algorithms |  | 
|  | allow querying different properties of objects |  | 
This clause makes use of the following exposition-only entities
.For a subexpression 
expr,
let 
MANDATE-NOTHROW(expr) be
expression-equivalent to 
expr.Mandates: 
noexcept(expr) is 
true. For function types 
F1 and 
F2 denoting
R1(Args1...) and 
R2(Args2...), respectively,
MATCHING-SIG(F1, F2) is 
true if and only if
same_as<R1(Args1&&...), R2(Args2&&...)>
is 
true.For a subexpression 
err,
let 
Err be 
decltype((err)) and
let 
AS-EXCEPT-PTR(err) be:
- err-  if  decay_t<Err>-  denotes the type  exception_ptr.
 
- Preconditions- :  !err-  is  false.
 
- Otherwise,
 make_exception_ptr(system_error(err))- 
if  decay_t<Err>-  denotes the type  error_code.
- Otherwise,  make_exception_ptr(err).
For a subexpression expr,
let AS-CONST(expr) be expression-equivalent to
[](const auto& x) noexcept -> const auto& { return x; }(expr)
 A 
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
. Let 
q be a query object,
let 
args be a (possibly empty) pack of subexpressions,
let 
env be a subexpression
that refers to a queryable object 
o of type 
O, and
let 
cenv be a subexpression referring to 
o
such that 
decltype((cenv)) is 
const O&.The type of a query expression cannot be 
void.The expression 
q(env, args...) is
equality-preserving (
[concepts.equality]) and
does not modify the query object or the arguments
.If the expression 
env.query(q, args...) is well-formed,
then it is expression-equivalent to 
q(env, args...).Unless otherwise specified,
the result of a query is valid as long as the queryable object is valid
.The exposition-only 
queryable concept specifies
the constraints on the types of queryable objects
.Let 
env be an object of type 
Env.The type 
Env models 
queryable
if for each callable object 
q and a pack of subexpressions 
args,
if 
requires { q(env, args...) } is 
true then
q(env, args...) meets any semantic requirements imposed by 
q. [
Example 1: 
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
. An 
asynchronous operation is
a distinct unit of program execution that
- can be explicitly started once at most; 
- once started, eventually completes exactly once
with a (possibly empty) set of result datums and
in exactly one of three  dispositions- :
success, failure, or cancellation;
 - A successful completion, also known as a  value completion- ,
can have an arbitrary number of result datums .
 
- 
An asynchronous operation's  async result- 
is its disposition and its (possibly empty) set of result datums .
- can complete on a different execution resource
than the execution resource on which it started; and 
-  - A child operation is an asynchronous operation
that is created by the parent operation and,
if started, completes before the parent operation completes .
- A  parent operation-  is the asynchronous operation
that created a particular child operation .
 
[
Note 1: 
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]
An asynchronous operation has an associated environment
.The caller of an asynchronous operation is
its parent operation or the function that created it
.An asynchronous operation has an associated receiver
.A 
receiver is an aggregation of three handlers
for the three asynchronous completion dispositions:
- a value completion handler for a value completion,
- an error completion handler for an error completion, and
- a stopped completion handler for a stopped completion.
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
. A completion function is
a customization point object (
[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 
completion tag
that is the unqualified type of the completion function
.  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
. 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
.A 
sender is a factory for one or more asynchronous operations
.Connecting 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
.A 
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
.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
.An asynchronous operation has one or more associated completion schedulers
for each of its possible dispositions
.A 
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
.A sender has an associated queryable object (
[exec.queryable])
known as its 
attributes
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
. An asynchronous operation has a finite set of possible completion signatures
corresponding to the completion operations
that the asynchronous operation potentially evaluates (
[basic.def.odr])
.For a completion function 
set,
receiver 
rcvr, and
pack of arguments 
args,
let 
c be the completion operation 
set(rcvr, args...), and
let 
F be
the function type 
decltype(auto(set))(decltype((args))...).A completion signature 
Sig is associated with 
c
if and only if
MATCHING-SIG(Sig, F) is 
true (
[exec.general])
.Together, a sender type and an environment type 
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 
Env.The type of the receiver does not affect
an asynchronous operation's completion signatures,
only the type of the receiver's environment
.A 
non-dependent sender is a sender type
whose completion signatures are knowable
independent of an execution environment
. A sender algorithm is a function that takes and/or returns a sender
.There are three categories of sender algorithms:
- A  sender factory-  is a function
that takes non-senders as arguments and that returns a sender .
- A  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 .
- A  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 .
The exposition-only type 
variant-or-empty<Ts...>
is defined as follows:
- If sizeof...(Ts) is greater than zero,
variant-or-empty<Ts...> denotes variant<Us...>
where Us... is the pack decay_t<Ts>...
with duplicate types removed.
- Otherwise, variant-or-empty<Ts...> denotes
the exposition-only class type:
namespace std::execution {
  struct empty-variant {        
    empty-variant() = delete;
  };
}
For type 
Sndr and pack of types 
Env,
let 
CS be 
completion_signatures_of_t<Sndr, Env...>.Then 
single-sender-value-type<Sndr, Env...> is ill-formed
if 
CS is ill-formed or
if 
sizeof...(Env) > 1 is 
true;
otherwise, it is an alias for:
- gather-signatures<set_value_t, CS, decay_t, type_identity_t>
if that type is well-formed,
- Otherwise, void
if gather-signatures<set_value_t, CS, tuple, variant> is
variant<tuple<>> or variant<>,
- Otherwise, gather-signatures<set_value_t, CS, decayed-tuple, type_identity_t>
if that
 type is well-formed,
- Otherwise, single-sender-value-type<Sndr, Env...> is ill-formed.
The exposition-only concept 
single-sender is defined as follows:
namespace std::execution {
  template<class Sndr, class... Env>
    concept single-sender = sender_in<Sndr, Env...> &&
      requires {
        typename single-sender-value-type<Sndr, Env...>;
      };
}
A type satisfies and models the exposition-only concept
valid-completion-signatures if
it is a specialization of the 
completion_signatures class template
.forwarding_query asks a query object
whether it should be forwarded through queryable adaptors
. The name 
forwarding_query denotes a query object
.For some query object 
q of type 
Q,
forwarding_query(q) is expression-equivalent to:
- MANDATE-NOTHROW(q.query(forwarding_query))- 
if that expression is well-formed .
 
- Mandates- : The expression above has type  bool-  and
is a core constant expression if  q-  is a core constant expression .
 
get_allocator asks a queryable object for its associated allocator
. The name 
get_allocator denotes a query object
.For a subexpression 
env,
get_allocator(env) is expression-equivalent to
MANDATE-NOTHROW(AS-CONST(env).query(get_allocator)).forwarding_query(get_allocator) is a core constant expression and
has value 
true. get_stop_token asks a queryable object for an associated stop token
. The name 
get_stop_token denotes a query object
.For a subexpression 
env,
get_stop_token(env) is expression-equivalent to:
- MANDATE-NOTHROW(AS-CONST(env).query(get_stop_token))- 
if that expression is well-formed .
 
- Otherwise,  never_stop_token{}.
forwarding_query(get_stop_token) is a core constant expression and
has value 
true. execution::get_env is a customization point object
.  For a subexpression 
o,
execution::get_env(o) is expression-equivalent to:
- MANDATE-NOTHROW(AS-CONST(o).get_env())- 
if that expression is well-formed .
 
The value of 
get_env(o) shall be valid while 
o is valid
.[
Note 1: 
When passed a sender object,
get_env returns the sender's associated attributes
.When passed a receiver,
get_env returns the receiver's associated execution environment
. — 
end note]
get_domain asks a queryable object
for its associated execution domain tag
. The name 
get_domain denotes a query object
.For a subexpression 
env,
get_domain(env) is expression-equivalent to
MANDATE-NOTHROW(AS-CONST(env).query(get_domain)).forwarding_query(execution::get_domain) is
a core constant expression and has value 
true. get_scheduler asks a queryable object for its associated scheduler
. The name 
get_scheduler denotes a query object
.For a subexpression 
env,
get_scheduler(env) is expression-equivalent to
MANDATE-NOTHROW(AS-CONST(env).query(get_scheduler)).Mandates: If the expression above is well-formed,
its type satisfies 
scheduler. forwarding_query(execution::get_scheduler) is
a core constant expression and has value 
true. 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 (
[intro.progress])
. The name 
get_delegation_scheduler denotes a query object
.For a subexpression 
env,
get_delegation_scheduler(env) is expression-equivalent to
MANDATE-NOTHROW(AS-CONST(env).query(get_delegation_scheduler)).Mandates: If the expression above is well-formed,
its type satisfies 
scheduler. forwarding_query(execution::get_delegation_scheduler) is
a core constant expression and has value 
true. get_forward_progress_guarantee asks a scheduler about
the forward progress guarantee of execution agents
created by that scheduler's associated execution resource (
[intro.progress])
. The name 
get_forward_progress_guarantee denotes a query object
.For a subexpression 
sch, let 
Sch be 
decltype((sch)).If 
Sch does not satisfy 
scheduler,
get_forward_progress_guarantee is ill-formed
.Otherwise,
get_forward_progress_guarantee(sch) is expression-equivalent to:
- MANDATE-NOTHROW(AS-CONST(sch).query(get_forward_progress_guarantee))- ,
if that expression is well-formed .
 
- Mandates- : The type of the expression above is  forward_progress_guarantee.
 
- Otherwise,  forward_progress_guarantee::weakly_parallel.
If 
get_forward_progress_guarantee(sch) for some scheduler 
sch
returns 
forward_progress_guarantee::concurrent,
all execution agents created by that scheduler's associated execution resource
shall provide the concurrent forward progress guarantee
.If it returns 
forward_progress_guarantee::parallel,
all such execution agents
shall provide at least the parallel forward progress guarantee
.get_completion_scheduler<completion-tag> obtains
the completion scheduler associated with a completion tag
from a sender's attributes
. The name 
get_completion_scheduler denotes a query object template
.For a subexpression 
q,
the expression 
get_completion_scheduler<completion-tag>(q)
is ill-formed if 
completion-tag is not one of
set_value_t, 
set_error_t, or 
set_stopped_t.Otherwise, 
get_completion_scheduler<completion-tag>(q)
is expression-equivalent to
MANDATE-NOTHROW(AS-CONST(q).query(get_completion_scheduler<completion-tag>))
Mandates: If the expression above is well-formed,
its type satisfies 
scheduler.Let 
completion-fn be a completion function (
[exec.async.ops]);
let 
completion-tag be
the associated completion tag of 
completion-fn;
let 
args be a pack of subexpressions; and
let 
sndr be a subexpression
such that 
sender<decltype((sndr))> is 
true and
get_completion_scheduler<completion-tag>(get_env(sndr))
is well-formed and denotes a scheduler 
sch.If an asynchronous operation
created by connecting 
sndr with a receiver 
rcvr
causes the evaluation of 
completion-fn(rcvr, args...),
the behavior is undefined
unless the evaluation happens on an execution agent
that belongs to 
sch's associated execution resource
.The expression
forwarding_query(get_completion_scheduler<completion-tag>)
is a core constant expression and has value 
true.get_await_completion_adaptor asks a queryable object for
its associated awaitable completion adaptor
. The name 
get_await_completion_adaptor denotes a query object
.For a subexpression env,
get_await_completion_adaptor(env)
is expression-equivalent to
MANDATE-NOTHROW(AS-CONST(env).query(get_await_completion_adaptor))
forwarding_query(execution::get_await_completion_adaptor)
is a core constant expression and has value 
true.  schedule is a customization point object
that accepts a scheduler
.  A valid invocation of 
schedule is a schedule-expression
. Let 
Sch be the type of a scheduler and
let 
Env be the type of an execution environment
for which 
sender_in<schedule_result_t<Sch>, Env>
is satisfied
. None of these operations,
nor a scheduler type's 
schedule function,
shall introduce data races
as a result of potentially concurrent (
[intro.races]) invocations
of those operations from different threads
. For any two values 
sch1 and 
sch2
of some scheduler type 
Sch,
sch1 == sch2 shall return 
true
only if both 
sch1 and 
sch2 share
the same associated execution resource
.For a given scheduler expression 
sch,
the expression
get_completion_scheduler<set_value_t>(get_env(schedule(sch)))
shall compare equal to 
sch.For a given scheduler expression 
sch,
if the expression 
get_domain(sch) is well-formed,
then the expression 
get_domain(get_env(schedule(sch)))
is also well-formed and has the same type
.A scheduler type's destructor shall not block
pending completion of any receivers
connected to the sender objects returned from 
schedule.[
Note 1: 
The ability to wait for completion of submitted function objects
can be provided by the associated execution resource of the scheduler
. — 
end note]
A receiver represents the continuation of an asynchronous operation
.The 
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 
get_env customization point object is used to access
a receiver's associated environment
.Class types that are marked 
final do not model the 
receiver concept
.Let 
rcvr be a receiver and
let 
op_state be an operation state associated with
an asynchronous operation created by connecting 
rcvr with a sender
.Let 
token be a stop token equal to
get_stop_token(get_env(rcvr)).token shall remain valid
for the duration of the asynchronous operation's lifetime (
[exec.async.ops])
.  [
Note 1: 
This means that, unless it knows about further guarantees
provided by the type of 
rcvr,
the implementation of 
op_state cannot use 
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]
 Its associated completion tag is 
set_value_t.The expression 
set_value(rcvr, vs...)
for a subexpression 
rcvr and
pack of subexpressions 
vs is ill-formed
if 
rcvr is an lvalue or an rvalue of const type
.Otherwise, it is expression-equivalent to
MANDATE-NOTHROW(rcvr.set_value(vs...)).  Its associated completion tag is 
set_error_t.The expression 
set_error(rcvr, err)
for some subexpressions 
rcvr and 
err is ill-formed
if 
rcvr is an lvalue or an rvalue of const type
.Otherwise, it is expression-equivalent to
MANDATE-NOTHROW(rcvr.set_error(err)).  Its associated completion tag is 
set_stopped_t.The expression 
set_stopped(rcvr)
for a subexpression 
rcvr is ill-formed
if 
rcvr is an lvalue or an rvalue of const type
.Otherwise, it is expression-equivalent to
MANDATE-NOTHROW(rcvr.set_stopped()).  namespace std::execution {
  template<class O>
    concept operation_state =
      derived_from<typename O::operation_state_concept, operation_state_t> &&
      requires (O& o) {
        start(o);
      };
}
  [
Note 1: 
The 
operation_state concept does not impose requirements
on any operations other than destruction and 
start,
including copy and move operations
.Invoking any such operation on an object
whose type models 
operation_state can lead to undefined behavior
. — 
end note]
 The program is ill-formed
if it performs a copy or move construction or assignment operation on
an operation state object created by connecting a library-provided sender
.The name 
start denotes a customization point object
that starts (
[exec.async.ops])
the asynchronous operation associated with the operation state object
.For a subexpression 
op,
the expression 
start(op) is ill-formed
if 
op is an rvalue
.Otherwise, it is expression-equivalent to
MANDATE-NOTHROW(op.start()).If 
op.start() does not start (
[exec.async.ops])
the asynchronous operation associated with the operation state 
op,
the behavior of calling 
start(op) is undefined
. Each algorithm has a default implementation
.Let 
sndr be the result of an invocation of such an algorithm or
an object equal to the result (
[concepts.equality]), and
let 
Sndr be 
decltype((sndr)).Let 
rcvr be a receiver of type 
Rcvr
with associated environment 
env of type 
Env
such that 
sender_to<Sndr, Rcvr> is 
true.For the default implementation of the algorithm that produced 
sndr,
connecting 
sndr to 
rcvr and
starting the resulting operation state (
[exec.async.ops])
necessarily results in the potential evaluation (
[basic.def.odr]) of
a set of completion operations
whose first argument is a subexpression equal to 
rcvr.Let 
Sigs be a pack of completion signatures corresponding to
this set of completion operations, and
let 
CS be
the type of the expression 
get_completion_signatures<Sndr, Env>().Then 
CS is
a specialization of
the class template 
completion_signatures (
[exec.cmplsig]),
the set of whose template arguments is 
Sigs.If none of the types in 
Sigs are dependent on the type 
Env, then
the expression 
get_completion_signatures<Sndr>() is well-formed and
its type is 
CS.If a user-provided implementation of the algorithm
that produced sndr is selected instead of the default:
 - Any completion signature
that is in the set of types
denoted by  completion_signatures_of_t<Sndr, Env>-  and
that is not part of  Sigs-  shall correspond to
error or stopped completion operations,
unless otherwise specified .
- If none of the types in  Sigs-  are dependent on the type  Env- , then
 completion_signatures_of_t<Sndr>-  and
 completion_signatures_of_t<Sndr, Env>- 
shall denote the same type .
Subclause 
[exec.snd] makes use of the following exposition-only entities
.For a queryable object 
env,
FWD-ENV(env) is an expression
whose type satisfies 
queryable
such that for a query object 
q and
a pack of subexpressions 
as,
the expression 
FWD-ENV(env).query(q, as...) is ill-formed
if 
forwarding_query(q) is 
false;
otherwise, it is expression-equivalent to 
env.query(q, as...).The type 
FWD-ENV-T(Env) is
decltype(FWD-ENV(declval<Env>())).For a query object 
q and a subexpression 
v,
MAKE-ENV(q, v) is an expression 
env
whose type satisfies 
queryable
such that the result of 
env.query(q) has
a value equal to 
v (
[concepts.equality])
.Unless otherwise stated,
the object to which 
env.query(q) refers remains valid
while 
env remains valid
.For two queryable objects 
env1 and 
env2,
a query object 
q, and
a pack of subexpressions 
as,
JOIN-ENV(env1, env2) is an expression 
env3
whose type satisfies 
queryable
such that 
env3.query(q, as...) is expression-equivalent to:
- env1.query(q, as...) if that expression is well-formed,
- otherwise, env2.query(q, as...) if that expression is well-formed,
- otherwise, env3.query(q, as...) is ill-formed.
The results of 
FWD-ENV, 
MAKE-ENV, and 
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
.For a scheduler 
sch,
SCHED-ATTRS(sch) is an expression 
o1
whose type satisfies 
queryable
such that 
o1.query(get_completion_scheduler<Tag>) is
an expression with the same type and value as 
sch
where 
Tag is one of 
set_value_t or 
set_stopped_t, and
such that 
o1.query(get_domain) is expression-equivalent to
sch.query(get_domain).SCHED-ENV(sch) is an expression 
o2
whose type satisfies 
queryable
such that 
o2.query(get_scheduler) is a prvalue
with the same type and value as 
sch, and
such that 
o2.query(get_domain) is expression-equivalent to
sch.query(get_domain). For two subexpressions 
rcvr and 
expr,
SET-VALUE(rcvr, expr) is expression-equivalent to
(expr, set_value(std::move(rcvr)))
if the type of 
expr is 
void;
otherwise, 
set_value(std::move(rcvr), expr).TRY-EVAL(rcvr, expr) is equivalent to:
try {
  expr;
} catch(...) {
  set_error(std::move(rcvr), current_exception());
}
if 
expr is potentially-throwing; otherwise, 
expr.  TRY-SET-VALUE(rcvr, expr) is
TRY-EVAL(rcvr, SET-VALUE(rcvr, expr))
except that 
rcvr is evaluated only once
. template<class Default = default_domain, class Sndr>
  constexpr auto completion-domain(const Sndr& sndr) noexcept;
COMPL-DOMAIN(T) is the type of the expression
get_domain(get_completion_scheduler<T>(get_env(sndr))). Effects: If all of the types
COMPL-DOMAIN(set_value_t),
COMPL-DOMAIN(set_error_t), and
COMPL-DOMAIN(set_stopped_t) are ill-formed,
completion-domain<Default>(sndr) is
a default-constructed prvalue of type 
Default.  Otherwise, if they all share a common type (
[meta.trans.other])
(ignoring those types that are ill-formed),
then 
completion-domain<Default>(sndr) is
a default-constructed prvalue of that type
.Otherwise, 
completion-domain<Default>(sndr) is ill-formed
.template<class Tag, class Env, class Default>
  constexpr decltype(auto) query-with-default(
    Tag, const Env& env, Default&& value) noexcept(see below);
Let 
e be the expression 
Tag()(env)
if that expression is well-formed;
otherwise, it is 
static_cast<Default>(std::forward<Default>(value)).Remarks: The expression in the noexcept clause is 
noexcept(e). template<class Sndr>
  constexpr auto get-domain-early(const Sndr& sndr) noexcept;
Effects: Equivalent to:
return Domain();
where 
Domain is
the decayed type of the first of the following expressions that is well-formed:
- get_domain(get_env(sndr))
- completion-domain(sndr)
- default_domain()
 template<class Sndr, class Env>
  constexpr auto get-domain-late(const Sndr& sndr, const Env& env) noexcept;
Effects: Equivalent to:
- If sender-for<Sndr, continues_on_t> is true, then
return Domain();
where Domain is the type of the following expression:
[] {
  auto [_, sch, _] = sndr;
  return query-with-default(get_domain, sch, default_domain());
}();
[ Note 1:  The  continues_on algorithm works
in tandem with  schedule_from ( [exec.schedule.from])
to give scheduler authors a way to customize both
how to transition onto ( continues_on) and off of ( schedule_from)
a given execution context .
Thus,  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  continues_on.
That is why it is given special treatment here .
 —  end note] 
- Otherwise,
return Domain();
where Domain is the first of the following expressions
that is well-formed and whose type is not void:
- get_domain(get_env(sndr))
- completion-domain<void>(sndr)
- get_domain(env)
- get_domain(get_scheduler(env))
- default_domain()
 
 template<callable Fun>
  requires is_nothrow_move_constructible_v<Fun>
struct emplace-from {
  Fun fun;                                                      
  using type = call-result-t<Fun>;
  constexpr operator type() && noexcept(nothrow-callable<Fun>) {
    return std::move(fun)();
  }
  constexpr type operator()() && noexcept(nothrow-callable<Fun>) {
    return std::move(fun)();
  }
};
[
Note 2: 
emplace-from is used to emplace non-movable types
into 
tuple, 
optional, 
variant, and similar types
.  — 
end note]
 struct on-stop-request {
  inplace_stop_source& stop-src;       
  void operator()() noexcept { stop-src.request_stop(); }
};
template<class T0, class T1, …, class Tn>
struct product-type {       
  T0 t0;                
  T1 t1;                
    ⋮
  Tn tn;                
  template<size_t I, class Self>
  constexpr decltype(auto) get(this Self&& self) noexcept;      
  template<class Self, class Fn>
  constexpr decltype(auto) apply(this Self&& self, Fn&& fn)     
    noexcept(see below);
};
[
Note 3: 
product-type is presented here in pseudo-code form
for the sake of exposition
.  It can be approximated in standard C++ 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]
[
Note 4: 
An expression of type 
product-type is usable as
the initializer of a structured binding declaration (
[dcl.struct.bind])
. — 
end note]
template<size_t I, class Self>
constexpr decltype(auto) get(this Self&& self) noexcept;
Effects: Equivalent to:
auto& [...ts] = self;
return std::forward_like<Self>(ts...[I]);
template<class Self, class Fn>
constexpr decltype(auto) apply(this Self&& self, Fn&& fn) noexcept(see below);
Constraints: The expression in the 
return statement below is well-formed
. Effects: Equivalent to:
auto& [...ts] = self;
return std::forward<Fn>(fn)(std::forward_like<Self>(ts)...);
Remarks: The expression in the 
noexcept clause is 
true
if the 
return statement above is not potentially throwing;
otherwise, 
false. template<class Tag, class Data = see below, class... Child>
  constexpr auto make-sender(Tag tag, Data&& data, Child&&... child);
Mandates: The following expressions are 
true:
- semiregular<Tag>
- movable-value<Data>
- (sender<Child> && ...)
- dependent_sender<Sndr> || sender_in<Sndr>,
where Sndr is basic-sender<Tag, Data,
 Child...>
as defined below.Recommended practice: If evaluation of  sender_in<Sndr> results in
an uncaught exception from
the evaluation of  get_completion_signatures<Sndr>(),
the implementation should include information about that exception in
the resulting diagnostic .
 
 Returns: A prvalue of
type 
basic-sender<Tag, decay_t<Data>, decay_t<Child>...>
that has been direct-list-initialized with the forwarded arguments,
where 
basic-sender is the following exposition-only class template except as noted below
. Remarks: The default template argument for the 
Data template parameter
denotes an unspecified empty trivially copyable class type
that models 
semiregular. namespace std::execution {
  template<class Tag>
  concept completion-tag =                                      
    same_as<Tag, set_value_t> || same_as<Tag, set_error_t> || same_as<Tag, set_stopped_t>;
  struct default-impls {                                        
    static constexpr auto get-attrs = see below;                
    static constexpr auto get-env = see below;                  
    static constexpr auto get-state = see below;                
    static constexpr auto start = see below;                    
    static constexpr auto complete = see below;                 
    template<class Sndr, class... Env>
      static consteval void check-types();                      
  };
  template<class Tag>
  struct impls-for : default-impls {};                          
  template<class Sndr, class Rcvr>                              
  using state-type = decay_t<call-result-t<
    decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&>>;
  template<class Index, class Sndr, class Rcvr>                 
  using env-type = call-result-t<
    decltype(impls-for<tag_of_t<Sndr>>::get-env), Index,
    state-type<Sndr, Rcvr>&, const Rcvr&>;
  template<class Sndr>
  using data-type = decltype(declval<Sndr>().template get<1>());                
  template<class Sndr, size_t I = 0>
  using child-type = decltype(declval<Sndr>().template get<I+2>());             
  template<class Sndr>
  using indices-for = remove_reference_t<Sndr>::indices-for;                    
  template<class Sndr, class Rcvr>
  struct basic-state {                                          
    basic-state(Sndr&& sndr, Rcvr&& rcvr) noexcept(see below)
      : rcvr(std::move(rcvr))
      , state(impls-for<tag_of_t<Sndr>>::get-state(std::forward<Sndr>(sndr), rcvr)) { }
    Rcvr rcvr;                                                  
    state-type<Sndr, Rcvr> state;                               
  };
  template<class Sndr, class Rcvr, class Index>
    requires valid-specialization<env-type, Index, Sndr, Rcvr>
  struct basic-receiver {                                       
    using receiver_concept = receiver_t;
    using tag-t = tag_of_t<Sndr>;                               
    using state-t = state-type<Sndr, Rcvr>;                     
    static constexpr const auto& complete = impls-for<tag-t>::complete;         
    template<class... Args>
      requires callable<decltype(complete), Index, state-t&, Rcvr&, set_value_t, Args...>
    void set_value(Args&&... args) && noexcept {
      complete(Index(), op->state, op->rcvr, set_value_t(), std::forward<Args>(args)...);
    }
    template<class Error>
      requires callable<decltype(complete), Index, state-t&, Rcvr&, set_error_t, Error>
    void set_error(Error&& err) && noexcept {
      complete(Index(), op->state, op->rcvr, set_error_t(), std::forward<Error>(err));
    }
    void set_stopped() && noexcept
      requires callable<decltype(complete), Index, state-t&, Rcvr&, set_stopped_t> {
      complete(Index(), op->state, op->rcvr, set_stopped_t());
    }
    auto get_env() const noexcept -> env-type<Index, Sndr, Rcvr> {
      return impls-for<tag-t>::get-env(Index(), op->state, op->rcvr);
    }
    basic-state<Sndr, Rcvr>* op;                                
  };
  constexpr auto connect-all = see below;                       
  template<class Sndr, class Rcvr>
  using connect-all-result = call-result-t<                     
    decltype(connect-all), basic-state<Sndr, Rcvr>*, Sndr, indices-for<Sndr>>;
  template<class Sndr, class Rcvr>
    requires valid-specialization<state-type, Sndr, Rcvr> &&
             valid-specialization<connect-all-result, Sndr, Rcvr>
  struct basic-operation : basic-state<Sndr, Rcvr> {            
    using operation_state_concept = operation_state_t;
    using tag-t = tag_of_t<Sndr>;                               
    connect-all-result<Sndr, Rcvr> inner-ops;                   
    basic-operation(Sndr&& sndr, Rcvr&& rcvr) noexcept(see below)               
      : basic-state<Sndr, Rcvr>(std::forward<Sndr>(sndr), std::move(rcvr)),
        inner-ops(connect-all(this, std::forward<Sndr>(sndr), indices-for<Sndr>()))
    {}
    void start() & noexcept {
      auto& [...ops] = inner-ops;
      impls-for<tag-t>::start(this->state, this->rcvr, ops...);
    }
  };
  template<class Tag, class Data, class... Child>
  struct basic-sender : product-type<Tag, Data, Child...> {     
    using sender_concept = sender_t;
    using indices-for = index_sequence_for<Child...>;           
    decltype(auto) get_env() const noexcept {
      auto& [_, data, ...child] = *this;
      return impls-for<Tag>::get-attrs(data, child...);
    }
    template<decays-to<basic-sender> Self, receiver Rcvr>
    auto connect(this Self&& self, Rcvr rcvr) noexcept(see below)
      -> basic-operation<Self, Rcvr> {
      return {std::forward<Self>(self), std::move(rcvr)};
    }
    template<decays-to<basic-sender> Self, class... Env>
    static constexpr auto get_completion_signatures();
  };
}
 It is unspecified whether a specialization of 
basic-sender
is an aggregate
.An expression of type 
basic-sender is usable as
the initializer of a structured binding declaration (
[dcl.struct.bind])
.The expression in the 
noexcept clause of
the constructor of 
basic-state is
is_nothrow_move_constructible_v<Rcvr> &&
nothrow-callable<decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&> &&
(same_as<state-type<Sndr, Rcvr>, get-state-result> ||
 is_nothrow_constructible_v<state-type<Sndr, Rcvr>, get-state-result>)
where 
get-state-result is
call-result-t<decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&>.
The object connect-all is initialized with
a callable object equivalent to the following lambda:
[]<class Sndr, class Rcvr, size_t... Is>(
  basic-state<Sndr, Rcvr>* op, Sndr&& sndr, index_sequence<Is...>) noexcept(see below)
    -> decltype(auto) {
    auto& [_, data, ...child] = sndr;
    return product-type{connect(
      std::forward_like<Sndr>(child),
      basic-receiver<Sndr, Rcvr, integral_constant<size_t, Is>>{op})...};
  }
Constraints: The expression in the 
return statement is well-formed
. Remarks: The expression in the 
noexcept clause is 
true
if the 
return statement is not potentially throwing;
otherwise, 
false. The expression in the noexcept clause of
the constructor of basic-operation is:
is_nothrow_constructible_v<basic-state<Self, Rcvr>, Self, Rcvr> &&
noexcept(connect-all(this, std::forward<Sndr>(sndr), indices-for<Sndr>()))
The expression in the noexcept clause of
the connect member function of basic-sender is:
is_nothrow_constructible_v<basic-operation<Self, Rcvr>, Self, Rcvr>
The member default-impls::get-attrs
is initialized with a callable object equivalent to the following lambda:
[](const auto&, const auto&... child) noexcept -> decltype(auto) {
  if constexpr (sizeof...(child) == 1)
    return (FWD-ENV(get_env(child)), ...);
  else
    return env<>();
}
The member default-impls::get-env
is initialized with a callable object equivalent to the following lambda:
[](auto, auto&, const auto& rcvr) noexcept -> decltype(auto) {
  return FWD-ENV(get_env(rcvr));
}
The member default-impls::get-state
is initialized with a callable object equivalent to the following lambda:
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept -> decltype(auto) {
  auto& [_, data, ...child] = sndr;
  return allocator-aware-forward(std::forward_like<Sndr>(data), rcvr);
}
The member default-impls::start
is initialized with a callable object equivalent to the following lambda:
[](auto&, auto&, auto&... ops) noexcept -> void {
  (execution::start(ops), ...);
}
The member 
default-impls::complete
is initialized with a callable object equivalent to the following lambda:
[]<class Index, class Rcvr, class Tag, class... Args>(
  Index, auto& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
    -> void requires callable<Tag, Rcvr, Args...> {
  static_assert(Index::value == 0);
  Tag()(std::move(rcvr), std::forward<Args>(args)...);
}
template<class Sndr, class... Env>
  static consteval void default-impls::check-types();
Let 
Is be the pack of integral template arguments of
the 
integer_sequence specialization denoted by
indices-for<Sndr>.Effects: Equivalent to:
(get_completion_signatures<child-type<Sndr, Is>, FWD-ENV-T(Env)...>(), ...);
[
Note 5: 
For any types 
T and 
S, and pack 
E,
let 
e be the expression
impls-for<T>::check-types<S, E...>().Then exactly one of the following is 
true:
- e is ill-formed, or
- the evaluation of e exits with an exception, or
- e is a core constant expression.
When 
e is a core constant expression,
the pack 
S, E... uniquely determines a set of completion signatures
. — 
end note]
template<class Tag, class Data, class... Child>
  template<class Sndr, class... Env>
    constexpr auto basic-sender<Tag, Data, Child...>::get_completion_signatures();
Let 
Rcvr be the type of a receiver whose
environment has type 
E, where
E is the first type in the list 
Env..., env<>.Let CHECK-TYPES() be the expression
impls-for<Tag>::template check-types<
Sndr, E>(), and
let CS be a type determined as follows:
- If  CHECK-TYPES()-  is a core constant expression,
let  op-  be an lvalue subexpression
whose type is  connect_result_t<Sndr, Rcvr>.
- Then  CS-  is the specialization of  completion_signatures- 
the set of whose template arguments
correspond to the set of completion operations
that are potentially evaluated ( [basic.def.odr]- )
as a result of evaluating  op.start().
- Otherwise,  CS-  is  completion_signatures<>.
Constraints: 
CHECK-TYPES() is a well-formed expression
. Effects: Equivalent to:
CHECK-TYPES();
return CS();
template<class... Fns>
struct overload-set : Fns... {
  using Fns::operator()...;
};
 
struct not-a-sender {
  using sender_concept = sender_t;
  template<class Sndr>
    static consteval auto get_completion_signatures() -> completion_signatures<> {
      throw unspecified-exception();
  }
};
where 
unspecified-exception is
a type derived from 
exception. 
constexpr void decay-copyable-result-datums(auto cs) {
  cs.for-each([]<class Tag, class... Ts>(Tag(*)(Ts...)) {
    if constexpr (!(is_constructible_v<decay_t<Ts>, Ts> &&...))
      throw unspecified-exception();
  });
}
where 
unspecified-exception is
a type derived from 
exception. template<class T, class Context>
  decltype(auto) allocator-aware-forward(T&& obj, Context&& context);       
allocator-aware-forward is an exposition-only function template
used to either
create a new object of type 
remove_cvref_t<T> from 
obj
or forward 
obj depending on whether an allocator is available
.  If the environment associated with 
context provides an allocator
(i.e., the expression 
get_allocator(get_env(context)) is valid),
let 
alloc be the result of this expression
and let 
P be 
remove_cvref_t<T>.Returns: 
- If alloc is not defined, returns std::forward<T>(obj),
- otherwise if P is a specialization of product-type,
returns an object of type P whose elements are initialized using
make_obj_using_allocator<decltype(e)>(std::forward_like<T>(e), alloc)
where e is the corresponding element of obj,
- otherwise, returns make_obj_using_allocator<P>(std::forward<T>(obj), alloc).
  The 
sender_in concept defines
the requirements for a sender type
that can create asynchronous operations given an associated environment type
.The 
sender_to concept defines
the requirements for a sender type
that can connect with a specific receiver type
.The 
get_env customization point object is used to access
a sender's associated attributes
.The connect customization point object is used to connect (
[exec.async.ops])
a sender and a receiver to produce an operation state
. namespace std::execution {
  template<auto>
    concept is-constant = true;                                 
  template<class Sndr>
    concept is-sender =                                         
      derived_from<typename Sndr::sender_concept, sender_t>;
  template<class Sndr>
    concept enable-sender =                                     
      is-sender<Sndr> ||
      is-awaitable<Sndr, env-promise<env<>>>;                 
  template<class Sndr>
    inline constexpr bool enable_sender = enable-sender<Sndr>;
  template<class Sndr>
    consteval bool is-dependent-sender-helper() try {           
      get_completion_signatures<Sndr>();
      return false;
    } catch (dependent_sender_error&) {
      return true;
    }
  template<class Sndr>
    concept sender =
      enable_sender<remove_cvref_t<Sndr>> &&
      requires (const remove_cvref_t<Sndr>& sndr) {
        { get_env(sndr) } -> queryable;
      } &&
      move_constructible<remove_cvref_t<Sndr>> &&
      constructible_from<remove_cvref_t<Sndr>, Sndr>;
  template<class Sndr, class... Env>
    concept sender_in =
      sender<Sndr> &&
      (sizeof...(Env) <= 1) &&
      (queryable<Env> &&...) &&
      is-constant<get_completion_signatures<Sndr, Env...>()>;
  template<class Sndr>
    concept dependent_sender =
      sender<Sndr> && bool_constant<is-dependent-sender-helper<Sndr>()>::value;
  template<class Sndr, class Rcvr>
    concept sender_to =
      sender_in<Sndr, env_of_t<Rcvr>> &&
      receiver_of<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>> &&
      requires (Sndr&& sndr, Rcvr&& rcvr) {
        connect(std::forward<Sndr>(sndr), std::forward<Rcvr>(rcvr));
      };
}
 Given a subexpression 
sndr,
let 
Sndr be 
decltype((sndr)) and
let 
rcvr be a receiver
with an associated environment whose type is 
Env.A completion operation is a 
permissible completion
for 
Sndr and 
Env
if its completion signature appears in the argument list of the specialization of 
completion_signatures denoted by
completion_signatures_of_t<Sndr, Env>.Sndr and 
Env model 
sender_in<Sndr, Env>
if all the completion operations
that are potentially evaluated by connecting 
sndr to 
rcvr and
starting the resulting operation state
are permissible completions for 
Sndr and 
Env. Remarks: Pursuant to 
[namespace.std],
users may specialize 
enable_sender to
true for cv-unqualified program-defined types that
model 
sender, and
false for types that do not
.  Such specializations shall
be usable in constant expressions (
[expr.const]) and
have type 
const bool.The exposition-only concepts
sender-of and 
sender-in-of
define the requirements for a sender type
that completes with a given unique set of value result types
.Let 
sndr be an expression
such that 
decltype((sndr)) is 
Sndr.The type 
tag_of_t<Sndr> is as follows:
- If the declaration
auto&& [tag, data, ...children] = sndr;
would be well-formed, tag_of_t<Sndr> is
an alias for decltype(auto(tag)).
- Otherwise, tag_of_t<Sndr> is ill-formed.
Let 
sender-for be an exposition-only concept defined as follows:
namespace std::execution {
  template<class Sndr, class Tag>
  concept sender-for =
    sender<Sndr> &&
    same_as<tag_of_t<Sndr>, Tag>;
}
For a type 
T,
SET-VALUE-SIG(T) denotes the type 
set_value_t()
if 
T is 
cv void;
otherwise, it denotes the type 
set_value_t(T).Library-provided sender types
- always expose an overload of a member connect
that accepts an rvalue sender and
- only expose an overload of a member connect
that accepts an lvalue sender if they model copy_constructible.
The sender concepts recognize awaitables as senders
.For [exec], an 
awaitable is an expression
that would be well-formed as the operand of a 
co_await expression
within a given context
.For a subexpression 
c,
let 
GET-AWAITER(c, p) be expression-equivalent to
the series of transformations and conversions applied to 
c
as the operand of an 
await-expression in a coroutine,
resulting in lvalue 
e as described by 
[expr.await],
where 
p is an lvalue referring to the coroutine's promise,
which has type 
Promise.[
Note 1: 
This includes the invocation of
the promise type's 
await_transform member if any,
the invocation of the 
operator co_await
picked by overload resolution if any, and
any necessary implicit conversions and materializations
. — 
end note]
Let 
GET-AWAITER(c) be
expression-equivalent to 
GET-AWAITER(c, q)
where 
q is an lvalue of
an unspecified empty class type 
none-such that
lacks an 
await_transform member, and
where 
coroutine_handle<none-such> behaves as
coroutine_handle<void>.Let 
is-awaitable be the following exposition-only concept:
namespace std {
  template<class T>
  concept await-suspend-result = see below;                     
  template<class A, class... Promise>
  concept is-awaiter =                                          
    requires (A& a, coroutine_handle<Promise...> h) {
      a.await_ready() ? 1 : 0;
      { a.await_suspend(h) } -> await-suspend-result;
      a.await_resume();
    };
  template<class C, class... Promise>
  concept is-awaitable =                                        
    requires (C (*fc)() noexcept, Promise&... p) {
      { GET-AWAITER(fc(), p...) } -> is-awaiter<Promise...>;
    };
}
await-suspend-result<T> is 
true
if and only if one of the following is 
true:
- T is void, or
- T is bool, or
- T is a specialization of coroutine_handle.
 For a subexpression 
c
such that 
decltype((c)) is type 
C, and
an lvalue 
p of type 
Promise,
await-result-
type<C, Promise> denotes
the type 
decltype(GET-AWAITER(c, p).await_resume()) and
await-result-type<C> denotes
the type 
decltype(GET-AWAITER(c).await_resume()).Let 
with-await-transform be the exposition-only class template:
namespace std::execution {
  template<class T, class Promise>
    concept has-as-awaitable =                                  
      requires (T&& t, Promise& p) {
        { std::forward<T>(t).as_awaitable(p) } -> is-awaitable<Promise&>;
      };
  template<class Derived>
    struct with-await-transform {                               
      template<class T>
        T&& await_transform(T&& value) noexcept {
          return std::forward<T>(value);
        }
      template<has-as-awaitable<Derived> T>
        auto await_transform(T&& value)
          noexcept(noexcept(std::forward<T>(value).as_awaitable(declval<Derived&>())))
          -> decltype(std::forward<T>(value).as_awaitable(declval<Derived&>())) {
          return std::forward<T>(value).as_awaitable(static_cast<Derived&>(*this));
        }
    };
}
Let 
env-promise be the exposition-only class template:
namespace std::execution {
  template<class Env>
  struct env-promise : with-await-transform<env-promise<Env>> { 
    unspecified get_return_object() noexcept;
    unspecified initial_suspend() noexcept;
    unspecified final_suspend() noexcept;
    void unhandled_exception() noexcept;
    void return_void() noexcept;
    coroutine_handle<> unhandled_stopped() noexcept;
    const Env& get_env() const noexcept;
  };
}
[
Note 2: 
Specializations of 
env-promise are used only for the purpose of type computation;
its members need not be defined
. — 
end note]
namespace std::execution {
  struct default_domain {
    template<sender Sndr, queryable... Env>
        requires (sizeof...(Env) <= 1)
      static constexpr sender decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)
        noexcept(see below);
    template<sender Sndr, queryable Env>
      static constexpr queryable decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept;
    template<class Tag, sender Sndr, class... Args>
      static constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)
        noexcept(see below);
  };
}
 template<sender Sndr, queryable... Env>
  requires (sizeof...(Env) <= 1)
constexpr sender decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)
  noexcept(see below);
Let 
e be the expression
tag_of_t<Sndr>().transform_sender(std::forward<Sndr>(sndr), env...)
if that expression is well-formed;
otherwise, 
std::forward<Sndr>(sndr).Remarks: The exception specification is equivalent to 
noexcept(e). Let 
e be the expression
tag_of_t<Sndr>().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env))
if that expression is well-formed;
otherwise, 
FWD-ENV(std::forward<Env>(env)).Mandates: 
noexcept(e) is 
true. template<class Tag, sender Sndr, class... Args>
constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)
  noexcept(see below);
Let e be the expression
  Tag().apply_sender(std::forward<Sndr>(sndr), std::forward<Args>(args)...)
Constraints: 
e is a well-formed expression
. Remarks: The exception specification is equivalent to 
noexcept(e). namespace std::execution {
  template<class Domain, class Tag, sender Sndr, class... Args>
    constexpr decltype(auto) apply_sender(Domain dom, Tag, Sndr&& sndr, Args&&... args)
      noexcept(see below);
}
Let e be the expression
dom.apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(args)...)
if that expression is well-formed; otherwise,
default_domain().apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(args)...)
Constraints: The expression 
e is well-formed
. Remarks: The exception specification is equivalent to 
noexcept(e).  Let 
CHECKED-COMPLSIGS(e) be 
e
if 
e is a core constant expression whose
type satisfies 
valid-completion-signatures;
otherwise, it is the following expression:
(e, throw except, completion_signatures())
Let 
get-complsigs<Sndr, Env...>()
be expression-equivalent to
remove_reference_t<Sndr>::template get_completion_signatures<Sndr, Env...>().Let NewSndr be Sndr
if sizeof...(Env) == 0 is true;
otherwise, decltype(s)
where s is the following expression:
transform_sender(
  get-domain-late(declval<Sndr>(), declval<Env>()...),
  declval<Sndr>(),
  declval<Env>()...)
 Constraints: 
sizeof...(Env) <= 1 is 
true. Effects: Equivalent to: 
return e;
where 
e is expression-equivalent to the following:
- CHECKED-COMPLSIGS(get-complsigs<NewSndr, Env...>())- 
if  get-complsigs<NewSndr, Env
 ...>()- 
is a well-formed expression .
 
- Otherwise,
 CHECKED-COMPLSIGS(get-complsigs<NewSndr>())- 
if  get-complsigs<NewSndr>()- 
is a well-formed expression .
- Otherwise,
 completion_signatures<
  SET-VALUE-SIG(await-result-type<NewSndr, env-promise<Env>...>),   
  set_error_t(exception_ptr),
  set_stopped_t()>- 
if  is-awaitable<NewSndr, env-promise<Env>...>- 
is  true.
- Otherwise,
 (throw dependent-sender-error(), completion_signatures())- 
if  sizeof...(
 Env) == 0-  is  true- ,
where  dependent-sender-error-  is
 dependent_sender_error-  or
an unspecified type derived publicly and unambiguously from
 dependent_sender_error.
- Otherwise,
 (throw except, completion_signatures()).
 Given a type 
Env, if
completion_signatures_of_t<Sndr> and
completion_signatures_of_t<Sndr, Env>
are both well-formed,
they shall denote the same type
.Let 
rcvr be an rvalue
whose type 
Rcvr models 
receiver, and
let 
Sndr be the type of a sender
such that 
sender_in<Sndr, env_of_t<Rcvr>> is 
true.Let 
Sigs... be the template arguments of
the 
completion_signatures specialization
named by 
completion_signatures_of_t<Sndr, env_of_t<Rcvr>>.Let 
CSO be a completion function
.If sender 
Sndr or its operation state cause
the expression 
CSO(rcvr, args...)
to be potentially evaluated (
[basic.def.odr])
then there shall be a signature 
Sig in 
Sigs...
such that
MATCHING-SIG(decayed-typeof<CSO>(decltype(args)...), Sig)
is 
true (
[exec.general])
.The name 
connect denotes a customization point object
.For subexpressions 
sndr and 
rcvr,
let 
Sndr be 
decltype((sndr)) and
Rcvr be 
decltype((rcvr)),
let 
new_sndr be the expression
transform_sender(decltype(get-domain-late(sndr, get_env(rcvr))){}, sndr, get_env(rcvr))
and let 
DS and 
DR be
decay_t<decltype((new_sndr))> and 
decay_t<Rcvr>, respectively
.Let connect-awaitable-promise be the following exposition-only class:
namespace std::execution {
  struct connect-awaitable-promise : with-await-transform<connect-awaitable-promise> {
    connect-awaitable-promise(DS&, DR& rcvr) noexcept : 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(rcvr));
      return noop_coroutine();
    }
    operation-state-task get_return_object() noexcept {
      return operation-state-task{
        coroutine_handle<connect-awaitable-promise>::from_promise(*this)};
    }
    env_of_t<DR> get_env() const noexcept {
      return execution::get_env(rcvr);
    }
  private:
    DR& rcvr;                           
  };
}
Let operation-state-task be the following exposition-only class:
namespace std::execution {
  struct operation-state-task {                              
    using operation_state_concept = operation_state_t;
    using promise_type = connect-awaitable-promise;
    explicit operation-state-task(coroutine_handle<> h) noexcept : coro(h) {}
    operation-state-task(operation-state-task&&) = delete;
    ~operation-state-task() { coro.destroy(); }
    void start() & noexcept {
      coro.resume();
    }
  private:
    coroutine_handle<> coro;                                    
  };
}
Let 
V name the type
await-result-type<DS, connect-awaitable-promise>,
let 
Sigs name the type
completion_signatures<
  SET-VALUE-SIG(V),         
  set_error_t(exception_ptr),
  set_stopped_t()>
and let 
connect-awaitable be an exposition-only coroutine
defined as follows:
namespace std::execution {
  template<class Fun, class... Ts>
  auto suspend-complete(Fun fun, Ts&&... as) noexcept {    
    auto fn = [&, fun]() noexcept { fun(std::forward<Ts>(as)...); };
    struct awaiter {
      decltype(fn) fn;                                     
      static constexpr bool await_ready() noexcept { return false; }
      void await_suspend(coroutine_handle<>) noexcept { fn(); }
      [[noreturn]] void await_resume() noexcept { unreachable(); }
    };
    return awaiter{fn};
  }
  operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs> {
    exception_ptr ep;
    try {
      if constexpr (same_as<V, void>) {
        co_await std::move(sndr);
        co_await suspend-complete(set_value, std::move(rcvr));
      } else {
        co_await suspend-complete(set_value, std::move(rcvr), co_await std::move(sndr));
      }
    } catch(...) {
      ep = current_exception();
    }
    co_await suspend-complete(set_error, std::move(rcvr), std::move(ep));
  }
}
The expression 
connect(sndr, rcvr) is expression-equivalent to:
- new_sndr.connect(rcvr)-  if that expression is well-formed .
 
- Otherwise,  connect-awaitable(new_sndr, rcvr).
Except that 
rcvr is evaluated only once
.Mandates: The following are 
true:
 The name 
schedule denotes a customization point object
.For a subexpression 
sch,
the expression 
schedule(sch) is expression-equivalent to
sch.schedule().Mandates: The type of 
sch.schedule() satisfies 
sender. If the expression
get_completion_scheduler<set_value_t>(get_env(sch.schedule())) == sch
is ill-formed or evaluates to 
false,
the behavior of calling 
schedule(sch) is undefined
.just, 
just_error, and 
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
. The names 
just, 
just_error, and 
just_stopped denote
customization point objects
.Let 
just-cpo be one of
just, 
just_error, or 
just_stopped.For a pack of subexpressions 
ts,
let 
Ts be the pack of types 
decltype((ts)).The expression 
just-cpo(ts...) is ill-formed if
- (movable-value<Ts> &&...) is false, or
- just-cpo is just_error and
sizeof...(ts) == 1 is false, or
- just-cpo is just_stopped and
sizeof...(ts) == 0 is false.
Otherwise, it is expression-equivalent to
make-sender(just-cpo, product-type{ts...}).For 
just, 
just_error, and 
just_stopped,
let 
set-cpo be
set_value, 
set_error, and 
set_stopped, respectively
.The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
just-cpo as follows:
namespace std::execution {
  template<>
  struct impls-for<decayed-typeof<just-cpo>> : default-impls {
    static constexpr auto start =
      [](auto& state, auto& rcvr) noexcept -> void {
        auto& [...ts] = state;
        set-cpo(std::move(rcvr), std::move(ts)...);
      };
  };
}
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
. read_env is a customization point object
.  For some query object 
q,
the expression 
read_env(q) is expression-equivalent to
make-sender(read_env, q).The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
read_env as follows:
namespace std::execution {
  template<>
  struct impls-for<decayed-typeof<read_env>> : default-impls {
    static constexpr auto start =
      [](auto query, auto& rcvr) noexcept -> void {
        TRY-SET-VALUE(rcvr, query(get_env(rcvr)));
      };
  };
  template<class Sndr, class Env>
    static consteval void check-types();
}
template<class Sndr, class Env>
  static consteval void check-types();
Let 
Q be 
decay_t<data-type<Sndr>>.Throws: An exception of an unspecified type derived from 
exception if
the expression 
Q()(env) is ill-formed or has type 
void, where
env is an lvalue subexpression whose type is 
Env. The bitwise inclusive 
or operator is overloaded
for the purpose of creating sender chains
.The adaptors also support function call syntax with equivalent semantics
.Unless otherwise specified:
- 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  connect- ,
and  start-  is called on the resulting operation state .
- A parent sender with more than one child sender has
an associated attributes object equal to  env<>{}.
- When a parent sender is connected to a receiver  rcvr- ,
any receiver used to connect a child sender has
an associated environment equal to  FWD-ENV(get_env(rcvr)).
- An adaptor whose child senders are all non-dependent ( [exec.async.ops]- )
is itself non-dependent .
- These requirements apply to any function
that is selected by the implementation of the sender adaptor .
- Recommended practice- : Implementations should use
the completion signatures of the adaptors
to communicate type errors to users and
to propagate any such type errors from child senders .
 
If a sender returned from a sender adaptor specified in 
[exec.adapt]
is specified to include 
set_error_t(Err)
among its set of completion signatures
where 
decay_t<Err> denotes the type 
exception_ptr,
but the implementation does not potentially evaluate
an error completion operation with an 
exception_ptr argument,
the implementation is allowed to omit
the 
exception_ptr error completion signature from the set
. For a pipeable sender adaptor closure object 
c and
an expression 
sndr
such that 
decltype((sndr)) models 
sender,
the following expressions are equivalent and yield a 
sender:
c(sndr)
sndr | c
Given an additional pipeable sender adaptor closure object d,
the expression c | d produces
another pipeable sender adaptor closure object e:
 e is a perfect forwarding call wrapper (
[func.require])
with the following properties:
- Its target object is an object  d2-  of type  decltype(auto(d))- 
direct-non-list-initialized with  d.
- It has one bound argument entity,
an object  c2-  of type  decltype(auto(c))- 
direct-non-list-initialized with  c.
- Its call pattern is  d2(c2(arg))- ,
where arg is the argument used in a function call expression of  e.
  
The expression 
c | d is well-formed if and only if
the initializations of the state entities (
[func.def]) of 
e
are all well-formed
.An object 
t of type 
T is
a pipeable sender adaptor closure object
if 
T models 
derived_from<sender_adaptor_closure<T>>,
T has no other base classes
of type 
sender_adaptor_closure<U> for any other type 
U, and
T does not satisfy 
sender.The template parameter 
D for 
sender_adaptor_closure can be
an incomplete type
.Before any expression of type 
cv D appears as
an operand to the 
| operator,
D shall be complete and
model 
derived_from<sender_adaptor_closure<D>>.The behavior of an expression involving an object of type 
cv D
as an operand to the 
| operator is undefined
if overload resolution selects a program-defined 
operator| function
. If a pipeable sender adaptor object accepts only one argument,
then it is a pipeable sender adaptor closure object
. If a pipeable sender adaptor object adaptor accepts more than one argument,
then let 
sndr be an expression
such that 
decltype((sndr)) models 
sender,
let 
args... be arguments
such that 
adaptor(sndr, args...) is a well-formed expression
as specified below, and
let 
BoundArgs be a pack that denotes 
decltype(auto(args))....The expression 
adaptor(args...) produces
a pipeable sender adaptor closure object 
f
that is a perfect forwarding call wrapper with the following properties:
- Its target object is a copy of adaptor .
- Its bound argument entities  bound_args-  consist of
objects of types  BoundArgs...-  direct-non-list-initialized with
 std::forward<decltype((args))>(args)...- , respectively .
- Its call pattern is  adaptor(rcvr, bound_args...)- ,
where  rcvr-  is
the argument used in a function call expression of  f.
The expression 
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
.write_env is a sender adaptor
that accepts a sender and a queryable object, and
that returns a sender that,
when connected with a receiver 
rcvr,
connects the adapted sender with a receiver
whose execution environment is the result of
joining the 
queryable object
to the result of 
get_env(rcvr). write_env is a customization point object
.  For some subexpressions 
sndr and 
env,
if 
decltype((sndr)) does not satisfy 
sender or
if 
decltype((env)) does not satisfy 
queryable,
the expression 
write_env(sndr, env) is ill-formed
.Otherwise, it is expression-equivalent to
make-sender(write_env, env, sndr).Let 
write-env-t denote the type 
decltype(auto(write_env)).The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
write-env-t as follows:
template<>
struct impls-for<write-env-t> : default-impls {
  static constexpr auto join-env(const auto& state, const auto& env) noexcept {
    return see below;
  }
  static constexpr auto get-env =
    [](auto, const auto& state, const auto& rcvr) noexcept {
      return join-env(state, FWD-ENV(get_env(rcvr)));
    };
  template<class Sndr, class... Env>
    static consteval void check-types();
};
Invocation of
impls-for<write-env-t>::join-env
returns an object 
e such that
- decltype(e) models queryable and
- given a query object q,
the expression e.query(q) is expression-equivalent
to state.query(q) if that expression is valid,
otherwise, e.query(q) is expression-equivalent
to env.query(q).
For a type 
Sndr and a pack of types 
Env,
let 
State be 
data-type<Sndr> and
let 
JoinEnv be the pack
decltype(join-env(declval<State>(), FWD-ENV(declval<Env>()))).Then 
impls-for<write-env-t>::check-types<Sndr, Env...>()
is expression-equivalent to
get_completion_signatures<child-
type<Sndr>, JoinEnv...>().unstoppable is a sender adaptor
that connects its inner sender
with a receiver that has the execution environment of the outer receiver
but with an object of type 
never_stop_token
as the result of the 
get_stop_token query. For a subexpression 
sndr,
unstoppable(sndr) is expression-equivalent to
write_env(sndr, prop(get_stop_token, never_stop_token{})).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
. The name 
starts_on denotes a customization point object
.For subexpressions 
sch and 
sndr,
if 
decltype((
sch)) does not satisfy 
scheduler, or
decltype((sndr)) does not satisfy 
sender,
starts_on(sch, sndr) is ill-formed
.Otherwise,
the expression 
starts_on(sch, sndr) is expression-equivalent to:
transform_sender(
  query-with-default(get_domain, sch, default_domain()),
  make-sender(starts_on, sch, sndr))
except that 
sch is evaluated only once
.Let 
out_sndr and 
env be subexpressions
such that 
OutSndr is 
decltype((out_sndr)).If 
sender-for<OutSndr, starts_on_t> is 
false,
then the expressions 
starts_on.transform_env(out_sndr, env) and
starts_on.transform_sender(out_sndr, env) are ill-formed; otherwise
- starts_on.transform_env(out_sndr, env) is equivalent to:
auto&& [_, sch, _] = out_sndr;
return JOIN-ENV(SCHED-ENV(sch), FWD-ENV(env));
- starts_on.transform_sender(out_sndr, env) is equivalent to:
auto&& [_, sch, sndr] = out_sndr;
return let_value(
  schedule(sch),
  [sndr = std::forward_like<OutSndr>(sndr)]() mutable
    noexcept(is_nothrow_move_constructible_v<decay_t<OutSndr>>) {
    return std::move(sndr);
  });
Let 
out_sndr be a subexpression denoting
a sender returned from 
starts_on(sch, sndr) or one equal to such, and
let 
OutSndr be the type 
decltype((out_sndr)).Let 
out_rcvr be a subexpression denoting a receiver
that has an environment of type 
Env
such that 
sender_in<OutSndr, Env> is 
true.Let 
op be an lvalue referring to the operation state
that results from connecting 
out_sndr with 
out_rcvr.Calling 
start(op) shall start 
sndr
on an execution agent of the associated execution resource of 
sch.If scheduling onto 
sch fails,
an error completion on 
out_rcvr shall be executed
on an unspecified execution agent
.continues_on adapts a sender into one
that completes on the specified scheduler
. The name 
continues_on denotes a pipeable sender adaptor object
.For subexpressions 
sch and 
sndr,
if 
decltype((sch)) does not satisfy 
scheduler, or
decltype((sndr)) does not satisfy 
sender,
continues_on(sndr, sch) is ill-formed
.Otherwise,
the expression 
continues_on(sndr, sch) is expression-equivalent to:
transform_sender(get-domain-early(sndr), make-sender(continues_on, sch, sndr))
except that 
sndr is evaluated only once
.The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
continues_on_t as follows:
namespace std::execution {
  template<>
  struct impls-for<continues_on_t> : default-impls {
    static constexpr auto get-attrs =
      [](const auto& data, const auto& child) noexcept -> decltype(auto) {
        return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
      };
  };
}
Let 
sndr and 
env be subexpressions
such that 
Sndr is 
decltype((sndr)).If 
sender-for<Sndr, continues_on_t> is 
false,
then
the expression 
continues_on.transform_sender(sndr, env) is ill-formed;
otherwise, it is equal to:
auto [_, data, child] = sndr;
return schedule_from(std::move(data), std::move(child));
[
Note 1: 
This causes the 
continues_on(sndr, sch) sender to become
schedule_from(sch, sndr) when it is connected with a receiver
whose execution domain does not customize 
continues_on. — 
end note]
Let 
out_sndr be a subexpression denoting
a sender returned from 
continues_on(sndr, sch) or one equal to such, and
let 
OutSndr be the type 
decltype((out_sndr)).Let 
out_rcvr be a subexpression denoting a receiver
that has an environment of type 
Env
such that 
sender_in<OutSndr, Env> is 
true.Let 
op be an lvalue referring to the operation state
that results from connecting 
out_sndr with 
out_rcvr.Calling 
start(op) shall
start 
sndr on the current execution agent and
execute completion operations on 
out_rcvr
on an execution agent of the execution resource associated with 
sch.If scheduling onto 
sch fails,
an error completion on 
out_rcvr shall be executed
on an unspecified execution agent
.schedule_from schedules work dependent on the completion of a sender
onto a scheduler's associated execution resource
.  [
Note 1: 
schedule_from is not meant to be used in user code;
it is used in the implementation of 
continues_on.  — 
end note]
The name 
schedule_from denotes a customization point object
.For some subexpressions 
sch and 
sndr,
let 
Sch be 
decltype((sch)) and
Sndr be 
decltype((sndr)).If 
Sch does not satisfy 
scheduler, or
Sndr does not satisfy 
sender,
schedule_from(sch, sndr) is ill-formed
.Otherwise,
the expression 
schedule_from(sch, sndr) is expression-equivalent to:
transform_sender(
  query-with-default(get_domain, sch, default_domain()),
  make-sender(schedule_from, sch, sndr))
except that 
sch is evaluated only once
.The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
schedule_from_t as follows:
namespace std::execution {
  template<>
  struct impls-for<schedule_from_t> : default-impls {
    static constexpr auto get-attrs = see below;
    static constexpr auto get-state = see below;
    static constexpr auto complete = see below;
    template<class Sndr, class... Env>
      static consteval void check-types();
  };
}
The member impls-for<schedule_from_t>::get-attrs
is initialized with a callable object equivalent to the following lambda:
[](const auto& data, const auto& child) noexcept -> decltype(auto) {
  return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
}
The member 
impls-for<schedule_from_t>::get-state
is initialized with a callable object equivalent to the following lambda:
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below)
    requires sender_in<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)> {
  auto& [_, sch, child] = sndr;
  using sched_t = decltype(auto(sch));
  using variant_t = see below;
  using receiver_t = see below;
  using operation_t = connect_result_t<schedule_result_t<sched_t>, receiver_t>;
  constexpr bool nothrow = noexcept(connect(schedule(sch), receiver_t{nullptr}));
  struct state-type {
    Rcvr& rcvr;                 
    variant_t async-result;     
    operation_t op-state;       
    explicit state-type(sched_t sch, Rcvr& rcvr) noexcept(nothrow)
      : rcvr(rcvr), op-state(connect(schedule(sch), receiver_t{this})) {}
  };
  return state-type{sch, rcvr};
}
template<class Sndr, class... Env>
  static consteval void check-types();
Effects: Equivalent to:
get_completion_signatures<schedule_result_t<data-type<Sndr>>, FWD-ENV-T(Env)...>();
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
decay-copyable-result-datums(cs);   
Objects of the local class 
state-type can be used
to initialize a structured binding
.Let 
Sigs be
a pack of the arguments to the 
completion_signatures specialization
named by 
completion_signatures_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>.Let 
as-tuple be an alias template such that
as-tuple<Tag(Args...)> denotes
the type 
decayed-tuple<Tag, Args...>, and
let 
is-nothrow-decay-copy-sig be a variable template such that
auto(is-nothrow-decay-copy-sig<Tag(Args...
)>) is
a constant expression of type 
bool and
equal to 
(is_nothrow_constructible_v<decay_t<Args>, Args> && ...).Let 
error-completion be a pack consisting of
the type 
set_error_t(exception_ptr)
if 
(is-nothrow-decay-copy-sig<Sigs> &&...) is 
false,
and an empty pack otherwise
.Then 
variant_t denotes
the type 
variant<monostate, as-tuple<Sigs>..., error-completion...>,
except with duplicate types removed
.receiver_t is an alias for the following exposition-only class:
namespace std::execution {
  struct receiver-type {
    using receiver_concept = receiver_t;
    state-type* state;          
    void set_value() && noexcept {
      visit(
        [this]<class Tuple>(Tuple& result) noexcept -> void {
          if constexpr (!same_as<monostate, Tuple>) {
            auto& [tag, ...args] = result;
            tag(std::move(state->rcvr), std::move(args)...);
          }
        },
        state->async-result);
    }
    template<class Error>
    void set_error(Error&& err) && noexcept {
      execution::set_error(std::move(state->rcvr), std::forward<Error>(err));
    }
    void set_stopped() && noexcept {
      execution::set_stopped(std::move(state->rcvr));
    }
    decltype(auto) get_env() const noexcept {
      return FWD-ENV(execution::get_env(state->rcvr));
    }
  };
}
 The expression in the 
noexcept clause of the lambda is 
true
if the construction of the returned 
state-type object
is not potentially throwing;
otherwise, 
false.The member impls-for<schedule_from_t>::complete
is initialized with a callable object equivalent to the following lambda:
[]<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept
    -> void {
  using result_t = decayed-tuple<Tag, Args...>;
  constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<Args>, Args> && ...);
  try {
    state.async-result.template emplace<result_t>(Tag(), std::forward<Args>(args)...);
  } catch (...) {
    if constexpr (!nothrow)
      state.async-result.template emplace<tuple<set_error_t,
                                                exception_ptr>>(set_error, current_exception());
  }
  start(state.op-state);
};
Let 
out_sndr be a subexpression denoting
a sender returned from 
schedule_from(sch, sndr) or one equal to such,
and let 
OutSndr be the type 
decltype((out_sndr)).Let 
out_rcvr be a subexpression denoting a receiver
that has an environment of type 
Env
such that 
sender_in<OutSndr, Env> is 
true.Let 
op be an lvalue referring to the operation state
that results from connecting 
out_sndr with 
out_rcvr.Calling 
start(op) shall
start 
sndr on the current execution agent and
execute completion operations on 
out_rcvr
on an execution agent of the execution resource associated with 
sch.If scheduling onto 
sch fails,
an error completion on 
out_rcvr shall be executed
on an unspecified execution agent
.The 
on sender adaptor has two forms:
- on(sch, sndr)- ,
which starts a sender  sndr-  on an execution agent
belonging to a scheduler  sch- 's associated execution resource and
that, upon  sndr- 's completion,
transfers execution back to the execution resource
on which the  on-  sender was started .
 
- on(sndr, sch, closure)- ,
which upon completion of a sender  sndr- ,
transfers execution to an execution agent
belonging to a scheduler  sch- 's associated execution resource,
then executes a sender adaptor closure  closure- 
with the async results of the sender, and
that then transfers execution back to the execution resource
on which  sndr-  completed .
 
The name 
on denotes a pipeable sender adaptor object
.For subexpressions 
sch and 
sndr,
on(sch, sndr) is ill-formed if any of the following is 
true:
- decltype((sch)) does not satisfy scheduler, or
- decltype((sndr)) does not satisfy sender and
sndr is not
a pipeable sender adaptor closure object ([exec.adapt.obj]), or
- decltype((sndr)) satisfies sender and
sndr is also a pipeable sender adaptor closure object.
Otherwise, if 
decltype((sndr)) satisfies 
sender,
the expression 
on(sch, sndr) is expression-equivalent to:
transform_sender(
  query-with-default(get_domain, sch, default_domain()),
  make-sender(on, sch, sndr))
except that 
sch is evaluated only once
.For subexpressions 
sndr, 
sch, and 
closure, if
- decltype((sch)) does not satisfy scheduler, or
- decltype((sndr)) does not satisfy sender, or
- closure is not a pipeable sender adaptor closure object ([exec.adapt.obj]),
the expression 
on(sndr, sch, closure) is ill-formed;
otherwise, it is expression-equivalent to:
transform_sender(
  get-domain-early(sndr),
  make-sender(on, product-type{sch, closure}, sndr))
except that 
sndr is evaluated only once
.Let 
out_sndr and 
env be subexpressions,
let 
OutSndr be 
decltype((out_sndr)), and
let 
Env be 
decltype((env)).If 
sender-for<OutSndr, on_t> is 
false,
then the expressions 
on.transform_env(out_sndr, env) and
on.transform_sender(out_sndr, env) are ill-formed
.Otherwise:
Let 
not-a-scheduler be an unspecified empty class type
.The expression 
on.transform_env(out_sndr, env)
has effects equivalent to:
auto&& [_, data, _] = out_sndr;
if constexpr (scheduler<decltype(data)>) {
  return JOIN-ENV(SCHED-ENV(std::forward_like<OutSndr>(data)), FWD-ENV(std::forward<Env>(env)));
} else {
  return std::forward<Env>(env);
}
The expression 
on.transform_sender(out_sndr, env)
has effects equivalent to:
auto&& [_, data, child] = out_sndr;
if constexpr (scheduler<decltype(data)>) {
  auto orig_sch =
    query-with-default(get_scheduler, env, not-a-scheduler());
  if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
    return not-a-sender{};
  } else {
    return continues_on(
      starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)),
      std::move(orig_sch));
  }
} else {
  auto& [sch, closure] = data;
  auto orig_sch = query-with-default(
    get_completion_scheduler<set_value_t>,
    get_env(child),
    query-with-default(get_scheduler, env, not-a-scheduler()));
  if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
    return not-a-sender{};
  } else {
    return write_env(
      continues_on(
        std::forward_like<OutSndr>(closure)(
          continues_on(
            write_env(std::forward_like<OutSndr>(child), SCHED-ENV(orig_sch)),
            sch)),
        orig_sch),
      SCHED-ENV(sch));
  }
}
Let 
out_sndr be a subexpression denoting
a sender returned from 
on(sch, sndr) or one equal to such, and
let 
OutSndr be the type 
decltype((out_sndr)).Let 
out_rcvr be a subexpression denoting a receiver
that has an environment of type 
Env
such that 
sender_in<OutSndr, Env> is 
true.Let 
op be an lvalue referring to the operation state
that results from connecting 
out_sndr with 
out_rcvr.Calling 
start(op) shall
- remember the current scheduler, get_scheduler(get_env(rcvr));
- start sndr on an execution agent belonging to
sch's associated execution resource;
- upon sndr's completion,
transfer execution back to the execution resource
associated with the scheduler remembered in step 1; and
- forward sndr's async result to out_rcvr.
If any scheduling operation fails,
an error completion on 
out_rcvr shall be executed
on an unspecified execution agent
.Let 
out_sndr be a subexpression denoting
a sender returned from 
on(sndr, sch, closure) or one equal to such, and
let 
OutSndr be the type 
decltype((out_sndr)).Let 
out_rcvr be a subexpression denoting a receiver
that has an environment of type 
Env
such that 
sender_in<OutSndr, Env> is 
true.Let 
op be an lvalue referring to the operation state
that results from connecting 
out_sndr with 
out_rcvr.Calling 
start(op) shall
- remember the current scheduler,
which is the first of the following expressions that is well-formed:
- get_completion_scheduler<set_value_t>(get_env(sndr))
- get_scheduler(get_env(rcvr));
 
- start sndr on the current execution agent;
- upon sndr's completion,
transfer execution to an agent
owned by sch's associated execution resource;
- forward sndr's async result as if by
connecting and starting a sender closure(S),
where S is a sender
that completes synchronously with sndr's async result; and
- 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 out_rcvr.
If any scheduling operation fails,
an error completion on 
out_rcvr shall be executed on
an unspecified execution agent
.then attaches an invocable as a continuation
for an input sender's value completion operation
.  upon_error and 
upon_stopped do the same
for the error and stopped completion operations, respectively,
sending the result of the invocable as a value completion
. The names 
then, 
upon_error, and 
upon_stopped
denote pipeable sender adaptor objects
.Let the expression 
then-cpo be one of
then, 
upon_error, or 
upon_stopped.For subexpressions 
sndr and 
f,
if 
decltype((sndr)) does not satisfy 
sender, or
decltype((f)) does not satisfy 
movable-value,
then-cpo(sndr, f) is ill-formed
.Otherwise,
the expression 
then-cpo(sndr, f) is expression-equivalent to:
transform_sender(get-domain-early(sndr), make-sender(then-cpo, f, sndr))
except that 
sndr is evaluated only once
.For 
then, 
upon_error, and 
upon_stopped,
let 
set-cpo be
set_value, 
set_error, and 
set_stopped, respectively
.The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
then-cpo as follows:
namespace std::execution {
  template<>
  struct impls-for<decayed-typeof<then-cpo>> : default-impls {
    static constexpr auto complete =
      []<class Tag, class... Args>
        (auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {
          if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
            TRY-SET-VALUE(rcvr,
                          invoke(std::move(fn), std::forward<Args>(args)...));
          } else {
            Tag()(std::move(rcvr), std::forward<Args>(args)...);
          }
        };
    template<class Sndr, class... Env>
      static consteval void check-types();
  };
}
template<class Sndr, class... Env>
  static consteval void check-types();
Effects: Equivalent to:
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
  if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts...>)
    throw unspecified-exception();
};
cs.for-each(overload-set{fn, [](auto){}});
where 
unspecified-exception is
a type derived from 
exception. The expression 
then-cpo(sndr, f) has undefined behavior
unless it returns a sender 
out_sndr that
- invokes f or a copy of such
with the value, error, or stopped result datums of sndr
for then, upon_error, and upon_stopped, respectively,
using the result value of f as out_sndr's value completion, and
- forwards all other completion operations unchanged.
let_value, 
let_error, and 
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
. For 
let_value, 
let_error, and 
let_stopped,
let 
set-cpo be
set_value, 
set_error, and 
set_stopped, respectively
.Let the expression 
let-cpo be one of
let_value, 
let_error, or 
let_stopped.For a subexpression 
sndr,
let 
let-env(sndr) be expression-equivalent to
the first well-formed expression below:
- SCHED-ENV(get_completion_scheduler<decayed-typeof<set-cpo>>(get_env(sndr)))
- MAKE-ENV(get_domain, get_domain(get_env(sndr)))
- (void(sndr), env<>{})
The names 
let_value, 
let_error, and 
let_stopped denote
pipeable sender adaptor objects
.For subexpressions 
sndr and 
f,
let 
F be the decayed type of 
f.If 
decltype((sndr)) does not satisfy 
sender or
if 
decltype((f)) does not satisfy 
movable-value,
the expression 
let-cpo(sndr, f) is ill-formed
.If 
F does not satisfy 
invocable,
the expression 
let_stopped(sndr, f) is ill-formed
.Otherwise,
the expression 
let-cpo(sndr, f) is expression-equivalent to:
transform_sender(get-domain-early(sndr), make-sender(let-cpo, f, sndr))
except that 
sndr is evaluated only once
.The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
let-cpo as follows:
namespace std::execution {
  template<class State, class Rcvr, class... Args>
  void let-bind(State& state, Rcvr& rcvr, Args&&... args);      
  template<>
  struct impls-for<decayed-typeof<let-cpo>> : default-impls {
    static constexpr auto get-state = see below;
    static constexpr auto complete = see below;
    template<class Sndr, class... Env>
      static consteval void check-types();
  };
}
Let receiver2 denote the following exposition-only class template:
namespace std::execution {
  template<class Rcvr, class Env>
  struct receiver2 {
    using receiver_concept = receiver_t;
    template<class... Args>
    void set_value(Args&&... args) && noexcept {
      execution::set_value(std::move(rcvr), std::forward<Args>(args)...);
    }
    template<class Error>
    void set_error(Error&& err) && noexcept {
      execution::set_error(std::move(rcvr), std::forward<Error>(err));
    }
    void set_stopped() && noexcept {
      execution::set_stopped(std::move(rcvr));
    }
    decltype(auto) get_env() const noexcept {
      return see below;
    }
    Rcvr& rcvr;                 
    Env env;                    
  };
}
 
Invocation of the function 
receiver2::get_env
returns an object 
e such that
- decltype(e) models queryable and
- given a query object q,
the expression e.query(q) is expression-equivalent
to env.query(q) if that expression is valid;
otherwise,
if the type of q satisfies forwarding-query,
e.query(q) is expression-equivalent
to get_env(rcvr).query(q);
otherwise,
e.query(q) is ill-formed.
template<class Sndr, class... Env>
  static consteval void check-types();
Effects: Equivalent to:
using LetFn = remove_cvref_t<data-type<Sndr>>;
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
auto fn = []<class... Ts>(decayed-typeof<set-cpo>(*)(Ts...)) {
  if constexpr (!is-valid-let-sender)   
    throw unspecified-exception();
};
cs.for-each(overload-set(fn, [](auto){}));
where 
unspecified-exception is
a type derived from 
exception, and
where 
is-valid-let-sender is 
true if and only if
all of the following are 
true:
where 
env-t is the pack
decltype(let-cpo.transform_env(declval<Sndr>(), declval<Env>())). impls-for<decayed-typeof<let-cpo>>::get-state
is initialized with a callable object equivalent to the following:
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires see below {
  auto& [_, fn, child] = sndr;
  using fn_t = decay_t<decltype(fn)>;
  using env_t = decltype(let-env(child));
  using args_variant_t = see below;
  using ops2_variant_t = see below;
  struct state-type {
    fn_t fn;                    
    env_t env;                  
    args_variant_t args;        
    ops2_variant_t ops2;        
  };
  return state-type{allocator-aware-forward(std::forward_like<Sndr>(fn), rcvr),
                    let-env(child), {}, {}};
}
Let 
Sigs be a pack of the arguments
to the 
completion_signatures specialization named by
completion_signatures_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>.Let 
LetSigs be a pack of those types in 
Sigs
with a return type of 
decayed-typeof<set-cpo>.Let 
as-tuple be an alias template
such that 
as-tuple<Tag(Args...)> denotes
the type 
decayed-tuple<Args...>.Then 
args_variant_t denotes
the type 
variant<monostate, as-tuple<LetSigs>...>
except with duplicate types removed
.Given a type 
Tag and a pack 
Args,
let 
as-sndr2 be an alias template
such that 
as-sndr2<Tag(Args...)> denotes
the type 
call-result-t<F, decay_t<Args>&...>.Then 
ops2_variant_t denotes
the type
variant<monostate, connect_result_t<as-sndr2<LetSigs>, receiver2<Rcvr, env_t>>...>
except with duplicate types removed
.The 
requires-clause constraining the above lambda is satisfied
if and only if
the types 
args_variant_t and 
ops2_variant_t are well-formed
.The exposition-only function template let-bind
has effects equivalent to:
using args_t = decayed-tuple<Args...>;
auto mkop2 = [&] {
  return connect(
    apply(std::move(state.fn),
          state.args.template emplace<args_t>(std::forward<Args>(args)...)),
    receiver2{rcvr, std::move(state.env)});
};
start(state.ops2.template emplace<decltype(mkop2())>(emplace-from{mkop2}));
impls-for<decayed-typeof<let-cpo>>::complete
is initialized with a callable object equivalent to the following:
[]<class Tag, class... Args>
  (auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {
    if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
      TRY-EVAL(rcvr, let-bind(state, rcvr, std::forward<Args>(args)...));
    } else {
      Tag()(std::move(rcvr), std::forward<Args>(args)...);
    }
  }
 Let 
sndr and 
env be subexpressions, and
let 
Sndr be 
decltype((sndr)).If
sender-for<Sndr, decayed-typeof<let-cpo>>
is 
false,
then the expression 
let-cpo.transform_env(sndr, env)
is ill-formed
.Otherwise, it is equal to:
auto& [_, _, child] = sndr;
return JOIN-ENV(let-env(child), FWD-ENV(env));
Let the subexpression 
out_sndr denote
the result of the invocation 
let-cpo(sndr, f) or
an object equal to such, and
let the subexpression 
rcvr denote a receiver
such that the expression 
connect(out_sndr, rcvr) is well-formed
.The expression 
connect(out_sndr, rcvr) has undefined behavior
unless it creates an asynchronous operation (
[exec.async.ops]) that,
when started:
- invokes f when set-cpo is called
with sndr's result datums,
- makes its completion dependent on
the completion of a sender returned by f, and
- propagates the other completion operations sent by sndr.
bulk, 
bulk_chunked, and 
bulk_unchunked
run a task repeatedly for every index in an index space
. The names 
bulk, 
bulk_chunked, and 
bulk_unchunked
denote pipeable sender adaptor objects
.Let 
bulk-algo be either
bulk, 
bulk_chunked, or 
bulk_unchunked.For subexpressions 
sndr, 
policy, 
shape, and 
f,
let
Policy be 
remove_cvref_t<decltype(policy)>,
Shape be 
decltype(auto(shape)), and
Func be 
decay_t<decltype((f))>.If
- decltype((sndr)) does not satisfy sender, or
- is_execution_policy_v<Policy> is false, or
- Shape does not satisfy integral, or
- Func does not model copy_constructible,
bulk-algo(sndr, policy, shape, f) is ill-formed
.Otherwise,
the expression bulk-algo(sndr, policy, shape, f)
is expression-equivalent to:
transform_sender(get-domain-early(sndr), make-sender(
    bulk-algo, product-type<see below, Shape, Func>{policy, shape, f}, sndr))
except that 
sndr is evaluated only once
.  The first template argument of 
product-type is 
Policy
if 
Policy models 
copy_constructible, and
const Policy& otherwise
.Let 
sndr and 
env be subexpressions such that
Sndr is 
decltype((sndr)).If 
sender-for<Sndr, bulk_t> is 
false, then
the expression 
bulk.transform_sender(sndr, env) is ill-formed;
otherwise, it is equivalent to:
auto [_, data, child] = sndr;
auto& [policy, shape, f] = data;
auto new_f = [func = std::move(f)](Shape begin, Shape end, auto&&... vs)
    noexcept(noexcept(f(begin, vs...))) {
  while (begin != end) func(begin++, vs...);
}
return bulk_chunked(std::move(child), policy, shape, std::move(new_f));
[
Note 1: 
This causes the 
bulk(sndr, policy, shape, f) sender to be
expressed in terms of 
bulk_chunked(sndr, policy, shape, f) when
it is connected to a receiver whose
execution domain does not customize 
bulk. — 
end note]
The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
bulk_chunked_t as follows:
namespace std::execution {
  template<>
  struct impls-for<bulk_chunked_t> : default-impls {
    static constexpr auto complete = see below;
    template<class Sndr, class... Env>
      static consteval void check-types();
  };
}
The member 
impls-for<bulk_chunked_t>::complete
is initialized with a callable object equivalent to the following lambda:
[]<class Index, class State, class Rcvr, class Tag, class... Args>
  (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
  -> void requires see below {
    if constexpr (same_as<Tag, set_value_t>) {
      auto& [policy, shape, f] = state;
      constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...));
      TRY-EVAL(rcvr, [&]() noexcept(nothrow) {
        f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
        Tag()(std::move(rcvr), std::forward<Args>(args)...);
      }());
    } else {
      Tag()(std::move(rcvr), std::forward<Args>(args)...);
    }
  }
The expression in the 
requires-clause of the lambda above is
true if and only
if 
Tag denotes a type other than 
set_value_t or
if the expression 
f(auto(shape), auto(shape), args...) is well-formed
.The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
bulk_unchunked_t as follows:
namespace std::execution {
  template<>
  struct impls-for<bulk_unchunked_t> : default-impls {
    static constexpr auto complete = see below;
  };
}
The member 
impls-for<bulk_unchunked_t>::complete
is initialized with a callable object equivalent to the following lambda:
[]<class Index, class State, class Rcvr, class Tag, class... Args>
  (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
  -> void requires see below {
    if constexpr (same_as<Tag, set_value_t>) {
      auto& [shape, f] = state;
      constexpr bool nothrow = noexcept(f(auto(shape), args...));
      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>(args)...);
      }());
    } else {
      Tag()(std::move(rcvr), std::forward<Args>(args)...);
    }
  }
The expression in the 
requires-clause of the lambda above
is 
true if and only
if 
Tag denotes a type other than 
set_value_t or
if the expression 
f(auto(shape), args...) is well-formed
.template<class Sndr, class... Env>
  static consteval void check-types();
Effects: Equivalent to:
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
  if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts&...>)
    throw unspecified-exception();
};
cs.for-each(overload-set(fn, [](auto){}));
where 
unspecified-exception is
a type derived from 
exception. Let the subexpression 
out_sndr denote
the result of the invocation
bulk-algo(sndr, policy, shape, f) or
an object equal to such, and
let the subexpression 
rcvr denote a receiver
such that the expression 
connect(out_sndr, rcvr) is well-formed
.The expression 
connect(out_sndr, rcvr) has undefined behavior
unless it creates an asynchronous operation (
[exec.async.ops]) that,
when started:
- If  sndr-  has a successful completion, where
 args-  is a pack of lvalue subexpressions
referring to the value completion result datums of  sndr- , or
decayed copies of those values if they model  copy_constructible- ,
then: 
- If out_sndr also completes successfully, then: 
- for bulk,
    invokes f(i, args...) for every i of type Shape
    from 0 to shape; 
- for bulk_unchunked,
    invokes f(i, args...) for every i of type Shape
    from 0 to shape; 
- Recommended practice- : The underlying scheduler should execute each iteration
    on a distinct execution agent .
 
- for  bulk_chunked- ,
    invokes  f(b, e, args...)-  zero or more times
    with pairs of  b-  and  e-  of type  Shape- 
    in range [ 0, shape- ],
    such that  b<e-  and
    for every  i-  of type  Shape-  from  0-  to  shape- ,
    there is exactly one invocation with a pair  b-  and  e- ,
    such that  i-  is in the range [ b, e- ) .
 
- If  out_sndr-  completes with  set_error(rcvr, eptr)- , then
  the asynchronous operation may invoke a subset of
  the invocations of  f- 
  before the error completion handler is called, and
   eptr-  is an  exception_ptr-  containing either:
     - an exception thrown by an invocation of f, or
- a bad_alloc exception if
    the implementation fails to allocate required resources, or
- an exception derived from runtime_error.
 
- If  out_sndr-  completes with  set_stopped(rcvr)- , then
  the asynchronous operation may invoke a subset of
  the invocations of  f- 
  before the stopped completion handler .
 
- If  sndr-  does not complete with  set_value- , then
the completion is forwarded to  recv.
- For  bulk-algo- ,
the parameter  policy-  describes
the manner in which
the execution of the asynchronous operations corresponding to these algorithms
may be parallelized and
the manner in which
they apply  f.
[
Note 2: 
The asynchronous operation corresponding to
bulk-algo(sndr, policy, shape, f)
can complete with 
set_stopped if cancellation is requested or
ignore cancellation requests
. — 
end note]
when_all and 
when_all_with_variant
both adapt multiple input senders into a sender
that completes when all input senders have completed
.  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
.  when_all_with_variant(sndrs...) is semantically equivalent to
w
hen_all(into_variant(sndrs)...),
where 
sndrs is a pack of subexpressions
whose types model 
sender. The names 
when_all and 
when_all_with_variant denote
customization point objects
.Let 
sndrs be a pack of subexpressions,
let 
Sndrs be a pack of the types 
decltype((sndrs))..., and
let 
CD be
the type 
common_type_t<decltype(get-domain-early(sndrs))...>.Let 
CD2 be 
CD if 
CD is well-formed, and
default_domain otherwise
.The expressions 
when_all(sndrs...) and
when_all_with_variant(sndrs...) are ill-formed
if any of the following is 
true:
- sizeof...(sndrs) is 0, or
- (sender<Sndrs> && ...) is false.
The expression when_all(sndrs...) is expression-equivalent to:
transform_sender(CD2(), make-sender(when_all, {}, sndrs...))
The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
when_all_t as follows:
namespace std::execution {
  template<>
  struct impls-for<when_all_t> : default-impls {
    static constexpr auto get-attrs = see below;
    static constexpr auto get-env = see below;
    static constexpr auto get-state = see below;
    static constexpr auto start = see below;
    static constexpr auto complete = see below;
    template<class Sndr, class... Env>
      static consteval void check-types();
  };
}
Let 
make-when-all-env be
the following exposition-only function template:
template<class Env>
  constexpr auto make-when-all-env(inplace_stop_source& stop_src,               
                                   Env&& env) noexcept {
  return see below;
}
Returns an object 
e such that
- decltype(e) models queryable, and
- e.query(get_stop_token) is expression-equivalent to
state.stop-src.get_token(), and
- given a query object q
with type other than cv stop_token_t and
whose type satisfies forwarding-query,
e.query(q) is expression-equivalent to get_env(rcvr).query(q).
Let 
when-all-env be an alias template such that
when-all-env<Env> denotes the type
decltype(make-
when-all-env(declval<inplace_stop_source&>(), declval<Env>())).template<class Sndr, class... Env>
  static consteval void check-types();
Let 
Is be the pack of integral template arguments of
the 
integer_sequence specialization denoted by
indices-for<Sndr>.Effects: Equivalent to:
auto fn = []<class Child>() {
  auto cs = get_completion_signatures<Child, when-all-env<Env>...>();
  if constexpr (cs.count-of(set_value) >= 2)
    throw unspecified-exception();
  decay-copyable-result-datums(cs); 
};
(fn.template operator()<child-type<Sndr, Is>>(), ...);
where 
unspecified-exception is
a type derived from 
exception. Throws: Any exception thrown as a result of evaluating the 
Effects, or
an exception of an unspecified type
derived from 
exception when 
CD is ill-formed
. The member 
impls-for<when_all_t>::get-attrs
is initialized with a callable object
equivalent to the following lambda expression:
[](auto&&, auto&&... child) noexcept {
  if constexpr (same_as<CD, default_domain>) {
    return env<>();
  } else {
    return MAKE-ENV(get_domain, CD());
  }
}
The member impls-for<when_all_t>::get-env
is initialized with a callable object
equivalent to the following lambda expression:
[]<class State, class Rcvr>(auto&&, State& state, const Receiver& rcvr) noexcept {
  return make-when-all-env(state.stop-src, get_env(rcvr));
}
The member impls-for<when_all_t>::get-state
is initialized with a callable object
equivalent to the following lambda expression:
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {
  return e;
}
where e is the expression
std::forward<Sndr>(sndr).apply(make-state<Rcvr>())
and where make-state is the following exposition-only class template:
enum class disposition { started, error, stopped };             
template<class Rcvr>
struct make-state {
  template<class... Sndrs>
  auto operator()(auto, auto, Sndrs&&... sndrs) const {
    using values_tuple = see below;
    using errors_variant = see below;
    using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, on-stop-request>;
    struct state-type {
      void arrive(Rcvr& rcvr) noexcept {                        
        if (0 == --count) {
          complete(rcvr);
        }
      }
      void complete(Rcvr& rcvr) noexcept;                       
      atomic<size_t> count{sizeof...(sndrs)};                   
      inplace_stop_source stop_src{};                           
      atomic<disposition> disp{disposition::started};           
      errors_variant errors{};                                  
      values_tuple values{};                                    
      optional<stop_callback> on_stop{nullopt};                 
    };
    return state-type{};
  }
};
Let 
copy-fail be 
exception_ptr
if decay-copying any of the child senders' result datums can potentially throw;
otherwise, 
none-such,
where 
none-such is an unspecified empty class type
.The alias 
values_tuple denotes the type
tuple<value_types_of_t<Sndrs, FWD-ENV-T(env_of_t<Rcvr>), decayed-tuple, optional>...>
if that type is well-formed; otherwise, 
tuple<>.The alias 
errors_variant denotes
the type 
variant<none-such, copy-fail, Es...>
with duplicate types removed,
where 
Es is the pack of the decayed types
of all the child senders' possible error result datums
.The member
void state-type::complete(Rcvr& rcvr) noexcept
behaves as follows:
- If disp is equal to disposition::started,
evaluates:
auto tie = []<class... T>(tuple<T...>& t) noexcept { return tuple<T&...>(t); };
auto set = [&](auto&... t) noexcept { set_value(std::move(rcvr), std::move(t)...); };
on_stop.reset();
apply(
  [&](auto&... opts) noexcept {
    apply(set, tuple_cat(tie(*opts)...));
  },
  values);
- Otherwise,
if disp is equal to disposition::error,
evaluates:
on_stop.reset();
visit(
  [&]<class Error>(Error& error) noexcept {
    if constexpr (!same_as<Error, none-such>) {
      set_error(std::move(rcvr), std::move(error));
    }
  },
  errors);
- Otherwise, evaluates:
on_stop.reset();
set_stopped(std::move(rcvr));
The member impls-for<when_all_t>::start
is initialized with a callable object
equivalent to the following lambda expression:
[]<class State, class Rcvr, class... Ops>(
    State& state, Rcvr& rcvr, Ops&... ops) noexcept -> void {
  state.on_stop.emplace(
    get_stop_token(get_env(rcvr)),
    on-stop-request{state.stop_src});
  if (state.stop_src.stop_requested()) {
    state.on_stop.reset();
    set_stopped(std::move(rcvr));
  } else {
    (start(ops), ...);
  }
}
The member 
impls-for<when_all_t>::complete
is initialized with a callable object
equivalent to the following lambda expression:
[]<class Index, class State, class Rcvr, class Set, class... Args>(
    this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void {
  if constexpr (same_as<Set, set_error_t>) {
    if (disposition::error != state.disp.exchange(disposition::error)) {
      state.stop_src.request_stop();
      TRY-EMPLACE-ERROR(state.errors, std::forward<Args>(args)...);
    }
  } else if constexpr (same_as<Set, set_stopped_t>) {
    auto expected = disposition::started;
    if (state.disp.compare_exchange_strong(expected, disposition::stopped)) {
      state.stop_src.request_stop();
    }
  } else if constexpr (!same_as<decltype(State::values), tuple<>>) {
    if (state.disp == disposition::started) {
      auto& opt = get<Index::value>(state.values);
      TRY-EMPLACE-VALUE(complete, opt, std::forward<Args>(args)...);
    }
  }
  state.arrive(rcvr);
}
where 
TRY-EMPLACE-ERROR(v, e),
for subexpressions 
v and 
e, is equivalent to:
try {
  v.template emplace<decltype(auto(e))>(e);
} catch (...) {
  v.template emplace<exception_ptr>(current_exception());
}
if the expression 
decltype(auto(e))(e) is potentially throwing;
otherwise, 
v.template emplace<decltype(auto(e))>(e);
and where 
TRY-EMPLACE-VALUE(c, o, as...),
for subexpressions 
c, 
o, and pack of subexpressions 
as,
is equivalent to:
try {
  o.emplace(as...);
} catch (...) {
  c(Index(), state, rcvr, set_error, current_exception());
  return;
}
if the expression 
decayed-tuple<decltype(as)...>{as...}
is potentially throwing;
otherwise, 
o.emplace(as...).The expression when_all_with_variant(sndrs...)
is expression-equivalent to:
transform_sender(CD2(), make-sender(when_all_with_variant, {}, sndrs...));
Given subexpressions 
sndr and 
env,
if
sender-for<decltype((sndr)), when_all_with_variant_t>
is 
false,
then the expression 
when_all_with_variant.transform_sender(sndr, env)
is ill-formed;
otherwise, it is equivalent to:
auto&& [_, _, ...child] = sndr;
return when_all(into_variant(std::forward_like<decltype((sndr))>(child))...);
[
Note 1: 
This causes the 
when_all_with_variant(sndrs...) sender
to become 
when_all(into_variant(sndrs)...)
when it is connected with a receiver
whose execution domain does not customize 
when_all_with_variant. — 
end note]
into_variant adapts a sender with multiple value completion signatures
into a sender with just one value completion signature
consisting of a 
variant of 
tuples
. The name 
into_variant denotes a pipeable sender adaptor object
.For a subexpression 
sndr, let 
Sndr be 
decltype((sndr)).If 
Sndr does not satisfy 
sender,
into_variant(sndr) is ill-formed
.Otherwise, the expression 
into_variant(sndr)
is expression-equivalent to:
transform_sender(get-domain-early(sndr), make-sender(into_variant, {}, sndr))
except that 
sndr is only evaluated once
.The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
into_variant as follows:
namespace std::execution {
  template<>
  struct impls-for<into_variant_t> : default-impls {
    static constexpr auto get-state = see below;
    static constexpr auto complete = see below;
    template<class Sndr, class... Env>
      static consteval void check-types() {
        auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
        decay-copyable-result-datums(cs);   
      }
  };
}
The member impls-for<into_variant_t>::get-state
is initialized with a callable object equivalent to the following lambda:
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept
  -> type_identity<value_types_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>> {
  return {};
}
The member 
impls-for<into_variant_t>::complete
is initialized with a callable object equivalent to the following lambda:
[]<class State, class Rcvr, class Tag, class... Args>(
    auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {
  if constexpr (same_as<Tag, set_value_t>) {
    using variant_type = typename State::type;
    TRY-SET-VALUE(rcvr, variant_type(decayed-tuple<Args...>{std::forward<Args>(args)...}));
  } else {
    Tag()(std::move(rcvr), std::forward<Args>(args)...);
  }
}
stopped_as_optional maps a sender's stopped completion operation
into a value completion operation as a disengaged 
optional.  The sender's value completion operation
is also converted into an 
optional.The result is a sender that never completes with stopped,
reporting cancellation by completing with a disengaged 
optional.The name 
stopped_as_optional denotes a pipeable sender adaptor object
.For a subexpression 
sndr, let 
Sndr be 
decltype((sndr)).The expression 
stopped_as_optional(sndr) is expression-equivalent to:
transform_sender(get-domain-early(sndr), make-sender(stopped_as_optional, {}, sndr))
except that 
sndr is only evaluated once
.The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
stopped_as_optional_t as follows:
namespace std::execution {
  template<>
  struct impls-for<stopped_as_optional_t> : default-impls {
    template<class Sndr, class... Env>
      static consteval void check-types() {
        default-impls::check-types<Sndr, Env...>();
        if constexpr (!requires {
          requires (!same_as<void, single-sender-value-type<child-type<Sndr>,
                                                            FWD-ENV-T(Env)...>>); })
          throw unspecified-exception();
      }
  };
}
where 
unspecified-exception is
a type derived from 
exception.Let 
sndr and 
env be subexpressions
such that 
Sndr is 
decltype((sndr)) and
Env is 
decltype((env)).If 
sender-for<Sndr, stopped_as_optional_t>
is 
false
then the expression 
stopped_as_optional.transform_sender(sndr, env)
is ill-formed;
otherwise,
if 
sender_in<child-type<Sndr>, FWD-ENV-T(Env)>
is 
false,
the expression 
stopped_as_optional.transform_sender(sndr, env)
is equivalent to 
not-a-sender();
otherwise, it is equivalent to:
auto&& [_, _, child] = sndr;
using V = single-sender-value-type<child-type<Sndr>, FWD-ENV-T(Env)>;
return let_stopped(
  then(std::forward_like<Sndr>(child),
       []<class... Ts>(Ts&&... ts) noexcept(is_nothrow_constructible_v<V, Ts...>) {
         return optional<V>(in_place, std::forward<Ts>(ts)...);
       }),
  []() noexcept { return just(optional<V>()); });
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
.The name 
stopped_as_error denotes a pipeable sender adaptor object
.For some subexpressions 
sndr and 
err,
let 
Sndr be 
decltype((sndr)) and
let 
Err be 
decltype((err)).If the type 
Sndr does not satisfy 
sender or
if the type 
Err does not satisfy 
movable-value,
stopped_as_error(sndr, err) is ill-formed
.Otherwise, the expression 
stopped_as_error(sndr, err)
is expression-equivalent to:
transform_sender(get-domain-early(sndr), make-sender(stopped_as_error, err, sndr))
except that 
sndr is only evaluated once
.Let 
sndr and 
env be subexpressions
such that 
Sndr is 
decltype((sndr)) and
Env is 
decltype((env)).If 
sender-for<Sndr, stopped_as_error_t> is 
false,
then the expression 
stopped_as_error.transform_sender(sndr, env)
is ill-formed;
otherwise, it is equivalent to:
auto&& [_, err, child] = sndr;
using E = decltype(auto(err));
return let_stopped(
  std::forward_like<Sndr>(child),
  [err = std::forward_like<Sndr>(err)]() mutable noexcept(is_nothrow_move_constructible_v<E>) {
    return just_error(std::move(err));
  });
associate tries to associate
a sender with an async scope such that
the scope can track the lifetime of any asynchronous operations
created with the sender
. Let associate-data be the following exposition-only class template:
namespace std::execution {
  template<scope_token Token, sender Sender>
  struct associate-data {                                       
    using wrap-sender =                                         
      remove_cvref_t<decltype(declval<Token&>().wrap(declval<Sender>()))>;
    explicit associate-data(Token t, Sender&& s)
      : sndr(t.wrap(std::forward<Sender>(s))),
        token(t) {
      if (!token.try_associate())
        sndr.reset();
    }
    associate-data(const associate-data& other)
      noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
               noexcept(other.token.try_associate()));
    associate-data(associate-data&& other)
      noexcept(is_nothrow_move_constructible_v<wrap-sender>);
    ~associate-data();
    optional<pair<Token, wrap-sender>>
      release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
  private:
    optional<wrap-sender> sndr;  
    Token token;                 
  };
  template<scope_token Token, sender Sender>
    associate-data(Token, Sender&&) -> associate-data<Token, Sender>;
}
 For an 
associate-data object 
a,
a.sndr.has_value() is 
true
if and only if
an association was successfully made and is owned by 
a.associate-data(const associate-data& other)
  noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
           noexcept(other.token.try_associate()));
Effects: Value-initializes 
sndr and
initializes 
token with 
other.token.  If 
other.sndr.has_value() is 
false,
no further effects;
otherwise,
calls 
token.try_associate() and,
if that returns 
true,
calls 
sndr.emplace(*other.sndr) and,
if that exits with an exception,
calls 
token.disassociate() before propagating the exception
.associate-data(associate-data&& other)
  noexcept(is_nothrow_move_constructible_v<wrap-sender>);
Effects: Initializes 
sndr with 
std::move(other.sndr) and
initializes 
token with 
std::move(other.token) and
then calls 
other.sndr.reset(). Effects: If 
sndr.has_value() returns 
false then no effect;
otherwise, invokes 
sndr.reset()
before invoking 
token.disassociate(). optional<pair<Token, wrap-sender>>
  release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
Effects: If sndr.has_value() returns false then
returns an optional that does not contain a value;
otherwise returns an optional
containing a value of type pair<Token, wrap-sender>
as if by:
return optional(pair(token, std::move(*sndr)));
Postconditions: 
sndr does not contain a value
. The name 
associate denotes a pipeable sender adaptor object
.For subexpressions 
sndr and 
token:
- If decltype((sndr)) does not satisfy sender, or
remove_cvref_t<decltype((token))>
does not satisfy scope_token, then
associate(sndr, token) is ill-formed.
- Otherwise,
the expression associate(sndr, token)
is expression-equivalent to:
transform_sender(get-domain-early(sndr),
                 make-sender(associate, associate-data(token, sndr)))
except that sndr is evaluated only once.
The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
associate_t as follows:
namespace std::execution {
  template<>
  struct impls-for<associate_t> : default-impls {
    static constexpr auto get-state = see below;                
    static constexpr auto start = see below;                    
    template<class Sndr, class... Env>
      static consteval void check-types() {                     
        using associate_data_t = remove_cvref_t<data-type<Sndr>>;
        using child_type_t = typename associate_data_t::wrap-sender;
        (void)get_completion_signatures<child_type_t, FWD-ENV-T(Env)...>();
    }
  };
}
The member impls-for<associate_t>::get-state
is initialized with a callable object equivalent to the following lambda:
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below) {
  auto [_, data] = std::forward<Sndr>(sndr);
  auto dataParts = std::move(data).release();
  using scope_tkn = decltype(dataParts->first);
  using wrap_sender = decltype(dataParts->second);
  using op_t = connect_result_t<wrap_sender, Rcvr>;
  struct op_state {
    bool associated = false;    
    union {
      Rcvr* rcvr;               
      struct {
        scope_tkn token;        
        op_t op;                
      } assoc;                  
    };
    explicit op_state(Rcvr& r) noexcept
      : rcvr(addressof(r)) {}
    explicit op_state(scope_tkn tkn, wrap_sender&& sndr, Rcvr& r) try
      : associated(true),
        assoc(tkn, connect(std::move(sndr), std::move(r))) {
    }
    catch (...) {
      tkn.disassociate();
      throw;
    }
    op_state(op_state&&) = delete;
    ~op_state() {
      if (associated) {
        assoc.op.~op_t();
        assoc.token.disassociate();
        assoc.token.~scope_tkn();
      }
    }
    void run() noexcept {       
      if (associated)
        start(assoc.op);
      else
        set_stopped(std::move(*rcvr));
    }
  };
  if (dataParts)
    return op_state{std::move(dataParts->first), std::move(dataParts->second), rcvr};
  else
    return op_state{rcvr};
}
The expression in the 
noexcept clause of
impls-for<associate_t>::get-state is
is_nothrow_constructible_v<remove_cvref_t<Sndr>, Sndr> &&
is_nothrow_move_constructible_v<wrap-sender> &&
nothrow-callable<connect_t, wrap-sender, Rcvr>
where 
wrap-sender is the type
remove_cvref_t<data-type<Sndr>>::wrap-sender.The member impls-for<associate_t>::start
is initialized with a callable object equivalent to the following lambda:
[](auto& state, auto&) noexcept -> void {
  state.run();
}
The evaluation of 
associate(sndr, token)
may cause side effects observable
via 
token's associated async scope object
.stop-when fuses an additional stop token 
t
into a sender so that, upon connecting to a receiver 
r,
the resulting operation state receives stop requests from both
t and the token returned from 
get_stop_token(get_env(r)). The name 
stop-when denotes an exposition-only sender adaptor
.For subexpressions 
sndr and 
token:
- If  decltype((sndr))-  does not satisfy  sender- , or
 remove_cvref_t<decltype((token))>- 
does not satisfy  stoppable_token- ,
then  stop-when(sndr, token)-  is ill-formed .
- Otherwise,
if  remove_cvref_t<decltype((token))>-  models
 unstoppable_token-  then
 stop-when(sndr, token)-  is expression-equivalent to
 sndr.
- Otherwise,
 stop-when(sndr, token)-  returns a sender  osndr.
- If  osndr-  is connected to a receiver  r- ,
let  rtoken-  be the result of  get_stop_token(get_env(r)).
- If the type of  rtoken-  models  unstoppable_token-  then
the effects of connecting  osndr-  to  r- 
are equivalent to
 connect(write_env(sndr, prop(get_stop_token, token)), r).
- Otherwise,
the effects of connecting  osndr-  to  r- 
are equivalent to
 connect(write_env(sndr, prop(get_stop_token, stoken)), r)- 
where  stoken-  is an object of
an exposition-only type  stoken-t-  such that:
   - stoken-t models stoppable_token;
- stoken.stop_requested() returns
  token.stop_requested() || rtoken.stop_reques-
 ted();
- stoken.stop_possible() returns
  token.stop_possible() || rtoken.stop_possible(); and
- for types Fn and Init such that both
  invocable<Fn> and
  constructible_from<Fn, Init>
  are modeled,
  stoken-t::callback_type<Fn> models
  stoppable-callback-for<Fn, stoken-t, Init>. [ Note 1:  For an object  fn of type  Fn
  constructed from a value,  init, of type  Init,
  registering  fn using
   stoken-t::callback_type<Fn>(stoken, init)
  results in an invocation of  fn when
  a callback registered with  token or  rtoken would be invoked .
fn is invoked at most once .
  —  end note] 
 
 
spawn_future attempts to associate the given input sender
with the given token's async scope and, on success,
eagerly starts the input sender;
the return value is a sender that, when connected and started,
completes with either
the result of the eagerly-started input sender or with
set_stopped if the input sender was not started
. The name 
spawn_future denotes a customization point object
.For subexpressions 
sndr, 
token, and 
env,
- let Sndr be decltype((sndr)),
- let Token be remove_cvref_t<decltype((token))>, and
- let Env be remove_cvref_t<decltype((env))>.
If any of
sender<Sndr>,
scope_token<Token>, or
queryable<Env>
are not satisfied,
the expression 
spawn_future(sndr, token, env) is ill-formed
.Let spawn-future-state-base be the exposition-only class template:
namespace std::execution {
  template<class Completions>
  struct spawn-future-state-base;                                   
  template<class... Sigs>
  struct spawn-future-state-base<completion_signatures<Sigs...>> {  
    using variant-t = see below;                                    
    variant-t result;                                               
    virtual void complete() noexcept = 0;                           
  };
}
 Let 
Sigs be the pack of arguments to
the 
completion_signatures specialization provided as
a parameter to the 
spawn-future-state-base class template
.Let 
as-tuple be an alias template that
transforms a completion signature 
Tag(Args...)
into the tuple specialization 
decayed-tuple<Tag, Args...>.- If  is_nothrow_constructible_v<decay_t<Arg>, Arg>-  is  true- 
for every type  Arg- 
in every parameter pack  Args- 
in every completion signature  Tag(Args...)- 
in  Sigs-  then
 variant-t-  denotes the type
 variant<monostate, tuple<set_stopped_t>, as-tuple<Sigs>...>- ,
except with duplicate types removed .
- Otherwise
 variant-t-  denotes the type
 variant<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, as-tuple<Sigs>...>- ,
except with duplicate types removed .
Let spawn-future-receiver be the exposition-only class template:
namespace std::execution {
  template<class Completions>
  struct spawn-future-receiver {                                
    using receiver_concept = receiver_t;
    spawn-future-state-base<Completions>* state;                
    template<class... T>
      void set_value(T&&... t) && noexcept {
        set-complete<set_value_t>(std::forward<T>(t)...);
      }
    template<class E>
      void set_error(E&& e) && noexcept {
        set-complete<set_error_t>(std::forward<E>(e));
      }
    void set_stopped() && noexcept {
      set-complete<set_stopped_t>();
    }
  private:
    template<class CPO, class... T>
      void set-complete(T&&... t) noexcept {                    
        constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<T>, T> && ...);
        try {
          state->result.template emplace<decayed-tuple<CPO, T...>>(CPO{},
                                                                   std::forward<T>(t)...);
        }
        catch (...) {
          if constexpr (!nothrow) {
            using tuple_t = decayed-tuple<set_error_t, exception_ptr>;
            state->result.template emplace<tuple_t>(set_error_t{}, current_exception());
          }
        }
        state->complete();
      }
  };
}
 Let 
ssource-t be an unspecified type
that models 
stoppable-source and
let 
ssource be an lvalue of type 
ssource-t.Let 
stoken-t be 
decltype(ssource.get_token()).Let future-spawned-sender be the alias template:
template<sender Sender, class Env>
using future-spawned-sender =                                   
  decltype(write_env(stop-when(declval<Sender>(), declval<stoken-t>()), declval<Env>()));
 Let spawn-future-state be the exposition-only class template:
namespace std::execution {
  template<class Alloc, scope_token Token, sender Sender, class Env>
  struct spawn-future-state                                                 
    : spawn-future-state-base<completion_signatures_of_t<future-spawned-sender<Sender, Env>>> {
    using sigs-t =                                                          
      completion_signatures_of_t<future-spawned-sender<Sender, Env>>;
    using receiver-t =                                                      
      spawn-future-receiver<sigs-t>;
    using op-t =                                                            
      connect_result_t<future-spawned-sender<Sender, Env>, receiver-t>;
    spawn-future-state(Alloc alloc, Sender&& sndr, Token token, Env env)    
      : alloc(std::move(alloc)),
        op(connect(
          write_env(stop-when(std::forward<Sender>(sndr), ssource.get_token()), std::move(env)),
          receiver-t(this))),
        token(std::move(token)),
        associated(token.try_associate()) {
          if (associated)
            start(op);
          else
            set_stopped(receiver-t(this));
        }
    void complete() noexcept override;                                      
    void consume(receiver auto& rcvr) noexcept;                             
    void abandon() noexcept;                                                
  private:
    using alloc-t =                                                         
      typename allocator_traits<Alloc>::template rebind_alloc<spawn-future-state>;
    alloc-t alloc;                                                          
    ssource-t ssource;                                                      
    op-t op;                                                                
    Token token;                                                            
    bool associated;                                                        
    void destroy() noexcept;                                                
  };
}
 For purposes of determining the existence of a data race,
complete, 
consume, and 
abandon
behave as atomic operations (
[intro.multithread])
.These operations on a single object of a type
that is a specialization of 
spawn-future-state
appear to occur in a single total order
.void complete() noexcept;
Effects: 
- No effects if this invocation of complete happens before
an invocation of consume or abandon on *this;
- otherwise,
if an invocation of consume on *this happens before
this invocation of complete then
there is a receiver, rcvr, registered and
that receiver is completed as if by consume(rcvr);
- otherwise,
destroy is invoked.
 void consume(receiver auto& rcvr) noexcept;
Effects: 
- If this invocation of consume happens before
an invocation of complete on *this then
rcvr is registered to be completed when
complete is subsequently invoked on *this;
- otherwise,
rcvr is completed as if by:
std::move(this->result).visit(
  [&rcvr](auto&& tuple) noexcept {
    if constexpr (!same_as<remove_reference_t<decltype(tuple)>, monostate>) {
      apply([&rcvr](auto cpo, auto&&... vals) {
        cpo(std::move(rcvr), std::move(vals)...);
      }, std::move(tuple));
    }
  });
 Effects: 
- If this invocation of abandon happens before
an invocation of complete on *this then
equivalent to:
ssource.request_stop();
- otherwise,
destroy is invoked.
 Effects: Equivalent to:
auto token = std::move(this->token);
bool associated = this->associated;
{
  auto alloc = std::move(this->alloc);
  allocator_traits<alloc-t>::destroy(alloc, this);
  allocator_traits<alloc-t>::deallocate(alloc, this, 1);
}
if (associated)
  token.disassociate();
The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
spawn_future_t as follows:
namespace std::execution {
  template<>
  struct impls-for<spawn_future_t> : default-impls {
    static constexpr auto start = see below;                    
  };
}
 The member impls-for<spawn_future_t>::start
is initialized with a callable object equivalent to the following lambda:
[](auto& state, auto& rcvr) noexcept -> void {
  state->consume(rcvr);
}
For the expression 
spawn_future(sndr, token, env)
let 
new_sender be the expression 
token.wrap(sndr) and
let 
alloc and 
senv be defined as follows:
- if the expression get_allocator(env) is well-formed, then
alloc is the result of get_allocator(env) and
senv is the expression env;
- otherwise,
if the expression get_allocator(get_env(new_sender)) is well-formed, then
alloc is the result of get_allocator(get_env(new_sender)) and
senv is the expression
JOIN-ENV(prop(get_allocator, alloc), env);
- otherwise,
alloc is allocator<void>() and
senv is the expression env.
The expression spawn_future(sndr, token, env)
has the following effects:
- Uses  alloc-  to allocate and construct an object  s-  of
a type that is a specialization of  spawn-future-state- 
from  alloc- ,  token.wrap(sndr)- ,  token- , and  senv.
- If an exception is thrown then
any constructed objects are destroyed and
any allocated memory is deallocated .
- Constructs an object  u-  of
a type that is a specialization of  unique_ptr-  such that:
   - u.get() is equal to the address of s, and
- u.get_deleter()(u.release()) is equivalent to
  u.release()->abandon().
 
- Returns  make-sender(spawn_future, std::move(u)).
The expression 
spawn_future(sndr, token) is expression-equivalent to
spawn_future(sndr, token, execution::env<>()).this_thread::sync_wait and 
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
.  sync_wait mandates
that the input sender has exactly one value completion signature
. Let sync-wait-env be the following exposition-only class type:
namespace std::this_thread {
  struct sync-wait-env {
    execution::run_loop* loop;                                  
    auto query(execution::get_scheduler_t) const noexcept {
      return loop->get_scheduler();
    }
    auto query(execution::get_delegation_scheduler_t) const noexcept {
      return loop->get_scheduler();
    }
  };
}
Let 
sync-wait-result-type and
sync-wait-with-variant-result-type
be exposition-only alias templates defined as follows:
namespace std::this_thread {
  template<execution::sender_in<sync-wait-env> Sndr>
    using sync-wait-result-type =
      optional<execution::value_types_of_t<Sndr, sync-wait-env, decayed-tuple,
               type_identity_t>>;
  template<execution::sender_in<sync-wait-env> Sndr>
    using sync-wait-with-variant-result-type =
      optional<execution::value_types_of_t<Sndr, sync-wait-env>>;
}
The name 
this_thread::sync_wait denotes a customization point object
.For a subexpression 
sndr, let 
Sndr be 
decltype((sndr)).The expression 
this_thread::sync_wait(sndr)
is expression-equivalent to the following,
except that 
sndr is evaluated only once:
apply_sender(get-domain-early(sndr), sync_wait, sndr)
Mandates: 
- The type  sync-wait-result-type<Sndr>-  is well-formed .
- same_as<decltype(e), sync-wait-result-type<Sndr>>- 
is  true- , where  e-  is the  apply_sender-  expression above .
 
Let sync-wait-state and sync-wait-receiver
be the following exposition-only class templates:
namespace std::this_thread {
  template<class Sndr>
  struct sync-wait-state {                                      
    execution::run_loop loop;                                   
    exception_ptr error;                                        
    sync-wait-result-type<Sndr> result;                         
  };
  template<class Sndr>
  struct sync-wait-receiver {                                   
    using receiver_concept = execution::receiver_t;
    sync-wait-state<Sndr>* state;                               
    template<class... Args>
    void set_value(Args&&... args) && noexcept;
    template<class Error>
    void set_error(Error&& err) && noexcept;
    void set_stopped() && noexcept;
    sync-wait-env get_env() const noexcept { return {&state->loop}; }
  };
}
template<class... Args>
void set_value(Args&&... args) && noexcept;
Effects: Equivalent to:
try {
  state->result.emplace(std::forward<Args>(args)...);
} catch (...) {
  state->error = current_exception();
}
state->loop.finish();
template<class Error>
void set_error(Error&& err) && noexcept;
Effects: Equivalent to:
state->error = AS-EXCEPT-PTR(std::forward<Error>(err));    
state->loop.finish();
void set_stopped() && noexcept;
Effects: Equivalent to 
state->loop.finish(). For a subexpression 
sndr, let 
Sndr be 
decltype((sndr)).If 
sender_to<Sndr, sync-wait-receiver<Sndr>>
is 
false,
the expression 
sync_wait.apply_sender(sndr) is ill-formed;
otherwise, it is equivalent to:
sync-wait-state<Sndr> state;
auto op = connect(sndr, sync-wait-receiver<Sndr>{&state});
start(op);
state.loop.run();
if (state.error) {
  rethrow_exception(std::move(state.error));
}
return std::move(state.result);
The behavior of 
this_thread::sync_wait(sndr) is undefined unless:
- It blocks the current thread of execution ( [defns.block]- )
with forward progress guarantee delegation ( [intro.progress]- )
until the specified sender completes .
- [ Note 1- :  - The default implementation of  sync_wait-  achieves
forward progress guarantee delegation by providing a  run_loop-  scheduler
via the  get_delegation_scheduler-  query
on the  sync-wait-receiver- 's environment .
- The  run_loop-  is driven by the current thread of execution .
-  —  end note- ] 
- It returns the specified sender's async results as follows:
 - For a value completion,
the result datums are returned in
a  tuple-  in an engaged  optional-  object .
- For an error completion, an exception is thrown .
- For a stopped completion, a disengaged  optional-  object is returned .
 
The name 
this_thread::sync_wait_with_variant denotes
a customization point object
.For a subexpression 
sndr,
let 
Sndr be 
decltype(into_variant(sndr)).The expression 
this_thread::sync_wait_with_variant(sndr)
is expression-equivalent to the following,
except 
sndr is evaluated only once:
apply_sender(get-domain-early(sndr), sync_wait_with_variant, sndr)
Mandates: 
- The type  sync-wait-with-variant-result-type<Sndr>- 
is well-formed .
- same_as<decltype(e), sync-wait-with-variant-result-type<Sndr>>- 
is  true- , where  e-  is the  apply_sender-  expression above .
 
The expression sync_wait_with_variant.apply_sender(sndr) is equivalent to:
using result_type = sync-wait-with-variant-result-type<Sndr>;
if (auto opt_value = sync_wait(into_variant(sndr))) {
  return result_type(std::move(get<0>(*opt_value)));
}
return result_type(nullopt);
The behavior of 
this_thread::sync_wait_with_variant(sndr)
is undefined unless:
- It blocks the current thread of execution ( [defns.block]- )
with forward progress guarantee delegation ( [intro.progress]- )
until the specified sender completes .
- [ Note 1- :  - The default implementation of  sync_wait_with_variant-  achieves
forward progress guarantee delegation by relying on
the forward progress guarantee delegation provided by  sync_wait.
-  —  end note- ] 
- It returns the specified sender's async results as follows:
 - For a value completion,
the result datums are returned in an engaged  optional-  object
that contains a  variant-  of  tuple- s .
- For an error completion, an exception is thrown .
- For a stopped completion, a disengaged  optional-  object is returned .
 
spawn attempts to associate the given input sender with
the given token's async scope and, on success,
eagerly starts the input sender
. The name 
spawn denotes a customization point object
.For subexpressions 
sndr, 
token, and 
env,
- let Sndr be decltype((sndr)),
- let Token be remove_cvref_t<decltype((token))>, and
- let Env be remove_cvref_t<decltype((env))>.
If any of
sender<Sndr>,
scope_token<Token>, or
queryable<Env>
are not satisfied,
the expression 
spawn(sndr, token, env) is ill-formed
.Let spawn-state-base be the exposition-only class:
namespace std::execution {
  struct spawn-state-base {                                 
    virtual void complete() noexcept = 0;                   
  };
}
 Let spawn-receiver be the exposition-only class:
namespace std::execution {
  struct spawn-receiver {                                   
    using receiver_concept = receiver_t;
    spawn-state-base* state;                                
    void set_value() && noexcept { state->complete(); }
    void set_stopped() && noexcept { state->complete(); }
  };
}
 Let spawn-state be the exposition-only class template:
namespace std::execution {
  template<class Alloc, scope_token Token, sender Sender>
  struct spawn-state : spawn-state-base {                   
    using op-t = connect_result_t<Sender, spawn-receiver>;  
    spawn-state(Alloc alloc, Sender&& sndr, Token token);   
    void complete() noexcept override;                      
    void run();                                             
  private:
    using alloc-t =                                         
      typename allocator_traits<Alloc>::template rebind_alloc<spawn-state>;
    alloc-t alloc;                                          
    op-t op;                                                
    Token token;                                            
    void destroy() noexcept;                                
  };
}
 spawn-state(Alloc alloc, Sender&& sndr, Token token);
Effects: Initializes
alloc with alloc,
token with token, and
op with:
connect(std::move(sndr), spawn-receiver(this))
Effects: Equivalent to:
if (token.try_associate())
  start(op);
else
  destroy();
void complete() noexcept override;
Effects: Equivalent to:
auto token = std::move(this->token);
destroy();
token.disassociate();
Effects: Equivalent to:
auto alloc = std::move(this->alloc);
allocator_traits<alloc-t>::destroy(alloc, this);
allocator_traits<alloc-t>::deallocate(alloc, this, 1);
For the expression 
spawn(sndr, token, env)
let 
new_sender be the expression 
token.wrap(sndr) and
let 
alloc and 
senv be defined as follows:
- if the expression get_allocator(env) is well-formed, then
alloc is the result of get_allocator(env) and
senv is the expression env,
- otherwise
if the expression get_allocator(get_env(new_sender)) is well-formed, then
alloc is the result of get_allocator(get_env(new_sender)) and
senv is the expression JOIN-ENV(prop(get_allocator, alloc), env),
- otherwise
alloc is allocator<void>() and
senv is the expression env.
The expression 
spawn(sndr, token, env) is of type 
void and
has the following effects:
- Uses alloc to allocate and construct an object o of
type that is a specialization of spawn-state from
alloc, write_env(token.wrap(sndr), senv), and token
and then
invokes o.run(). If an exception is thrown then
any constructed objects are destroyed and any allocated memory is deallocated .
The expression 
spawn(sndr, token) is expression-equivalent to
spawn(sndr, token, execution::env<>()).completion_signatures is a type
that encodes a set of completion signatures (
[exec.async.ops])
. [
Example 1: 
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()>;
};
 
Declares 
my_sender to be a sender
that can complete by calling one of the following
for a receiver expression 
rcvr:
- set_value(rcvr)
- set_value(rcvr, int{...}, float{...})
- set_error(rcvr, exception_ptr{...})
- set_error(rcvr, error_code{...})
- set_stopped(rcvr)
 — 
end example]
This subclause makes use of the following exposition-only entities:
template<class Fn>
  concept completion-signature = see below;
A type 
Fn satisfies 
completion-signature
if and only if it is a function type with one of the following forms:
- set_value_t(Vs...)- ,
where  Vs-  is a pack of object or reference types .
 
- set_error_t(Err)- ,
where  Err-  is an object or reference type .
 
template<bool>
  struct indirect-meta-apply {
    template<template<class...> class T, class... As>
      using meta-apply = T<As...>;                              
  };
template<class...>
  concept always-true = true;                                   
template<class Tag,
         valid-completion-signatures Completions,
         template<class...> class Tuple,
         template<class...> class Variant>
  using gather-signatures = see below;
 Let 
Fns be a pack of the arguments of
the 
completion_signatures specialization named by 
Completions,
let 
TagFns be a pack of the function types in 
Fns
whose return types are 
Tag, and
let 
Tsn be a pack of the function argument types
in the 
n-th type in 
TagFns.Then, given two variadic templates Tuple and Variant,
the type gather-signatures<Tag, Completions, Tuple, Variant>
names the type
META-APPLY(Variant, META-APPLY(Tuple, Ts0...),
                    META-APPLY(Tuple, Ts1...),
                    …,
                    META-APPLY(Tuple, Tsm−1...))
where m is the size of the pack TagFns and
META-APPLY(T, As...) is equivalent to:
typename indirect-meta-apply<always-true<As...>>::template meta-apply<T, As...>
[
Note 1: 
The purpose of 
META-APPLY is to make it valid
to use non-variadic templates as 
Variant and 
Tuple arguments
to 
gather-signatures. — 
end note]
namespace std::execution {
  template<completion-signature... Fns>
    struct completion_signatures {
      template<class Tag>
        static constexpr size_t count-of(Tag) { return see below; }
      template<class Fn>
        static constexpr void for-each(Fn&& fn) {               
          (std::forward<Fn>(fn)(static_cast<Fns*>(nullptr)), ...);
        }
    };
  template<class Sndr, class Env = env<>,
           template<class...> class Tuple = decayed-tuple,
           template<class...> class Variant = variant-or-empty>
      requires sender_in<Sndr, Env>
    using value_types_of_t =
      gather-signatures<set_value_t, completion_signatures_of_t<Sndr, Env>, Tuple, Variant>;
  template<class Sndr, class Env = env<>,
           template<class...> class Variant = variant-or-empty>
      requires sender_in<Sndr, Env>
    using error_types_of_t =
      gather-signatures<set_error_t, completion_signatures_of_t<Sndr, Env>,
                        type_identity_t, Variant>;
  template<class Sndr, class Env = env<>>
      requires sender_in<Sndr, Env>
    constexpr bool sends_stopped =
      !same_as<type-list<>,
               gather-signatures<set_stopped_t, completion_signatures_of_t<Sndr, Env>,
                                 type-list, type-list>>;
}
 For a subexpression 
tag,
let 
Tag be the decayed type of 
tag.completion_signatures<Fns...>::count-of(
tag)
returns the count of function types in 
Fns... that
are of the form 
Tag(Ts...) where 
Ts is a pack of types
. namespace std::execution {
  template<class QueryTag, class ValueType>
  struct prop {
    QueryTag query_;            
    ValueType value_;           
    constexpr const ValueType& query(QueryTag) const noexcept {
      return value_;
    }
  };
  template<class QueryTag, class ValueType>
    prop(QueryTag, ValueType) -> prop<QueryTag, unwrap_reference_t<ValueType>>;
}
 Class template 
prop is for building a queryable object
from a query object and a value
.Mandates: 
callable<QueryTag, prop-like<ValueType>>
is modeled,
where 
prop-like is the following exposition-only class template:
template<class ValueType>
struct prop-like {              
  const ValueType& query(auto) const noexcept;
};
 [
Example 1: 
template<sender Sndr>
sender auto parameterize_work(Sndr sndr) {
  
  auto e = prop(get_allocator, my_alloc{});
  
  return write_env(sndr, e);
}
 — 
end example]
Specializations of 
prop are not assignable
.namespace std::execution {
  template<queryable... Envs>
  struct env {
    Envs0 envs0;               
    Envs1 envs1;               
      ⋮
    Envsn−1 envsn−1;           
    template<class QueryTag>
      constexpr decltype(auto) query(QueryTag q) const noexcept(see below);
  };
  template<class... Envs>
    env(Envs...) -> env<unwrap_reference_t<Envs>...>;
}
 The class template 
env is used to construct a queryable object
from several queryable objects
.Query invocations on the resulting object are resolved
by attempting to query each subobject in lexical order
.Specializations of 
env are not assignable
.[
Example 1: 
template<sender Sndr>
sender auto parameterize_work(Sndr sndr) {
  
  
  
  auto e = env{prop(get_allocator, my_alloc{}),
               prop(get_scheduler, my_sched{})};
  
  return write_env(sndr, e);
}
 — 
end example]
template<class QueryTag>
constexpr decltype(auto) query(QueryTag q) const noexcept(see below);
Let 
has-query be the following exposition-only concept:
template<class Env, class QueryTag>
  concept has-query =                   
    requires (const Env& env) {
      env.query(QueryTag());
    };
Let 
fe be the first element of
envs0, 
envs1, 
…, 
envsn−1
such that the expression 
fe.query(q) is well-formed
.Constraints: 
(has-query<Envs, QueryTag> || ...) is 
true. Effects: Equivalent to: return fe.query(q);
Remarks: The expression in the 
noexcept clause is equivalent
to 
noexcept(fe.query(q)). A 
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 
run member function removes elements from the queue and
executes them in a loop on the thread of execution that calls 
run.A 
run_loop instance has an associated 
count
that corresponds to the number of work items that are in its queue
.Concurrent invocations of the member functions of 
run_loop
other than 
run and its destructor do not introduce data races
.The member functions
pop-front, 
push-back, and 
finish
execute atomically
.Recommended practice: Implementations should use an intrusive queue of operation states
to hold the work units to make scheduling allocation-free
. namespace std::execution {
  class run_loop {
    
    class run-loop-scheduler;                                   
    class run-loop-sender;                                      
    struct run-loop-opstate-base {                              
      virtual void execute() = 0;                               
      run_loop* loop;                                           
      run-loop-opstate-base* next;                              
    };
    template<class Rcvr>
      using run-loop-opstate = unspecified;                     
    
    run-loop-opstate-base* pop-front();                         
    void push-back(run-loop-opstate-base*);                     
  public:
    
    run_loop() noexcept;
    run_loop(run_loop&&) = delete;
    ~run_loop();
    
    run-loop-scheduler get_scheduler();
    void run();
    void finish();
  };
}
 class run-loop-scheduler;
run-loop-scheduler is an unspecified type
that models 
scheduler. Instances of 
run-loop-scheduler remain valid
until the end of the lifetime of the 
run_loop instance
from which they were obtained
.Two instances of 
run-loop-scheduler compare equal
if and only if they were obtained from the same 
run_loop instance
.Let 
sch be an expression of type 
run-loop-scheduler.The expression 
schedule(sch)
has type 
run-loop-
sender and
is not potentially-throwing if 
sch is not potentially-throwing
.run-loop-sender is an exposition-only type
that satisfies 
sender.  completion_signatures_of_t<run-
loop-sender> is
completion_signatures<set_value_t(), set_error_t(exception_ptr), set_stopped_t()>
An instance of 
run-loop-sender remains valid
until the end of the lifetime of its associated 
run_loop instance
.Let 
sndr be an expression of type 
run-loop-sender,
let 
rcvr be an expression
such that 
receiver_of<decltype((rcvr)), CS> is 
true
where 
CS is the 
completion_signatures specialization above
.Let 
C be either 
set_value_t or 
set_stopped_t.Then:
- The expression  connect(sndr, rcvr)- 
has type  run-loop-opstate<decay_t<decltype((rcvr))>>- 
and is potentially-throwing if and only if
 (void(sndr), auto(rcvr))-  is potentially-throwing .
- The expression  get_completion_scheduler<C>(get_env(sndr))- 
is potentially-throwing if and only if  sndr-  is potentially-throwing,
has type  run-loop-scheduler- , and
compares equal to the  run-loop-
 scheduler-  instance
from which  sndr-  was obtained .
template<class Rcvr>
  struct run-loop-opstate;
run-loop-opstate<Rcvr>
inherits privately and unambiguously from 
run-loop-opstate-base. Let 
o be a non-const lvalue of type 
run-loop-opstate<Rcvr>,
and let 
REC(o) be a non-const lvalue reference to an instance of type 
Rcvr
that was initialized with the expression 
rcvr
passed to the invocation of connect that returned 
o.Then:
- The object to which REC(o) refers
remains valid for the lifetime of the object to which o refers.
- The type run-loop-opstate<Rcvr> overrides
run-loop-opstate-base::execute()
such that o.execute() is equivalent to:
if (get_stop_token(REC(o)).stop_requested()) {
  set_stopped(std::move(REC(o)));
} else {
  set_value(std::move(REC(o)));
}
- The expression start(o) is equivalent to:
try {
  o.loop->push-back(addressof(o));
} catch(...) {
  set_error(std::move(REC(o)), current_exception());
}
Postconditions: 
count is 
0 and 
state is 
starting.  Otherwise, has no effects
. run-loop-opstate-base* pop-front();
Effects: Blocks (
[defns.block]) until one of the following conditions is 
true:
- count is 0 and state is finishing,
in which case pop-front sets state to finished
and returns nullptr; or
- count is greater than 0,
in which case an item is removed from the front of the queue,
count is decremented by 1, and
the removed item is returned.
 void push-back(run-loop-opstate-base* item);
Effects: Adds 
item to the back of the queue and
increments 
count by 
1. Synchronization: This operation synchronizes with
the 
pop-front operation that obtains 
item. run-loop-scheduler get_scheduler();
Returns: An instance of 
run-loop-scheduler
that can be used to schedule work onto this 
run_loop instance
. Preconditions: 
state is either 
starting or 
finishing. Effects: If 
state is 
starting,
sets the 
state to 
running,
otherwise leaves 
state unchanged
.  Then, equivalent to:
while (auto* op = pop-front()) {
  op->execute();
}
Remarks: When 
state changes, it does so without introducing data races
. Preconditions: 
state is either 
starting or 
running. Effects: Changes 
state to 
finishing. Synchronization: 
finish synchronizes with the 
pop-front operation
that returns 
nullptr. as_awaitable transforms an object into one
that is awaitable within a particular coroutine
.  Subclause 
[exec.coro.util] makes use of
the following exposition-only entities:
namespace std::execution {
  template<class Sndr, class Promise>
    concept awaitable-sender =
      single-sender<Sndr, env_of_t<Promise>> &&
      sender_to<Sndr, awaitable-receiver> &&    
      requires (Promise& p) {
        { p.unhandled_stopped() } -> convertible_to<coroutine_handle<>>;
      };
  template<class Sndr>
    concept has-queryable-await-completion-adaptor =            
      sender<Sndr> &&
      requires(Sndr&& sender) {
        get_await_completion_adaptor(get_env(sender));
      };
  template<class Sndr, class Promise>
    class sender-awaitable;                                     
}
The type sender-awaitable<Sndr, Promise> is equivalent to:
namespace std::execution {
  template<class Sndr, class Promise>
  class sender-awaitable {
    struct unit {};                                             
    using value-type =                                          
      single-sender-value-type<Sndr, env_of_t<Promise>>;
    using result-type =                                         
      conditional_t<is_void_v<value-type>, unit, value-type>;
    struct awaitable-receiver;                                  
    variant<monostate, result-type, exception_ptr> result{};    
    connect_result_t<Sndr, awaitable-receiver> state;           
  public:
    sender-awaitable(Sndr&& sndr, Promise& p);
    static constexpr bool await_ready() noexcept { return false; }
    void await_suspend(coroutine_handle<Promise>) noexcept { start(state); }
    value-type await_resume();
  };
}
awaitable-receiver is equivalent to:
struct awaitable-receiver {
  using receiver_concept = receiver_t;
  variant<monostate, result-type, exception_ptr>* result-ptr;   
  coroutine_handle<Promise> continuation;                       
  
};
Let 
rcvr be an rvalue expression of type 
awaitable-receiver,
let 
crcvr be a const lvalue that refers to 
rcvr,
let 
vs be a pack of subexpressions, and
let 
err be an expression of type 
Err.Then:
- If constructible_from<result-type, decltype((vs))...>
is satisfied,
the expression set_value(
 rcvr, vs...) is equivalent to:
try {
  rcvr.result-ptr->template emplace<1>(vs...);
} catch(...) {
  rcvr.result-ptr->template emplace<2>(current_exception());
}
rcvr.continuation.resume();
Otherwise,  set_value(rcvr, vs...) is ill-formed .
- The expression set_error(rcvr, err) is equivalent to:
rcvr.result-ptr->template emplace<2>(AS-EXCEPT-PTR(err));    
rcvr.continuation.resume();
- The expression set_stopped(rcvr) is equivalent to:
static_cast<coroutine_handle<>>(rcvr.continuation.promise().unhandled_stopped()).resume();
- For any expression tag
whose type satisfies forwarding-query and
for any pack of subexpressions as,
get_env(crcvr).query(tag, as...) is expression-equivalent to:
tag(get_env(as_const(crcvr.continuation.promise())), as...)
sender-awaitable(Sndr&& sndr, Promise& p);
Effects: Initializes state with
connect(std::forward<Sndr>(sndr),
        awaitable-receiver{addressof(result), coroutine_handle<Promise>::from_promise(p)})
value-type await_resume();
Effects: Equivalent to:
if (result.index() == 2)
  rethrow_exception(get<2>(result));
if constexpr (!is_void_v<value-type>)
  return std::forward<value-type>(get<1>(result));
as_awaitable is a customization point object
.  For subexpressions 
expr and 
p
where 
p is an lvalue,
Expr names the type 
decltype((expr)) and
Promise names the type 
decay_t<decltype((p))>,
as_awaitable(expr, p) is expression-equivalent to,
except that the evaluations of 
expr and 
p
are indeterminately sequenced:
- expr.as_awaitable(p)-  if that expression is well-formed .
 
- Mandates- :  is-awaitable<A, Promise>-  is  true- ,
where  A-  is the type of the expression above .
 
- Otherwise,  (void(p), expr)- 
if  is-awaitable<Expr, U>-  is  true- ,
where  U-  is an unspecified class type
that is not  Promise-  and
that lacks a member named  await_transform.
- Preconditions- :  is-awaitable<Expr, Promise>-  is  true-  and
the expression  co_await expr- 
in a coroutine with promise type  U-  is expression-equivalent to
the same expression in a coroutine with promise type  Promise.
 
- Otherwise,  sender-awaitable{adapted-expr, p}- 
if
 has-queryable-await-completion-adaptor<Expr>- 
and
 awaitable-sender<decltype((adapted-expr)), Promise>- 
are both satisfied, where  adapted-expr-  is
 get_await_completion_adaptor(get_env(expr))(expr)- ,
except that  expr-  is evaluated only once .
- Otherwise,  (void(p), expr).
with_awaitable_senders,
when used as the base class of a coroutine promise type,
makes senders awaitable in that coroutine type
. In addition, it provides a default implementation of 
unhandled_stopped
such that if a sender completes by calling 
set_stopped,
it is treated as if an uncatchable "stopped" exception were thrown
from the 
await-expression.[
Note 1: 
The coroutine is never resumed, and
the 
unhandled_stopped of the coroutine caller's promise type is called
. — 
end note]
namespace std::execution {
  template<class-type Promise>
    struct with_awaitable_senders {
      template<class OtherPromise>
        requires (!same_as<OtherPromise, void>)
      void set_continuation(coroutine_handle<OtherPromise> h) noexcept;
      coroutine_handle<> continuation() const noexcept { return continuation; }
      coroutine_handle<> unhandled_stopped() noexcept {
        return stopped-handler(continuation.address());
      }
      template<class Value>
      see below await_transform(Value&& value);
    private:
      [[noreturn]] static coroutine_handle<>
        default-unhandled-stopped(void*) noexcept {             
        terminate();
      }
      coroutine_handle<> continuation{};                        
      coroutine_handle<> (*stopped-handler)(void*) noexcept =   
        &default-unhandled-stopped;
    };
}
 template<class OtherPromise>
  requires (!same_as<OtherPromise, void>)
void set_continuation(coroutine_handle<OtherPromise> h) noexcept;
Effects: Equivalent to:
continuation = h;
if constexpr ( requires(OtherPromise& other) { other.unhandled_stopped(); } ) {
  stopped-handler = [](void* p) noexcept -> coroutine_handle<> {
    return coroutine_handle<OtherPromise>::from_address(p)
      .promise().unhandled_stopped();
  };
} else {
  stopped-handler = &default-unhandled-stopped;
}
Effects: Equivalent to:
return as_awaitable(std::forward<Value>(value), static_cast<Promise&>(*this));
affine_on adapts a sender into one that completes on
the specified scheduler
.  If the algorithm determines that the adapted sender already completes
on the correct scheduler it can avoid any scheduling operation
.The name 
affine_on denotes a pipeable sender adaptor
object
.For subexpressions 
sch and 
sndr, if 
decltype((sch))
does not satisfy 
scheduler, or 
decltype((sndr))
does not satisfy 
sender, 
affine_on(sndr, sch)
is ill-formed
.Otherwise, the expression 
affine_on(sndr, sch) is
expression-equivalent to:
transform_sender(get-domain-early(sndr), make-sender(affine_on, sch, sndr))
except that 
sndr is evaluated only once
.The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
affine_on_t as follows:
namespace std::execution {
  template<>
  struct impls-for<affine_on_t> : default-impls {
    static constexpr auto get-attrs =
      [](const auto& data, const auto& child) noexcept -> decltype(auto) {
        return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
      };
  };
}
Let 
out_sndr be a subexpression denoting a sender
returned from 
affine_on(sndr, sch) or one equal to such,
and let 
OutSndr be the type 
decltype((out_sndr)).Let 
out_rcvr be a subexpression denoting a receiver that
has an environment of type 
Env such that 
sender_in<OutSndr, Env>
is 
true.Let 
op be an lvalue referring to the operation state that
results from connecting 
out_sndr to 
out_rcvr.Calling 
start(op) will start 
sndr on the current
execution agent and execute completion operations on 
out_rcvr
on an execution agent of the execution resource associated with
sch.If the current execution resource is the same as the execution
resource associated with 
sch, the completion operation on
out_rcvr may be called before 
start(op) completes
.If scheduling onto 
sch fails, an error completion on
out_rcvr shall be executed on an unspecified execution
agent
.namespace std::execution {
  class inline_scheduler {
    class inline-sender;                
    template<receiver R>
      class inline-state;               
  public:
    using scheduler_concept = scheduler_t;
    constexpr inline-sender schedule() noexcept { return {}; }
    constexpr bool operator==(const inline_scheduler&) const noexcept = default;
  };
}
  All objects of type 
inline_scheduler are equal
. inline-sender is an exposition-only type that satisfies
sender.  The type 
completion_signatures_of_t<inline-sender>
is 
completion_signatures<set_value_t()>.Let 
sndr be an expression of type 
inline-sender,
let 
rcvr be an expression such that
receiver_of<decltype((rcvr)), CS> is 
true
where 
CS is 
completion_signatures<set_value_t()>,
then:
- the expression connect(sndr, rcvr) has
type inline-state<remove_cvref_t<decltype((rcvr))>>
and is potentially-throwing if and only if
((void)sndr, auto(rcvr)) is potentially-throwing, and
- the expression
get_completion_scheduler<set_value_t>(get_env(sndr)) has
type inline_scheduler and is potentially-throwing
if and only if get_env(sndr) is potentially-throwing.
Let 
o be a non-
const lvalue of type
inline-state<Rcvr>, and let 
REC(o) be
a non-
const lvalue reference to an object of type 
Rcvr that
was initialized with the expression 
rcvr passed to an
invocation of 
connect that returned 
o, then:
- the object to which REC(o) refers remains valid for
the lifetime of the object to which o refers, and
- the expression start(o) is equivalent to
set_value(std::move(REC(o))).
namespace std::execution {
  class task_scheduler {
    class ts-sender;                    
    template<receiver R>
      class state;                      
  public:
    using scheduler_concept = scheduler_t;
    template<class Sch, class Allocator = allocator<void>>
      requires (!same_as<task_scheduler, remove_cvref_t<Sch>>)
        && scheduler<Sch>
    explicit task_scheduler(Sch&& sch, Allocator alloc = {});
    ts-sender schedule();
    friend bool operator==(const task_scheduler& lhs, const task_scheduler& rhs)
        noexcept;
    template<class Sch>
      requires (!same_as<task_scheduler, Sch>)
      && scheduler<Sch>
    friend bool operator==(const task_scheduler& lhs, const Sch& rhs) noexcept;
  private:
    shared_ptr<void> sch_; 
  };
}
  Given an object 
s of type 
task_scheduler, let
SCHED(s) be the object owned by 
s.sch_. template<class Sch, class Allocator = allocator<void>>
  requires(!same_as<task_scheduler, remove_cvref_t<Sch>>) && scheduler<Sch>
explicit task_scheduler(Sch&& sch, Allocator alloc = {});
Effects: Initialize 
sch_ with
allocate_shared<remove_cvref_t<Sch>>(alloc, std::forward<Sch>(sch)). Recommended practice: Implementations should avoid the use of dynamically
allocated memory for small scheduler objects
. Remarks: Any allocations performed by construction of 
ts-sender or
state objects resulting from calls on 
*this are
performed using a copy of 
alloc. Effects: Returns an object of type 
ts-sender containing a sender
initialized with 
schedule(SCHED(*this)). bool operator==(const task_scheduler& lhs, const task_scheduler& rhs) noexcept;
Effects: Equivalent to: return lhs == SCHED(rhs);
template<class Sch>
  requires (!same_as<task_scheduler, Sch>)
        && scheduler<Sch>
bool operator==(const task_scheduler& lhs, const Sch& rhs) noexcept;
Returns: 
false if the type of 
SCHED(lhs) is not 
Sch,
otherwise 
SCHED(lhs) == rhs. namespace std::execution {
  class task_scheduler::ts-sender {     
  public:
    using sender_concept = sender_t;
    template<receiver Rcvr>
      state<Rcvr> connect(Rcvr&& rcvr);
  };
}
ts-sender is an exposition-only class that models
sender (
[exec.snd]) and for which
completion_signatures_of_t<ts-sender> denotes:
completion_signatures<
  set_value_t(),
  set_error_t(error_code),
  set_error_t(exception_ptr),
  set_stopped_t()>
 Let 
sch be an object of type 
task_scheduler
and let 
sndr be an object of type 
ts-sender obtained
from 
schedule(sch).Then 
get_completion_scheduler<set_value_t>(get_env(sndr)) == sch
is 
true.The object 
SENDER(sndr) is the sender object contained by
sndr or an object move constructed from it
.template<receiver Rcvr>
  state<Rcvr> connect(Rcvr&& rcvr);
Effects: Let 
r be an object of a type that models 
receiver
and whose completion handlers result in invoking the corresponding
completion handlers of 
rcvr or copy thereof
.  Returns an object of type 
state<Rcvr> containing
an operation state object initialized with 
connect(SENDER(*this),
std::move(r)).namespace std::execution {
  template<receiver R>
  class task_scheduler::state {         
  public:
    using operation_state_concept = operation_state_t;
    void start() & noexcept;
  };
}
state is an exposition-only class template whose
specializations model 
operation_state (
[exec.opstate])
. Effects: Equivalent to 
start(st) where 
st is the operation
state object contained by 
*this. The 
task class template represents a sender that can
be used as the return type of coroutines
.The first template parameter 
T defines the type of the value
completion datum (
[exec.async.ops]) if 
T is not 
void.Otherwise, there are no value completion datums
.Inside coroutines returning 
task<T, E> the operand of
co_return (if any) becomes the argument of 
set_value.The second template parameter 
Environment is used to customize
the behavior of 
task.namespace std::execution {
  template<class T, class Environment>
  class task {
    
    template<receiver Rcvr>
      class state;                              
  public:
    using sender_concept = sender_t;
    using completion_signatures = see below;
    using allocator_type = see below;
    using scheduler_type = see below;
    using stop_source_type = see below;
    using stop_token_type = decltype(declval<stop_source_type>().get_token());
    using error_types = see below;
    
    class promise_type;
    task(task&&) noexcept;
    ~task();
    template<receiver Rcvr>
      state<Rcvr> connect(Rcvr&& rcvr);
  private:
    coroutine_handle<promise_type> handle;      
  };
}
 task<T, E> models 
sender (
[exec.snd])
if 
T is 
void, a reference type, or a 
cv-unqualified
non-array object type and 
E is a class type
.  Otherwise a program that instantiates the definition of 
task<T, E>
is ill-formed
.The nested types of 
task template specializations
are determined based on the 
Environment parameter:
- allocator_type-  is  Environment::allocator_type- 
if that  qualified-id-  is valid and denotes a type,
 allocator<byte>-  otherwise .
 
- scheduler_type-  is  Environment::scheduler_type- 
if that  qualified-id-  is valid and denotes a type,
 task_scheduler-  otherwise .
 
- stop_source_type-  is  Environment::stop_source_type- 
if that  qualified-id-  is valid and denotes a type,
 inplace_stop_source-  otherwise .
 
- error_types-  is  Environment::error_types-  if
that  qualified-id-  is valid and denotes a type,
 completion_signatures<set_error_t(exception_ptr)>-  otherwise .
 
A program is ill-formed if 
error_types is not a
specialization of 
completion_signatures<ErrorSigs...> or
ErrorSigs contains an element which is not of the form
set_error_t(E) for some type 
E.The type alias 
completion_signatures is a specialization
of 
execution::completion_signatures with the template
arguments (in unspecified order):
- set_value_t() if T is void,
and set_value_t(T) otherwise;
- template arguments of the specialization of
execution::completion_signatures denoted by error_types;
and
- set_stopped_t().
allocator_type shall meet the 
Cpp17Allocator
requirements
. task(task&& other) noexcept;
Effects: Initializes 
handle with 
exchange(other.handle,
{}). Effects: Equivalent to:
if (handle)
  handle.destroy();
template<receiver Rcvr>
  state<Rcvr> connect(Rcvr&& recv);
Preconditions: 
bool(handle) is 
true. Effects: Equivalent to:
return state<Rcvr>(exchange(handle, {}), std::forward<Rcvr>(recv));
namespace std::execution {
  template<class T, class Environment>
  template<receiver Rcvr>
  class task<T, Environment>::state {           
  public:
    using operation_state_concept = operation_state_t;
    template<class R>
      state(coroutine_handle<promise_type> h, R&& rr);
    ~state();
    void start() & noexcept;
  private:
    using own-env-t = see below;                
    coroutine_handle<promise_type> handle;      
    remove_cvref_t<Rcvr>           rcvr;        
    own-env-t                      own-env;     
    Environment                    environment; 
  };
}
 The type 
own-env-t is 
Environment::template
env_type<decltype(get_env(declval<Rcvr>()))> if that
qualified-id is valid and denotes a type, 
env<> otherwise
.template<class R>
  state(coroutine_handle<promise_type> h, R&& rr);
Effects: Initializes
- handle with std::move(h); 
- rcvr with std::forward<R>(rr); 
- own-env- 
with  own-env-t(get_env(rcvr))-  if that expression
is valid and  own-env-t()-  otherwise .
 - If neither of these expressions is valid, the program is ill-formed .
- environment-  with
 Environment(own-env)-  if that expression is
valid, otherwise  Environment(get_env(rcvr))- 
if this expression is valid, otherwise  Environment().
 - If neither of these expressions is valid, the program is ill-formed .
 Effects: Equivalent to:
if (handle)
  handle.destroy();
Effects: Let 
prom be the object 
handle.promise().  Associates 
STATE(prom), 
RCVR(prom), and 
SCHED(prom)
with 
*this as follows:
- SCHED(prom)-  is the object initialized
with  scheduler_type(get_scheduler(get_env(rcvr)))- 
if that expression is valid and  scheduler_type()-  otherwise .
 - If neither of these expressions is valid, the program is ill-formed .
Let 
st be 
get_stop_token(get_env(rcvr)).Initializes 
prom.token and
prom.source such that
- prom.token.stop_requested() returns
st.stop_requested();
- prom.token.stop_possible() returns
st.stop_possible(); and
- for types Fn and Init such that both
invocable<Fn> and
constructible_from<Fn, Init> are modeled,
stop_token_type::callback_type<Fn> models
stoppable-callback-for<Fn, stop_token_type, Init>.
After that invokes 
handle.resume().namespace std::execution {
  template<class T, class Environment>
  class task<T, Environment>::promise_type {
  public:
    template<class... Args>
      promise_type(const Args&... args);
    task get_return_object() noexcept;
    auto initial_suspend() noexcept;
    auto final_suspend() noexcept;
    void uncaught_exception();
    coroutine_handle<> unhandled_stopped();
    void return_void();                 
    template<class V>
      void return_value(V&& value);     
    template<class E>
      unspecified yield_value(with_error<E> error);
    template<class A>
      auto await_transform(A&& a);
    template<class Sch>
      auto await_transform(change_coroutine_scheduler<Sch> sch);
    unspecified get_env() const noexcept;
    template<class... Args>
      void* operator new(size_t size, Args&&... args);
    void operator delete(void* pointer, size_t size) noexcept;
  private:
    using error-variant = see below;    
    allocator_type    alloc;            
    stop_source_type  source;           
    stop_token_type   token;            
    optional<T>       result;           
    error-variant     errors;           
  };
}
Let 
prom be an object of 
promise_type
and let 
tsk be the 
task object
created by 
prom.get_return_object().The description below
refers to objects 
STATE(prom),
RCVR(prom),
and 
SCHED(prom)
associated with 
tsk
during evaluation of 
task::state<Rcvr>::start
for some receiver 
Rcvr.error-variant is a 
variant<monostate,
remove_cvref_t<E>...>, with duplicate types removed, where 
E...
are the parameter types of the template arguments of the specialization of
execution::completion_signatures denoted by
error_types. template<class... Args>
  promise_type(const Args&... args);
Mandates: The first parameter of type 
allocator_arg_t (if any) is not
the last parameter
. Effects: If 
Args contains an element of type 
allocator_arg_t
then 
alloc is initialized with the corresponding next
element of 
args.  Otherwise, 
alloc is initialized with 
allocator_type().task get_return_object() noexcept;
Returns: A 
task object whose member 
handle is
coroutine_handle<promise_type>::from_promise(*this). auto initial_suspend() noexcept;
Returns: An awaitable object of unspecified type (
[expr.await]) whose
member functions arrange for
- the calling coroutine to be suspended,
- the coroutine to be resumed on an execution agent of the
execution resource associated with SCHED(*this).
 auto final_suspend() noexcept;
Returns: An awaitable object of unspecified type (
[expr.await]) whose
member functions arrange for the completion of the asynchronous
operation associated with 
STATE(*this) by invoking:
- set_error(std::move(RCVR(*this)), std::move(e))
if errors.index() is greater than zero and
e is the value held by errors, otherwise
- set_value(std::move(RCVR(*this))) if is_void<T> is true,
and otherwise
- set_value(std::move(RCVR(*this)), *result).
 template<class Err>
  auto yield_value(with_error<Err> err);
Mandates: 
std::move(err.error) is convertible to exactly one of the
set_error_t argument types of 
error_types.  Returns: An awaitable object of unspecified type (
[expr.await]) whose
member functions arrange for the calling coroutine to be suspended
and then completes the asynchronous operation associated with
STATE(*this) by invoking 
set_error(std::move(RCVR(*this)),
Cerr(std::move(err.error))). Returns: If 
same_as<inline_scheduler, scheduler_type> is 
true
returns 
as_awaitable(std::forward<Sender>(sndr), *this);
otherwise returns
as_awaitable(affine_on(std::forward<Sender>(sndr), SCHED(*this)), *this). Effects: Equivalent to:
return await_transform(just(exchange(SCHED(*this), scheduler_type(sch.scheduler))), *this);
void uncaught_exception();
Effects: If the signature 
set_error_t(exception_ptr) is not an element
of 
error_types, calls 
terminate() (
[except.terminate])
.  Otherwise, stores 
current_exception() into 
errors.coroutine_handle<> unhandled_stopped();
Effects: Completes the asynchronous operation associated with 
STATE(*this)
by invoking 
set_stopped(std::move(RCVR(*this))). Returns: 
noop_coroutine(). unspecified get_env() const noexcept;
Returns: An object 
env such that queries are forwarded as follows:
- env.query(get_scheduler)-  returns  scheduler_type(SCHED(*this)).
 
- env.query(get_allocator)-  returns  alloc.
 
- env.query(get_stop_token)-  returns  token.
 
- For any other query  q-  and arguments  a...-  a
call to  env.query(q, a...)-  returns
 STATE(*this).
- environment.query(q, a...)-  if this expression
is well-formed and  forwarding_query(q)-  is well-formed and is  true.
 - Otherwise  env.query(q, a...)-  is ill-formed .
 template<class... Args>
  void* operator new(size_t size, const Args&... args);
If there is no parameter with type 
allocator_arg_t then let
alloc be 
allocator_type().Otherwise, let 
arg_next be the parameter
following the first 
allocator_arg_t parameter,
and let 
alloc be 
allocator_type(arg_next).Let 
PAlloc be 
allocator_traits<allocator_type>::template
rebind_alloc<U>, where 
U is an unspecified type
whose size and alignment are both 
__STDCPP_DEFAULT_NEW_ALIGNMENT__.Mandates: 
- The first parameter of type  allocator_arg_t-  (if any) is not the last parameter .
- allocator_type(arg_next)-  is a valid expression if there is a parameter
of type  allocator_arg_t.
 
- allocator_traits<PAlloc>::pointer-  is a pointer type .
 
 Effects: Initializes an allocator 
palloc of type 
PAlloc with
alloc.  Uses 
palloc to allocate storage for the
smallest array of 
U sufficient to provide storage for a
coroutine state of size 
size, and unspecified additional
state necessary to ensure that 
operator delete can later
deallocate this memory block with an allocator equal to 
palloc.Returns: A pointer to the allocated storage
. void operator delete(void* pointer, size_t size) noexcept;
Preconditions: 
pointer was returned from an invocation of the above overload
of 
operator new with a size argument equal to 
size. Effects: Deallocates the storage pointed to by 
pointer using an
allocator equal to that used to allocate it
. The 
scope_token concept defines the requirements on
a type 
Token that can be used to create associations
between senders and an async scope
.Let 
test-sender and 
test-env
be unspecified types such that
sender_in<test-sender, test-env>
is modeled
.namespace std::execution {
  template<class Token>
    concept scope_token =
      copyable<Token> &&
      requires(const Token token) {
        { token.try_associate() } -> same_as<bool>;
        { token.disassociate() } noexcept -> same_as<void>;
        { token.wrap(declval<test-sender>()) } -> sender_in<test-env>;
      };
}
 - no exceptions are thrown from
copy construction,
move construction,
copy assignment, or
move assignment
of objects of type Token; and
- given an lvalue token of type (possibly const) Token,
for all expressions sndr such that
decltype((
 sndr)) models sender:- token.wrap(sndr) is a valid expression,
- decltype(token.wrap(sndr)) models sender, and
- completion_signatures_of_t<decltype(token.wrap(sndr)), E>
  contains the same completion signatures as
  completion_signatures_of_t<decltype((sndr)), E>
  for all types E such that
  sender_in<decltype((sndr)), E> is modeled.
 
Scopes of type 
simple_counting_scope and 
counting_scope
maintain counts of associations
.Let:
- Scope be either simple_counting_scope or counting_scope,
- scope be an object of type Scope,
- tkn be an object of type Scope::token
obtained from scope.get_token(),
- jsndr be a sender obtained from scope.join(), and
- op be an operation state obtained from
connecting jsndr to a receiver.
During its lifetime scope goes through different states
which govern what operations are allowed and the result of these operations:
- unused- :
a newly constructed object starts in the  unused-  state .
 
- open- :
when  tkn.try_associate()-  is called
while  scope-  is in the  unused-  state,
 scope-  moves to the  open-  state .
 
- open-and-joining- :
when the operation state  op-  is started
while  scope-  is in the  unused-  or  open-  state,
 scope-  moves to the  open-and-joining-  state .
 
- closed- :
when  scope.close()-  is called
while  scope-  is in the  open-  state,
 scope-  moves to the  closed-  state .
 
- unused-and-closed- :
when  scope.close()-  is called
while  scope-  is in the  unused-  state,
 scope-  moves to the  unused-and-closed-  state .
 
- closed-and-joining- :
when  scope.close()-  is called
while  scope-  is in the  open-and-joining-  state or
the operation state  op-  is started
while  scope-  is in
the  closed-  or  unused-and-closed-  state,
 scope-  moves to the  closed-and-joining-  state .
 
- joined- :
when the count of associations drops to zero
while  scope-  is in
the  open-and-joining-  or  closed-and-joining-  state,
 scope-  moves to the  joined-  state .
 
Recommended practice: For 
simple_counting_scope and 
counting_scope,
implementations should store the state and the count of associations
in a single member of type 
size_t. struct scope-join-t {};     
enum scope-state-type {     
  unused,                   
  open,                     
  closed,                   
  open-and-joining,         
  closed-and-joining,       
  unused-and-closed,        
  joined,                   
};
The exposition-only class template 
impls-for (
[exec.snd.expos])
is specialized for 
scope-join-t as follows:
namespace std::execution {
  template<>
  struct impls-for<scope-join-t> : default-impls {
    template<class Scope, class Rcvr>
    struct state {                          
      struct rcvr-t {                       
        using receiver_concept = receiver_t;
        Rcvr& rcvr;                         
        void set_value() && noexcept {
          execution::set_value(std::move(rcvr));
        }
        template<class E>
          void set_error(E&& e) && noexcept {
            execution::set_error(std::move(rcvr), std::forward<E>(e));
          }
        void set_stopped() && noexcept {
          execution::set_stopped(std::move(rcvr));
        }
        decltype(auto) get_env() const noexcept {
          return execution::get_env(rcvr);
        }
      };
      using sched-sender =                  
        decltype(schedule(get_scheduler(get_env(declval<Rcvr&>()))));
      using op-t =                          
        connect_result_t<sched-sender, rcvr-t>;
      Scope* scope;                         
      Rcvr& receiver;                       
      op-t op;                              
      state(Scope* scope, Rcvr& rcvr)       
        noexcept(nothrow-callable<connect_t, sched-sender, rcvr-t>)
        : scope(scope),
          receiver(rcvr),
          op(connect(schedule(get_scheduler(get_env(rcvr))), rcvr-t(rcvr))) {}
      void complete() noexcept {            
        start(op);
      }
      void complete-inline() noexcept {     
        set_value(std::move(receiver));
      }
    };
    static constexpr auto get-state =       
      []<class Rcvr>(auto&& sender, Rcvr& receiver)
        noexcept(is_nothrow_constructible_v<state<Rcvr>, data-type<decltype(sender)>, Rcvr&>) {
        auto[_, self] = sender;
        return state(self, receiver);
      };
    static constexpr auto start =           
      [](auto& s, auto&) noexcept {
        if (s.scope->start-join-sender(s))
          s.complete-inline();
      };
  };
}
 namespace std::execution {
  class simple_counting_scope {
  public:
    
    struct token;
    static constexpr size_t max_associations = implementation-defined;
    
    simple_counting_scope() noexcept;
    simple_counting_scope(simple_counting_scope&&) = delete;
    ~simple_counting_scope();
    
    token get_token() noexcept;
    void close() noexcept;
    sender auto join() noexcept;
  private:
    size_t count;                                       
    scope-state-type state;                             
    bool try-associate() noexcept;                      
    void disassociate() noexcept;                       
    template<class State>
      bool start-join-sender(State& state) noexcept;    
  };
}
 For purposes of determining the existence of a data race,
get_token,
close,
join,
try-associate,
disassociate, and
start-join-sender
behave as atomic operations (
[intro.multithread])
.These operations on a single object of
type 
simple_counting_scope appear to occur in a single total order
.simple_counting_scope() noexcept;
Postconditions: 
count is 
0 and 
state is 
unused. ~simple_counting_scope();
Effects: If 
state is not one of
joined, 
unused, or 
unused-and-closed,
invokes 
terminate (
[except.terminate])
.  Otherwise, has no effects
.token get_token() noexcept;
Returns: An object 
t of type 
simple_counting_scope::token such that
t.scope == this is 
true. Effects: If 
state is
- unused, then changes state to unused-and-closed;
- open, then changes state to closed;
- open-and-joining,
then changes state to closed-and-joining;
- otherwise, no effects.
 Postconditions: Any subsequent call to 
try-associate() on 
*this
returns 
false. Returns: 
make-sender(scope-join-t(), this). bool try-associate() noexcept;
Effects: If 
count is equal to 
max_associations, then no effects
.  Otherwise, if 
state is
- unused,
then increments count and changes state to open;
- open or open-and-joining,
then increments count;
- otherwise, no effects.
Returns: 
true if 
count was incremented, 
false otherwise
. void disassociate() noexcept;
Preconditions: 
count is greater than zero
. Effects: Decrements 
count.  If 
count is zero after decrementing and
state is 
open-and-joining or 
closed-and-joining,
changes 
state to 
joined and
calls 
complete() on all objects registered with 
*this.[
Note 1: 
Calling 
complete() on any registered object
can cause 
*this to be destroyed
. — 
end note]
template<class State>
  bool start-join-sender(State& st) noexcept;
Effects: If 
state is
- unused, unused-and-closed, or joined, then
changes state to joined and returns true;
- open or open-and-joining, then
changes state to open-and-joining,
registers st with *this and returns false;
- closed or closed-and-joining, then
changes state to closed-and-joining,
registers st with *this and returns false.
 namespace std::execution {
  struct simple_counting_scope::token {
    template<sender Sender>
      Sender&& wrap(Sender&& snd) const noexcept;
    bool try_associate() const noexcept;
    void disassociate() const noexcept;
  private:
    simple_counting_scope* scope;   
  };
}
 template<sender Sender>
  Sender&& wrap(Sender&& snd) const noexcept;
Returns: 
std::forward<Sender>(snd). bool try_associate() const noexcept;
Effects: Equivalent to: return scope->try-associate();
void disassociate() const noexcept;
Effects: Equivalent to 
scope->disassociate(). namespace std::execution {
  class counting_scope {
  public:
    struct token {
      template<sender Sender>
        sender auto wrap(Sender&& snd) const noexcept(see below);
      bool try_associate() const noexcept;
      void disassociate() const noexcept;
    private:
      counting_scope* scope;                            
    };
    static constexpr size_t max_associations = implementation-defined;
    counting_scope() noexcept;
    counting_scope(counting_scope&&) = delete;
    ~counting_scope();
    token get_token() noexcept;
    void close() noexcept;
    sender auto join() noexcept;
    void request_stop() noexcept;
  private:
    size_t count;                                       
    scope-state-type state;                             
    inplace_stop_source s_source;                       
    bool try-associate() noexcept;                      
    void disassociate() noexcept;                       
    template<class State>
      bool start-join-sender(State& state) noexcept;    
  };
}
 counting_scope differs from 
simple_counting_scope by
adding support for cancellation
.  Unless specified below, the semantics of members of 
counting_scope
are the same as the corresponding members of 
simple_counting_scope.token get_token() noexcept;
Returns: An object 
t of type 
counting_scope::token such that
t.scope == this is 
true. void request_stop() noexcept;
Effects: Equivalent to 
s_source.request_stop(). Remarks: Calls to 
request_stop do not introduce data races
. template<sender Sender>
  sender auto counting_scope::token::wrap(Sender&& snd) const
    noexcept(is_nothrow_constructible_v<remove_cvref_t<Sender>, Sender>);
Effects: Equivalent to:
return stop-when(std::forward<Sender>(snd), scope->s_source.get_token());
Let 
sch be an object of type 
parallel_scheduler, and
let 
BACKEND-OF(sch) be 
*ptr,
where 
sch is associated with 
ptr.The expression 
get_forward_progress_guarantee(sch) has the value
forward_progress_guarantee::parallel.Let 
sch2 be an object of type 
parallel_scheduler.Two objects 
sch and 
sch2 compare equal if and only if
BACKEND-OF(sch) and
BACKEND-OF(sch2) refer to the same object
. A 
proxy for rcvr with base B is
an lvalue 
r of type 
B such that:
- r.set_value()-  has effects equivalent to
 set_value(std::move(rcvr)).
 
- r.set_error(e)- , where  e-  is an  exception_ptr-  object,
has effects equivalent to  set_error(std::move(rcvr), std::move(e)).
 
- r.set_stopped()-  has effects equivalent to
 set_stopped(std::move(rcvr)).
 
  [
Note 1: 
The storage referenced by 
s can be used as temporary storage
for operations launched via calls to 
parallel_scheduler_backend. — 
end note]
 Let 
b be 
BACKEND-OF(sch),
let 
sndr be the object returned by 
schedule(sch), and
let 
rcvr be a receiver
.If 
rcvr is connected to 
sndr and
the resulting operation state is started, then:
- If  sndr-  completes successfully,
then  b.schedule(r, s)-  is called, where
 - r is a proxy for rcvr
with base system_context_replaceability::receiver_proxy and
- s is a preallocated backend storage for r.
 
- All other completion operations are forwarded unchanged .
parallel_scheduler provides a customized implementation of
the 
bulk_chunked algorithm (
[exec.bulk])
.  If a receiver 
rcvr is connected to the sender
returned by 
bulk_chunked(sndr, pol, shape, f) and
the resulting operation state is started, then:
- If  sndr-  completes with values  vals- ,
let  args-  be a pack of lvalue subexpressions designating  vals- ,
then  b.schedule_bulk_chunked(shape, r, s)-  is called, where
 - r is a bulk chunked proxy for rcvr
with callable f and arguments args and
- s is a preallocated backend storage for r.
 
- All other completion operations are forwarded unchanged .
[
Note 2: 
Customizing the behavior of 
bulk_chunked
affects the default implementation of 
bulk. — 
end note]
parallel_scheduler provides a customized implementation of
the 
bulk_unchunked algorithm (
[exec.bulk])
.  If a receiver 
rcvr is connected to the sender
returned by 
bulk_unchunked(sndr, pol, shape, f) and
the resulting operation state is started, then:
- If  sndr-  completes with values  vals- ,
let  args-  be a pack of lvalue subexpressions designating  vals- ,
then  b.schedule_bulk_unchunked(shape, r, s)-  is called, where
 - r is a bulk unchunked proxy for rcvr
with callable f and arguments args and
- s is a preallocated backend storage for r.
 
- All other completion operations are forwarded unchanged .
parallel_scheduler get_parallel_scheduler();
Effects: Let 
eb be the result of 
system_context_replaceability::query_parallel_scheduler_backend().   Otherwise, returns a 
parallel_scheduler object
associated with 
eb.Facilities in the 
system_context_replaceability namespace
allow users to replace the default implementation of 
parallel_scheduler.shared_ptr<parallel_scheduler_backend> query_parallel_scheduler_backend();
query_parallel_scheduler_backend() returns
the implementation object for a parallel scheduler
. Returns: A non-null shared pointer to an object
that implements the 
parallel_scheduler_backend interface
. namespace std::execution::system_context_replaceability {
  struct receiver_proxy {
    virtual ~receiver_proxy() = default;
  protected:
    virtual bool query-env(unspecified) noexcept = 0;   
  public:
    virtual void set_value() noexcept = 0;
    virtual void set_error(exception_ptr) noexcept = 0;
    virtual void set_stopped() noexcept = 0;
    template<class P, class-type Query>
      optional<P> try_query(Query q) noexcept;
  };
  struct bulk_item_receiver_proxy : receiver_proxy {
    virtual void execute(size_t, size_t) noexcept = 0;
  };
}
 receiver_proxy represents a receiver
that will be notified
by the implementations of 
parallel_scheduler_backend
to trigger the completion operations
.  bulk_item_receiver_proxy is derived from 
receiver_proxy and
is used for 
bulk_chunked and 
bulk_unchunked customizations
that will also receive notifications
from implementations of 
parallel_scheduler_backend
corresponding to different iterations
. Mandates: 
P is a cv-unqualified non-array object type
. Returns: Let 
env be the environment of the receiver represented by 
*this.  If
- Query is not a member of an implementation-defined set
of supported queries; or
- P is not a member of an implementation-defined set
of supported result types for Query; or
- the expression q(env) is not well-formed or
does not have type cv P,
then returns 
nullopt.Otherwise, returns 
q(env).Remarks: 
get_stop_token_t is
in the implementation-defined set of supported queries, and
inplace_stop_token is a member
of the implementation-defined set of supported result types
for 
get_stop_token_t. namespace std::execution::system_context_replaceability {
  struct parallel_scheduler_backend {
    virtual ~parallel_scheduler_backend() = default;
    virtual void schedule(receiver_proxy&, span<byte>) noexcept = 0;
    virtual void schedule_bulk_chunked(size_t, bulk_item_receiver_proxy&,
                                       span<byte>) noexcept = 0;
    virtual void schedule_bulk_unchunked(size_t, bulk_item_receiver_proxy&,
                                         span<byte>) noexcept = 0;
  };
}
 virtual void schedule(receiver_proxy& r, span<byte> s) noexcept = 0;
Preconditions: The ends of
the lifetimes of 
*this,
the object referred to by 
r, and
any storage referenced by 
s
all happen after
the beginning of the evaluation of
the call to 
set_value, 
set_error, or 
set_done
on 
r (see below)
. Effects: A derived class shall implement this function such that:
- One of the following expressions is evaluated:
 - r.set_value(), if no error occurs, and the work is successful;
- r.set_error(eptr), if an error occurs,
where eptr is an object of type exception_ptr;
- r.set_stopped(), if the work is canceled.
 
- Any call to  r.set_value()-  happens on
an execution agent of the execution context represented by  *this.
 Remarks: The storage referenced by 
s
may be used by 
*this as temporary storage
for the duration of the operation launched by this call
. virtual void schedule_bulk_chunked(size_t n, bulk_item_receiver_proxy& r,
                                   span<byte> s) noexcept = 0;
Preconditions: The ends of
the lifetimes of 
*this,
the object referred to by 
r, and
any storage referenced by 
s
all happen after
the beginning of the evaluation of one of the expressions below
. Effects: A derived class shall implement this function such that:
- Eventually, one of the following expressions is evaluated:
 - r.set_value(), if no error occurs, and the work is successful;
- r.set_error(eptr), if an error occurs,
where eptr is an object of type exception_ptr;
- r.set_stopped(), if the work is canceled.
 
- If  r.execute(b, e)-  is called,
then  b-  and  e-  are in the range [ 0, n- ] and
 b < e.
- For each  i-  in [ 0, n- ),
there is at most one call to  r.execute(b, e)- 
such that  i-  is in the range [ b, e- ) .
- If  r.set_value()-  is called,
then for each  i-  in [ 0, n- ),
there is exactly one call to  r.execute(b, e)- 
such that  i-  is in the range [ b, e- ) .
- All calls to  execute-  on  r-  happen before
the call to either  set_value- ,  set_error- , or  set_stopped- 
on  r.
- All calls to  execute-  and  set_value-  on  r-  are made
on execution agents of the execution context represented by  *this.
 Remarks: The storage referenced by 
s may be used by 
*this
as temporary storage for the duration of the operation launched by this call
. virtual void schedule_bulk_unchunked(size_t n, bulk_item_receiver_proxy& r,
                                     span<byte> s) noexcept = 0;
Preconditions: The ends of
the lifetimes of 
*this,
the object referred to by 
r, and
any storage referenced by 
s
all happen after
the beginning of the evaluation of one of the expressions below
. Effects: A derived class shall implement this function such that:
- Eventually, one of the following expressions is evaluated:
 - r.set_value(), if no error occurs, and the work is successful;
- r.set_error(eptr), if an error occurs,
where eptr is an object of type exception_ptr;
- r.set_stopped(), if the work is canceled.
 
- If  r.execute(b, e)-  is called,
then  b-  is in the range [ 0, n- ) and
 e-  is equal to  b + 1.
- For each  i-  in [ 0, n- ),
there is at most one call to  r.execute(i, i + 1).
- If  r.set_value()-  is called,
then for each  i-  in [ 0, n- ),
there is exactly one call to  r.execute(i, i + 1).
- All calls to  execute-  on  r-  happen before
the call to either  set_value- ,  set_error- , or  set_stopped- 
on  r.
- All calls to  execute-  and  set_value-  on  r-  are made
on execution agents of the execution context represented by  *this.
 Remarks: The storage referenced by 
s may be used by 
*this
as temporary storage for the duration of the operation launched by this call
.