Skip to content

Commit f2449df

Browse files
committed
exec::repeat_effect: No set_value Completion Signature
exec::repeat_effect_until provides the asynchronous analogue of an until loop (i.e. a while not loop). The algorithm repeatedly: 1. Connects the wrapped sender, and then 2. Starts the resulting operation state and allows it to run Until the result is true (or converts to true) at which point the asynchronous loop ends and the overall asynchronous operation ends with a nullary set_value completion signal. exec::repeat_effect on the other hand provides the asynchronous analogue of an infinite loop. Notably unlike until loops infinite loops never complete successfully. Despite this exec::repeat_effect(snd) was previously implemented as being equivalent to: exec::repeat_effect_until(snd | stdexec::then([] noexcept { return false; })) Note that this means that the resulting sender advertised stdexec:: set_value_t() as a completion signature, but never actually sent it (because the sender wrapped by exec::repeat_effect_until always connected to form an asynchronous operation which sends false). Addressed the above by updating exec::repeat_effect_until such that when its child operation completes it examines sent type T: - If std::remove_cvref_t<T>::value is a constant expression which evaluates to true, unconditionally completes the operation with a nullary set_value completion signature (i.e. the runtime branch is eliminated without relying on the optimizer), otherwise - If T satisfies the conditions laid out above except that the constant expression evaluates to false, unconditionally starts the next loop (i.e. the call to stdexec::set_value is not emitted and therefore the operation's receiver need not support receipt thereof), otherwise - Proceeds as before this change Moreover updated the computation of exec::repeat_effect_until's completion signatures to reflect the above: That is, if all set_value completions of the child sender send a type which falls under the second bullet above, no set_value completion signature will be advertised from exec::repeat_effect_until. Lastly updated the sender synthesized by exec::repeat_effect such that it sends std::false_type rather than bool, thereby causing it to no longer advertise set_value completion signatures (or require handling thereof from its receiver).
1 parent c03da7a commit f2449df

File tree

2 files changed

+35
-8
lines changed

2 files changed

+35
-8
lines changed

include/exec/repeat_effect_until.hpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ namespace exec {
6464
};
6565
};
6666

67+
template <typename _T, bool _B>
68+
concept __compile_time_bool_of = std::remove_cvref_t<_T>::value == _B;
69+
6770
STDEXEC_PRAGMA_PUSH()
6871
STDEXEC_PRAGMA_IGNORE_GNU("-Wtsan")
6972

@@ -120,10 +123,15 @@ namespace exec {
120123
if constexpr (same_as<_Tag, set_value_t>) {
121124
// If the sender completed with true, we're done
122125
STDEXEC_TRY {
123-
const bool __done = (static_cast<bool>(static_cast<_Args &&>(__args)) && ...);
124-
if (__done) {
126+
if constexpr ((__compile_time_bool_of<_Args, true> && ...)) {
125127
stdexec::set_value(static_cast<_Receiver &&>(this->__receiver()));
126128
return;
129+
} else if constexpr (!(__compile_time_bool_of<_Args, false> && ...)) {
130+
const bool __done = (static_cast<bool>(static_cast<_Args &&>(__args)) && ...);
131+
if (__done) {
132+
stdexec::set_value(static_cast<_Receiver &&>(this->__receiver()));
133+
return;
134+
}
127135
}
128136
__destroy();
129137
STDEXEC_TRY {
@@ -163,16 +171,23 @@ namespace exec {
163171
// There's something funny going on with __if_c here. Use std::conditional_t instead. :-(
164172
std::conditional_t<
165173
((sizeof...(_Args) == 1) && (convertible_to<_Args, bool> && ...)),
166-
completion_signatures<>,
174+
std::conditional_t<
175+
(__compile_time_bool_of<_Args, false> && ...),
176+
completion_signatures<>,
177+
completion_signatures<set_value_t()>>,
167178
__mexception<_INVALID_ARGUMENT_TO_REPEAT_EFFECT_UNTIL_<>, _WITH_SENDER_<_Sender>>
168179
>;
169180

181+
template <class...>
182+
using __delete_set_value_t = completion_signatures<>;
183+
170184
template <class _Sender, class... _Env>
171185
using __completions_t = stdexec::transform_completion_signatures<
172186
__completion_signatures_of_t<__decay_t<_Sender> &, _Env...>,
173187
stdexec::transform_completion_signatures<
174188
__completion_signatures_of_t<stdexec::schedule_result_t<exec::trampoline_scheduler>, _Env...>,
175-
__eptr_completion
189+
__eptr_completion,
190+
__delete_set_value_t
176191
>,
177192
__mbind_front_q<__values_t, _Sender>::template __f
178193
>;
@@ -222,8 +237,8 @@ namespace exec {
222237
struct _never {
223238
template <class... _Args>
224239
STDEXEC_ATTRIBUTE(host, device, always_inline)
225-
constexpr auto operator()(_Args &&...) const noexcept -> bool {
226-
return false;
240+
constexpr std::false_type operator()(_Args &&...) const noexcept {
241+
return {};
227242
}
228243
};
229244

test/exec/test_repeat_effect_until.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,23 @@ namespace {
189189
REQUIRE(called);
190190
}
191191

192+
template <typename Receiver>
193+
struct no_set_value_receiver : Receiver {
194+
explicit no_set_value_receiver(Receiver r) noexcept
195+
: Receiver(std::move(r)) {
196+
}
197+
void set_value() && noexcept = delete;
198+
};
199+
192200
TEST_CASE("repeat_effect repeats until an error is encountered", "[adaptors][repeat_effect]") {
193201
int counter = 0;
194202
ex::sender auto snd = exec::repeat_effect(
195203
succeed_n_sender(10, ex::set_error, std::string("error")) | ex::then([&] { ++counter; }));
196-
auto op = ex::connect(std::move(snd), expect_error_receiver{std::string("error")});
204+
static_assert(!all_contained_in<
205+
ex::completion_signatures<ex::set_value_t()>,
206+
ex::completion_signatures_of_t<decltype(snd), ex::env<>>>);
207+
auto op = ex::connect(
208+
std::move(snd), no_set_value_receiver(expect_error_receiver{std::string("error")}));
197209
ex::start(op);
198210
REQUIRE(counter == 10);
199211
}
@@ -202,7 +214,7 @@ namespace {
202214
int counter = 0;
203215
ex::sender auto snd = exec::repeat_effect(
204216
succeed_n_sender(10, ex::set_stopped) | ex::then([&] { ++counter; }));
205-
auto op = ex::connect(std::move(snd), expect_stopped_receiver{});
217+
auto op = ex::connect(std::move(snd), no_set_value_receiver(expect_stopped_receiver{}));
206218
ex::start(op);
207219
REQUIRE(counter == 10);
208220
}

0 commit comments

Comments
 (0)