kokkos-execution 0.0.1
Loading...
Searching...
No Matches
test_continues_on.cpp
Go to the documentation of this file.
3
12
24
25using host_execution_space = Kokkos::DefaultHostExecutionSpace;
26
28
29using namespace Kokkos::utils::callbacks;
30
38
41 using schd_t = typename ContinuesOnTest::scheduler_t;
42 using schd_sndr_t = typename ContinuesOnTest::schedule_sender_t;
43
45
46 static_assert(stdexec::__nothrow_connectable<schedule_from_sndr_t, Tests::Utils::SinkReceiver>);
47
48 return true;
49}
50static_assert(test_schedule_from_sndr_traits());
51
58static_assert(test_continues_on_sndr_traits());
59
61TEST_F(ContinuesOnTest, queryable_get_exec) {
62 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
63
64 const host_execution_space exec_h{};
65
67 const context_t esc_A{exec_A}, esc_B{exec_B};
68
69 auto schs_A = stdexec::schedule(esc_A.get_scheduler());
70
72 static_assert(!stdexec::__queryable_with<
73 decltype(stdexec::get_env(schs_A)),
75 >);
76
77 auto schs_A_then = std::move(schs_A) | THEN_LABELED('A'); // NOLINT(performance-move-const-arg)
78
79 const auto sch_B = esc_B.get_scheduler();
80 auto schs_A_then_con_B = std::move(schs_A_then) // NOLINT(performance-move-const-arg)
81 | stdexec::continues_on(sch_B);
82
84 ASSERT_EQ(stdexec::get_completion_scheduler<stdexec::set_value_t>(stdexec::get_env(schs_A_then_con_B)), sch_B);
85
86 auto schs_A_then_con_B_then = std::move(schs_A_then_con_B) // NOLINT(performance-move-const-arg)
87 | THEN_LABELED('B');
88
89 static_assert(std::same_as<
90 stdexec::__demangle_t<decltype(schs_A_then_con_B_then)>,
92 stdexec::then_t,
95 stdexec::continues_on_t,
98 stdexec::schedule_from_t,
99 stdexec::__,
101 stdexec::then_t,
104 >
105 >
106 >
107 >
108 >);
109
111 auto sch_h = esc_h.get_scheduler();
112 auto schs_A_then_con_B_then_con_h_then = std::move(schs_A_then_con_B_then) // NOLINT(performance-move-const-arg)
113 | stdexec::continues_on(sch_h) | THEN_LABELED('h');
114 ASSERT_EQ(
115 stdexec::get_completion_scheduler<stdexec::set_value_t>(stdexec::get_env(schs_A_then_con_B_then_con_h_then)),
116 sch_h);
117
118 auto op_state = stdexec::connect(
119 std::move(schs_A_then_con_B_then_con_h_then), // NOLINT(performance-move-const-arg)
121 .state = std::addressof(esc_h.m_state), .runloop_state = nullptr, .result = nullptr});
122
124 constexpr auto check_env = []<typename T, Kokkos::ExecutionSpace Exec>() constexpr -> bool {
125 return std::same_as<
126 stdexec::env_of_t<T>,
127 stdexec::__env::__fwd<stdexec::env<
128 stdexec::prop<
131 >,
132 stdexec::__env::__fwd<Kokkos::Execution::Impl::env>
133 >>
134 >;
135 };
136
141 using then_rcvr_t =
145 std::string_view,
147 Kokkos::RangePolicy<host_execution_space, Kokkos::LaunchBounds<1>>
148 >
149 >>;
150 static_assert(std::same_as<decltype(op_state.inner_opstate.rcvr.rcvr.rcvr), then_rcvr_t>);
151 static_assert(
152 stdexec::__queryable_with<stdexec::env_of_t<then_rcvr_t>, Kokkos::Execution::ExecutionSpaceImpl::get_exec_t>);
153 static_assert(check_env.template operator()<then_rcvr_t, host_execution_space>());
154 ASSERT_EQ(
155 Kokkos::Execution::ExecutionSpaceImpl::get_exec(stdexec::get_env(op_state.inner_opstate.rcvr.rcvr.rcvr)).get(),
156 exec_h);
157
160 static_assert(std::same_as<decltype(op_state.inner_opstate.rcvr.rcvr), con_h_then_rcvr_t>);
161 static_assert(stdexec::__queryable_with<
162 stdexec::env_of_t<con_h_then_rcvr_t>,
164 >);
165 static_assert(check_env.template operator()<con_h_then_rcvr_t, host_execution_space>());
166 ASSERT_EQ(
167 Kokkos::Execution::ExecutionSpaceImpl::get_exec(stdexec::get_env(op_state.inner_opstate.rcvr.rcvr)).get(),
168 exec_h);
169
171 using sfrom_con_h_then_rcvr_t =
173 static_assert(std::same_as<decltype(op_state.inner_opstate.rcvr), sfrom_con_h_then_rcvr_t>);
174 static_assert(stdexec::__queryable_with<
175 stdexec::env_of_t<sfrom_con_h_then_rcvr_t>,
177 >);
178 static_assert(check_env.template operator()<sfrom_con_h_then_rcvr_t, TEST_EXECUTION_SPACE>());
179 ASSERT_EQ(
180 Kokkos::Execution::ExecutionSpaceImpl::get_exec(stdexec::get_env(op_state.inner_opstate.rcvr)).get(), exec_B);
181
182 using then_sfrom_con_h_then_rcvr_t =
184 sfrom_con_h_then_rcvr_t,
186 std::string_view,
188 Kokkos::RangePolicy<TEST_EXECUTION_SPACE, Kokkos::LaunchBounds<1>>
189 >
190 >>;
191 static_assert(
192 std::same_as<decltype(op_state.inner_opstate.inner_opstate.rcvr.rcvr.rcvr), then_sfrom_con_h_then_rcvr_t>);
193 static_assert(stdexec::__queryable_with<
194 stdexec::env_of_t<then_sfrom_con_h_then_rcvr_t>,
196 >);
197 static_assert(check_env.template operator()<then_sfrom_con_h_then_rcvr_t, TEST_EXECUTION_SPACE>());
198 ASSERT_EQ(
200 stdexec::get_env(op_state.inner_opstate.inner_opstate.rcvr.rcvr.rcvr))
201 .get(),
202 exec_B);
203
204 using con_B_then_sfrom_con_h_then_rcvr_t =
206 static_assert(
207 std::same_as<decltype(op_state.inner_opstate.inner_opstate.rcvr.rcvr), con_B_then_sfrom_con_h_then_rcvr_t>);
208 static_assert(stdexec::__queryable_with<
209 stdexec::env_of_t<con_B_then_sfrom_con_h_then_rcvr_t>,
211 >);
212 static_assert(check_env.template operator()<con_B_then_sfrom_con_h_then_rcvr_t, TEST_EXECUTION_SPACE>());
213 ASSERT_EQ(
215 stdexec::get_env(op_state.inner_opstate.inner_opstate.rcvr.rcvr))
216 .get(),
217 exec_B);
218
219 using sfrom_con_B_then_sfrom_con_h_then_rcvr_t =
221 static_assert(
222 std::same_as<decltype(op_state.inner_opstate.inner_opstate.rcvr), sfrom_con_B_then_sfrom_con_h_then_rcvr_t>);
223 static_assert(stdexec::__queryable_with<
224 stdexec::env_of_t<sfrom_con_B_then_sfrom_con_h_then_rcvr_t>,
226 >);
227 static_assert(check_env.template operator()<sfrom_con_B_then_sfrom_con_h_then_rcvr_t, TEST_EXECUTION_SPACE>());
228 ASSERT_EQ(
229 Kokkos::Execution::ExecutionSpaceImpl::get_exec(stdexec::get_env(op_state.inner_opstate.inner_opstate.rcvr))
230 .get(),
231 exec_A);
232
233 using then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t =
235 sfrom_con_B_then_sfrom_con_h_then_rcvr_t,
237 std::string_view,
239 Kokkos::RangePolicy<TEST_EXECUTION_SPACE, Kokkos::LaunchBounds<1>>
240 >
241 >>;
242 static_assert(std::same_as<
243 decltype(op_state.inner_opstate.inner_opstate.inner_opstate.rcvr),
244 then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t
245 >);
246 static_assert(stdexec::__queryable_with<
247 stdexec::env_of_t<then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t>,
249 >);
250 static_assert(check_env.template operator()<then_sfrom_con_B_then_sfrom_con_h_then_rcvr_t, TEST_EXECUTION_SPACE>());
251 ASSERT_EQ(
253 stdexec::get_env(op_state.inner_opstate.inner_opstate.inner_opstate.rcvr))
254 .get(),
255 exec_A);
256}
257
259TEST_F(ContinuesOnTest, then_sync_wait) {
260 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
261
262 const context_t esc{exec};
263
264 stdexec::sender auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
265
266 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
267
268 ASSERT_THAT(
269 recorder_listener_t::record([chain = std::move(chain)]() mutable { stdexec::sync_wait(std::move(chain)); }),
270 ::testing::ElementsAre(
273
274 ASSERT_EQ(data(), 1);
275}
276
283TEST_F(ContinuesOnTest, transition_to_same_execution_space_instance) {
284 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
285
286 const context_t esc{exec};
287
288 auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
289 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
290 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
291
292 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
293
294 ASSERT_THAT(
295 recorder_listener_t::record([chain = std::move(chain)]() mutable { stdexec::sync_wait(std::move(chain)); }),
296 ::testing::ElementsAre(
301
302 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
303}
304
309TEST_F(ContinuesOnTest, transition_to_another_execution_space_instance_and_back_same_type) {
310 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
311
312 const auto [exec_A, exec_B] = Kokkos::Experimental::partition_space(exec, 1, 1);
313
314 const context_t esc_A{exec_A};
315 Tests::Utils::show_exec_space_id(exec_A, "exec_A");
316 const context_t esc_B{exec_B};
317 Tests::Utils::show_exec_space_id(exec_B, "exec_B");
318
319 auto chain = stdexec::just() | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data)
320 | stdexec::continues_on(esc_B.get_scheduler()) | THEN_INCREMENT(data)
321 | stdexec::continues_on(esc_A.get_scheduler()) | THEN_INCREMENT(data);
322
323 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
324
325 const auto recorded_events = recorder_listener_t::record(
326 [chain = std::move(chain)]() mutable { stdexec::sync_wait(std::move(chain)); });
327
328 if (Tests::Utils::are_same_instances(exec_A, exec_B)) {
329 ASSERT_THAT(
330 recorded_events,
331 ::testing::ElementsAre(
335 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
336 } else {
337 ASSERT_THAT(
338 recorded_events,
339 ::testing::ElementsAre(
341 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "schedule_from")),
343 MATCHER_FOR_BEGIN_FENCE(exec_B, dispatch_label(exec, "schedule_from")),
345 MATCHER_FOR_BEGIN_FENCE(exec_A, dispatch_label(exec, "sync_wait"))));
346 }
347
348 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
349}
350
355TEST_F(ContinuesOnTest, transition_to_another_execution_space_instance_and_back_different_type) {
356 const view_s_t data(Kokkos::view_alloc(exec, "data - shared space"));
357
358 const host_execution_space exec_h{};
359
361 const context_t esc{exec};
362
364 Tests::Utils::show_exec_space_id(exec_h, "exec_h");
365
366 auto chain = stdexec::just() | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data)
367 | stdexec::continues_on(esc_h.get_scheduler()) | THEN_INCREMENT(data)
368 | stdexec::continues_on(esc.get_scheduler()) | THEN_INCREMENT(data);
369
370 ASSERT_EQ(data(), 0) << "Eager execution is not allowed.";
371
372 const auto recorded_events = recorder_listener_t::record(
373 [chain = std::move(chain)]() mutable { stdexec::sync_wait(std::move(chain)); });
374
376 ASSERT_THAT(
377 recorded_events,
378 ::testing::ElementsAre(
380 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
383 } else {
384 ASSERT_THAT(
385 recorded_events,
386 ::testing::ElementsAre(
389 MATCHER_FOR_BEGIN_PFOR(exec_h, dispatch_label(exec_h, "then")),
390 MATCHER_FOR_BEGIN_FENCE(exec_h, dispatch_label(exec_h, "schedule_from")),
393 }
394
395 ASSERT_EQ(data(), 3) << "A synchronization is missing.";
396}
397
400 using sndr_t =
401 decltype(stdexec::just_stopped() | stdexec::continues_on(std::declval<typename ContinuesOnTest::scheduler_t>()) | stdexec::then([]() {
402 }));
403
404 static_assert(Tests::Utils::has_completion_signatures<sndr_t, stdexec::__mset<stdexec::set_stopped_t()>>);
405
406 static_assert(
407 Tests::Utils::has_completion_signatures<sndr_t, stdexec::__mset<stdexec::set_stopped_t()>, stdexec::env<>>);
408
411
412 return true;
413}
415
417consteval bool test_sndr_no_throw_transformable() {
418 using sndr_continues_on_t =
419 decltype(stdexec::just() | stdexec::continues_on(std::declval<typename ContinuesOnTest::scheduler_t>()));
420
421 static_assert(std::same_as<
422 stdexec::__demangle_t<sndr_continues_on_t>,
424 stdexec::continues_on_t,
427 stdexec::schedule_from_t,
428 stdexec::__,
430 >
431 >
432 >);
433
434 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
436 stdexec::set_value_t,
437 sndr_continues_on_t&&,
438 stdexec::env<>
439 >);
440
441 using sndr_schedule_from_t = decltype(stdexec::schedule_from(
442 stdexec::schedule(std::declval<typename ContinuesOnTest::scheduler_t>())));
443
444 static_assert(std::same_as<
445 stdexec::__demangle_t<sndr_schedule_from_t>,
447 stdexec::schedule_from_t,
448 stdexec::__,
450 >
451 >);
452
453 static_assert(stdexec::__detail::__has_nothrow_transform_sender<
455 stdexec::set_value_t,
456 sndr_schedule_from_t&&,
457 stdexec::env<>
458 >);
459
460 return true;
461}
462static_assert(test_sndr_no_throw_transformable());
463
464} // namespace Tests::ExecutionSpaceImpl
constexpr std::string dispatch_label(const Exec &, Label &&label)
Get the dispatch label from Exec and label.
#define MATCHER_FOR_BEGIN_PFOR(_exec_, _label_)
#define MATCHER_FOR_BEGIN_FENCE(_exec_, _label_)
RecorderListener< EventDiscardMatcher< TEST_EXECUTION_SPACE >, BeginFenceEvent, BeginParallelForEvent > recorder_listener_t
#define THEN_INCREMENT(_data_)
Add a then using Tests::Utils::Functors::Increment that may throw. // NOLINTNEXTLINE(cppcoreguideline...
Definition increment.hpp:35
#define THEN_LABELED(_id_)
Add a then using Tests::Utils::Functors::Labeled. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage).
Definition labeled.hpp:18
consteval bool test_schedule_from_sndr_traits()
consteval bool test_sndr_no_throw_transformable()
Definition test_bulk.cpp:71
consteval bool test_continues_on_after_just_stopped()
consteval bool test_continues_on_sndr_traits()
typename stdexec::__basic_sender< Args... >::type basic_sender_t
See https://github.com/NVIDIA/stdexec/pull/1873#discussion_r2834863237.
Definition stdexec.hpp:10
consteval bool check_continues_on()
Check how the scheduler customizes stdexec::continues_on.
void show_exec_space_id(const Exec &exec, std::string_view label="", std::ostream &out=std::cout)
Definition kokkos.hpp:33
bool are_same_instances(const Exec &exec, const OtherExec &other_exec)
Definition kokkos.hpp:13
Execution context using a Kokkos execution space under the hood.
auto get_scheduler() const noexcept -> ExecutionSpaceImpl::Scheduler< Exec >
Wrap a Kokkos execution space to make it cheap to copy/move in new environments.
Definition get_exec.hpp:31
Inspired by https://github.com/kokkos/kokkos/blob/69273c3a4e7b6adeb95066341ca201d62fe1e698/core/src/i...
Definition then.hpp:16
decltype(std::declval< const context_t >().get_scheduler()) scheduler_t
Definition context.hpp:26
decltype(stdexec::schedule(std::declval< scheduler_t >())) schedule_sender_t
Definition context.hpp:27
Kokkos::Execution::ExecutionSpaceContext< Exec > context_t
Definition context.hpp:25
Kokkos::DefaultHostExecutionSpace host_execution_space