diff --git a/libs/core/async_cuda/include/hpx/async_cuda/transform_stream.hpp b/libs/core/async_cuda/include/hpx/async_cuda/transform_stream.hpp index ea86f87e58b4..2039e1d5e629 100644 --- a/libs/core/async_cuda/include/hpx/async_cuda/transform_stream.hpp +++ b/libs/core/async_cuda/include/hpx/async_cuda/transform_stream.hpp @@ -295,17 +295,25 @@ namespace hpx::cuda::experimental { using invoke_function_transformation = invoke_function_transformation_helper::type; +#if defined(HPX_CLANG_VERSION) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif template - static consteval auto get_completion_signatures() - -> hpx::execution::experimental:: - transform_completion_signatures_of, Env, - hpx::execution::experimental::completion_signatures< - hpx::execution::experimental::set_error_t( - std::exception_ptr)>, - invoke_function_transformation> + static consteval auto get_completion_signatures() -> hpx:: + execution::experimental::transform_completion_signatures< + hpx::execution::experimental::completion_signatures_of_t< + std::decay_t, Env>, + hpx::execution::experimental::completion_signatures< + hpx::execution::experimental::set_error_t( + std::exception_ptr)>, + invoke_function_transformation> { return {}; } +#if defined(HPX_CLANG_VERSION) +#pragma clang diagnostic pop +#endif constexpr auto get_env() const noexcept { diff --git a/libs/core/async_mpi/include/hpx/async_mpi/transform_mpi.hpp b/libs/core/async_mpi/include/hpx/async_mpi/transform_mpi.hpp index 4559850fa782..5f535848befe 100644 --- a/libs/core/async_mpi/include/hpx/async_mpi/transform_mpi.hpp +++ b/libs/core/async_mpi/include/hpx/async_mpi/transform_mpi.hpp @@ -185,12 +185,16 @@ namespace hpx::mpi::experimental { hpx::execution::experimental::completion_signatures<>; // clang-format off +#if defined(HPX_CLANG_VERSION) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif template friend auto tag_invoke( hpx::execution::experimental::get_completion_signatures_t, transform_mpi_sender const&, Env const&) - -> hpx::execution::experimental::transform_completion_signatures_of< - Sender, Env, + -> hpx::execution::experimental::transform_completion_signatures< + hpx::execution::experimental::completion_signatures_of_t, hpx::execution::experimental::completion_signatures< hpx::execution::experimental::set_error_t(std::exception_ptr) >, @@ -198,6 +202,9 @@ namespace hpx::mpi::experimental { default_set_error, no_set_stopped_signature >; +#if defined(HPX_CLANG_VERSION) +#pragma clang diagnostic pop +#endif // clang-format on template diff --git a/libs/core/executors/CMakeLists.txt b/libs/core/executors/CMakeLists.txt index 8deb14943381..9032be62137c 100644 --- a/libs/core/executors/CMakeLists.txt +++ b/libs/core/executors/CMakeLists.txt @@ -26,6 +26,9 @@ set(executors_headers hpx/executors/execution_policy_parameters.hpp hpx/executors/execution_policy_scheduling_property.hpp hpx/executors/execution_policy.hpp + hpx/executors/executor_scheduler.hpp + hpx/executors/executor_scheduler_bulk.hpp + hpx/executors/fwd/executor_scheduler_fwd.hpp hpx/executors/explicit_scheduler_executor.hpp hpx/executors/fork_join_executor.hpp hpx/executors/limiting_executor.hpp diff --git a/libs/core/executors/include/hpx/executors/executor_scheduler.hpp b/libs/core/executors/include/hpx/executors/executor_scheduler.hpp new file mode 100644 index 000000000000..fe07ae53dbd6 --- /dev/null +++ b/libs/core/executors/include/hpx/executors/executor_scheduler.hpp @@ -0,0 +1,142 @@ +// Copyright (c) 2026 The STE||AR-Group +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/// \file parallel/executors/executor_scheduler.hpp + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace hpx::execution::experimental { + /////////////////////////////////////////////////////////////////////////// + HPX_CXX_CORE_EXPORT template + struct executor_operation_state + { + HPX_NO_UNIQUE_ADDRESS std::decay_t exec_; + HPX_NO_UNIQUE_ADDRESS std::decay_t receiver_; + + template + executor_operation_state(Exec&& exec, Recv&& recv) + : exec_(HPX_FORWARD(Exec, exec)) + , receiver_(HPX_FORWARD(Recv, recv)) + { + } + + executor_operation_state(executor_operation_state&&) = delete; + executor_operation_state(executor_operation_state const&) = delete; + executor_operation_state& operator=( + executor_operation_state&&) = delete; + executor_operation_state& operator=( + executor_operation_state const&) = delete; + + ~executor_operation_state() = default; + + friend void tag_invoke(start_t, executor_operation_state& os) noexcept + { + hpx::detail::try_catch_exception_ptr( + [&]() { + hpx::parallel::execution::post(os.exec_, [&os]() mutable { + hpx::execution::experimental::set_value( + HPX_MOVE(os.receiver_)); + }); + }, + [&](std::exception_ptr ep) { + hpx::execution::experimental::set_error( + HPX_MOVE(os.receiver_), HPX_MOVE(ep)); + }); + } + }; + + /////////////////////////////////////////////////////////////////////////// + HPX_CXX_CORE_EXPORT template + struct executor_sender + { + using sender_concept = hpx::execution::experimental::sender_t; + + HPX_NO_UNIQUE_ADDRESS std::decay_t exec_; + + using completion_signatures = + hpx::execution::experimental::completion_signatures< + hpx::execution::experimental::set_value_t(), + hpx::execution::experimental::set_error_t(std::exception_ptr)>; + + template + friend auto tag_invoke( + hpx::execution::experimental::get_completion_signatures_t, + executor_sender const&, Env) noexcept -> completion_signatures; + + friend constexpr auto tag_invoke( + hpx::execution::experimental::get_completion_scheduler_t< + hpx::execution::experimental::set_value_t>, + executor_sender const& s) noexcept + { + return executor_scheduler{s.exec_}; + } + + template + friend executor_operation_state tag_invoke( + connect_t, executor_sender&& s, Receiver&& receiver) + { + return {HPX_MOVE(s.exec_), HPX_FORWARD(Receiver, receiver)}; + } + + template + friend executor_operation_state tag_invoke( + connect_t, executor_sender const& s, Receiver&& receiver) + { + return {s.exec_, HPX_FORWARD(Receiver, receiver)}; + } + }; + + /////////////////////////////////////////////////////////////////////////// + HPX_CXX_CORE_EXPORT template + struct executor_scheduler + { + using executor_type = std::decay_t; + + HPX_NO_UNIQUE_ADDRESS executor_type exec_; + + constexpr executor_scheduler() = default; + + template + requires(!std::is_same_v, executor_scheduler>) + explicit executor_scheduler(Exec&& exec) + : exec_(HPX_FORWARD(Exec, exec)) + { + } + + constexpr bool operator==(executor_scheduler const& rhs) const noexcept + { + return exec_ == rhs.exec_; + } + + constexpr bool operator!=(executor_scheduler const& rhs) const noexcept + { + return !(*this == rhs); + } + + friend executor_sender tag_invoke( + schedule_t, executor_scheduler&& sched) + { + return {HPX_MOVE(sched.exec_)}; + } + + friend executor_sender tag_invoke( + schedule_t, executor_scheduler const& sched) + { + return {sched.exec_}; + } + }; +} // namespace hpx::execution::experimental diff --git a/libs/core/executors/include/hpx/executors/executor_scheduler_bulk.hpp b/libs/core/executors/include/hpx/executors/executor_scheduler_bulk.hpp new file mode 100644 index 000000000000..333628ecfe78 --- /dev/null +++ b/libs/core/executors/include/hpx/executors/executor_scheduler_bulk.hpp @@ -0,0 +1,188 @@ +// Copyright (c) 2026 The STE||AR-Group +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace hpx::execution::experimental { + + namespace detail { + template + struct executor_bulk_receiver + { + using receiver_concept = hpx::execution::experimental::receiver_t; + + HPX_NO_UNIQUE_ADDRESS std::decay_t exec_; + HPX_NO_UNIQUE_ADDRESS std::decay_t receiver_; + HPX_NO_UNIQUE_ADDRESS std::decay_t shape_; + HPX_NO_UNIQUE_ADDRESS std::decay_t f_; + + template + friend void tag_invoke( + set_error_t, executor_bulk_receiver&& r, Error&& error) noexcept + { + hpx::execution::experimental::set_error( + HPX_MOVE(r.receiver_), HPX_FORWARD(Error, error)); + } + + friend void tag_invoke( + set_stopped_t, executor_bulk_receiver&& r) noexcept + { + hpx::execution::experimental::set_stopped( + HPX_MOVE(r.receiver_)); + } + + template + friend void tag_invoke( + set_value_t, executor_bulk_receiver&& r, Ts&&... ts) noexcept + { + hpx::detail::try_catch_exception_ptr( + [&]() { + hpx::parallel::execution::bulk_sync_execute( + r.exec_, r.f_, r.shape_, ts...); + + hpx::execution::experimental::set_value( + HPX_MOVE(r.receiver_), HPX_FORWARD(Ts, ts)...); + }, + [&](std::exception_ptr ep) { + hpx::execution::experimental::set_error( + HPX_MOVE(r.receiver_), HPX_MOVE(ep)); + }); + } + }; + + template + struct executor_bulk_sender + { + HPX_NO_UNIQUE_ADDRESS std::decay_t exec_; + HPX_NO_UNIQUE_ADDRESS std::decay_t sender_; + HPX_NO_UNIQUE_ADDRESS std::decay_t shape_; + HPX_NO_UNIQUE_ADDRESS std::decay_t f_; + + using sender_concept = hpx::execution::experimental::sender_t; + +#if defined(HPX_CLANG_VERSION) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + template + friend auto tag_invoke( + hpx::execution::experimental::get_completion_signatures_t, + executor_bulk_sender const&, Env const&) -> hpx::execution:: + experimental::transform_completion_signatures< + hpx::execution::experimental::completion_signatures_of_t< + Sender, Env>, + hpx::execution::experimental::completion_signatures< + hpx::execution::experimental::set_error_t( + std::exception_ptr)>>; +#if defined(HPX_CLANG_VERSION) +#pragma clang diagnostic pop +#endif + + struct env + { + std::decay_t const& pred_snd; + std::decay_t const& exec; + + template + requires( + meta::value> && + hpx::execution::experimental::detail:: + has_completion_scheduler_v>) + friend constexpr auto tag_invoke( + hpx::execution::experimental::get_completion_scheduler_t< + CPO> + tag, + env const& e) noexcept + { + return tag( + hpx::execution::experimental::get_env(e.pred_snd)); + } + + friend constexpr auto tag_invoke( + hpx::execution::experimental::get_completion_scheduler_t< + hpx::execution::experimental::set_value_t>, + env const& e) noexcept + { + return hpx::execution::experimental::executor_scheduler< + Executor>{e.exec}; + } + }; + + friend constexpr auto tag_invoke( + hpx::execution::experimental::get_env_t, + executor_bulk_sender const& s) noexcept + { + return env{s.sender_, s.exec_}; + } + + template + friend auto tag_invoke(connect_t, + executor_bulk_sender&& s, + Receiver&& receiver) + { + return hpx::execution::experimental::connect( + HPX_MOVE(s.sender_), + executor_bulk_receiver, + Shape, F>{HPX_MOVE(s.exec_), + HPX_FORWARD(Receiver, receiver), HPX_MOVE(s.shape_), + HPX_MOVE(s.f_)}); + } + + template + friend auto tag_invoke(connect_t, + executor_bulk_sender& s, + Receiver&& receiver) + { + return hpx::execution::experimental::connect(s.sender_, + executor_bulk_receiver, + Shape, F>{s.exec_, HPX_FORWARD(Receiver, receiver), + s.shape_, s.f_}); + } + + template + friend auto tag_invoke(connect_t, + executor_bulk_sender const& s, + Receiver&& receiver) + { + return hpx::execution::experimental::connect(s.sender_, + executor_bulk_receiver, + Shape, F>{s.exec_, HPX_FORWARD(Receiver, receiver), + s.shape_, s.f_}); + } + }; + } // namespace detail + + template + auto tag_invoke(bulk_t, executor_scheduler const& sched, + Sender&& sender, Shape const& shape, F&& f) + { + return detail::executor_bulk_sender, + Shape, std::decay_t>{ + sched.exec_, HPX_FORWARD(Sender, sender), shape, HPX_FORWARD(F, f)}; + } +} // namespace hpx::execution::experimental diff --git a/libs/core/executors/include/hpx/executors/fwd/executor_scheduler_fwd.hpp b/libs/core/executors/include/hpx/executors/fwd/executor_scheduler_fwd.hpp new file mode 100644 index 000000000000..a81c3f850e8f --- /dev/null +++ b/libs/core/executors/include/hpx/executors/fwd/executor_scheduler_fwd.hpp @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2026 Hartmut Kaiser +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/// \file parallel/executors/fwd/executor_scheduler_fwd.hpp + +#pragma once + +#include + +namespace hpx::execution::experimental { + + // Forward declarations, see executor_scheduler.hpp + HPX_CXX_CORE_EXPORT template + struct executor_scheduler; + + HPX_CXX_CORE_EXPORT template + struct executor_sender; + + HPX_CXX_CORE_EXPORT template + struct executor_operation_state; + +} // namespace hpx::execution::experimental diff --git a/libs/core/executors/include/hpx/executors/parallel_executor.hpp b/libs/core/executors/include/hpx/executors/parallel_executor.hpp index 59dc73a3d172..cdd4b3224ac5 100644 --- a/libs/core/executors/include/hpx/executors/parallel_executor.hpp +++ b/libs/core/executors/include/hpx/executors/parallel_executor.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -458,6 +459,8 @@ namespace hpx::execution { #if defined(__NVCC__) || defined(__CUDACC__) constexpr ~parallel_policy_executor() {} +#else + ~parallel_policy_executor() = default; #endif private: @@ -669,6 +672,15 @@ namespace hpx::execution { #endif } } + + template + requires std::is_same_v + friend hpx::execution::experimental::executor_scheduler + tag_invoke( + hpx::execution::experimental::get_scheduler_t, Exec const& exec) + { + return hpx::execution::experimental::executor_scheduler(exec); + } /// \endcond public: @@ -997,6 +1009,7 @@ namespace hpx::execution { #endif } } + /// \endcond public: diff --git a/libs/core/executors/include/hpx/executors/sequenced_executor.hpp b/libs/core/executors/include/hpx/executors/sequenced_executor.hpp index 6d61e286ba3c..801c6bb50cb1 100644 --- a/libs/core/executors/include/hpx/executors/sequenced_executor.hpp +++ b/libs/core/executors/include/hpx/executors/sequenced_executor.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -236,6 +237,15 @@ namespace hpx::execution { #endif } + template + requires std::is_same_v + friend hpx::execution::experimental::executor_scheduler + tag_invoke( + hpx::execution::experimental::get_scheduler_t, Exec const& exec) + { + return hpx::execution::experimental::executor_scheduler(exec); + } + private: friend class hpx::serialization::access; diff --git a/libs/core/executors/include/hpx/executors/thread_pool_scheduler_bulk.hpp b/libs/core/executors/include/hpx/executors/thread_pool_scheduler_bulk.hpp index f0e0b6c88e48..24c3cdb576be 100644 --- a/libs/core/executors/include/hpx/executors/thread_pool_scheduler_bulk.hpp +++ b/libs/core/executors/include/hpx/executors/thread_pool_scheduler_bulk.hpp @@ -707,16 +707,17 @@ namespace hpx::execution::experimental::detail { using sender_concept = hpx::execution::experimental::sender_t; - template #if defined(HPX_CLANG_VERSION) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif + template friend auto tag_invoke( hpx::execution::experimental::get_completion_signatures_t, thread_pool_bulk_sender const&, Env const&) - -> hpx::execution::experimental::transform_completion_signatures_of< - Sender, Env, + -> hpx::execution::experimental::transform_completion_signatures< + hpx::execution::experimental::completion_signatures_of_t, hpx::execution::experimental::completion_signatures< hpx::execution::experimental::set_error_t( std::exception_ptr)>>; diff --git a/libs/core/executors/tests/unit/CMakeLists.txt b/libs/core/executors/tests/unit/CMakeLists.txt index e11e726808c1..5fcba3bf2b12 100644 --- a/libs/core/executors/tests/unit/CMakeLists.txt +++ b/libs/core/executors/tests/unit/CMakeLists.txt @@ -9,6 +9,7 @@ set(tests annotation_property created_executor execution_policy_mappings + executor_algorithm_bulk explicit_scheduler_executor fork_join_executor fork_join_executor_from diff --git a/libs/core/executors/tests/unit/executor_algorithm_bulk.cpp b/libs/core/executors/tests/unit/executor_algorithm_bulk.cpp new file mode 100644 index 000000000000..fc18799cbfa8 --- /dev/null +++ b/libs/core/executors/tests/unit/executor_algorithm_bulk.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2026 The STE||AR-Group +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace ex = hpx::execution::experimental; + +void test_sequential_bulk() +{ + hpx::execution::sequenced_executor exec; + auto sched = ex::get_scheduler(exec); + + std::atomic call_count{0}; + + auto snd = ex::schedule(sched) | ex::bulk(10, [&](int i) { + (void) i; + ++call_count; + // Should run sequentially, so it's safe + }); + + hpx::this_thread::experimental::sync_wait(snd); + + HPX_TEST_EQ(call_count.load(), 10); +} + +void test_parallel_bulk() +{ + hpx::execution::parallel_executor exec; + auto sched = ex::get_scheduler(exec); + + std::atomic call_count{0}; + + auto snd = ex::schedule(sched) | ex::bulk(1000, [&](int i) { + (void) i; + ++call_count; + }); + + hpx::this_thread::experimental::sync_wait(snd); + + HPX_TEST_EQ(call_count.load(), 1000); +} + +void test_parallel_bulk_with_value() +{ + hpx::execution::parallel_executor exec; + auto sched = ex::get_scheduler(exec); + + std::atomic call_count{0}; + + auto snd = ex::schedule(sched) | ex::then([]() { return 42; }) | + ex::bulk(100, [&](int i, int val) { + (void) i; + HPX_TEST_EQ(val, 42); + ++call_count; + }); + + hpx::this_thread::experimental::sync_wait(snd); + + HPX_TEST_EQ(call_count.load(), 100); +} + +int hpx_main() +{ + test_sequential_bulk(); + test_parallel_bulk(); + test_parallel_bulk_with_value(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + std::vector const cfg = {"hpx.os_threads=all"}; + hpx::local::init_params init_args; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +}