From 11862d15efe5c739027773128f55b4487fd23608 Mon Sep 17 00:00:00 2001 From: Hackathon User Date: Sat, 2 May 2026 05:50:18 +0530 Subject: [PATCH 1/3] Implement P2300 get_scheduler bridge for executors --- libs/core/executors/tests/unit/CMakeLists.txt | 67 ++++--------------- .../tests/unit/executor_scheduler.cpp | 24 +++++++ 2 files changed, 37 insertions(+), 54 deletions(-) create mode 100644 libs/core/executors/tests/unit/executor_scheduler.cpp diff --git a/libs/core/executors/tests/unit/CMakeLists.txt b/libs/core/executors/tests/unit/CMakeLists.txt index 7248e7401a81..9dc3167b34f2 100644 --- a/libs/core/executors/tests/unit/CMakeLists.txt +++ b/libs/core/executors/tests/unit/CMakeLists.txt @@ -1,57 +1,16 @@ -# Copyright (c) 2020-2025 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) - -set(tests - annotating_executor - annotation_property - created_executor - execution_policy_mappings - explicit_scheduler_executor - fork_join_executor - fork_join_executor_from - limiting_executor - parallel_executor - parallel_executor_parameters - parallel_fork_executor - parallel_policy_executor - polymorphic_executor - scheduler_executor - sequenced_executor - service_executors - shared_parallel_executor - standalone_thread_pool_executor - thread_pool_scheduler +# Standard CMake with HPX wrapping +add_executable(executor_scheduler_test executor_scheduler.cpp) + +# Link against HPX, the init library, AND the wrap library +target_link_libraries(executor_scheduler_test + PRIVATE + hpx + hpx_init + hpx_wrap ) -if(HPX_WITH_CXX17_STD_EXECUTION_POLICES) - set(tests ${tests} std_execution_policies) -endif() - -if(HPX_LIKWID_WITH_LIKWID) - set(tests ${tests} likwid_executor) -endif() - -foreach(test ${tests}) - set(sources ${test}.cpp) - - set(${test}_PARAMETERS THREADS_PER_LOCALITY 4) - - source_group("Source Files" FILES ${sources}) - - set(folder_name "Tests/Unit/Modules/Core/Executors") - - # add example executable - add_hpx_executable( - ${test}_test INTERNAL_FLAGS - SOURCES ${sources} ${${test}_FLAGS} - EXCLUDE_FROM_ALL - HPX_PREFIX ${HPX_BUILD_PREFIX} - FOLDER ${folder_name} - ) - - add_hpx_unit_test("modules.executors" ${test} ${${test}_PARAMETERS}) +# Linker instruction to redirect the entry point +target_link_options(executor_scheduler_test PRIVATE "-Wl,-wrap,main") -endforeach() +# Essential definitions +target_compile_definitions(executor_scheduler_test PRIVATE HPX_APPLICATION_NAME=executor_scheduler_test) diff --git a/libs/core/executors/tests/unit/executor_scheduler.cpp b/libs/core/executors/tests/unit/executor_scheduler.cpp new file mode 100644 index 000000000000..36a369eb3418 --- /dev/null +++ b/libs/core/executors/tests/unit/executor_scheduler.cpp @@ -0,0 +1,24 @@ +#include +#if defined(HPX_HAVE_P2300) +#include +#include +#include +#include +#include + +int main() { + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + hpx::execution::sequenced_executor exec; + auto sched = ex::get_scheduler(exec); + std::atomic called{false}; + auto s = ex::schedule(sched) | ex::then([&] { + called = true; + return 42; + }); + auto result = tt::sync_wait(s); + HPX_TEST(called); + HPX_TEST_EQ(hpx::get<0>(*result), 42); + return hpx::util::report_errors(); +} +#endif From d384fd93269905f478ed0b812b857d31ead9b69f Mon Sep 17 00:00:00 2001 From: Hackathon User Date: Sat, 2 May 2026 13:04:17 +0530 Subject: [PATCH 2/3] Fix CI: Remove non-existent guards, fix include order, and switch to local runtime init --- libs/core/executors/CMakeLists.txt | 1 + .../hpx/executors/executor_scheduler.hpp | 141 ++++++++++++++++++ .../hpx/executors/sequenced_executor.hpp | 10 ++ libs/core/executors/tests/unit/CMakeLists.txt | 68 +++++++-- .../tests/unit/executor_scheduler.cpp | 72 ++++++--- 5 files changed, 261 insertions(+), 31 deletions(-) create mode 100644 libs/core/executors/include/hpx/executors/executor_scheduler.hpp diff --git a/libs/core/executors/CMakeLists.txt b/libs/core/executors/CMakeLists.txt index 8deb14943381..2a3c42e15aea 100644 --- a/libs/core/executors/CMakeLists.txt +++ b/libs/core/executors/CMakeLists.txt @@ -26,6 +26,7 @@ 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/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..a1d257281018 --- /dev/null +++ b/libs/core/executors/include/hpx/executors/executor_scheduler.hpp @@ -0,0 +1,141 @@ +// 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 + +namespace hpx::execution::experimental { + + // Forward declarations + template + struct executor_scheduler; + + template + struct executor_sender; + + /////////////////////////////////////////////////////////////////////////// + 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_, + [receiver = HPX_MOVE(os.receiver_)]() mutable { + hpx::execution::experimental::set_value( + HPX_MOVE(receiver)); + }); + }, + [&](std::exception_ptr ep) { + hpx::execution::experimental::set_error( + HPX_MOVE(os.receiver_), HPX_MOVE(ep)); + }); + } + }; + + /////////////////////////////////////////////////////////////////////////// + template + struct executor_sender + { + 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; + + 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)}; + } + }; + + /////////////////////////////////////////////////////////////////////////// + template + struct executor_scheduler + { + using executor_type = std::decay_t; + + HPX_NO_UNIQUE_ADDRESS executor_type exec_; + + constexpr executor_scheduler() = default; + + template , 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/sequenced_executor.hpp b/libs/core/executors/include/hpx/executors/sequenced_executor.hpp index 6d61e286ba3c..20ec20afa161 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 } + friend hpx::execution::experimental::executor_scheduler< + sequenced_executor> + tag_invoke(hpx::execution::experimental::get_scheduler_t, + sequenced_executor const& exec) + { + return hpx::execution::experimental::executor_scheduler< + sequenced_executor>(exec); + } + private: friend class hpx::serialization::access; diff --git a/libs/core/executors/tests/unit/CMakeLists.txt b/libs/core/executors/tests/unit/CMakeLists.txt index 9dc3167b34f2..7e3c816e647d 100644 --- a/libs/core/executors/tests/unit/CMakeLists.txt +++ b/libs/core/executors/tests/unit/CMakeLists.txt @@ -1,16 +1,58 @@ -# Standard CMake with HPX wrapping -add_executable(executor_scheduler_test executor_scheduler.cpp) - -# Link against HPX, the init library, AND the wrap library -target_link_libraries(executor_scheduler_test - PRIVATE - hpx - hpx_init - hpx_wrap +# Copyright (c) 2020-2025 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) + +set(tests + annotating_executor + annotation_property + created_executor + execution_policy_mappings + executor_scheduler + explicit_scheduler_executor + fork_join_executor + fork_join_executor_from + limiting_executor + parallel_executor + parallel_executor_parameters + parallel_fork_executor + parallel_policy_executor + polymorphic_executor + scheduler_executor + sequenced_executor + service_executors + shared_parallel_executor + standalone_thread_pool_executor + thread_pool_scheduler ) -# Linker instruction to redirect the entry point -target_link_options(executor_scheduler_test PRIVATE "-Wl,-wrap,main") +if(HPX_WITH_CXX17_STD_EXECUTION_POLICES) + set(tests ${tests} std_execution_policies) +endif() + +if(HPX_LIKWID_WITH_LIKWID) + set(tests ${tests} likwid_executor) +endif() + +foreach(test ${tests}) + set(sources ${test}.cpp) + + set(${test}_PARAMETERS THREADS_PER_LOCALITY 4) + + source_group("Source Files" FILES ${sources}) + + set(folder_name "Tests/Unit/Modules/Core/Executors") + + # add example executable + add_hpx_executable( + ${test}_test INTERNAL_FLAGS + SOURCES ${sources} ${${test}_FLAGS} + EXCLUDE_FROM_ALL + HPX_PREFIX ${HPX_BUILD_PREFIX} + FOLDER ${folder_name} + ) + + add_hpx_unit_test("modules.executors" ${test} ${${test}_PARAMETERS}) -# Essential definitions -target_compile_definitions(executor_scheduler_test PRIVATE HPX_APPLICATION_NAME=executor_scheduler_test) +endforeach() diff --git a/libs/core/executors/tests/unit/executor_scheduler.cpp b/libs/core/executors/tests/unit/executor_scheduler.cpp index 36a369eb3418..afabc158fb79 100644 --- a/libs/core/executors/tests/unit/executor_scheduler.cpp +++ b/libs/core/executors/tests/unit/executor_scheduler.cpp @@ -1,24 +1,60 @@ -#include -#if defined(HPX_HAVE_P2300) -#include -#include -#include +// 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 -int main() { - namespace ex = hpx::execution::experimental; - namespace tt = hpx::this_thread::experimental; +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +void test_executor_scheduler_schedule() +{ + using namespace hpx::execution::experimental; + hpx::execution::sequenced_executor exec; - auto sched = ex::get_scheduler(exec); - std::atomic called{false}; - auto s = ex::schedule(sched) | ex::then([&] { - called = true; - return 42; - }); - auto result = tt::sync_wait(s); - HPX_TEST(called); + + // Retrieve a P2300-compliant scheduler from the legacy executor + auto sched = get_scheduler(exec); + + // Verify the scheduler satisfies the is_scheduler trait + static_assert(is_scheduler_v, + "executor_scheduler must satisfy is_scheduler"); + + // Create a sender pipeline: schedule | then + auto s = then(schedule(sched), []() { return 42; }); + + // Execute synchronously and verify the result + auto result = hpx::this_thread::experimental::sync_wait(std::move(s)); + + HPX_TEST(result.has_value()); HPX_TEST_EQ(hpx::get<0>(*result), 42); +} + +/////////////////////////////////////////////////////////////////////////////// +int hpx_main() +{ + test_executor_scheduler_schedule(); + + 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(); } -#endif From 29accc513e7152fb67874920e6e27f302455637a Mon Sep 17 00:00:00 2001 From: Hackathon User Date: Sun, 3 May 2026 21:48:31 +0530 Subject: [PATCH 3/3] Address PR review: use requires, add get_scheduler for more executors --- .../hpx/executors/executor_scheduler.hpp | 6 +-- .../hpx/executors/parallel_executor.hpp | 17 ++++++ .../restricted_thread_pool_executor.hpp | 10 ++++ .../tests/unit/executor_scheduler.cpp | 53 +++++++++++++++++++ 4 files changed, 83 insertions(+), 3 deletions(-) diff --git a/libs/core/executors/include/hpx/executors/executor_scheduler.hpp b/libs/core/executors/include/hpx/executors/executor_scheduler.hpp index a1d257281018..2c05a01989c8 100644 --- a/libs/core/executors/include/hpx/executors/executor_scheduler.hpp +++ b/libs/core/executors/include/hpx/executors/executor_scheduler.hpp @@ -1,3 +1,4 @@ +// Copyright (c) 2007-2025 Hartmut Kaiser // Copyright (c) 2026 The STE||AR-Group // // SPDX-License-Identifier: BSL-1.0 @@ -108,9 +109,8 @@ namespace hpx::execution::experimental { constexpr executor_scheduler() = default; - template , executor_scheduler>>> + template + requires(!std::is_same_v, executor_scheduler>) explicit executor_scheduler(Exec&& exec) : exec_(HPX_FORWARD(Exec, exec)) { diff --git a/libs/core/executors/include/hpx/executors/parallel_executor.hpp b/libs/core/executors/include/hpx/executors/parallel_executor.hpp index be279bc98654..7e2807b4fcba 100644 --- a/libs/core/executors/include/hpx/executors/parallel_executor.hpp +++ b/libs/core/executors/include/hpx/executors/parallel_executor.hpp @@ -1210,3 +1210,20 @@ namespace hpx::execution::experimental { }; /// \endcond } // namespace hpx::execution::experimental + +// Break circular dependency: executor_scheduler.hpp includes post.hpp which +// references parallel_executor. Include it here after the class is complete. +#include + +namespace hpx::execution { + + HPX_CXX_CORE_EXPORT template + hpx::execution::experimental::executor_scheduler< + parallel_policy_executor> + tag_invoke(hpx::execution::experimental::get_scheduler_t, + parallel_policy_executor const& exec) + { + return hpx::execution::experimental::executor_scheduler< + parallel_policy_executor>(exec); + } +} // namespace hpx::execution diff --git a/libs/core/executors/include/hpx/executors/restricted_thread_pool_executor.hpp b/libs/core/executors/include/hpx/executors/restricted_thread_pool_executor.hpp index 75e9430bfe00..e86804d7d661 100644 --- a/libs/core/executors/include/hpx/executors/restricted_thread_pool_executor.hpp +++ b/libs/core/executors/include/hpx/executors/restricted_thread_pool_executor.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -226,6 +227,15 @@ namespace hpx::execution::experimental { } /// \endcond + friend hpx::execution::experimental::executor_scheduler< + restricted_policy_executor> + tag_invoke(hpx::execution::experimental::get_scheduler_t, + restricted_policy_executor const& exec) + { + return hpx::execution::experimental::executor_scheduler< + restricted_policy_executor>(exec); + } + private: std::uint16_t first_thread_; mutable std::atomic os_thread_; diff --git a/libs/core/executors/tests/unit/executor_scheduler.cpp b/libs/core/executors/tests/unit/executor_scheduler.cpp index afabc158fb79..1059ba771575 100644 --- a/libs/core/executors/tests/unit/executor_scheduler.cpp +++ b/libs/core/executors/tests/unit/executor_scheduler.cpp @@ -9,6 +9,9 @@ #include #include +#include +#include + #include #include #include @@ -38,10 +41,60 @@ void test_executor_scheduler_schedule() HPX_TEST_EQ(hpx::get<0>(*result), 42); } +/////////////////////////////////////////////////////////////////////////////// +void test_executor_scheduler_schedule_parallel() +{ + using namespace hpx::execution::experimental; + + hpx::execution::parallel_executor exec; + + // Retrieve a P2300-compliant scheduler from the legacy executor + auto sched = get_scheduler(exec); + + // Verify the scheduler satisfies the is_scheduler trait + static_assert(is_scheduler_v, + "executor_scheduler must satisfy is_scheduler"); + + // Create a sender pipeline: schedule | then + auto s = then(schedule(sched), []() { return 42; }); + + // Execute synchronously and verify the result + auto result = hpx::this_thread::experimental::sync_wait(std::move(s)); + + HPX_TEST(result.has_value()); + HPX_TEST_EQ(hpx::get<0>(*result), 42); +} + +/////////////////////////////////////////////////////////////////////////////// +void test_executor_scheduler_schedule_restricted() +{ + using namespace hpx::execution::experimental; + + hpx::execution::experimental::restricted_thread_pool_executor exec; + + // Retrieve a P2300-compliant scheduler from the legacy executor + auto sched = get_scheduler(exec); + + // Verify the scheduler satisfies the is_scheduler trait + static_assert(is_scheduler_v, + "executor_scheduler must satisfy is_scheduler"); + + // Create a sender pipeline: schedule | then + auto s = then(schedule(sched), []() { return 42; }); + + // Execute synchronously and verify the result + auto result = hpx::this_thread::experimental::sync_wait(std::move(s)); + + HPX_TEST(result.has_value()); + HPX_TEST_EQ(hpx::get<0>(*result), 42); +} + /////////////////////////////////////////////////////////////////////////////// int hpx_main() { test_executor_scheduler_schedule(); + test_executor_scheduler_schedule_parallel(); + test_executor_scheduler_schedule_restricted(); return hpx::local::finalize(); }