diff --git a/execution.bs b/execution.bs
index 4ed717f..44dc99b 100644
--- a/execution.bs
+++ b/execution.bs
@@ -4897,19 +4897,21 @@ enum class forward_progress_guarantee {
5. For two subexpressions `rcvr` and `expr`, let SET-VALUE(rcvr,
expr)
be `(expr, set_value(rcvr))` if the type of `expr` is `void`;
- otherwise, it is `set_value(rcvr, expr)`. Let TRY-SET-VALUE(rcvr,
- expr)
be:
+ otherwise, it is `set_value(rcvr, expr)`. Let
+ TRY-EVAL(rcvr, expr)
be:
try { - SET-VALUE(rcvr, expr); + expr; } catch(...) { set_error(rcvr, current_exception()); }- if `expr` is potentially-throwing, except that `rcvr` is evaluated only once; - or
SET-VALUE(rcvr, expr)
otherwise.
+ if `expr` is potentially-throwing; otherwise, `expr`. Let
+ TRY-SET-VALUE(rcvr, expr)
be
+ TRY-EVAL(rcvr, SET-VALUE(rcvr, expr))
+ except that `rcvr` is evaluated only once.
6. template<class Default = default_domain, class Sndr> @@ -5142,6 +5144,12 @@ enum class forward_progress_guarantee { ... Childn-1 childn-1; // exposition only }; + + template <class Sndr> + using data-type = decltype((declval<Sndr>().data)); // exposition only + + template <class Sndr, size_t N = 0> + using child-type = decltype((declval<Sndr>().childN)); // exposition only2. It is unspecified whether instances of
basic-sender
can be
@@ -5177,7 +5185,7 @@ enum class forward_progress_guarantee {
with a callable object equal to the following lambda:
- []<class Sndr>(Sndr&& sndr, auto& rcvr) noexcept -> decltype(auto) { + []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept -> decltype(auto) { return get<1>(std::forward<Sndr>(sndr)); }@@ -6029,74 +6037,125 @@ template<class Domain, class Tag, sender Sndr, class... Args> class="wg21note">`schedule_from` is not meant to be used in user code; it is used in the implementation of `transfer`. -3. The name `schedule_from` denotes a customization point object. For some +2. 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` is ill-formed. Otherwise, the expression - `schedule_from(sch, sndr)` is expression-equivalent to: + satisfy `sender`, `schedule_from` is ill-formed. + +3. Otherwise, the expression `schedule_from(sch, sndr)` is expression-equivalent + to:
transform_sender( query-or-default(get_domain, sch, default_domain()), - make-schedule-from-sender(sch, sndr)); + make-sender(schedule_from, sch, sndr));- where
make-schedule-from-sender(sch, sndr)
is expression-equivalent to
- make-sender(schedule_from, sch, sndr)
and returns a sender object
- `sndr2` that behaves as follows:
- 1. When `sndr2` is connected with some receiver `out_rcvr`, it:
+4. The exposition-only class template impls-for
+ ([exec.snd.general]) is specialized for `schedule_from_t` as
+ follows:
- 1. Constructs a receiver `rcvr` such that when a receiver completion
- operation Tag(rcvr, args...)
is called, it
- decay-copies `args...` into `op_state` (see below) as `args2...` and
- constructs a receiver `rcvr2` such that:
+ + template<> + struct impls-for<tag_t<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; + }; +- 1. When `set_value(rcvr2)` is called, it calls -
Tag(out_rcvr, std::move(args2)...)
.
+ 1. The member impls-for<schedule_from_t>::get-attrs
is initialized
+ with a callable object equal to the following lambda:
- 2. `set_error(rcvr2, err)` is expression-equivalent to `set_error(out_rcvr, err)`.
+ + [](const auto& data, const auto& child) noexcept -> decltype(auto) { + return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child))); + } ++ + 2. The member
impls-for<schedule_from_t>::get-state
is initialized
+ with a callable object equal to the following lambda:
+
+ + []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) + requires sender_in<child-type<Sndr>, env_of_t<Rcvr>> { + return apply( + [&]<class Sch, class Child>(auto, Sch sch, Child&& child) { + using variant-type = see below; + using receiver-type = see below; + + struct state-type { + Rcvr& rcvr; + variant-type async_result; + connect_result_t<schedule_result_t<Sch>, receiver-type> op_state2; + + explicit state-type(Sch sch, Rcvr& rcvr) + : rcvr(rcvr), op_state2(connect(schedule(sch), receiver-type{{}, this})) {} + }; + return state-type{sch, rcvr}; + }, + std::forward<Sndr>(sndr)); + } ++ + 1. Let `Sigs` be a pack of the arguments to the + `completion_signatures` specialiation named by + `completion_signatures_of_t
decayed-tuple<Tag, Args...>
.
+ Then `variant-type` names the type
+ variant<monostate, as-tuple<Sigs>...>
.
- 3. `set_stopped(rcvr2)` is expression-equivalent to `set_stopped(out_rcvr)`.
+ 2. Let `receiver-type` denote the following class:
- 4. `get_env(rcvr2)` is equal to `get_env(rcvr)`.
+ + struct receiver-type : receiver_adaptor<receiver-type> { + state-type* state; + + Rcvr&& base() && noexcept { return std::move(state->rcvr); } + const Rcvr& base() const & noexcept { return state->rcvr; } + + 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); + } + }; +- It then calls `schedule(sch)`, resulting in a sender `sndr3`. It then - calls `connect(sndr3, rcvr2)`, resulting in an operation state - `op_state3`. It then calls `start(op_state3)`. If any of these - throws an exception, it catches it and calls `set_error(out_rcvr, - current_exception())`. If any of these expressions would be - ill-formed, then
Tag(rcvr, args...)
is ill-formed.
+ 3. The member impls-for<schedule_from_t>::complete
+ is initialized with a callable object equal 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<result_t, Tag, Args...>; + TRY-EVAL(std::move(rcvr), [&]() noexcept(nothrow) { + state.async_result.template emplace<result_t>(Tag(), std::forward- 2. Calls `connect(sndr, rcvr)` resulting in an operation state `op_state2`. If - this expression would be ill-formed, `connect(sndr2, out_rcvr)` is - ill-formed. +5. Let the subexpression `out_sndr` denote the result of the invocation + `schedule_from(sch, sndr)` or an object copied or moved from 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 ([async.ops]) that, when started: - 3. Returns an operation state `op_state` that contains `op_state2`. When - `start(op_state)` is called, calls `start(op_state2)`. The lifetime - of `op_state3` ends when `op_state` is destroyed. - - 2. If a sender `out_sndr` returned from `schedule_from(sch, sndr)` is - connected with a receiver `rcvr` with environmment `env` such that -(args)...); }()); + if ((state.async_result.index() + 1) > 1) // not valueless and index != 0 + start(state.op_state2); + }; +
transform_sender(get-domain-late(out_sndr, env), out_sndr,
- env)
does not return a sender that completes on an execution
- agent belonging to the associated execution resource of `sch` and
- completing with the same async result ([async.ops]) as `sndr`, the
- behavior of calling `connect(out_sndr, rcvr)` is undefined.
+ - eventually completes on an execution agent belonging to the associated
+ execution resource of `sch`, and
+ - completes with the same async result as `sndr`.
-3. For a sender `out_sndr` returned from `schedule_from(sch, sndr)`,
- `get_env(out_sndr)` shall return a queryable object `q` such that
- `get_domain(q)` is expression-equivalent to `get_domain(sch)` and
- `get_completion_schedulerQ
whose type satisfies
- forwarding-query
, the expression Q(q,
- args...)
shall be equivalent to Q(get_env(sndr),
- args...)
.
#### `execution::then`, `execution::upon_error`, `execution::upon_stopped` [exec.then] #### {#spec-execution.senders.adaptor.then}