From 573e48f23407fe5a7227ca53adc5f1228c211a3b Mon Sep 17 00:00:00 2001 From: Hartmut Kaiser Date: Sat, 22 Jan 2022 16:40:58 -0600 Subject: [PATCH] Adding environmental query CPOs - flyby: fixing global_fixture linking problems with MSVC --- .../include/hpx/config/compiler_specific.hpp | 3 +- libs/core/execution/CMakeLists.txt | 2 +- .../hpx/execution/algorithms/let_value.hpp | 9 +- libs/core/execution_base/CMakeLists.txt | 10 +- .../hpx/execution_base/get_allocator.hpp | 46 +++++ .../get_delegatee_scheduler.hpp | 48 +++++ .../include/hpx/execution_base/get_env.hpp | 179 +++++++++++++++++ .../hpx/execution_base/get_scheduler.hpp | 48 +++++ .../hpx/execution_base/get_stop_token.hpp | 48 +++++ .../include/hpx/execution_base/read.hpp | 94 +++++++++ .../include/hpx/execution_base/sender.hpp | 75 +++----- .../execution_base/tests/unit/CMakeLists.txt | 14 +- .../tests/unit/environment_queries.cpp | 182 ++++++++++++++++++ .../tests/unit/forwarding_env_query.cpp | 43 +++++ .../execution_base/tests/unit/get_env.cpp | 130 +++++++++++++ .../functional/detail/tag_fallback_invoke.hpp | 1 + .../testing/include/hpx/modules/testing.hpp | 46 ++--- libs/core/testing/src/testing.cpp | 13 +- libs/core/type_support/CMakeLists.txt | 3 +- .../hpx/type_support/hide_from_adl.hpp | 28 +++ .../include/hpx/type_support/unwrap_ref.hpp | 12 +- 21 files changed, 937 insertions(+), 97 deletions(-) create mode 100644 libs/core/execution_base/include/hpx/execution_base/get_allocator.hpp create mode 100644 libs/core/execution_base/include/hpx/execution_base/get_delegatee_scheduler.hpp create mode 100644 libs/core/execution_base/include/hpx/execution_base/get_env.hpp create mode 100644 libs/core/execution_base/include/hpx/execution_base/get_scheduler.hpp create mode 100644 libs/core/execution_base/include/hpx/execution_base/get_stop_token.hpp create mode 100644 libs/core/execution_base/include/hpx/execution_base/read.hpp create mode 100644 libs/core/execution_base/tests/unit/environment_queries.cpp create mode 100644 libs/core/execution_base/tests/unit/forwarding_env_query.cpp create mode 100644 libs/core/execution_base/tests/unit/get_env.cpp create mode 100644 libs/core/type_support/include/hpx/type_support/hide_from_adl.hpp diff --git a/libs/core/config/include/hpx/config/compiler_specific.hpp b/libs/core/config/include/hpx/config/compiler_specific.hpp index 382ada5d39f1..29b59a8dd6f1 100644 --- a/libs/core/config/include/hpx/config/compiler_specific.hpp +++ b/libs/core/config/include/hpx/config/compiler_specific.hpp @@ -166,4 +166,5 @@ # endif #endif // clang-format on -#endif + +#endif // defined(DOXYGEN) diff --git a/libs/core/execution/CMakeLists.txt b/libs/core/execution/CMakeLists.txt index 288c2ba4d65d..463406d5247c 100644 --- a/libs/core/execution/CMakeLists.txt +++ b/libs/core/execution/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2019-2021 The STE||AR-Group +# Copyright (c) 2019-2022 The STE||AR-Group # # SPDX-License-Identifier: BSL-1.0 # Distributed under the Boost Software License, Version 1.0. (See accompanying diff --git a/libs/core/execution/include/hpx/execution/algorithms/let_value.hpp b/libs/core/execution/include/hpx/execution/algorithms/let_value.hpp index e500ca70f7b7..6ac85d480796 100644 --- a/libs/core/execution/include/hpx/execution/algorithms/let_value.hpp +++ b/libs/core/execution/include/hpx/execution/algorithms/let_value.hpp @@ -73,14 +73,7 @@ namespace hpx { namespace execution { namespace experimental { using value_types = hpx::util::detail::unique_t< hpx::util::detail::concat_pack_of_packs_t, - value_types::template apply -#if defined(HPX_CLANG_VERSION) && HPX_CLANG_VERSION < 110000 - > - // - >>; -#else - >>>; -#endif + value_types::template apply> /**/>>; // hpx::util::pack acts as a concrete type in place of Tuple. It is // required for computing successor_sender_types, but disappears diff --git a/libs/core/execution_base/CMakeLists.txt b/libs/core/execution_base/CMakeLists.txt index fdd9808d157c..e25eb48299e2 100644 --- a/libs/core/execution_base/CMakeLists.txt +++ b/libs/core/execution_base/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2019-2020 The STE||AR-Group +# Copyright (c) 2019-2022 The STE||AR-Group # # SPDX-License-Identifier: BSL-1.0 # Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -8,11 +8,17 @@ set(execution_base_headers hpx/execution_base/agent_base.hpp hpx/execution_base/agent_ref.hpp hpx/execution_base/any_sender.hpp + hpx/execution_base/completion_scheduler.hpp hpx/execution_base/context_base.hpp hpx/execution_base/detail/spinlock_deadlock_detection.hpp hpx/execution_base/execution.hpp - hpx/execution_base/completion_scheduler.hpp + hpx/execution_base/get_allocator.hpp + hpx/execution_base/get_env.hpp + hpx/execution_base/get_scheduler.hpp + hpx/execution_base/get_delegatee_scheduler.hpp + hpx/execution_base/get_stop_token.hpp hpx/execution_base/operation_state.hpp + hpx/execution_base/read.hpp hpx/execution_base/receiver.hpp hpx/execution_base/resource_base.hpp hpx/execution_base/sender.hpp diff --git a/libs/core/execution_base/include/hpx/execution_base/get_allocator.hpp b/libs/core/execution_base/include/hpx/execution_base/get_allocator.hpp new file mode 100644 index 000000000000..165db59f1455 --- /dev/null +++ b/libs/core/execution_base/include/hpx/execution_base/get_allocator.hpp @@ -0,0 +1,46 @@ +// Copyright (c) 2022 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) + +#pragma once + +#include +#include +#include +#include + +namespace hpx::execution::experimental { + + // 1. execution::get_allocator is used to ask an object for its associated + // allocator. + // + // 2. The name execution::get_allocator denotes a customization point object. + // For some subexpression r, if the type of r is (possibly cv-qualified) + // no_env, then execution::get_allocator(r) is ill-formed. + // Otherwise, it is expression equivalent to: + // + // 1. tag_invoke(execution::get_allocator, as_const(r)), if this expression + // is well formed. + // Mandates: The tag_invoke expression above is not potentially- + // throwing and its type satisfies Allocator. + // + // 2. Otherwise, execution::get_allocator(r) is ill-formed. + // + // 3. execution::get_allocator() (with no arguments) is expression-equivalent + // to execution::read(execution::get_allocator). + // + inline constexpr struct get_allocator_t final + : hpx::functional::detail::tag_fallback + { + friend inline constexpr auto tag_fallback_invoke( + get_allocator_t) noexcept; + + } get_allocator{}; + + constexpr auto tag_fallback_invoke(get_allocator_t) noexcept + { + return hpx::execution::experimental::read(get_allocator); + } +} // namespace hpx::execution::experimental diff --git a/libs/core/execution_base/include/hpx/execution_base/get_delegatee_scheduler.hpp b/libs/core/execution_base/include/hpx/execution_base/get_delegatee_scheduler.hpp new file mode 100644 index 000000000000..c0ef1245215b --- /dev/null +++ b/libs/core/execution_base/include/hpx/execution_base/get_delegatee_scheduler.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2022 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) + +#pragma once + +#include +#include +#include +#include + +namespace hpx::execution::experimental { + + // 1. execution::get_delegatee_scheduler is used to ask an object for a + // scheduler that may be used to delegate work to for the purpose of + // forward progress delegation. + // + // 2. The name execution::get_scheduler denotes a customization point + // object. + // For some subexpression r, if the type of r is (possibly cv-qualified) + // no_env, then execution::get_delegatee_scheduler(r) is ill-formed. + // Otherwise, it is expression equivalent to: + // + // 1. tag_invoke(execution::get_delegatee_scheduler, as_const(r)), if + // this expression is well formed. + // Mandates: The tag_invoke expression above is not potentially- + // throwing and its type satisfies execution::scheduler. + // + // 2. Otherwise, execution::get_delegatee_scheduler(r) is ill-formed. + // + // 3. execution::get_delegatee_scheduler() (with no arguments) is expression- + // equivalent to execution::read(execution::get_delegatee_scheduler). + // + inline constexpr struct get_delegatee_scheduler_t final + : hpx::functional::detail::tag_fallback + { + friend inline constexpr auto tag_fallback_invoke( + get_delegatee_scheduler_t) noexcept; + + } get_delegatee_scheduler{}; + + constexpr auto tag_fallback_invoke(get_delegatee_scheduler_t) noexcept + { + return hpx::execution::experimental::read(get_delegatee_scheduler); + } +} // namespace hpx::execution::experimental diff --git a/libs/core/execution_base/include/hpx/execution_base/get_env.hpp b/libs/core/execution_base/include/hpx/execution_base/get_env.hpp new file mode 100644 index 000000000000..f86983514e8e --- /dev/null +++ b/libs/core/execution_base/include/hpx/execution_base/get_env.hpp @@ -0,0 +1,179 @@ +// Copyright (c) 2022 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) + +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace hpx::execution::experimental { + + // 1. An execution environment contains state associated with the + // completion of an asynchronous operation. Every receiver has an + // associated execution environment, accessible with the get_env + // receiver query. The state of an execution environment is accessed + // with customization point objects. An execution environment may + // respond to any number of these environment queries. + // + // 2. An environment query is a customization point object that accepts + // as its first argument an execution environment. For an environment + // query EQ and an object e of type no_env, the expression EQ(e) shall + // be ill-formed. + // + namespace exec_envs { + + // no_env is a special environment used by the sender concept and by + // the get_completion_signatures customization point when the user + // has specified no environment argument. + // + // [Note: A user may choose to not specify an environment in order + // to see if a sender knows its completion signatures + // independent of any particular execution environment. + // -- end note] + struct no_env + { + template + friend std::enable_if_t>> + tag_invoke(Tag, Env) = delete; + }; + + struct empty_env + { + }; + + template > + struct env + { + using BaseEnv = util::hidden_from_adl_t; + + HPX_NO_UNIQUE_ADDRESS util::unwrap_reference_t value_; + HPX_NO_UNIQUE_ADDRESS BaseEnv base_env_{}; + + // Forward only the receiver queries + template >> + friend constexpr auto tag_invoke( + Tag2 tag, env const& self, Args&&... args) noexcept + -> functional::tag_invoke_result_t + { + return HPX_FORWARD(Tag2, tag)( + self.base_env_, HPX_FORWARD(Args, args)...); + } + + template + friend constexpr auto + tag_invoke(Tag, env const& self, Args&&...) noexcept( + std::is_nothrow_copy_constructible_v< + util::unwrap_reference_t>) + -> util::unwrap_reference_t + { + return self.value_; + } + }; + + template + struct make_env_t + { + template + constexpr auto operator()(Value&& value) const + noexcept(std::is_nothrow_copy_constructible_v< + util::unwrap_reference_t>>) + -> env> + { + return {HPX_FORWARD(Value, value)}; + } + + template + constexpr auto operator()(Value&& value, BaseEnv&& base_env) const + -> env, + util::hide_from_adl>> + { + return { + HPX_FORWARD(Value, value), HPX_FORWARD(BaseEnv, base_env)}; + } + }; + + // For making an evaluation environment from a key/value pair, and + // optionally another environment. + template + inline constexpr exec_envs::make_env_t make_env{}; + + } // namespace exec_envs + + using exec_envs::empty_env; + using exec_envs::env; + using exec_envs::make_env; + using exec_envs::no_env; + + template + struct is_no_env : std::is_same, no_env> + { + }; + + template + inline constexpr bool is_no_env_v = is_no_env::value; + + // get_env is a customization point object. For some subexpression r, + // get_env(r) is expression-equivalent to: + // + // 1. tag_invoke(execution::get_env, r) if that expression is well-formed. + // 2. Otherwise, empty_env{}. + // + inline constexpr struct get_env_t final + : hpx::functional::detail::tag_fallback + { + template + friend constexpr HPX_FORCEINLINE auto tag_fallback_invoke( + get_env_t, EnvProvider&&) noexcept + { + return empty_env{}; + } + } get_env{}; + + template + using env_of_t = decltype(get_env(std::declval())); + + template + using make_env_t = + decltype(make_env(std::declval(), std::declval())); + + // execution::forwarding_env_query is used to ask a customization point + // object whether it is an environment query that should be forwarded + // through environment adaptors. + // + // The name execution::forwarding_env_query denotes a customization point + // object. For some subexpression t, execution::forwarding_env_query(t) + // is expression equivalent to: + // + // 1. tag_invoke(execution::forwarding_env_query, t), contextually + // converted to bool, if the tag_invoke expression is well formed. + // Mandates: The tag_invoke expression is indeed contextually + // convertible to bool, that expression and the contextual conversion + // are not potentially-throwing and are core constant expressions if t + // is a core constant expression. + // 2. Otherwise, false. + // + inline constexpr struct forwarding_env_query_t final + : hpx::functional::detail::tag_fallback + { + template + friend constexpr HPX_FORCEINLINE auto tag_fallback_invoke( + forwarding_env_query_t, T&&) noexcept + { + return true; + } + + } forwarding_env_query{}; + +} // namespace hpx::execution::experimental diff --git a/libs/core/execution_base/include/hpx/execution_base/get_scheduler.hpp b/libs/core/execution_base/include/hpx/execution_base/get_scheduler.hpp new file mode 100644 index 000000000000..7ca9a610db0b --- /dev/null +++ b/libs/core/execution_base/include/hpx/execution_base/get_scheduler.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2022 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) + +#pragma once + +#include +#include +#include +#include + +namespace hpx::execution::experimental { + + // 1. execution::get_scheduler is used to ask an object for its associated + // scheduler. + // + // 2. The name execution::get_scheduler denotes a customization point object. + // For some subexpression r, if the type of r is (possibly cv-qualified) + // no_env, then execution::get_scheduler(r) is ill-formed. + // + // Otherwise, it is expression equivalent to: + // + // 1. tag_invoke(execution::get_scheduler, as_const(r)), if this expression + // is well formed. + // + // Mandates: The tag_invoke expression above is not potentially- + // throwing and its type satisfies execution::scheduler. + // + // 2. Otherwise, execution::get_scheduler(r) is ill-formed. + // + // 3. execution::get_scheduler() (with no arguments) is expression-equivalent + // to execution::read(execution::get_scheduler). + // + inline constexpr struct get_scheduler_t final + : hpx::functional::detail::tag_fallback + { + friend inline constexpr auto tag_fallback_invoke( + get_scheduler_t) noexcept; + + } get_scheduler{}; + + constexpr auto tag_fallback_invoke(get_scheduler_t) noexcept + { + return hpx::execution::experimental::read(get_scheduler); + } +} // namespace hpx::execution::experimental diff --git a/libs/core/execution_base/include/hpx/execution_base/get_stop_token.hpp b/libs/core/execution_base/include/hpx/execution_base/get_stop_token.hpp new file mode 100644 index 000000000000..f2dcb0043601 --- /dev/null +++ b/libs/core/execution_base/include/hpx/execution_base/get_stop_token.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2022 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) + +#pragma once + +#include +#include +#include +#include + +namespace hpx::execution::experimental { + + // 1. execution::get_stop_token is used to ask an object for an + // associated stop token. + // + // 2. The name execution::get_stop_token denotes a customization + // point object. For some subexpression r, if the type of r is + // (possibly cv-qualified) no_env, then execution::get_stop_token(r) + // is ill-formed. + // Otherwise, it is expression equivalent to: + // + // 1. tag_invoke(execution::get_stop_token, as_const(r)), if this + // expression is well formed. + // + // Mandates: The tag_invoke expression above is not potentially + // -throwing and its type satisfies stoppable_token. + // + // 2. Otherwise, never_stop_token{}. + // + // 3. execution::get_stop_token() (with no arguments) is expression- + // equivalent to execution::read(execution::get_stop_token). + // + inline constexpr struct get_stop_token_t final + : hpx::functional::detail::tag_fallback + { + friend inline constexpr auto tag_fallback_invoke( + get_stop_token_t) noexcept; + + } get_stop_token{}; + + constexpr auto tag_fallback_invoke(get_stop_token_t) noexcept + { + return hpx::execution::experimental::read(get_stop_token); + } +} // namespace hpx::execution::experimental diff --git a/libs/core/execution_base/include/hpx/execution_base/read.hpp b/libs/core/execution_base/include/hpx/execution_base/read.hpp new file mode 100644 index 000000000000..dfb20dff729c --- /dev/null +++ b/libs/core/execution_base/include/hpx/execution_base/read.hpp @@ -0,0 +1,94 @@ +// Copyright (c) 2022 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) + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace hpx::execution::experimental { + + namespace detail { + + template + struct read_sender + { + constexpr read_sender() = default; + + read_sender(read_sender&&) = default; + read_sender(read_sender const&) = default; + read_sender& operator=(read_sender&&) = default; + read_sender& operator=(read_sender const&) = default; + + template + struct operation_state + { + HPX_NO_UNIQUE_ADDRESS std::decay_t receiver; + + template + operation_state(Receiver_&& receiver) noexcept + : receiver(HPX_FORWARD(Receiver_, receiver)) + { + } + + operation_state(operation_state&&) = delete; + operation_state& operator=(operation_state&&) = delete; + operation_state(operation_state const&) = delete; + operation_state& operator=(operation_state const&) = delete; + + friend void tag_invoke(start_t, operation_state& os) noexcept + { + hpx::detail::try_catch_exception_ptr( + [&]() { + auto env = hpx::execution::experimental::get_env( + os.receiver); + hpx::execution::experimental::set_value( + HPX_MOVE(os.receiver), Tag()(HPX_MOVE(env))); + }, + [&](std::exception_ptr ep) { + hpx::execution::experimental::set_error( + HPX_MOVE(os.receiver), HPX_MOVE(ep)); + }); + } + }; + + template + friend auto tag_invoke( + connect_t, read_sender&&, Receiver&& receiver) + { + return operation_state{ + HPX_FORWARD(Receiver, receiver)}; + } + + template + friend auto tag_invoke(connect_t, read_sender&, Receiver&& receiver) + { + return operation_state{ + HPX_FORWARD(Receiver, receiver)}; + } + }; + } // namespace detail + + inline constexpr struct read_t final : hpx::functional::tag + { + template + friend constexpr auto tag_invoke(read_t, Tag) noexcept + { + return detail::read_sender{}; + } + } read{}; + +} // namespace hpx::execution::experimental diff --git a/libs/core/execution_base/include/hpx/execution_base/sender.hpp b/libs/core/execution_base/include/hpx/execution_base/sender.hpp index 7a0441c5a605..1d357ed5a35e 100644 --- a/libs/core/execution_base/include/hpx/execution_base/sender.hpp +++ b/libs/core/execution_base/include/hpx/execution_base/sender.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2020 Thomas Heller +// Copyright (c) 2022 Hartmut Kaiser // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -70,7 +72,7 @@ namespace hpx { namespace execution { namespace experimental { /// receiver with the corresponding value, error and done channels: /// * `hpx::execution::experimental::connect` /// - /// In addition, `hpx::execution::experimental::::sender_traits ` needs to + /// In addition, `hpx::execution::experimental::sender_traits ` needs to /// be specialized in some form. /// /// A sender's destructor shall not block pending completion of submitted @@ -86,23 +88,23 @@ namespace hpx { namespace execution { namespace experimental { /// by a sender. This can be either specialized directly for user defined /// sender types or embedded value_types, error_types and sends_done /// inside the sender type can be provided. - template + template struct sender_traits; - template - struct sender_traits : sender_traits + template + struct sender_traits : sender_traits { }; - template - struct sender_traits : sender_traits + template + struct sender_traits : sender_traits { }; - template - struct sender_traits : sender_traits + template + struct sender_traits : sender_traits { }; - template - struct sender_traits : sender_traits + template + struct sender_traits : sender_traits { }; @@ -124,7 +126,7 @@ namespace hpx { namespace execution { namespace experimental { template struct is_sender : std::integral_constant>::value && + std::is_move_constructible_v> && detail::specialized>(nullptr)> { }; @@ -137,32 +139,6 @@ namespace hpx { namespace execution { namespace experimental { void operator()() {} }; - namespace detail { - template - struct is_executor_of_base_impl : std::false_type - { - }; - - template - struct is_executor_of_base_impl&>::value && - std::is_constructible, F>::value && - std::is_destructible>::value && - std::is_move_constructible>::value && - std::is_copy_constructible::value && - hpx::traits::is_equality_comparable::value>> - : std::true_type - { - }; - - template - struct is_executor_base - : is_executor_of_base_impl, - invocable_archetype> - { - }; - } // namespace detail - namespace detail { template struct has_member_connect : std::false_type @@ -171,8 +147,8 @@ namespace hpx { namespace execution { namespace experimental { template struct has_member_connect().connect( - std::declval()))>::type> : std::true_type + hpx::util::always_void_t().connect(std::declval()))>> : std::true_type { }; } // namespace detail @@ -229,8 +205,8 @@ namespace hpx { namespace execution { namespace experimental { template struct has_member_schedule().schedule())>::type> : std::true_type + hpx::util::always_void_t().schedule())>> + : std::true_type { }; } // namespace detail @@ -326,11 +302,11 @@ namespace hpx { namespace execution { namespace experimental { { }; - template + template struct sender_traits_base; - template - struct sender_traits_base + template + struct sender_traits_base { template