11// Copyright (c) Microsoft Corporation.
22// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
33
4+ #include < atomic>
45#include < cassert>
6+ #include < chrono>
57#include < execution>
8+ #include < thread>
9+
610using namespace std ;
711using execution::receiver, execution::scheduler, execution::sender;
812
13+ atomic<bool > scheduled_start_has_finished = false ;
14+ atomic<bool > delayed_start_has_finished = false ;
15+
916struct CustomError {};
10- enum class CanTagInvokeOn { No, NoSender, Yes };
11- enum class OnStartDoes { Nothing, SetValues, SetError, SetDone };
17+ enum class CanTagInvokeOn { No, Yes };
18+ enum class OnStartDoes { Nothing, SetValues, SetError, SetDone, SetValuesDelayed, SetErrorDelayed, SetDoneDelayed };
1219template <CanTagInvokeOn TagInvokeOn, OnStartDoes OnStart = OnStartDoes::Nothing>
1320struct sender_on : public execution ::sender_base {
1421 int _id = 0 ;
@@ -31,13 +38,47 @@ struct sender_on : public execution::sender_base {
3138 friend constexpr void tag_invoke (execution::start_t , sender_on_state& state) noexcept {
3239 ++state._id ;
3340 if constexpr (OnStart == OnStartDoes::SetValues) {
34- tag_invoke (execution::set_value, state._rec , state._id );
41+ tag_invoke (execution::set_value, move ( state._rec ) , state._id );
3542 } else if constexpr (OnStart == OnStartDoes::SetError) {
36- tag_invoke (execution::set_error, state._rec , CustomError{});
43+ tag_invoke (execution::set_error, move ( state._rec ) , CustomError{});
3744 } else if constexpr (OnStart == OnStartDoes::SetDone) {
38- tag_invoke (execution::set_done, state._rec );
45+ tag_invoke (execution::set_done, move (state._rec ));
46+ } else if constexpr (OnStart == OnStartDoes::SetValuesDelayed) {
47+ if (is_constant_evaluated ()) {
48+ tag_invoke (execution::set_value, move (state._rec ), state._id );
49+ } else {
50+ delayed_start (state);
51+ }
52+ } else if constexpr (OnStart == OnStartDoes::SetErrorDelayed) {
53+ if (is_constant_evaluated ()) {
54+ tag_invoke (execution::set_error, move (state._rec ), CustomError{});
55+ } else {
56+ delayed_start (state);
57+ }
58+ } else if constexpr (OnStart == OnStartDoes::SetDoneDelayed) {
59+ if (is_constant_evaluated ()) {
60+ tag_invoke (execution::set_done, move (state._rec ));
61+ } else {
62+ delayed_start (state);
63+ }
3964 }
4065 }
66+
67+ static void delayed_start (sender_on_state& state) noexcept {
68+ thread ([&state]() {
69+ scheduled_start_has_finished.wait (false );
70+ this_thread::sleep_for (1000ms);
71+ if constexpr (OnStart == OnStartDoes::SetValuesDelayed) {
72+ tag_invoke (execution::set_value, move (state._rec ), state._id );
73+ } else if constexpr (OnStart == OnStartDoes::SetErrorDelayed) {
74+ tag_invoke (execution::set_error, move (state._rec ), CustomError{});
75+ } else if constexpr (OnStart == OnStartDoes::SetDoneDelayed) {
76+ tag_invoke (execution::set_done, move (state._rec ));
77+ }
78+ delayed_start_has_finished.store (true );
79+ delayed_start_has_finished.notify_all ();
80+ }).detach ();
81+ }
4182 };
4283
4384 template <receiver Receiver>
@@ -113,8 +154,45 @@ struct scheduler_on {
113154 tag_invoke (execution::set_error, move (state._rec ), CustomError{});
114155 } else if constexpr (OnStart == OnStartDoes::SetDone) {
115156 tag_invoke (execution::set_done, move (state._rec ));
157+ } else if constexpr (OnStart == OnStartDoes::SetValuesDelayed) {
158+ if (is_constant_evaluated ()) {
159+ tag_invoke (execution::set_value, move (state._rec ));
160+ } else {
161+ delayed_start (state);
162+ }
163+ } else if constexpr (OnStart == OnStartDoes::SetErrorDelayed) {
164+ if (is_constant_evaluated ()) {
165+ tag_invoke (execution::set_error, move (state._rec ), CustomError{});
166+ } else {
167+ delayed_start (state);
168+ }
169+ } else if constexpr (OnStart == OnStartDoes::SetDoneDelayed) {
170+ if (is_constant_evaluated ()) {
171+ tag_invoke (execution::set_done, move (state._rec ));
172+ } else {
173+ delayed_start (state);
174+ }
116175 }
117176 }
177+
178+ static void delayed_start (sender_scheduled_state& state) noexcept {
179+ thread ([&state]() {
180+ this_thread::sleep_for (1000ms);
181+ if constexpr (OnStart == OnStartDoes::SetValuesDelayed) {
182+ tag_invoke (execution::set_value, move (state._rec ));
183+ } else if constexpr (OnStart == OnStartDoes::SetErrorDelayed) {
184+ tag_invoke (execution::set_error, move (state._rec ), CustomError{});
185+ delayed_start_has_finished.store (true ); // we never call start on the inner sender so fake it
186+ delayed_start_has_finished.notify_all ();
187+ } else if constexpr (OnStart == OnStartDoes::SetDoneDelayed) {
188+ tag_invoke (execution::set_done, move (state._rec ));
189+ delayed_start_has_finished.store (true ); // we never call start on the inner sender so fake it
190+ delayed_start_has_finished.notify_all ();
191+ }
192+ scheduled_start_has_finished.store (true );
193+ scheduled_start_has_finished.notify_all ();
194+ }).detach ();
195+ }
118196 };
119197
120198 template <receiver Receiver>
@@ -134,37 +212,26 @@ static_assert(scheduler<scheduler_on<OnStartDoes::SetValues>>);
134212static_assert (scheduler<scheduler_on<OnStartDoes::SetError>>);
135213static_assert (scheduler<scheduler_on<OnStartDoes::SetDone>>);
136214
137- template <OnStartDoes OnStart>
138- constexpr void test_with_tag_invoke () {
215+ template <CanTagInvokeOn WithTagInvoke, OnStartDoes OnStart>
216+ constexpr void test_all () {
217+ if (!is_constant_evaluated ()) {
218+ scheduled_start_has_finished.store (WithTagInvoke == CanTagInvokeOn::Yes);
219+ delayed_start_has_finished.store (false );
220+ }
221+
139222 receiver_tracker tracker;
140- sender auto new_sender = execution::on (scheduler_on<OnStart>{}, sender_on<CanTagInvokeOn::Yes , OnStart>{});
223+ sender auto new_sender = execution::on (scheduler_on<OnStart>{}, sender_on<WithTagInvoke , OnStart>{});
141224 execution::operation_state auto new_state = execution::connect (new_sender, receiver_on{tracker});
142225
143- assert (new_sender._id == 1 );
144- assert (new_state._id == 2 );
145- execution::start (new_state);
146- assert (new_state._id == 3 );
147- if constexpr (OnStart == OnStartDoes::Nothing) {
148- assert (tracker.nothing_called ());
149- } else if constexpr (OnStart == OnStartDoes::SetValues) {
150- assert (tracker.set_value_called ());
151- } else if constexpr (OnStart == OnStartDoes::SetError) {
152- assert (tracker.set_error_called ());
153- } else if constexpr (OnStart == OnStartDoes::SetDone) {
154- assert (tracker.set_done_called ());
226+ if constexpr (WithTagInvoke == CanTagInvokeOn::Yes) {
227+ assert (new_sender._id == 1 );
228+ assert (new_state._id == 2 );
155229 }
156230
157- static_assert (same_as<decltype (new_sender), sender_on<CanTagInvokeOn::Yes, OnStart>>);
158- static_assert (same_as<decltype (new_state),
159- typename sender_on<CanTagInvokeOn::Yes, OnStart>::template sender_on_state<receiver_on>>);
160- }
161-
162- template <OnStartDoes OnStart>
163- constexpr void test_without_tag_invoke () {
164- receiver_tracker tracker;
165- sender auto new_sender = execution::on (scheduler_on<OnStart>{}, sender_on<CanTagInvokeOn::No, OnStart>{});
166- execution::operation_state auto new_state = execution::connect (new_sender, receiver_on{tracker});
167231 execution::start (new_state);
232+ if constexpr (WithTagInvoke == CanTagInvokeOn::Yes) {
233+ assert (new_state._id == 3 );
234+ }
168235
169236 if constexpr (OnStart == OnStartDoes::Nothing) {
170237 assert (tracker.nothing_called ());
@@ -174,24 +241,50 @@ constexpr void test_without_tag_invoke() {
174241 assert (tracker.set_error_called ());
175242 } else if constexpr (OnStart == OnStartDoes::SetDone) {
176243 assert (tracker.set_done_called ());
244+ } else if constexpr (OnStart == OnStartDoes::SetValuesDelayed) {
245+ if (!is_constant_evaluated ()) {
246+ delayed_start_has_finished.wait (false );
247+ }
248+ assert (tracker.set_value_called ());
249+ } else if constexpr (OnStart == OnStartDoes::SetErrorDelayed) {
250+ if (!is_constant_evaluated ()) {
251+ delayed_start_has_finished.wait (false );
252+ }
253+ assert (tracker.set_error_called ());
254+ } else if constexpr (OnStart == OnStartDoes::SetDoneDelayed) {
255+ if (!is_constant_evaluated ()) {
256+ delayed_start_has_finished.wait (false );
257+ }
258+ assert (tracker.set_done_called ());
177259 }
178260
179- static_assert (same_as<decltype (new_sender),
180- execution::_On::_OutputSender<scheduler_on<OnStart>, sender_on<CanTagInvokeOn::No, OnStart>>>);
181- static_assert (same_as<decltype (new_state), execution::_On::_OutputSenderState<scheduler_on<OnStart>,
182- sender_on<CanTagInvokeOn::No, OnStart>, receiver_on>>);
261+ if constexpr (WithTagInvoke == CanTagInvokeOn::Yes) {
262+ static_assert (same_as<decltype (new_sender), sender_on<WithTagInvoke, OnStart>>);
263+ static_assert (same_as<decltype (new_state),
264+ typename sender_on<WithTagInvoke, OnStart>::template sender_on_state<receiver_on>>);
265+ } else {
266+ static_assert (same_as<decltype (new_sender),
267+ execution::_On::_OutputSender<scheduler_on<OnStart>, sender_on<CanTagInvokeOn::No, OnStart>>>);
268+ static_assert (same_as<decltype (new_state), execution::_On::_OutputSenderState<scheduler_on<OnStart>,
269+ sender_on<CanTagInvokeOn::No, OnStart>, receiver_on>>);
270+ }
183271}
184272
185273constexpr bool test_on () {
186- test_with_tag_invoke<OnStartDoes::Nothing>();
187- test_with_tag_invoke<OnStartDoes::SetValues>();
188- test_with_tag_invoke<OnStartDoes::SetError>();
189- test_with_tag_invoke<OnStartDoes::SetDone>();
190-
191- test_without_tag_invoke<OnStartDoes::Nothing>();
192- test_without_tag_invoke<OnStartDoes::SetValues>();
193- test_without_tag_invoke<OnStartDoes::SetError>();
194- test_without_tag_invoke<OnStartDoes::SetDone>();
274+ test_all<CanTagInvokeOn::Yes, OnStartDoes::Nothing>();
275+ test_all<CanTagInvokeOn::Yes, OnStartDoes::SetValues>();
276+ test_all<CanTagInvokeOn::Yes, OnStartDoes::SetError>();
277+ test_all<CanTagInvokeOn::Yes, OnStartDoes::SetDone>();
278+ test_all<CanTagInvokeOn::Yes, OnStartDoes::SetValuesDelayed>();
279+ test_all<CanTagInvokeOn::Yes, OnStartDoes::SetErrorDelayed>();
280+ test_all<CanTagInvokeOn::Yes, OnStartDoes::SetDoneDelayed>();
281+
282+ test_all<CanTagInvokeOn::No, OnStartDoes::Nothing>();
283+ test_all<CanTagInvokeOn::No, OnStartDoes::SetValues>();
284+ test_all<CanTagInvokeOn::No, OnStartDoes::SetError>();
285+ test_all<CanTagInvokeOn::No, OnStartDoes::SetDone>();
286+ test_all<CanTagInvokeOn::No, OnStartDoes::SetValuesDelayed>();
287+ test_all<CanTagInvokeOn::No, OnStartDoes::SetErrorDelayed>();
195288
196289 return true ;
197290}
0 commit comments