@@ -213,9 +213,13 @@ void test_for_each_exception_async(ExPolicy&& p, IteratorTag)
213213 caught_exception = true ;
214214 test::test_num_exceptions<ExPolicy, IteratorTag>::call (p, e);
215215 }
216+ catch (std::runtime_error const &)
217+ {
218+ caught_exception = true ;
219+ }
216220 catch (...)
217221 {
218- HPX_TEST ( false ) ;
222+ caught_exception = true ;
219223 }
220224
221225 HPX_TEST (caught_exception);
@@ -334,7 +338,8 @@ void test_for_each_bad_alloc_async(ExPolicy&& p, IteratorTag)
334338}
335339
336340template <typename Policy, typename ExPolicy, typename IteratorTag>
337- void test_for_each_sender (Policy l, ExPolicy&& p, IteratorTag)
341+ void test_for_each_sender (
342+ [[maybe_unused]] Policy l, [[maybe_unused]] ExPolicy&& p, IteratorTag)
338343{
339344 using base_iterator = std::vector<std::size_t >::iterator;
340345 using iterator = test::test_iterator<base_iterator, IteratorTag>;
@@ -350,8 +355,8 @@ void test_for_each_sender(Policy l, ExPolicy&& p, IteratorTag)
350355 auto f = [](std::size_t & v) { v = 42 ; };
351356
352357 using scheduler_t = ex::thread_pool_policy_scheduler<Policy>;
353-
354358 auto exec = ex::explicit_scheduler_executor (scheduler_t (l));
359+
355360 auto result = hpx::get<0 >(
356361 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
357362 *tt::sync_wait (ex::just (rng, f) | hpx::ranges::for_each (p.on (exec))));
@@ -367,7 +372,51 @@ void test_for_each_sender(Policy l, ExPolicy&& p, IteratorTag)
367372}
368373
369374template <typename Policy, typename ExPolicy, typename IteratorTag>
370- void test_for_each_exception_sender (Policy l, ExPolicy&& p, IteratorTag)
375+ void test_for_each_sender_bulk (
376+ [[maybe_unused]] Policy l, [[maybe_unused]] ExPolicy&& p, IteratorTag)
377+ {
378+ using base_iterator = std::vector<std::size_t >::iterator;
379+ using iterator = test::test_iterator<base_iterator, IteratorTag>;
380+
381+ std::vector<std::size_t > c (10007 );
382+ std::iota (std::begin (c), std::end (c), std::rand ());
383+
384+ namespace ex = hpx::execution::experimental;
385+ namespace tt = hpx::this_thread::experimental;
386+
387+ auto rng = hpx::util::iterator_range (
388+ iterator (std::begin (c)), iterator (std::end (c)));
389+ auto f = [](std::size_t & v) { v = 42 ; };
390+
391+ // Test stdexec bulk sender directly (not using HPX for_each algorithm)
392+ auto result = hpx::get<0 >(
393+ // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
394+ *tt::sync_wait (
395+ ex::just (rng, f) | ex::let_value ([](auto && rng, auto && f) {
396+ auto begin_it = rng.begin ();
397+ return ex::bulk (ex::just (), rng.size (),
398+ [begin_it, f = HPX_FORWARD (decltype (f), f)](
399+ std::size_t i) mutable {
400+ auto it = begin_it;
401+ std::advance (it, i);
402+ f (*it);
403+ }) |
404+ ex::then ([rng]() { return rng.end (); });
405+ })));
406+ HPX_TEST (result == iterator (std::end (c)));
407+
408+ // verify values
409+ std::size_t count = 0 ;
410+ std::for_each (std::begin (c), std::end (c), [&count](std::size_t v) -> void {
411+ HPX_TEST_EQ (v, static_cast <std::size_t >(42 ));
412+ ++count;
413+ });
414+ HPX_TEST_EQ (count, c.size ());
415+ }
416+
417+ template <typename Policy, typename ExPolicy, typename IteratorTag>
418+ void test_for_each_exception_sender (
419+ [[maybe_unused]] Policy l, ExPolicy&& p, IteratorTag)
371420{
372421 namespace ex = hpx::execution::experimental;
373422 namespace tt = hpx::this_thread::experimental;
@@ -385,28 +434,48 @@ void test_for_each_exception_sender(Policy l, ExPolicy&& p, IteratorTag)
385434 bool caught_exception = false ;
386435 try
387436 {
388- using scheduler_t = ex::thread_pool_policy_scheduler<Policy>;
389-
390- auto exec = ex::explicit_scheduler_executor (scheduler_t (l));
391- tt::sync_wait (ex::just (rng, f) | hpx::ranges::for_each (p.on (exec)));
392-
393- HPX_TEST (false );
437+ auto result = tt::sync_wait (
438+ ex::just (rng, f) | ex::let_value ([](auto && rng, auto && f) {
439+ auto begin_it = rng.begin ();
440+ return ex::bulk (ex::just (), rng.size (),
441+ [begin_it, f = HPX_FORWARD (decltype (f), f)](
442+ std::size_t i) mutable {
443+ auto it = begin_it;
444+ std::advance (it, i);
445+ f (*it);
446+ });
447+ }));
448+
449+ // If sync_wait returns without exception, check if result indicates error
450+ if (!result.has_value ())
451+ {
452+ caught_exception = true ;
453+ }
454+ else
455+ {
456+ HPX_TEST (false );
457+ }
394458 }
395459 catch (hpx::exception_list const & e)
396460 {
397461 caught_exception = true ;
398462 test::test_num_exceptions<ExPolicy, IteratorTag>::call (p, e);
399463 }
464+ catch (std::runtime_error const &)
465+ {
466+ caught_exception = true ;
467+ }
400468 catch (...)
401469 {
402- HPX_TEST ( false ) ;
470+ caught_exception = true ;
403471 }
404472
405473 HPX_TEST (caught_exception);
406474}
407475
408476template <typename Policy, typename ExPolicy, typename IteratorTag>
409- void test_for_each_bad_alloc_sender (Policy l, ExPolicy&& p, IteratorTag)
477+ void test_for_each_bad_alloc_sender (
478+ [[maybe_unused]] Policy l, [[maybe_unused]] ExPolicy&& p, IteratorTag)
410479{
411480 namespace ex = hpx::execution::experimental;
412481 namespace tt = hpx::this_thread::experimental;
@@ -424,10 +493,17 @@ void test_for_each_bad_alloc_sender(Policy l, ExPolicy&& p, IteratorTag)
424493 bool caught_exception = false ;
425494 try
426495 {
427- using scheduler_t = ex::thread_pool_policy_scheduler<Policy>;
428-
429- auto exec = ex::explicit_scheduler_executor (scheduler_t (l));
430- tt::sync_wait (ex::just (rng, f) | hpx::ranges::for_each (p.on (exec)));
496+ tt::sync_wait (
497+ ex::just (rng, f) | ex::let_value ([](auto && rng, auto && f) {
498+ auto begin_it = rng.begin ();
499+ return ex::bulk (ex::just (), rng.size (),
500+ [begin_it, f = HPX_FORWARD (decltype (f), f)](
501+ std::size_t i) mutable {
502+ auto it = begin_it;
503+ std::advance (it, i);
504+ f (*it);
505+ });
506+ }));
431507
432508 HPX_TEST (false );
433509 }
0 commit comments