exec.schedule.from/p11 specifies shedule_from's completion operation as follows:
[]<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 {
state.async-result.template emplace<result_t>(Tag(), std::forward<Args>(args)...);
} catch (...) {
if constexpr (!nothrow) {
set_error(std::move(rcvr), current_exception());
return;
}
}
start(state.op-state);
};
so if emplacing the result tuple throws, set_error is immediately called on the downstream receiver. no attempt is made to post the completion to the specified scheduler. this is probably not the right behavior.
Proposed resolution
the right thing, i think, is to catch the exception, emplace the exception_ptr into the async-result variant, and then start the schedule operation, as shown below:
[]<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 {
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);
}
we also need to change how we specify the variant type of state.async-result:
Let Sigs be a pack of the arguments to the completion_signatures
specialization named by completion_signatures_of_t<child-type<Sndr>,
-env_of_t<Rcvr>>. Let as-tuple be an alias template that transforms
-a completion signature Tag(Args...) into the tuple specialization
-decayed-tuple<Tag, Args...>. Then variant_t denotes the type
-variant<monostate, as-tuple<Sigs>...>, except with duplicate types
-removed.
+env_of_t<Rcvr>>. Let as-tuple be an alias template such that
+as-tuple<Tag(Args...)> denotes the tuple specialization
+decayed-tuple<Tag, Args...>, and let is-nothrow-decay-copy-sig be
+a variable template such that is-nothrow-decay-copy-sig<Tag(Args...)>
+is a core constant expression of type bool const and whose value
+is true if the types Args... are all nothrow decay-copyable, and false
+otherwise. 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.
exec.schedule.from/p11 specifies
shedule_from's completion operation as follows:so if emplacing the result tuple throws,
set_erroris immediately called on the downstream receiver. no attempt is made to post the completion to the specified scheduler. this is probably not the right behavior.Proposed resolution
the right thing, i think, is to catch the exception, emplace the
exception_ptrinto theasync-resultvariant, and then start the schedule operation, as shown below:we also need to change how we specify the variant type of
state.async-result: