From bd32982b0afba90227844c4b755c086f3e2fbb5a Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 6 Sep 2023 13:17:36 +0300 Subject: [PATCH 01/11] move is_(trivially_)relocatable to experimental --- .../algorithms/traits/pointer_category.hpp | 2 +- .../hpx/type_support/is_relocatable.hpp | 4 +- .../type_support/is_trivially_relocatable.hpp | 10 +- .../include/hpx/type_support/relocate_at.hpp | 6 +- .../type_support/uninitialized_relocate.hpp | 4 +- .../tests/unit/is_relocatable.cpp | 67 +++++----- .../tests/unit/is_trivially_relocatable.cpp | 119 +++++++++--------- .../type_support/tests/unit/relocate_at.cpp | 12 +- .../tests/unit/uninitialized_relocate.cpp | 18 +-- 9 files changed, 122 insertions(+), 120 deletions(-) diff --git a/libs/core/algorithms/include/hpx/algorithms/traits/pointer_category.hpp b/libs/core/algorithms/include/hpx/algorithms/traits/pointer_category.hpp index 0cc691dad638..0d59da372ae9 100644 --- a/libs/core/algorithms/include/hpx/algorithms/traits/pointer_category.hpp +++ b/libs/core/algorithms/include/hpx/algorithms/traits/pointer_category.hpp @@ -145,7 +145,7 @@ namespace hpx::traits { { using type = std::conditional_t< std::is_same_v, iter_value_t> && - is_relocatable_v>, + hpx::experimental::is_relocatable_v>, relocatable_pointer_tag, general_pointer_tag>; }; diff --git a/libs/core/type_support/include/hpx/type_support/is_relocatable.hpp b/libs/core/type_support/include/hpx/type_support/is_relocatable.hpp index 2d0c6d919ccb..3a3dc990a3da 100644 --- a/libs/core/type_support/include/hpx/type_support/is_relocatable.hpp +++ b/libs/core/type_support/include/hpx/type_support/is_relocatable.hpp @@ -8,7 +8,7 @@ #include -namespace hpx { +namespace hpx::experimental { template struct is_relocatable @@ -32,4 +32,4 @@ namespace hpx { template inline constexpr bool is_relocatable_from_v = is_relocatable_from::value; -} // namespace hpx +} // namespace hpx::experimental diff --git a/libs/core/type_support/include/hpx/type_support/is_trivially_relocatable.hpp b/libs/core/type_support/include/hpx/type_support/is_trivially_relocatable.hpp index 1a777ed11986..63ffe92946c2 100644 --- a/libs/core/type_support/include/hpx/type_support/is_trivially_relocatable.hpp +++ b/libs/core/type_support/include/hpx/type_support/is_trivially_relocatable.hpp @@ -10,7 +10,7 @@ // Macro to specialize template for given type #define HPX_DECLARE_TRIVIALLY_RELOCATABLE(T) \ - namespace hpx { \ + namespace hpx::experimental { \ template <> \ struct is_trivially_relocatable : std::true_type \ { \ @@ -18,7 +18,7 @@ } #define HPX_DECLARE_TRIVIALLY_RELOCATABLE_TEMPLATE(T) \ - namespace hpx { \ + namespace hpx::experimental { \ template \ struct is_trivially_relocatable> : std::true_type \ { \ @@ -26,14 +26,14 @@ } #define HPX_DECLARE_TRIVIALLY_RELOCATABLE_TEMPLATE_IF(T, Condition) \ - namespace hpx { \ + namespace hpx::experimental { \ template \ struct is_trivially_relocatable> : Condition \ { \ }; \ } -namespace hpx { +namespace hpx::experimental { template // All trivially copyable types are trivially relocatable @@ -100,4 +100,4 @@ namespace hpx { template inline constexpr bool is_trivially_relocatable_v = is_trivially_relocatable::value; -} // namespace hpx +} // namespace hpx::experimental diff --git a/libs/core/type_support/include/hpx/type_support/relocate_at.hpp b/libs/core/type_support/include/hpx/type_support/relocate_at.hpp index 8aa1750097ff..dc087eec9977 100644 --- a/libs/core/type_support/include/hpx/type_support/relocate_at.hpp +++ b/libs/core/type_support/include/hpx/type_support/relocate_at.hpp @@ -61,7 +61,7 @@ namespace hpx::experimental { template constexpr bool relocate_using_memmove = - hpx::is_trivially_relocatable_v && !std::is_volatile_v; + is_trivially_relocatable_v && !std::is_volatile_v; template , int> = 0> @@ -126,7 +126,7 @@ namespace hpx::experimental { // noexcept if the memmove path is taken or if the move path is noexcept noexcept(detail::relocate_at_helper(src, dst))) { - static_assert(hpx::is_relocatable_v, + static_assert(is_relocatable_v, "new (dst) T(std::move(*src)) must be well-formed"); return detail::relocate_at_helper(src, dst); @@ -136,7 +136,7 @@ namespace hpx::experimental { T relocate(T* src) noexcept(noexcept(detail::relocate_helper(src))) { static_assert( - hpx::is_relocatable_v, "T(std::move(*src)) must be well-formed"); + is_relocatable_v, "T(std::move(*src)) must be well-formed"); return detail::relocate_helper(src); } diff --git a/libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp index 750dc601d660..9e8941d300e7 100644 --- a/libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp +++ b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp @@ -39,10 +39,10 @@ namespace hpx::experimental { using out_type = typename std::iterator_traits::value_type; constexpr static bool valid_relocation = - hpx::is_relocatable_from_v; + is_relocatable_from_v; constexpr static bool is_buffer_memcpyable = - hpx::is_trivially_relocatable_v && + is_trivially_relocatable_v && // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The important check std::is_same_v, std::remove_cv_t> && diff --git a/libs/core/type_support/tests/unit/is_relocatable.cpp b/libs/core/type_support/tests/unit/is_relocatable.cpp index f0a09884493c..3f9322289c7e 100644 --- a/libs/core/type_support/tests/unit/is_relocatable.cpp +++ b/libs/core/type_support/tests/unit/is_relocatable.cpp @@ -11,31 +11,34 @@ #include // for std::shared_ptr, std::unique_ptr #include +using hpx::experimental::is_relocatable_from_v; +using hpx::experimental::is_relocatable_v; + // Integral types are relocatable -static_assert(hpx::is_relocatable_v); -static_assert(hpx::is_relocatable_v); +static_assert(is_relocatable_v); +static_assert(is_relocatable_v); // Pointer types are relocatable -static_assert(hpx::is_relocatable_v); -static_assert(hpx::is_relocatable_v); -static_assert(hpx::is_relocatable_v); -static_assert(hpx::is_relocatable_v); +static_assert(is_relocatable_v); +static_assert(is_relocatable_v); +static_assert(is_relocatable_v); +static_assert(is_relocatable_v); // Array types are not move-constructible and thus not relocatable -static_assert(!hpx::is_relocatable_v); -static_assert(!hpx::is_relocatable_v); -static_assert(!hpx::is_relocatable_v); -static_assert(!hpx::is_relocatable_v); +static_assert(!is_relocatable_v); +static_assert(!is_relocatable_v); +static_assert(!is_relocatable_v); +static_assert(!is_relocatable_v); // Function types are not move-constructible and thus not relocatable -static_assert(!hpx::is_relocatable_v); +static_assert(!is_relocatable_v); // Void types are not move-constructible and thus not relocatable -static_assert(!hpx::is_relocatable_v); -static_assert(!hpx::is_relocatable_v); +static_assert(!is_relocatable_v); +static_assert(!is_relocatable_v); // std::mutex is not relocatable -static_assert(!hpx::is_relocatable_v); +static_assert(!is_relocatable_v); struct not_destructible { @@ -44,14 +47,14 @@ struct not_destructible ~not_destructible() = delete; }; -static_assert(!hpx::is_relocatable_v); +static_assert(!is_relocatable_v); struct not_move_constructible { not_move_constructible(not_move_constructible const&); not_move_constructible(not_move_constructible&&) = delete; }; -static_assert(!hpx::is_relocatable_v); +static_assert(!is_relocatable_v); struct not_copy_constructible { @@ -59,16 +62,16 @@ struct not_copy_constructible not_copy_constructible(not_copy_constructible&&); }; -static_assert(hpx::is_relocatable_v); +static_assert(is_relocatable_v); // reference types are not relocatable -static_assert(!hpx::is_relocatable_v); -static_assert(!hpx::is_relocatable_v); -static_assert(!hpx::is_relocatable_v); -static_assert(!hpx::is_relocatable_v); -static_assert(!hpx::is_relocatable_v); -static_assert(!hpx::is_relocatable_v); -static_assert(!hpx::is_relocatable_v); +static_assert(!is_relocatable_v); +static_assert(!is_relocatable_v); +static_assert(!is_relocatable_v); +static_assert(!is_relocatable_v); +static_assert(!is_relocatable_v); +static_assert(!is_relocatable_v); +static_assert(!is_relocatable_v); /* Tests for is_relocatable_from @@ -77,35 +80,35 @@ static_assert(!hpx::is_relocatable_v); // clang-format off // Reference types are not relocatable -static_assert(!hpx::is_relocatable_from_v< +static_assert(!is_relocatable_from_v< int (&)[], int (&)[4]>); // Array types are not move constructible -static_assert(!hpx::is_relocatable_from_v< +static_assert(!is_relocatable_from_v< int[4], int[4]>); // This is a simple pointer -static_assert(hpx::is_relocatable_from_v< +static_assert(is_relocatable_from_v< int (*)[4], int (*)[4]>); // Can move from const shared_ptr -static_assert(hpx::is_relocatable_from_v< +static_assert(is_relocatable_from_v< std::shared_ptr, const std::shared_ptr>); // Can't move away from a const unique_ptr -static_assert(!hpx::is_relocatable_from_v< +static_assert(!is_relocatable_from_v< std::unique_ptr, const std::unique_ptr>); // Can move away from a non-const unique_ptr -static_assert(hpx::is_relocatable_from_v< +static_assert(is_relocatable_from_v< std::unique_ptr, std::unique_ptr>); // Can move away from a non-const unique_ptr, the dest's constness does not matter -static_assert(hpx::is_relocatable_from_v< +static_assert(is_relocatable_from_v< const std::unique_ptr, std::unique_ptr>); // Can't move away from a const unique_ptr, the dest's constness does not matter -static_assert(!hpx::is_relocatable_from_v< +static_assert(!is_relocatable_from_v< const std::unique_ptr, const std::unique_ptr>); // clang-format on diff --git a/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp b/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp index c20bb642b208..210d9cf8f4a8 100644 --- a/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp +++ b/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp @@ -9,6 +9,8 @@ #include +using hpx::experimental::is_trivially_relocatable_v; + // Trivially copyable types are trivially relocatable struct empty { @@ -47,14 +49,12 @@ struct non_assignable_but_trivially_copyable ~non_assignable_but_trivially_copyable() = default; }; -static_assert(hpx::is_trivially_relocatable_v); -static_assert(hpx::is_trivially_relocatable_v); -static_assert( - hpx::is_trivially_relocatable_v); -static_assert( - hpx::is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v); static_assert( - hpx::is_trivially_relocatable_v); + is_trivially_relocatable_v); // Non trivially copyable types should not be considered trivially // relocatable by default @@ -88,9 +88,9 @@ struct not_trivially_copyable_3 ~not_trivially_copyable_3(); }; -static_assert(!hpx::is_trivially_relocatable_v); -static_assert(!hpx::is_trivially_relocatable_v); -static_assert(!hpx::is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); // The HPX_DECLARE_TRIVIALLY_RELOCATABLE macro declares types as trivially // relocatable @@ -120,46 +120,43 @@ HPX_DECLARE_TRIVIALLY_RELOCATABLE(explicitly_trivially_relocatable_1); HPX_DECLARE_TRIVIALLY_RELOCATABLE(explicitly_trivially_relocatable_2); // Explicitly declared trivially relocatable types are trivially relocatable -static_assert( - hpx::is_trivially_relocatable_v); -static_assert( - hpx::is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v); // c-v-ref-array qualified versions of explicitly declared trivially relocatable // types are trivially relocatable static_assert( - hpx::is_trivially_relocatable_v); -static_assert(hpx::is_trivially_relocatable_v< - explicitly_trivially_relocatable_1 volatile>); -static_assert(hpx::is_trivially_relocatable_v< - explicitly_trivially_relocatable_1 const volatile>); + is_trivially_relocatable_v); static_assert( - hpx::is_trivially_relocatable_v); + is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 const volatile>); +static_assert(is_trivially_relocatable_v); static_assert( - hpx::is_trivially_relocatable_v); + is_trivially_relocatable_v); // Chain of c-v-array qualifiers are supported -static_assert(hpx::is_trivially_relocatable_v< - explicitly_trivially_relocatable_1[10][10]>); -static_assert(hpx::is_trivially_relocatable_v< - explicitly_trivially_relocatable_1 const[10]>); -static_assert(hpx::is_trivially_relocatable_v< +static_assert( + is_trivially_relocatable_v); +static_assert( + is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v< explicitly_trivially_relocatable_1 volatile[10]>); -static_assert(hpx::is_trivially_relocatable_v< +static_assert(is_trivially_relocatable_v< explicitly_trivially_relocatable_1 const volatile[10]>); // References and temporaries are not trivially relocatable // clang-format off -static_assert(!hpx::is_trivially_relocatable_v< +static_assert(!is_trivially_relocatable_v< explicitly_trivially_relocatable_1&>); -static_assert(!hpx::is_trivially_relocatable_v< +static_assert(!is_trivially_relocatable_v< explicitly_trivially_relocatable_1&&>); -static_assert(!hpx::is_trivially_relocatable_v< +static_assert(!is_trivially_relocatable_v< explicitly_trivially_relocatable_1 (&)[10]>); -static_assert(!hpx::is_trivially_relocatable_v< +static_assert(!is_trivially_relocatable_v< explicitly_trivially_relocatable_1 (&&)[10]>); -static_assert(!hpx::is_trivially_relocatable_v< +static_assert(!is_trivially_relocatable_v< explicitly_trivially_relocatable_1 const volatile&>); // clang-format on @@ -169,15 +166,15 @@ struct derived_from_explicitly_trivially_relocatable { }; -static_assert(!hpx::is_trivially_relocatable_v< - derived_from_explicitly_trivially_relocatable>); +static_assert( + !is_trivially_relocatable_v); // Polymorphic types are not trivially relocatable struct polymorphic { virtual int f(); }; -static_assert(!hpx::is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); // Test that it is not breaking to declare an already known // trivially copyable type to be trivially relocatable @@ -195,7 +192,7 @@ HPX_DECLARE_TRIVIALLY_RELOCATABLE( static_assert(std::is_trivially_copyable_v< trivially_copyable_explicitly_trivially_relocatable>); -static_assert(hpx::is_trivially_relocatable_v< +static_assert(is_trivially_relocatable_v< trivially_copyable_explicitly_trivially_relocatable>); // Testing the HPX_DECLARE_TRIVIALLY_RELOCATABLE_TEMPLATE macro @@ -210,21 +207,19 @@ struct non_trivially_copyable static_assert(!std::is_trivially_copyable_v>); HPX_DECLARE_TRIVIALLY_RELOCATABLE_TEMPLATE(non_trivially_copyable); -static_assert( - hpx::is_trivially_relocatable_v>); +static_assert(is_trivially_relocatable_v>); // Testing the HPX_DECLARE_TRIVIALLY_RELOCATABLE_TEMPLATE_IF macro struct trivially_relocatable_struct { }; -static_assert(hpx::is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v); struct non_trivially_relocatable_struct { non_trivially_relocatable_struct(non_trivially_relocatable_struct const&); }; -static_assert( - !hpx::is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); template struct non_trivially_copyable_container @@ -243,45 +238,45 @@ struct non_trivially_copyable_container template struct my_metafunction - : std::bool_constant && - hpx::is_trivially_relocatable_v> + : std::bool_constant && + is_trivially_relocatable_v> { }; HPX_DECLARE_TRIVIALLY_RELOCATABLE_TEMPLATE_IF( non_trivially_copyable_container, my_metafunction) -static_assert(hpx::is_trivially_relocatable_v>); static_assert( - !hpx::is_trivially_relocatable_v>); static_assert( - !hpx::is_trivially_relocatable_v>); static_assert( - !hpx::is_trivially_relocatable_v>); // Primitive data types are trivially relocatable -static_assert(hpx::is_trivially_relocatable_v, - "int should be Trivially Relocatable"); -static_assert(hpx::is_trivially_relocatable_v, +static_assert( + is_trivially_relocatable_v, "int should be Trivially Relocatable"); +static_assert(is_trivially_relocatable_v, "double should be Trivially Relocatable"); -static_assert(hpx::is_trivially_relocatable_v, - "char should be Trivially Relocatable"); -static_assert(hpx::is_trivially_relocatable_v, - "void* should be Trivially Relocatable"); -static_assert(hpx::is_trivially_relocatable_v, - "int* should be Trivially Relocatable"); -static_assert(hpx::is_trivially_relocatable_v, +static_assert( + is_trivially_relocatable_v, "char should be Trivially Relocatable"); +static_assert( + is_trivially_relocatable_v, "void* should be Trivially Relocatable"); +static_assert( + is_trivially_relocatable_v, "int* should be Trivially Relocatable"); +static_assert(is_trivially_relocatable_v, "double* should be Trivially Relocatable"); -static_assert(hpx::is_trivially_relocatable_v, - "char* should be Trivially Relocatable"); +static_assert( + is_trivially_relocatable_v, "char* should be Trivially Relocatable"); // Void and function types are not trivially relocatable -static_assert(!hpx::is_trivially_relocatable_v); -static_assert(!hpx::is_trivially_relocatable_v); -static_assert(!hpx::is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); int main(int, char*[]) {} diff --git a/libs/core/type_support/tests/unit/relocate_at.cpp b/libs/core/type_support/tests/unit/relocate_at.cpp index 7e99db32ec5f..ca9b90c00a4b 100644 --- a/libs/core/type_support/tests/unit/relocate_at.cpp +++ b/libs/core/type_support/tests/unit/relocate_at.cpp @@ -9,6 +9,9 @@ #include #include +using hpx::experimental::is_trivially_relocatable_v; +using hpx::experimental::relocate_at; + struct non_trivially_relocatable_struct { static int count; @@ -34,8 +37,7 @@ struct non_trivially_relocatable_struct }; int non_trivially_relocatable_struct::count = 0; -static_assert( - !hpx::is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); struct trivially_relocatable_struct { @@ -62,7 +64,7 @@ struct trivially_relocatable_struct int trivially_relocatable_struct::count = 0; HPX_DECLARE_TRIVIALLY_RELOCATABLE(trivially_relocatable_struct); -static_assert(hpx::is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v); int hpx_main() { @@ -83,7 +85,7 @@ int hpx_main() // a single object was constructed HPX_TEST(non_trivially_relocatable_struct::count == 1); - hpx::experimental::relocate_at(ptr1, ptr2); + relocate_at(ptr1, ptr2); // count = 1 + 1 (from the move construction) - 1 (from the destruction) HPX_TEST(non_trivially_relocatable_struct::count == 1); @@ -111,7 +113,7 @@ int hpx_main() // a single object was constructed HPX_TEST(trivially_relocatable_struct::count == 1); - hpx::experimental::relocate_at(ptr1, ptr2); + relocate_at(ptr1, ptr2); // count = 1 + 0 (relocation on trivially relocatable // objects does not trigger move constructors diff --git a/libs/core/type_support/tests/unit/uninitialized_relocate.cpp b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp index c2d55b671b67..c023d6658acb 100644 --- a/libs/core/type_support/tests/unit/uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp @@ -11,6 +11,9 @@ #define N 50 #define K 10 +using hpx::experimental::is_trivially_relocatable_v; +using hpx::experimental::uninitialized_relocate; + struct trivially_relocatable_struct { static int count; @@ -43,7 +46,7 @@ int trivially_relocatable_struct::move_count = 0; int trivially_relocatable_struct::dtor_count = 0; HPX_DECLARE_TRIVIALLY_RELOCATABLE(trivially_relocatable_struct); -static_assert(hpx::is_trivially_relocatable_v); +static_assert(is_trivially_relocatable_v); struct non_trivially_relocatable_struct { @@ -78,8 +81,7 @@ int non_trivially_relocatable_struct::count = 0; int non_trivially_relocatable_struct::move_count = 0; int non_trivially_relocatable_struct::dtor_count = 0; -static_assert( - !hpx::is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); struct non_trivially_relocatable_struct_throwing { @@ -121,8 +123,8 @@ int non_trivially_relocatable_struct_throwing::count = 0; int non_trivially_relocatable_struct_throwing::move_count = 0; int non_trivially_relocatable_struct_throwing::dtor_count = 0; -static_assert(!hpx::is_trivially_relocatable_v< - non_trivially_relocatable_struct_throwing>); +static_assert( + !is_trivially_relocatable_v); int hpx_main() { @@ -150,7 +152,7 @@ int hpx_main() HPX_TEST(trivially_relocatable_struct::count == N); // relocate them to ptr2 - hpx::experimental::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + uninitialized_relocate(ptr1, ptr1 + N, ptr2); // All creations - destructions balance out HPX_TEST(trivially_relocatable_struct::count == N); @@ -194,7 +196,7 @@ int hpx_main() HPX_TEST(non_trivially_relocatable_struct::count == N); // relocate them to ptr2 - hpx::experimental::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + uninitialized_relocate(ptr1, ptr1 + N, ptr2); // All creations - destructions balance out HPX_TEST(non_trivially_relocatable_struct::count == N); @@ -241,7 +243,7 @@ int hpx_main() // relocate them to ptr2 try { - hpx::experimental::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + uninitialized_relocate(ptr1, ptr1 + N, ptr2); HPX_TEST(false); // should never reach this } catch (int forty_two) From 056fa742b2dfd692b03b46504410d8242f83f32c Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 8 Sep 2023 14:37:52 +0300 Subject: [PATCH 02/11] type_support::uninitialized_relocate -> primitive --- libs/core/type_support/CMakeLists.txt | 2 +- .../type_support/uninitialized_relocate.hpp | 176 ---------------- .../uninitialized_relocate_n_primitive.hpp | 196 ++++++++++++++++++ .../type_support/tests/unit/CMakeLists.txt | 2 +- ...=> uninitialized_relocate_n_primitive.cpp} | 12 +- 5 files changed, 204 insertions(+), 184 deletions(-) delete mode 100644 libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp create mode 100644 libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp rename libs/core/type_support/tests/unit/{uninitialized_relocate.cpp => uninitialized_relocate_n_primitive.cpp} (95%) diff --git a/libs/core/type_support/CMakeLists.txt b/libs/core/type_support/CMakeLists.txt index 1e931219e277..5bf549a16256 100644 --- a/libs/core/type_support/CMakeLists.txt +++ b/libs/core/type_support/CMakeLists.txt @@ -24,7 +24,7 @@ set(type_support_headers hpx/type_support/meta.hpp hpx/type_support/relocate_at.hpp hpx/type_support/static.hpp - hpx/type_support/uninitialized_relocate.hpp + hpx/type_support/uninitialized_relocate_n_primitive.hpp hpx/type_support/unwrap_ref.hpp hpx/type_support/unused.hpp hpx/type_support/void_guard.hpp diff --git a/libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp deleted file mode 100644 index 9e8941d300e7..000000000000 --- a/libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) 2023 Isidoros Tsaousis-Seiras -// -// 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 - -#if defined(HPX_HAVE_P1144_STD_RELOCATE_AT) -#include -#endif - -namespace hpx::experimental { - -#if defined(HPX_HAVE_P1144_STD_RELOCATE_AT) - using std::uninitialized_relocate; -#else - - namespace detail { - - enum struct relocate_strategy - { - buffer_memcpy = 0, - for_loop_nothrow, - for_loop_try_catch - }; - - template - struct choose_uninitialized_relocate_helper - { - using in_type = typename std::iterator_traits::value_type; - using out_type = typename std::iterator_traits::value_type; - - constexpr static bool valid_relocation = - is_relocatable_from_v; - - constexpr static bool is_buffer_memcpyable = - is_trivially_relocatable_v && - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The important check - std::is_same_v, - std::remove_cv_t> && - // can only relocate between same types - !std::is_volatile_v && !std::is_volatile_v && - // volatile types are not memcpyable - std::is_pointer_v && std::is_pointer_v; - // ^^ the best we can do to check for contiguous iterators - - constexpr static bool can_move_construct_nothrow = - std::is_nothrow_constructible_v>; - // Checks if the move constructor is noexcept to skip - // the try-catch block - - // Using an enum to distinguish implementations - constexpr static relocate_strategy value = is_buffer_memcpyable ? - relocate_strategy::buffer_memcpy : - can_move_construct_nothrow ? - relocate_strategy::for_loop_nothrow : - relocate_strategy::for_loop_try_catch; - }; - - template ::value == - relocate_strategy::buffer_memcpy, - int> = 0> - FwdIter uninitialized_relocate_helper( - InIter first, InIter last, FwdIter dst) noexcept - { - auto n_objects = std::distance(first, last); - - if (n_objects != 0) - { - std::byte const* first_byte = - reinterpret_cast(std::addressof(*first)); - std::byte const* last_byte = - reinterpret_cast(std::addressof(*last)); - - std::byte* dst_byte = const_cast( - reinterpret_cast(std::addressof(*dst))); - - auto n_bytes = std::distance(first_byte, last_byte); - - // Ideally we would want to convey to the compiler - // That the new buffer actually contains objects - // within their lifetime. But this is not possible - // with current language features. - std::memmove(dst_byte, first_byte, n_bytes); - - dst += n_objects; - } - - return dst; - } - - template ::value == - relocate_strategy::for_loop_nothrow, - // Either the buffer is not contiguous or the types are no-throw - // move constructible but not trivially relocatable - int> = 0> - FwdIter uninitialized_relocate_helper( - InIter first, InIter last, FwdIter dst) noexcept - { - for (; first != last; ++first, ++dst) - { - // if the type is trivially relocatable this will be a memcpy - // otherwise it will be a move + destroy - relocate_at_helper( - std::addressof(*first), std::addressof(*dst)); - } - - return dst; - } - - template ::value == - relocate_strategy::for_loop_try_catch, - int> = 0> - FwdIter uninitialized_relocate_helper( - InIter first, InIter last, FwdIter dst) - { - FwdIter original_dst = dst; - - for (; first != last; ++first, ++dst) - { - try - { - // the move + destroy version will be used - relocate_at_helper( - std::addressof(*first), std::addressof(*dst)); - } - catch (...) - { - // destroy all objects other that the one - // that caused the exception - // (relocate_at already destroyed that one) - - // destroy all objects constructed so far - std::destroy(original_dst, dst); - // destroy all the objects not relocated yet - std::destroy(++first, last); - - throw; - } - } - - return dst; - } - - } // namespace detail - - template - FwdIter - uninitialized_relocate(InIter first, InIter last, FwdIter dst) noexcept( - detail::choose_uninitialized_relocate_helper::value != - detail::relocate_strategy::for_loop_try_catch) - { - static_assert(detail::choose_uninitialized_relocate_helper::valid_relocation, - "uninitialized_move(first, last, dst) must be well-formed"); - return detail::uninitialized_relocate_helper(first, last, dst); - } - -#endif // defined(HPX_HAVE_P1144_STD_RELOCATE_AT) - -} // namespace hpx::experimental diff --git a/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp new file mode 100644 index 000000000000..9285780977eb --- /dev/null +++ b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp @@ -0,0 +1,196 @@ +// Copyright (c) 2023 Isidoros Tsaousis-Seiras +// +// 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 + +#if defined(HPX_HAVE_P1144_STD_RELOCATE_AT) +#include +#endif + +namespace hpx::experimental::util { + +#if defined(HPX_HAVE_P1144_STD_RELOCATE_AT) + using std::uninitialized_relocate; +#else + + namespace detail { + struct buffer_memcpy_tag + { + }; + + struct for_loop_nothrow_tag + { + }; + + struct for_loop_try_catch_tag + { + }; + + template + struct relocation_traits + { + using in_type = typename std::iterator_traits::value_type; + using out_type = typename std::iterator_traits::value_type; + + constexpr static bool valid_relocation = + is_relocatable_from_v; + // ^^^^ Cannot relocate between unrelated types + + constexpr static bool is_memcpyable = + is_trivially_relocatable_v && + // ^^^^ The important check + !std::is_volatile_v && !std::is_volatile_v; + // ^^^^ Cannot memcpy volatile types + + constexpr static bool is_buffer_memcpyable = + is_memcpyable && iterators_are_contiguous_v; + + constexpr static bool can_move_construct_nothrow = + std::is_nothrow_constructible_v; + // This is to skip the try-catch block + // type_dst is treated as an rvalue by is_nothrow_constructible + + constexpr static bool is_noexcept_relocatable_v = + can_move_construct_nothrow || is_memcpyable; + // If memcpy is not possible, we need to check if the move + // constructor is noexcept + + // Using a tag to distinguish implementations + // clang-format off + using implementation_tag = std::conditional_t< + is_buffer_memcpyable, + buffer_memcpy_tag, + std::conditional_t + >; + // clang-format on + }; + + template + FwdIter uninitialized_relocate_n_primitive_helper( + InIter first, Size n, FwdIter dst, buffer_memcpy_tag) noexcept + { + if (n != 0) + { + std::byte const* first_byte = + reinterpret_cast(std::addressof(*first)); + + std::byte* dst_byte = const_cast( + reinterpret_cast(std::addressof(*dst))); + + Size n_bytes = n * sizeof(*first); + + // Ideally we would want to convey to the compiler + // That the new buffer actually contains objects + // within their lifetime. But this is not possible + // with current language features. + std::memmove(dst_byte, first_byte, n_bytes); + + dst += n; + } + + return dst; + } + + template + // Either the buffer is not contiguous or the types are no-throw + // move constructible but not trivially relocatable + FwdIter uninitialized_relocate_n_primitive_helper( + InIter first, Size n, FwdIter dst, for_loop_nothrow_tag) noexcept + { + for (Size i = 0; i < n; ++first, ++dst, ++i) + { + // if the type is trivially relocatable this will be a memcpy + // otherwise it will be a move + destroy + hpx::experimental::detail::relocate_at_helper( + std::addressof(*first), std::addressof(*dst)); + } + + return dst; + } + + template + FwdIter uninitialized_relocate_n_primitive_helper( + InIter first, Size n, FwdIter dst, for_loop_try_catch_tag) + { + FwdIter original_dst = dst; + + for (Size i = 0; i < n; ++first, ++dst, ++i) + { + try + { + // the move + destroy version will be used + hpx::experimental::detail::relocate_at_helper( + std::addressof(*first), std::addressof(*dst)); + } + catch (...) + { + // destroy all objects other that the one + // that caused the exception + // (relocate_at already destroyed that one) + + // destroy all objects constructed so far + std::destroy(original_dst, dst); + // destroy all the objects not relocated yet + std::destroy_n(++first, n - i - 1); + // Note: + // using destroy_n instead of destroy + advance + // to avoid calculating the distance + + throw; + } + } + + return dst; + } + + } // namespace detail + + template + // clang-format off + FwdIter uninitialized_relocate_n_primitive(InIter first, Size n, + FwdIter dst, iterators_are_contiguous_t) noexcept( + detail::relocation_traits::is_noexcept_relocatable_v) + { + static_assert( + detail::relocation_traits::valid_relocation, + "uninitialized_move(first, last, dst) must be well-formed"); + + using implementation_tag = typename detail::relocation_traits::implementation_tag; + + return detail::uninitialized_relocate_n_primitive_helper( + first, n, dst, implementation_tag{}); + } + // clang-format on + + template + FwdIter uninitialized_relocate_n_primitive(InIter first, Size n, + FwdIter dst) noexcept(detail::relocation_traits::is_noexcept_relocatable_v) + { + using iterators_are_contiguous_default_t = + std::bool_constant && + std::is_pointer_v>; + + return uninitialized_relocate_n_primitive( + first, n, dst, iterators_are_contiguous_default_t{}); + } + +#endif // defined(HPX_HAVE_P1144_STD_RELOCATE_AT) + +} // namespace hpx::experimental::util diff --git a/libs/core/type_support/tests/unit/CMakeLists.txt b/libs/core/type_support/tests/unit/CMakeLists.txt index 83886f941586..ae53059b2564 100644 --- a/libs/core/type_support/tests/unit/CMakeLists.txt +++ b/libs/core/type_support/tests/unit/CMakeLists.txt @@ -4,7 +4,7 @@ # 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 relocate relocate_at uninitialized_relocate) +set(tests relocate relocate_at uninitialized_relocate_n_primitive) if(HPX_WITH_CXX20_COROUTINES) set(tests ${tests} generator) diff --git a/libs/core/type_support/tests/unit/uninitialized_relocate.cpp b/libs/core/type_support/tests/unit/uninitialized_relocate_n_primitive.cpp similarity index 95% rename from libs/core/type_support/tests/unit/uninitialized_relocate.cpp rename to libs/core/type_support/tests/unit/uninitialized_relocate_n_primitive.cpp index c023d6658acb..451156245cf7 100644 --- a/libs/core/type_support/tests/unit/uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/uninitialized_relocate_n_primitive.cpp @@ -6,13 +6,13 @@ #include #include -#include +#include #define N 50 #define K 10 using hpx::experimental::is_trivially_relocatable_v; -using hpx::experimental::uninitialized_relocate; +using hpx::experimental::uninitialized_relocate_n_primitive; struct trivially_relocatable_struct { @@ -152,7 +152,7 @@ int hpx_main() HPX_TEST(trivially_relocatable_struct::count == N); // relocate them to ptr2 - uninitialized_relocate(ptr1, ptr1 + N, ptr2); + uninitialized_relocate_n_primitive(ptr1, ptr1 + N, ptr2); // All creations - destructions balance out HPX_TEST(trivially_relocatable_struct::count == N); @@ -196,7 +196,7 @@ int hpx_main() HPX_TEST(non_trivially_relocatable_struct::count == N); // relocate them to ptr2 - uninitialized_relocate(ptr1, ptr1 + N, ptr2); + uninitialized_relocate_n_primitive(ptr1, ptr1 + N, ptr2); // All creations - destructions balance out HPX_TEST(non_trivially_relocatable_struct::count == N); @@ -243,8 +243,8 @@ int hpx_main() // relocate them to ptr2 try { - uninitialized_relocate(ptr1, ptr1 + N, ptr2); - HPX_TEST(false); // should never reach this + uninitialized_relocate_n_primitive(ptr1, ptr1 + N, ptr2); + HPX_UNREACHABLE; // should error out } catch (int forty_two) { From 3a685e8fc8fbb16753680dcf9752f3657d61ea86 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 8 Sep 2023 14:39:12 +0300 Subject: [PATCH 03/11] hpx seq-par uninialized_relocate(_n) algorithm --- libs/core/algorithms/CMakeLists.txt | 1 + .../algorithms/uninitialized_relocate.hpp | 651 ++++++++++++++++++ 2 files changed, 652 insertions(+) create mode 100644 libs/core/algorithms/include/hpx/parallel/algorithms/uninitialized_relocate.hpp diff --git a/libs/core/algorithms/CMakeLists.txt b/libs/core/algorithms/CMakeLists.txt index 0c0650078d4b..6d10912d4d63 100644 --- a/libs/core/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/CMakeLists.txt @@ -97,6 +97,7 @@ set(algorithms_headers hpx/parallel/algorithms/uninitialized_default_construct.hpp hpx/parallel/algorithms/uninitialized_fill.hpp hpx/parallel/algorithms/uninitialized_move.hpp + hpx/parallel/algorithms/uninitialized_relocate.hpp hpx/parallel/algorithms/uninitialized_value_construct.hpp hpx/parallel/algorithms/unique.hpp hpx/parallel/container_algorithms/adjacent_difference.hpp diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/uninitialized_relocate.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/uninitialized_relocate.hpp new file mode 100644 index 000000000000..8fe7f4c55076 --- /dev/null +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/uninitialized_relocate.hpp @@ -0,0 +1,651 @@ +// Copyright (c) 2014-2023 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/algorithms/uninitialized_relocate.hpp + +#pragma once + +#if defined(DOXYGEN) +namespace hpx { + + /// Relocates the elements in the range, defined by [first, last), to an + /// uninitialized memory area beginning at \a dest. If an exception is + /// thrown during the move-construction of an element, all elements left + /// in the input range, as well as all objects already constructed in the + /// destination range are destroyed. After this algorithm completes, the + /// source range should be freed or reused without destroying the objects. + /// + /// \note Complexity: time: O(n), space: O(1) + /// 1) For "trivially relocatable" underlying types (T) and + /// a contiguous iterator range [first, last): + /// std::distance(first, last)*sizeof(T) bytes are copied. + /// 2) For "trivially relocatable" underlying types (T) and + /// a non-contiguous iterator range [first, last): + /// std::distance(first, last) memory copies of sizeof(T) + /// bytes each are performed. + /// 3) For "non-trivially relocatable" underlying types (T): + /// std::distance(first, last) move assignments and + /// destructions are performed. + /// + /// \note Declare a type as "trivially relocatable" using the + /// `HPX_DECLARE_TRIVIALLY_RELOCATABLE` macros found in + /// . + /// + /// \tparam InIter1 The type of the source iterator first (deduced). + /// This iterator type must meet the requirements of an + /// input iterator. + /// \tparam InIter2 The type of the source iterator last (deduced). + /// This iterator type must meet the requirements of an + /// input iterator. + /// \tparam FwdIter The type of the iterator representing the + /// destination range (deduced). + /// This iterator type must meet the requirements of a + /// forward iterator. + /// + /// \param first Refers to the beginning of the sequence of elements + /// the algorithm will be applied to. + /// \param last Refers to the end of the sequence of elements the + /// algorithm will be applied to. + /// \param dest Refers to the beginning of the destination range. + /// + /// The assignments in the parallel \a uninitialized_relocate algorithm invoked + /// without an execution policy object will execute in sequential order in + /// the calling thread. + /// + /// \returns The \a uninitialized_relocate algorithm returns \a FwdIter. + /// The \a uninitialized_relocate algorithm returns the output + /// iterator to the element in the destination range, one past + /// the last element relocated. + /// + template + FwdIter uninitialized_relocate(InIter1 first, InIter2 last, FwdIter dest); + + /// Relocates the elements in the range defined by [first, first + count), to an + /// uninitialized memory area beginning at \a dest. If an exception is + /// thrown during the move-construction of an element, all elements left + /// in the input range, as well as all objects already constructed in the + /// destination range are destroyed. After this algorithm completes, the + /// source range should be freed or reused without destroying the objects. + /// + /// \note Complexity: time: O(n), space: O(1) + /// 1) For "trivially relocatable" underlying types (T) and + /// a contiguous iterator range [first, last): + /// std::distance(first, last)*sizeof(T) bytes are copied. + /// 2) For "trivially relocatable" underlying types (T) and + /// a non-contiguous iterator range [first, last): + /// std::distance(first, last) memory copies of sizeof(T) + /// bytes each are performed. + /// 3) For "non-trivially relocatable" underlying types (T): + /// std::distance(first, last) move assignments and + /// destructions are performed. + /// + /// \note Declare a type as "trivially relocatable" using the + /// `HPX_DECLARE_TRIVIALLY_RELOCATABLE` macros found in + /// . + /// + /// \tparam ExPolicy The type of the execution policy to use (deduced). + /// It describes the manner in which the execution + /// of the algorithm may be parallelized and the manner + /// in which it executes the assignments. + /// \tparam InIter1 The type of the source iterator first (deduced). + /// This iterator type must meet the requirements of an + /// input iterator. + /// \tparam InIter2 The type of the source iterator last (deduced). + /// This iterator type must meet the requirements of an + /// input iterator. + /// \tparam FwdIter The type of the iterator representing the + /// destination range (deduced). + /// This iterator type must meet the requirements of a + /// forward iterator. + /// + /// \param policy The execution policy to use for the scheduling of + /// the iterations. + /// \param first Refers to the beginning of the sequence of elements + /// the algorithm will be applied to. + /// \param last Refers to the end of the sequence of elements the + /// algorithm will be applied to. + /// \param dest Refers to the beginning of the destination range. + /// + //// The assignments in the parallel \a uninitialized_relocate_n algorithm + /// invoked with an execution policy object of type + /// \a sequenced_policy execute in sequential order in the + /// calling thread. + /// + /// The assignments in the parallel \a uninitialized_relocate algorithm invoked + /// with an execution policy object of type \a parallel_policy or + /// \a parallel_task_policy are permitted to execute in an + /// unordered fashion in unspecified threads, and indeterminately sequenced + /// within each thread. + /// + /// \returns The \a uninitialized_relocate algorithm returns a + /// \a hpx::future, if the execution policy is of type + /// \a sequenced_task_policy or + /// \a parallel_task_policy and + /// returns \a FwdIter otherwise. + /// The \a uninitialized_relocate algorithm returns the output + /// iterator to the element in the destination range, one past + /// the last element relocated. + /// + template + hpx::parallel::util::detail::algorithm_result_t + uninitialized_relocate( + ExPolicy&& policy, InIter1 first, InIter2 last, FwdIter dest); + + /// Relocates the elements in the range, defined by [first, last), to an + /// uninitialized memory area beginning at \a dest. If an exception is + /// thrown during the move-construction of an element, all elements left + /// in the input range, as well as all objects already constructed in the + /// destination range are destroyed. After this algorithm completes, the + /// source range should be freed or reused without destroying the objects. + /// + /// \note Complexity: time: O(n), space: O(1) + /// 1) For "trivially relocatable" underlying types (T) and + /// a contiguous iterator range [first, first+count): + /// `count*sizeof(T)` bytes are copied. + /// 2) For "trivially relocatable" underlying types (T) and + /// a non-contiguous iterator range [first, first+count): + /// `count` memory copies of sizeof(T) bytes each are performed. + /// 3) For "non-trivially relocatable" underlying types (T): + /// `count` move assignments and destructions are performed. + /// + /// \note Declare a type as "trivially relocatable" using the + /// `HPX_DECLARE_TRIVIALLY_RELOCATABLE` macros found in + /// . + /// + /// \tparam InIter The type of the source iterator first (deduced). + /// This iterator type must meet the requirements of an + /// input iterator. + /// \tparam Size The type of the argument specifying the number of + /// elements to relocate. + /// \tparam FwdIter The type of the iterator representing the + /// destination range (deduced). + /// This iterator type must meet the requirements of a + /// forward iterator. + /// + /// \param first Refers to the beginning of the sequence of elements + /// the algorithm will be applied to. + /// \param count Refers to the number of elements starting at + /// \a first the algorithm will be applied to. + /// \param dest Refers to the beginning of the destination range. + /// + /// The assignments in the parallel \a uninitialized_relocate_n algorithm + /// invoked without an execution policy object will execute in sequential + /// order in the calling thread. + /// + /// \returns The \a uninitialized_relocate_n algorithm returns \a FwdIter. + /// The \a uninitialized_relocate_n algorithm returns the output + /// iterator to the element in the destination range, one past + /// the last element relocated. + /// + template + FwdIter uninitialized_relocate_n(InIter first, Size count, FwdIter dest); + + /// Relocates the elements in the range, defined by [first, last), to an + /// uninitialized memory area beginning at \a dest. If an exception is + /// thrown during the move-construction of an element, all elements left + /// in the input range, as well as all objects already constructed in the + /// destination range are destroyed. After this algorithm completes, the + /// source range should be freed or reused without destroying the objects. + /// + /// \note Complexity: time: O(n), space: O(1) + /// 1) For "trivially relocatable" underlying types (T) and + /// a contiguous iterator range [first, first+count): + /// `count*sizeof(T)` bytes are copied. + /// 2) For "trivially relocatable" underlying types (T) and + /// a non-contiguous iterator range [first, first+count): + /// `count` memory copies of sizeof(T) bytes each are performed. + /// 3) For "non-trivially relocatable" underlying types (T): + /// `count` move assignments and destructions are performed. + /// + /// \note Declare a type as "trivially relocatable" using the + /// `HPX_DECLARE_TRIVIALLY_RELOCATABLE` macros found in + /// . + /// + /// \tparam ExPolicy The type of the execution policy to use (deduced). + /// It describes the manner in which the execution + /// of the algorithm may be parallelized and the manner + /// in which it executes the assignments. + /// \tparam InIter The type of the source iterator first (deduced). + /// This iterator type must meet the requirements of an + /// input iterator. + /// \tparam Size The type of the argument specifying the number of + /// elements to relocate. + /// \tparam FwdIter The type of the iterator representing the + /// destination range (deduced). + /// This iterator type must meet the requirements of a + /// forward iterator. + /// + /// \param policy The execution policy to use for the scheduling of + /// the iterations. + /// \param first Refers to the beginning of the sequence of elements + /// the algorithm will be applied to. + /// \param count Refers to the number of elements starting at + /// \a first the algorithm will be applied to. + /// \param dest Refers to the beginning of the destination range. + /// + /// The assignments in the parallel \a uninitialized_relocate_n algorithm + /// invoked with an execution policy object of type + /// \a sequenced_policy execute in sequential order in the + /// calling thread. + /// + /// The assignments in the parallel \a uninitialized_relocate_n algorithm + /// invoked with an execution policy object of type + /// \a parallel_policy or + /// \a parallel_task_policy are permitted to execute in an + /// unordered fashion in unspecified threads, and indeterminately sequenced + /// within each thread. + /// + /// \returns The \a uninitialized_relocate_n algorithm returns a + /// \a hpx::future if the execution policy is of type + /// \a sequenced_task_policy or + /// \a parallel_task_policy and + /// returns \a FwdIter otherwise. + /// The \a uninitialized_relocate_n algorithm returns the output + /// iterator to the element in the destination range, one past + /// the last element relocated. + /// + template + hpx::parallel::util::detail::algorithm_result_t + uninitialized_relocate_n( + ExPolicy&& policy, InIter first, Size count, FwdIter dest); +} // namespace hpx + +#else // DOXYGEN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace hpx::parallel { + + /////////////////////////////////////////////////////////////////////////// + // uninitialized_relocate + namespace detail { + /// \cond NOINTERNAL + + /////////////////////////////////////////////////////////////////////// + + // clang-format off + template && + hpx::traits::is_forward_iterator_v && + std::is_integral_v + )> + // clang-format on + typename util::detail::algorithm_result>::type + parallel_uninitialized_relocate_n( + ExPolicy&& policy, InIter first, Size count, FwdIter dest) + { + if (count == 0) + { + return util::detail::algorithm_result>:: + get(util::in_out_result{first, dest}); + } + + using zip_iterator = hpx::util::zip_iterator; + using partition_result_type = std::pair; + + return util::partitioner_with_cleanup, partition_result_type>:: + call( + HPX_FORWARD(ExPolicy, policy), zip_iterator(first, dest), + count, + [policy](zip_iterator t, std::size_t part_size) mutable + -> partition_result_type { + using hpx::get; + + auto iters = t.get_iterator_tuple(); + + InIter part_source = get<0>(iters); + FwdIter part_dest = get<1>(iters); + + // returns (dest begin, dest end) + return std::make_pair(part_dest, + hpx::experimental::util:: + uninitialized_relocate_n_primitive( + part_source, part_size, part_dest)); + }, + // finalize, called once if no error occurred + [first, dest, count](auto&& data) mutable + -> util::in_out_result { + // make sure iterators embedded in function object that is + // attached to futures are invalidated + util::detail::clear_container(data); + + std::advance(first, count); + std::advance(dest, count); + return util::in_out_result{ + first, dest}; + }, + // cleanup function, called for each partition which + // didn't fail, but only if at least one failed + [](partition_result_type&& r) -> void { + std::destroy(r.first, r.second); + }); + } + + ///////////////////////////////////////////////////////////////////////////// + // uninitialized_relocate_n + + /// \cond NOINTERNAL + template + struct uninitialized_relocate_n + : public algorithm, IterPair> + { + constexpr uninitialized_relocate_n() noexcept + : algorithm( + "uninitialized_relocate_n") + { + } + + // non vectorized overload + // clang-format off + template && + hpx::traits::is_input_iterator_v && + hpx::traits::is_forward_iterator_v + )> + // clang-format on + static util::in_out_result sequential( + ExPolicy&& policy, InIter first, std::size_t count, + FwdIter dest) noexcept(hpx::experimental::util::detail:: + relocation_traits::is_noexcept_relocatable_v) + { + return util::in_out_result{first, + hpx::experimental::util::uninitialized_relocate_n_primitive( + first, count, dest)}; + } + + // clang-format off + template && + hpx::traits::is_input_iterator_v && + hpx::traits::is_forward_iterator_v + )> + // clang-format on + static util::detail::algorithm_result_t> + parallel(ExPolicy&& policy, InIter first, std::size_t count, + FwdIter dest) noexcept(hpx::experimental::util::detail:: + relocation_traits::is_noexcept_relocatable_v) + { + return parallel_uninitialized_relocate_n( + HPX_FORWARD(ExPolicy, policy), first, count, dest); + } + }; + /// \endcond + + ///////////////////////////////////////////////////////////////////////////// + // uninitialized_relocate + /// \cond NOINTERNAL + template + struct uninitialized_relocate + : public algorithm, IterPair> + { + constexpr uninitialized_relocate() noexcept + : algorithm( + "uninitialized_relocate") + { + } + + // non vectorized overload + template && + hpx::traits::is_input_iterator_v&& + hpx::traits::is_input_iterator_v&& + hpx::traits::is_forward_iterator_v + )> + // clang-format on + static util::in_out_result sequential( + ExPolicy&& policy, InIter1 first, InIter2 last, + FwdIter dest) noexcept(hpx::experimental::util::detail::relocation_traits< + InIter1, FwdIter>::is_noexcept_relocatable_v) + { + auto count = std::distance(first, last); + + return util::in_out_result{first, + hpx::experimental::util::uninitialized_relocate_n_primitive( + first, count, dest)}; + } + + template && + hpx::traits::is_input_iterator_v&& + hpx::traits::is_input_iterator_v&& + hpx::traits::is_forward_iterator_v + )> + // clang-format on + static util::detail::algorithm_result_t> + parallel(ExPolicy&& policy, InIter1 first, InIter2 last, + FwdIter dest) noexcept(hpx::experimental::util::detail:: + relocation_traits::is_noexcept_relocatable_v) + { + auto count = std::distance(first, last); + + return parallel_uninitialized_relocate_n( + HPX_FORWARD(ExPolicy, policy), first, count, dest); + } + }; + /// \endcond + + } // namespace detail +} // namespace hpx::parallel + +namespace hpx::experimental { + + /////////////////////////////////////////////////////////////////////////// + // CPO for hpx::uninitialized_relocate_n + inline constexpr struct uninitialized_relocate_n_t final + : hpx::detail::tag_parallel_algorithm + { + // clang-format off + template && + hpx::traits::is_iterator_v && + std::is_integral_v + )> + // clang-format on + friend FwdIter tag_fallback_invoke( + hpx::experimental::uninitialized_relocate_n_t, InIter first, + Size count, + FwdIter dest) noexcept(util::detail::relocation_traits::is_noexcept_relocatable_v) + { + static_assert(hpx::traits::is_input_iterator_v, + "The 'first' argument must meet the requirements " + "of an input iterator."); + static_assert(hpx::traits::is_forward_iterator_v, + "The 'dest' argument must meet the requirements of a forward " + "iterator."); + static_assert(util::detail::relocation_traits::valid_relocation, + "Relocating from this source type to this destination type is " + "ill-formed"); + + // if count is representing a negative value, we do nothing + if (hpx::parallel::detail::is_negative(count)) + { + return dest; + } + + return parallel::util::get_second_element( + hpx::parallel::detail::uninitialized_relocate_n< + parallel::util::in_out_result>() + .call(hpx::execution::seq, first, + static_cast(count), dest)); + } + + // clang-format off + template && + hpx::traits::is_iterator_v && + hpx::traits::is_iterator_v && + std::is_integral_v + )> + // clang-format on + friend typename parallel::util::detail::algorithm_result::type + tag_fallback_invoke(hpx::experimental::uninitialized_relocate_n_t, + ExPolicy&& policy, InIter first, Size count, + FwdIter dest) noexcept(util::detail::relocation_traits::is_noexcept_relocatable_v) + { + static_assert(hpx::traits::is_input_iterator_v, + "The 'first' argument must meet the requirements " + "of an input iterator."); + static_assert(hpx::traits::is_forward_iterator_v, + "The 'dest' argument must meet the requirements of a forward " + "iterator."); + static_assert(util::detail::relocation_traits::valid_relocation, + "Relocating from this source type to this destination type is " + "ill-formed"); + + // if count is representing a negative value, we do nothing + if (hpx::parallel::detail::is_negative(count)) + { + return parallel::util::detail::algorithm_result::get(HPX_MOVE(dest)); + } + + return parallel::util::get_second_element( + hpx::parallel::detail::uninitialized_relocate_n< + parallel::util::in_out_result>() + .call(HPX_FORWARD(ExPolicy, policy), first, + static_cast(count), dest)); + } + } uninitialized_relocate_n{}; + + /////////////////////////////////////////////////////////////////////////// + // CPO for hpx::uninitialized_relocate + inline constexpr struct uninitialized_relocate_t final + : hpx::detail::tag_parallel_algorithm + { + // clang-format off + template && + hpx::traits::is_iterator_v && + hpx::traits::is_iterator_v + )> + // clang-format on + friend FwdIter tag_fallback_invoke(uninitialized_relocate_t, + InIter1 first, InIter2 last, + FwdIter dest) noexcept(util::detail::relocation_traits::is_noexcept_relocatable_v) + { + static_assert(hpx::traits::is_input_iterator_v && + hpx::traits::is_input_iterator_v, + "The 'first' and 'last' arguments must meet the requirements " + "of input iterators."); + static_assert(hpx::traits::is_forward_iterator_v, + "The 'dest' argument must meet the requirements of a forward " + "iterator."); + static_assert(util::detail::relocation_traits::valid_relocation, + "Relocating from this source type to this destination type is " + "ill-formed"); + // if count is representing a negative value, we do nothing + if (hpx::parallel::detail::is_negative(std::distance(first, last))) + { + return dest; + } + + return parallel::util::get_second_element( + hpx::parallel::detail::uninitialized_relocate< + parallel::util::in_out_result>() + .call(hpx::execution::seq, first, last, dest)); + } + + // clang-format off + template && + hpx::traits::is_iterator_v && + hpx::traits::is_iterator_v && + hpx::traits::is_iterator_v + )> + // clang-format on + friend typename hpx::parallel::util::detail::algorithm_result::type + tag_fallback_invoke(uninitialized_relocate_t, ExPolicy&& policy, + InIter1 first, InIter2 last, + FwdIter dest) noexcept(util::detail::relocation_traits::is_noexcept_relocatable_v) + { + static_assert(hpx::traits::is_input_iterator_v && + hpx::traits::is_input_iterator_v, + "The 'first' and 'last' arguments must meet the requirements " + "of input iterators."); + static_assert(hpx::traits::is_forward_iterator_v, + "The 'dest' argument must meet the requirements of a forward " + "iterator."); + static_assert(util::detail::relocation_traits::valid_relocation, + "Relocating from this source type to this destination type is " + "ill-formed"); + + auto count = std::distance(first, last); + + // if count is representing a negative value, we do nothing + if (hpx::parallel::detail::is_negative(count)) + { + return parallel::util::detail::algorithm_result::get(HPX_MOVE(dest)); + } + + return parallel::util::get_second_element( + hpx::parallel::detail::uninitialized_relocate_n< + parallel::util::in_out_result>() + .call(HPX_FORWARD(ExPolicy, policy), first, count, dest)); + } + } uninitialized_relocate{}; +} // namespace hpx::experimental +#endif // DOXYGEN From 77bc1b0ade5f9ea408436720ee5390947554e811 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 8 Sep 2023 14:39:47 +0300 Subject: [PATCH 04/11] tests for par-seq uninitialized_relocate(_n) --- .../tests/unit/algorithms/CMakeLists.txt | 3 + .../algorithms/uninitialized_relocate.cpp | 276 ++++++++++++++++ .../algorithms/uninitialized_relocate_par.cpp | 301 ++++++++++++++++++ .../algorithms/uninitialized_relocaten.cpp | 276 ++++++++++++++++ 4 files changed, 856 insertions(+) create mode 100644 libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate.cpp create mode 100644 libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate_par.cpp create mode 100644 libs/core/algorithms/tests/unit/algorithms/uninitialized_relocaten.cpp diff --git a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt index cbdca61a4bdd..252c0fcff0a8 100644 --- a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt @@ -146,6 +146,9 @@ set(tests uninitialized_filln uninitialized_move uninitialized_moven + uninitialized_relocate + uninitialized_relocaten + uninitialized_relocate_par uninitialized_value_construct uninitialized_value_constructn unique diff --git a/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate.cpp b/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate.cpp new file mode 100644 index 000000000000..69b7f26e07a0 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate.cpp @@ -0,0 +1,276 @@ +// Copyright (c) 2023 Isidoros Tsaousis-Seiras +// +// 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 + +#define N 50 +#define K 10 + +using hpx::experimental::is_trivially_relocatable_v; +using hpx::experimental::uninitialized_relocate; + +struct trivially_relocatable_struct +{ + static int count; + static int move_count; + static int dtor_count; + int data; + + explicit trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + trivially_relocatable_struct(trivially_relocatable_struct&& other) + : data(other.data) + { + move_count++; + count++; + } + ~trivially_relocatable_struct() + { + dtor_count++; + count--; + } + + // making sure the address is never directly accessed + friend void operator&(trivially_relocatable_struct) = delete; +}; +int trivially_relocatable_struct::count = 0; +int trivially_relocatable_struct::move_count = 0; +int trivially_relocatable_struct::dtor_count = 0; + +HPX_DECLARE_TRIVIALLY_RELOCATABLE(trivially_relocatable_struct); +static_assert(is_trivially_relocatable_v); + +struct non_trivially_relocatable_struct +{ + static int count; + static int move_count; + static int dtor_count; + int data; + + explicit non_trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + // mark as noexcept to enter simpler relocation path + non_trivially_relocatable_struct( + non_trivially_relocatable_struct&& other) noexcept + : data(other.data) + { + move_count++; + count++; + } + ~non_trivially_relocatable_struct() + { + dtor_count++; + count--; + } + + // making sure the address is never directly accessed + friend void operator&(non_trivially_relocatable_struct) = delete; +}; +int non_trivially_relocatable_struct::count = 0; +int non_trivially_relocatable_struct::move_count = 0; +int non_trivially_relocatable_struct::dtor_count = 0; + +static_assert(!is_trivially_relocatable_v); + +struct non_trivially_relocatable_struct_throwing +{ + static int count; + static int move_count; + static int dtor_count; + + int data; + + explicit non_trivially_relocatable_struct_throwing(int data) + : data(data) + { + count++; + } + // do not mark as noexcept to enter try-catch relocation path + non_trivially_relocatable_struct_throwing( + non_trivially_relocatable_struct_throwing&& other) + : data(other.data) + { + if (move_count == K) + { + throw 42; + } + move_count++; + + count++; + } + ~non_trivially_relocatable_struct_throwing() + { + dtor_count++; + count--; + } + + // making sure the address is never directly accessed + friend void operator&(non_trivially_relocatable_struct_throwing) = delete; +}; + +int non_trivially_relocatable_struct_throwing::count = 0; +int non_trivially_relocatable_struct_throwing::move_count = 0; +int non_trivially_relocatable_struct_throwing::dtor_count = 0; + +static_assert( + !is_trivially_relocatable_v); + +int hpx_main() +{ + { + void* mem1 = std::malloc(N * sizeof(trivially_relocatable_struct)); + void* mem2 = std::malloc(N * sizeof(trivially_relocatable_struct)); + + HPX_TEST(mem1 && mem2); + + trivially_relocatable_struct* ptr1 = + static_cast(mem1); + trivially_relocatable_struct* ptr2 = + static_cast(mem2); + + HPX_TEST(trivially_relocatable_struct::count == 0); + HPX_TEST(trivially_relocatable_struct::move_count == 0); + HPX_TEST(trivially_relocatable_struct::dtor_count == 0); + + for (int i = 0; i < N; i++) + { + hpx::construct_at(ptr1 + i, 1234); + } + + // N objects constructed + HPX_TEST(trivially_relocatable_struct::count == N); + + // relocate them to ptr2 + uninitialized_relocate(ptr1, ptr1 + N, ptr2); + + // All creations - destructions balance out + HPX_TEST(trivially_relocatable_struct::count == N); + + // No move constructor or destructor should be called + HPX_TEST(trivially_relocatable_struct::move_count == 0); + HPX_TEST(trivially_relocatable_struct::dtor_count == 0); + + for (int i = 0; i < N; i++) + { + HPX_TEST(ptr2[i].data == 1234); + } + + std::destroy(ptr2, ptr2 + N); + + std::free(mem1); + std::free(mem2); + } + { + void* mem1 = std::malloc(N * sizeof(non_trivially_relocatable_struct)); + void* mem2 = std::malloc(N * sizeof(non_trivially_relocatable_struct)); + + HPX_TEST(mem1 && mem2); + + non_trivially_relocatable_struct* ptr1 = + static_cast(mem1); + non_trivially_relocatable_struct* ptr2 = + static_cast(mem2); + + HPX_TEST(non_trivially_relocatable_struct::count == 0); + HPX_TEST(non_trivially_relocatable_struct::move_count == 0); + HPX_TEST(non_trivially_relocatable_struct::dtor_count == 0); + + for (int i = 0; i < N; i++) + { + hpx::construct_at(ptr1 + i, 1234); + } + + // N objects constructed + HPX_TEST(non_trivially_relocatable_struct::count == N); + + // relocate them to ptr2 + uninitialized_relocate(ptr1, ptr1 + N, ptr2); + + // All creations - destructions balance out + HPX_TEST(non_trivially_relocatable_struct::count == N); + + // Every object was moved from and then destroyed + HPX_TEST(non_trivially_relocatable_struct::move_count == N); + HPX_TEST(non_trivially_relocatable_struct::dtor_count == N); + + for (int i = 0; i < N; i++) + { + HPX_TEST(ptr2[i].data == 1234); + } + + std::destroy(ptr2, ptr2 + N); + + std::free(mem1); + std::free(mem2); + } + { + void* mem1 = + std::malloc(N * sizeof(non_trivially_relocatable_struct_throwing)); + void* mem2 = + std::malloc(N * sizeof(non_trivially_relocatable_struct_throwing)); + + HPX_TEST(mem1 && mem2); + + non_trivially_relocatable_struct_throwing* ptr1 = + static_cast(mem1); + non_trivially_relocatable_struct_throwing* ptr2 = + static_cast(mem2); + + HPX_TEST(non_trivially_relocatable_struct_throwing::count == 0); + HPX_TEST(non_trivially_relocatable_struct_throwing::move_count == 0); + HPX_TEST(non_trivially_relocatable_struct_throwing::dtor_count == 0); + + for (int i = 0; i < N; i++) + { + hpx::construct_at(ptr1 + i, 1234); + } + + // N objects constructed + HPX_TEST(non_trivially_relocatable_struct_throwing::count == N); + + // relocate them to ptr2 + try + { + uninitialized_relocate(ptr1, ptr1 + N, ptr2); + HPX_UNREACHABLE; // should have thrown + } + catch (...) + { + } + + // K move constructors were called + HPX_TEST(non_trivially_relocatable_struct_throwing::move_count == K); + + // K - 1 destructors were called to balance out the move constructors + // (- 1 because the last move constructor throws) + // and then N + 1 destructors were called: K on the old range and + // N - (K - 1) = N - K + 1 on the new range + HPX_TEST( + non_trivially_relocatable_struct_throwing::dtor_count == N + K); + + // It stops at K, so K-1 move-destruct pairs have been executed + // after this N - (K - 1) destructs will be done on the old range + // and K - 1 on the new range. giving 2*N total destructs + + std::free(mem1); + std::free(mem2); + } + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + hpx::local::init(hpx_main, argc, argv); + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate_par.cpp b/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate_par.cpp new file mode 100644 index 000000000000..8ab01ddb7c04 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate_par.cpp @@ -0,0 +1,301 @@ +// Copyright (c) 2023 Isidoros Tsaousis-Seiras +// +// 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 + +#define N 5000 +#define K 10 + +using hpx::experimental::is_trivially_relocatable_v; +using hpx::experimental::uninitialized_relocate; + +struct trivially_relocatable_struct +{ + static std::atomic count; + static std::atomic move_count; + static std::atomic dtor_count; + int data; + + explicit trivially_relocatable_struct(int data) + : data(data) + { + count.fetch_add(1); + } + trivially_relocatable_struct(trivially_relocatable_struct&& other) + : data(other.data) + { + move_count.fetch_add(1); + count++; + } + ~trivially_relocatable_struct() + { + dtor_count.fetch_add(1); + count--; + } + + // making sure the address is never directly accessed + friend void operator&(trivially_relocatable_struct) = delete; +}; +std::atomic trivially_relocatable_struct::count = 0; +std::atomic trivially_relocatable_struct::move_count = 0; +std::atomic trivially_relocatable_struct::dtor_count = 0; + +HPX_DECLARE_TRIVIALLY_RELOCATABLE(trivially_relocatable_struct); +static_assert(is_trivially_relocatable_v); + +struct non_trivially_relocatable_struct +{ + static std::atomic count; + static std::atomic move_count; + static std::atomic dtor_count; + int data; + + explicit non_trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + // mark as noexcept to enter simpler relocation path + non_trivially_relocatable_struct( + non_trivially_relocatable_struct&& other) noexcept + : data(other.data) + { + move_count.fetch_add(1); + count++; + } + ~non_trivially_relocatable_struct() + { + dtor_count.fetch_add(1); + count--; + } + + // making sure the address is never directly accessed + friend void operator&(non_trivially_relocatable_struct) = delete; +}; +std::atomic non_trivially_relocatable_struct::count = 0; +std::atomic non_trivially_relocatable_struct::move_count = 0; +std::atomic non_trivially_relocatable_struct::dtor_count = 0; + +static_assert( + !is_trivially_relocatable_v); + +struct non_trivially_relocatable_struct_throwing +{ + static std::atomic count; + static std::atomic move_count; + static std::atomic dtor_count; + + int data; + + explicit non_trivially_relocatable_struct_throwing(int data) + : data(data) + { + count++; + } + // do not mark as noexcept to enter try-catch relocation path + non_trivially_relocatable_struct_throwing( + non_trivially_relocatable_struct_throwing&& other) + : data(other.data) + { + if (move_count.load() == K) + { + throw 42; + } + move_count.fetch_add(1); + + count++; + } + ~non_trivially_relocatable_struct_throwing() + { + dtor_count.fetch_add(1); + count--; + } + + // making sure the address is never directly accessed + friend void operator&(non_trivially_relocatable_struct_throwing) = delete; +}; + +std::atomic non_trivially_relocatable_struct_throwing::count = 0; +std::atomic non_trivially_relocatable_struct_throwing::move_count = 0; +std::atomic non_trivially_relocatable_struct_throwing::dtor_count = 0; + +static_assert(!is_trivially_relocatable_v< + non_trivially_relocatable_struct_throwing>); + +char msg[256]; + +int hpx_main() +{ + { + void* mem1 = std::malloc(N * sizeof(trivially_relocatable_struct)); + void* mem2 = std::malloc(N * sizeof(trivially_relocatable_struct)); + + HPX_TEST(mem1 && mem2); + + trivially_relocatable_struct* ptr1 = + static_cast(mem1); + trivially_relocatable_struct* ptr2 = + static_cast(mem2); + + HPX_TEST(trivially_relocatable_struct::count.load() == 0); + HPX_TEST(trivially_relocatable_struct::move_count.load() == 0); + HPX_TEST(trivially_relocatable_struct::dtor_count.load() == 0); + + for (int i = 0; i < N; i++) + { + hpx::construct_at(ptr1 + i, 1234); + } + + // N objects constructed + HPX_TEST(trivially_relocatable_struct::count.load() == N); + + // relocate them to ptr2 + uninitialized_relocate(hpx::execution::par, ptr1, ptr1 + N, ptr2); + + sprintf(msg, + "count: %d, move_count: %d, dtor_count: %d", + trivially_relocatable_struct::count.load(), + trivially_relocatable_struct::move_count.load(), + trivially_relocatable_struct::dtor_count.load()); + + // All creations - destructions balance out + HPX_TEST_MSG(trivially_relocatable_struct::count.load() == N, msg); + + // No move constructor or destructor should be called + HPX_TEST_MSG(trivially_relocatable_struct::move_count.load() == 0, msg); + HPX_TEST_MSG(trivially_relocatable_struct::dtor_count.load() == 0, msg); + + for (int i = 0; i < N; i++) + { + HPX_TEST(ptr2[i].data == 1234); + } + + std::destroy_n(ptr2, N); + + std::free(mem1); + std::free(mem2); + } + { + void* mem1 = std::malloc(N * sizeof(non_trivially_relocatable_struct)); + void* mem2 = std::malloc(N * sizeof(non_trivially_relocatable_struct)); + + HPX_TEST(mem1 && mem2); + + non_trivially_relocatable_struct* ptr1 = + static_cast(mem1); + non_trivially_relocatable_struct* ptr2 = + static_cast(mem2); + + HPX_TEST(non_trivially_relocatable_struct::count.load() == 0); + HPX_TEST(non_trivially_relocatable_struct::move_count.load() == 0); + HPX_TEST(non_trivially_relocatable_struct::dtor_count.load() == 0); + + for (int i = 0; i < N; i++) + { + hpx::construct_at(ptr1 + i, 1234); + } + + // N objects constructed + HPX_TEST(non_trivially_relocatable_struct::count.load() == N); + + // relocate them to ptr2 + uninitialized_relocate(hpx::execution::par, ptr1, ptr1 + N, ptr2); + + sprintf(msg, + "count: %d, move_count: %d, dtor_count: %d", + non_trivially_relocatable_struct::count.load(), + non_trivially_relocatable_struct::move_count.load(), + non_trivially_relocatable_struct::dtor_count.load()); + + + // All creations - destructions balance out + HPX_TEST_MSG(non_trivially_relocatable_struct::count.load() == N, msg); + + // Every object was moved from and then destroyed + HPX_TEST_MSG(non_trivially_relocatable_struct::move_count.load() == N, msg); + HPX_TEST_MSG(non_trivially_relocatable_struct::dtor_count.load() == N, msg); + + for (int i = 0; i < N; i++) + { + HPX_TEST(ptr2[i].data == 1234); + } + + std::destroy_n(ptr2, N); + + std::free(mem1); + std::free(mem2); + } + { + void* mem1 = + std::malloc(N * sizeof(non_trivially_relocatable_struct_throwing)); + void* mem2 = + std::malloc(N * sizeof(non_trivially_relocatable_struct_throwing)); + + HPX_TEST(mem1 && mem2); + + non_trivially_relocatable_struct_throwing* ptr1 = + static_cast(mem1); + non_trivially_relocatable_struct_throwing* ptr2 = + static_cast(mem2); + + HPX_TEST(non_trivially_relocatable_struct_throwing::count.load() == 0); + HPX_TEST(non_trivially_relocatable_struct_throwing::move_count.load() == 0); + HPX_TEST(non_trivially_relocatable_struct_throwing::dtor_count.load() == 0); + + for (int i = 0; i < N; i++) + { + hpx::construct_at(ptr1 + i, 1234); + } + + // N objects constructed + HPX_TEST(non_trivially_relocatable_struct_throwing::count.load() == N); + + // relocate them to ptr2 + try + { + uninitialized_relocate(hpx::execution::par, ptr1, ptr1 + N, ptr2); + HPX_UNREACHABLE; // should have thrown + } + catch (...) + { + } + + sprintf(msg, + "count: %d, move_count: %d, dtor_count: %d", + non_trivially_relocatable_struct_throwing::count.load(), + non_trivially_relocatable_struct_throwing::move_count.load(), + non_trivially_relocatable_struct_throwing::dtor_count.load()); + + + // K move constructors were called + HPX_TEST_MSG(non_trivially_relocatable_struct_throwing::move_count.load() == K, msg); + + // K - 1 destructors were called to balance out the move constructors + // (- 1 because the last move constructor throws) + // and then N + 1 destructors were called: K on the old range and + // N - (K - 1) = N - K + 1 on the new range + HPX_TEST_MSG( + non_trivially_relocatable_struct_throwing::dtor_count.load() == N + K, msg); + + // It stops at K, so K-1 move-destruct pairs have been executed + // after this N - (K - 1) destructs will be done on the old range + // and K - 1 on the new range. giving 2*N total destructs + + std::free(mem1); + std::free(mem2); + } + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + hpx::local::init(hpx_main, argc, argv); + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocaten.cpp b/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocaten.cpp new file mode 100644 index 000000000000..7814ed4041bb --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocaten.cpp @@ -0,0 +1,276 @@ +// Copyright (c) 2023 Isidoros Tsaousis-Seiras +// +// 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 + +#define N 50 +#define K 10 + +using hpx::experimental::is_trivially_relocatable_v; +using hpx::experimental::uninitialized_relocate_n; + +struct trivially_relocatable_struct +{ + static int count; + static int move_count; + static int dtor_count; + int data; + + explicit trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + trivially_relocatable_struct(trivially_relocatable_struct&& other) + : data(other.data) + { + move_count++; + count++; + } + ~trivially_relocatable_struct() + { + dtor_count++; + count--; + } + + // making sure the address is never directly accessed + friend void operator&(trivially_relocatable_struct) = delete; +}; +int trivially_relocatable_struct::count = 0; +int trivially_relocatable_struct::move_count = 0; +int trivially_relocatable_struct::dtor_count = 0; + +HPX_DECLARE_TRIVIALLY_RELOCATABLE(trivially_relocatable_struct); +static_assert(is_trivially_relocatable_v); + +struct non_trivially_relocatable_struct +{ + static int count; + static int move_count; + static int dtor_count; + int data; + + explicit non_trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + // mark as noexcept to enter simpler relocation path + non_trivially_relocatable_struct( + non_trivially_relocatable_struct&& other) noexcept + : data(other.data) + { + move_count++; + count++; + } + ~non_trivially_relocatable_struct() + { + dtor_count++; + count--; + } + + // making sure the address is never directly accessed + friend void operator&(non_trivially_relocatable_struct) = delete; +}; +int non_trivially_relocatable_struct::count = 0; +int non_trivially_relocatable_struct::move_count = 0; +int non_trivially_relocatable_struct::dtor_count = 0; + +static_assert(!is_trivially_relocatable_v); + +struct non_trivially_relocatable_struct_throwing +{ + static int count; + static int move_count; + static int dtor_count; + + int data; + + explicit non_trivially_relocatable_struct_throwing(int data) + : data(data) + { + count++; + } + // do not mark as noexcept to enter try-catch relocation path + non_trivially_relocatable_struct_throwing( + non_trivially_relocatable_struct_throwing&& other) + : data(other.data) + { + if (move_count == K) + { + throw 42; + } + move_count++; + + count++; + } + ~non_trivially_relocatable_struct_throwing() + { + dtor_count++; + count--; + } + + // making sure the address is never directly accessed + friend void operator&(non_trivially_relocatable_struct_throwing) = delete; +}; + +int non_trivially_relocatable_struct_throwing::count = 0; +int non_trivially_relocatable_struct_throwing::move_count = 0; +int non_trivially_relocatable_struct_throwing::dtor_count = 0; + +static_assert( + !is_trivially_relocatable_v); + +int hpx_main() +{ + { + void* mem1 = std::malloc(N * sizeof(trivially_relocatable_struct)); + void* mem2 = std::malloc(N * sizeof(trivially_relocatable_struct)); + + HPX_TEST(mem1 && mem2); + + trivially_relocatable_struct* ptr1 = + static_cast(mem1); + trivially_relocatable_struct* ptr2 = + static_cast(mem2); + + HPX_TEST(trivially_relocatable_struct::count == 0); + HPX_TEST(trivially_relocatable_struct::move_count == 0); + HPX_TEST(trivially_relocatable_struct::dtor_count == 0); + + for (int i = 0; i < N; i++) + { + hpx::construct_at(ptr1 + i, 1234); + } + + // N objects constructed + HPX_TEST(trivially_relocatable_struct::count == N); + + // relocate them to ptr2 + uninitialized_relocate_n(ptr1, N, ptr2); + + // All creations - destructions balance out + HPX_TEST(trivially_relocatable_struct::count == N); + + // No move constructor or destructor should be called + HPX_TEST(trivially_relocatable_struct::move_count == 0); + HPX_TEST(trivially_relocatable_struct::dtor_count == 0); + + for (int i = 0; i < N; i++) + { + HPX_TEST(ptr2[i].data == 1234); + } + + std::destroy(ptr2, ptr2 + N); + + std::free(mem1); + std::free(mem2); + } + { + void* mem1 = std::malloc(N * sizeof(non_trivially_relocatable_struct)); + void* mem2 = std::malloc(N * sizeof(non_trivially_relocatable_struct)); + + HPX_TEST(mem1 && mem2); + + non_trivially_relocatable_struct* ptr1 = + static_cast(mem1); + non_trivially_relocatable_struct* ptr2 = + static_cast(mem2); + + HPX_TEST(non_trivially_relocatable_struct::count == 0); + HPX_TEST(non_trivially_relocatable_struct::move_count == 0); + HPX_TEST(non_trivially_relocatable_struct::dtor_count == 0); + + for (int i = 0; i < N; i++) + { + hpx::construct_at(ptr1 + i, 1234); + } + + // N objects constructed + HPX_TEST(non_trivially_relocatable_struct::count == N); + + // relocate them to ptr2 + uninitialized_relocate_n(ptr1, N, ptr2); + + // All creations - destructions balance out + HPX_TEST(non_trivially_relocatable_struct::count == N); + + // Every object was moved from and then destroyed + HPX_TEST(non_trivially_relocatable_struct::move_count == N); + HPX_TEST(non_trivially_relocatable_struct::dtor_count == N); + + for (int i = 0; i < N; i++) + { + HPX_TEST(ptr2[i].data == 1234); + } + + std::destroy(ptr2, ptr2 + N); + + std::free(mem1); + std::free(mem2); + } + { + void* mem1 = + std::malloc(N * sizeof(non_trivially_relocatable_struct_throwing)); + void* mem2 = + std::malloc(N * sizeof(non_trivially_relocatable_struct_throwing)); + + HPX_TEST(mem1 && mem2); + + non_trivially_relocatable_struct_throwing* ptr1 = + static_cast(mem1); + non_trivially_relocatable_struct_throwing* ptr2 = + static_cast(mem2); + + HPX_TEST(non_trivially_relocatable_struct_throwing::count == 0); + HPX_TEST(non_trivially_relocatable_struct_throwing::move_count == 0); + HPX_TEST(non_trivially_relocatable_struct_throwing::dtor_count == 0); + + for (int i = 0; i < N; i++) + { + hpx::construct_at(ptr1 + i, 1234); + } + + // N objects constructed + HPX_TEST(non_trivially_relocatable_struct_throwing::count == N); + + // relocate them to ptr2 + try + { + uninitialized_relocate_n(ptr1, N, ptr2); + HPX_UNREACHABLE; // should have thrown + } + catch (...) + { + } + + // K move constructors were called + HPX_TEST(non_trivially_relocatable_struct_throwing::move_count == K); + + // K - 1 destructors were called to balance out the move constructors + // (- 1 because the last move constructor throws) + // and then N + 1 destructors were called: K on the old range and + // N - (K - 1) = N - K + 1 on the new range + HPX_TEST( + non_trivially_relocatable_struct_throwing::dtor_count == N + K); + + // It stops at K, so K-1 move-destruct pairs have been executed + // after this N - (K - 1) destructs will be done on the old range + // and K - 1 on the new range. giving 2*N total destructs + + std::free(mem1); + std::free(mem2); + } + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + hpx::local::init(hpx_main, argc, argv); + return hpx::util::report_errors(); +} From 58dc8844386785cd8cb5e083e0daf9f12afa6f20 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 8 Sep 2023 14:48:24 +0300 Subject: [PATCH 05/11] tests for relocation (nitpicks) --- libs/core/type_support/tests/unit/fail_relocate_at.cpp | 4 +++- .../tests/unit/fail_uninitialized_relocate.cpp | 6 ++++-- libs/core/type_support/tests/unit/relocate.cpp | 9 ++++++++- libs/core/type_support/tests/unit/relocate_at.cpp | 2 ++ .../tests/unit/uninitialized_relocate_n_primitive.cpp | 9 +++++---- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/libs/core/type_support/tests/unit/fail_relocate_at.cpp b/libs/core/type_support/tests/unit/fail_relocate_at.cpp index be88f03d36ee..90f31f45d6ca 100644 --- a/libs/core/type_support/tests/unit/fail_relocate_at.cpp +++ b/libs/core/type_support/tests/unit/fail_relocate_at.cpp @@ -9,6 +9,8 @@ #include #include +using hpx::experimental::relocate_at; + int main(int argc, char* argv[]) { int a[10]; @@ -17,5 +19,5 @@ int main(int argc, char* argv[]) int(*p)[10] = &a; int(*q)[10] = &b; - hpx::experimental::relocate_at(p, q); + relocate_at(p, q); } diff --git a/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp b/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp index affcb672fd67..9e72b1bfc121 100644 --- a/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp @@ -7,7 +7,9 @@ // This test should fail to compile #include -#include +#include + +using hpx::experimental::util::uninitialized_relocate_n_primitive; int main(int argc, char* argv[]) { @@ -17,5 +19,5 @@ int main(int argc, char* argv[]) int(*p)[10] = &a; int(*q)[10] = &b; - hpx::experimental::uninitialized_relocate(p, p + 1, q); + uninitialized_relocate_n_primitive(p, 1, q); } diff --git a/libs/core/type_support/tests/unit/relocate.cpp b/libs/core/type_support/tests/unit/relocate.cpp index 3871598b40e2..c7728445c625 100644 --- a/libs/core/type_support/tests/unit/relocate.cpp +++ b/libs/core/type_support/tests/unit/relocate.cpp @@ -15,8 +15,13 @@ #include #include #include + +#include #include +using hpx::experimental::is_trivially_relocatable_v; +using hpx::experimental::relocate; + struct non_trivially_relocatable_struct { static int count; @@ -42,6 +47,8 @@ struct non_trivially_relocatable_struct }; int non_trivially_relocatable_struct::count = 0; +static_assert(!is_trivially_relocatable_v); + int hpx_main() { void* mem1 = std::malloc(sizeof(non_trivially_relocatable_struct)); @@ -56,7 +63,7 @@ int hpx_main() // a single object was constructed HPX_TEST(non_trivially_relocatable_struct::count == 1); - non_trivially_relocatable_struct obj2 = hpx::experimental::relocate(ptr1); + non_trivially_relocatable_struct obj2 = relocate(ptr1); // count = 1 + 1 (from the move construction) - 1 (from the destruction) HPX_TEST(non_trivially_relocatable_struct::count == 1); diff --git a/libs/core/type_support/tests/unit/relocate_at.cpp b/libs/core/type_support/tests/unit/relocate_at.cpp index ca9b90c00a4b..d514dbb60702 100644 --- a/libs/core/type_support/tests/unit/relocate_at.cpp +++ b/libs/core/type_support/tests/unit/relocate_at.cpp @@ -7,6 +7,8 @@ #include #include #include + +#include #include using hpx::experimental::is_trivially_relocatable_v; diff --git a/libs/core/type_support/tests/unit/uninitialized_relocate_n_primitive.cpp b/libs/core/type_support/tests/unit/uninitialized_relocate_n_primitive.cpp index 451156245cf7..854d7d564308 100644 --- a/libs/core/type_support/tests/unit/uninitialized_relocate_n_primitive.cpp +++ b/libs/core/type_support/tests/unit/uninitialized_relocate_n_primitive.cpp @@ -6,13 +6,14 @@ #include #include +#include #include #define N 50 #define K 10 using hpx::experimental::is_trivially_relocatable_v; -using hpx::experimental::uninitialized_relocate_n_primitive; +using hpx::experimental::util::uninitialized_relocate_n_primitive; struct trivially_relocatable_struct { @@ -152,7 +153,7 @@ int hpx_main() HPX_TEST(trivially_relocatable_struct::count == N); // relocate them to ptr2 - uninitialized_relocate_n_primitive(ptr1, ptr1 + N, ptr2); + uninitialized_relocate_n_primitive(ptr1, N, ptr2); // All creations - destructions balance out HPX_TEST(trivially_relocatable_struct::count == N); @@ -196,7 +197,7 @@ int hpx_main() HPX_TEST(non_trivially_relocatable_struct::count == N); // relocate them to ptr2 - uninitialized_relocate_n_primitive(ptr1, ptr1 + N, ptr2); + uninitialized_relocate_n_primitive(ptr1, N, ptr2); // All creations - destructions balance out HPX_TEST(non_trivially_relocatable_struct::count == N); @@ -243,7 +244,7 @@ int hpx_main() // relocate them to ptr2 try { - uninitialized_relocate_n_primitive(ptr1, ptr1 + N, ptr2); + uninitialized_relocate_n_primitive(ptr1, N, ptr2); HPX_UNREACHABLE; // should error out } catch (int forty_two) From fcaa2f39d09a4a1dcdf95d32786c36b785271ad4 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 8 Sep 2023 17:24:16 +0300 Subject: [PATCH 06/11] clang-format + namespaces --- .../algorithms/uninitialized_relocate.hpp | 27 ++++++------ .../algorithms/uninitialized_relocate_par.cpp | 42 ++++++++++--------- .../uninitialized_relocate_n_primitive.hpp | 4 +- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/uninitialized_relocate.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/uninitialized_relocate.hpp index 8fe7f4c55076..9e00159bba60 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/uninitialized_relocate.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/uninitialized_relocate.hpp @@ -313,15 +313,14 @@ namespace hpx::parallel { get(util::in_out_result{first, dest}); } - using zip_iterator = hpx::util::zip_iterator; + using zip_iter = ::hpx::util::zip_iterator; using partition_result_type = std::pair; return util::partitioner_with_cleanup, partition_result_type>:: call( - HPX_FORWARD(ExPolicy, policy), zip_iterator(first, dest), - count, - [policy](zip_iterator t, std::size_t part_size) mutable + HPX_FORWARD(ExPolicy, policy), zip_iter(first, dest), count, + [](zip_iter t, std::size_t part_size) mutable -> partition_result_type { using hpx::get; @@ -378,10 +377,9 @@ namespace hpx::parallel { hpx::traits::is_forward_iterator_v )> // clang-format on - static util::in_out_result sequential( - ExPolicy&& policy, InIter first, std::size_t count, - FwdIter dest) noexcept(hpx::experimental::util::detail:: - relocation_traits sequential(ExPolicy&&, + InIter first, std::size_t count, FwdIter dest) noexcept(hpx:: + experimental::util::detail::relocation_traits::is_noexcept_relocatable_v) { return util::in_out_result{first, @@ -435,7 +433,7 @@ namespace hpx::parallel { )> // clang-format on static util::in_out_result sequential( - ExPolicy&& policy, InIter1 first, InIter2 last, + ExPolicy&&, InIter1 first, InIter2 last, FwdIter dest) noexcept(hpx::experimental::util::detail::relocation_traits< InIter1, FwdIter>::is_noexcept_relocatable_v) { @@ -490,9 +488,8 @@ namespace hpx::experimental { std::is_integral_v )> // clang-format on - friend FwdIter tag_fallback_invoke( - hpx::experimental::uninitialized_relocate_n_t, InIter first, - Size count, + friend FwdIter tag_fallback_invoke(uninitialized_relocate_n_t, + InIter first, Size count, FwdIter dest) noexcept(util::detail::relocation_traits::is_noexcept_relocatable_v) { @@ -532,8 +529,8 @@ namespace hpx::experimental { // clang-format on friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::experimental::uninitialized_relocate_n_t, - ExPolicy&& policy, InIter first, Size count, + tag_fallback_invoke(uninitialized_relocate_n_t, ExPolicy&& policy, + InIter first, Size count, FwdIter dest) noexcept(util::detail::relocation_traits::is_noexcept_relocatable_v) { @@ -605,7 +602,7 @@ namespace hpx::experimental { } // clang-format off - template && hpx::traits::is_iterator_v && diff --git a/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate_par.cpp b/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate_par.cpp index 8ab01ddb7c04..e5fcd1d28ece 100644 --- a/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate_par.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/uninitialized_relocate_par.cpp @@ -4,10 +4,10 @@ // 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 #define N 5000 @@ -83,8 +83,7 @@ std::atomic non_trivially_relocatable_struct::count = 0; std::atomic non_trivially_relocatable_struct::move_count = 0; std::atomic non_trivially_relocatable_struct::dtor_count = 0; -static_assert( - !is_trivially_relocatable_v); +static_assert(!is_trivially_relocatable_v); struct non_trivially_relocatable_struct_throwing { @@ -126,8 +125,8 @@ std::atomic non_trivially_relocatable_struct_throwing::count = 0; std::atomic non_trivially_relocatable_struct_throwing::move_count = 0; std::atomic non_trivially_relocatable_struct_throwing::dtor_count = 0; -static_assert(!is_trivially_relocatable_v< - non_trivially_relocatable_struct_throwing>); +static_assert( + !is_trivially_relocatable_v); char msg[256]; @@ -159,8 +158,7 @@ int hpx_main() // relocate them to ptr2 uninitialized_relocate(hpx::execution::par, ptr1, ptr1 + N, ptr2); - sprintf(msg, - "count: %d, move_count: %d, dtor_count: %d", + sprintf(msg, "count: %d, move_count: %d, dtor_count: %d", trivially_relocatable_struct::count.load(), trivially_relocatable_struct::move_count.load(), trivially_relocatable_struct::dtor_count.load()); @@ -208,19 +206,19 @@ int hpx_main() // relocate them to ptr2 uninitialized_relocate(hpx::execution::par, ptr1, ptr1 + N, ptr2); - sprintf(msg, - "count: %d, move_count: %d, dtor_count: %d", + sprintf(msg, "count: %d, move_count: %d, dtor_count: %d", non_trivially_relocatable_struct::count.load(), non_trivially_relocatable_struct::move_count.load(), non_trivially_relocatable_struct::dtor_count.load()); - // All creations - destructions balance out HPX_TEST_MSG(non_trivially_relocatable_struct::count.load() == N, msg); - // Every object was moved from and then destroyed - HPX_TEST_MSG(non_trivially_relocatable_struct::move_count.load() == N, msg); - HPX_TEST_MSG(non_trivially_relocatable_struct::dtor_count.load() == N, msg); + // Every object was moved from and then destroyed + HPX_TEST_MSG( + non_trivially_relocatable_struct::move_count.load() == N, msg); + HPX_TEST_MSG( + non_trivially_relocatable_struct::dtor_count.load() == N, msg); for (int i = 0; i < N; i++) { @@ -246,8 +244,10 @@ int hpx_main() static_cast(mem2); HPX_TEST(non_trivially_relocatable_struct_throwing::count.load() == 0); - HPX_TEST(non_trivially_relocatable_struct_throwing::move_count.load() == 0); - HPX_TEST(non_trivially_relocatable_struct_throwing::dtor_count.load() == 0); + HPX_TEST( + non_trivially_relocatable_struct_throwing::move_count.load() == 0); + HPX_TEST( + non_trivially_relocatable_struct_throwing::dtor_count.load() == 0); for (int i = 0; i < N; i++) { @@ -267,22 +267,24 @@ int hpx_main() { } - sprintf(msg, - "count: %d, move_count: %d, dtor_count: %d", + sprintf(msg, "count: %d, move_count: %d, dtor_count: %d", non_trivially_relocatable_struct_throwing::count.load(), non_trivially_relocatable_struct_throwing::move_count.load(), non_trivially_relocatable_struct_throwing::dtor_count.load()); - // K move constructors were called - HPX_TEST_MSG(non_trivially_relocatable_struct_throwing::move_count.load() == K, msg); + HPX_TEST_MSG( + non_trivially_relocatable_struct_throwing::move_count.load() == K, + msg); // K - 1 destructors were called to balance out the move constructors // (- 1 because the last move constructor throws) // and then N + 1 destructors were called: K on the old range and // N - (K - 1) = N - K + 1 on the new range HPX_TEST_MSG( - non_trivially_relocatable_struct_throwing::dtor_count.load() == N + K, msg); + non_trivially_relocatable_struct_throwing::dtor_count.load() == + N + K, + msg); // It stops at K, so K-1 move-destruct pairs have been executed // after this N - (K - 1) destructs will be done on the old range diff --git a/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp index 9285780977eb..7d1b682feea6 100644 --- a/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp +++ b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp @@ -70,9 +70,9 @@ namespace hpx::experimental::util { // clang-format off using implementation_tag = std::conditional_t< is_buffer_memcpyable, - buffer_memcpy_tag, + buffer_memcpy_tag, std::conditional_t >; From 85315729dd00233affb321d468b9f8333429f22b Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Sat, 9 Sep 2023 17:29:01 +0300 Subject: [PATCH 07/11] fix unrelated failing test --- .../tests/performance/stencil3_iterators.cpp | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/libs/core/iterator_support/tests/performance/stencil3_iterators.cpp b/libs/core/iterator_support/tests/performance/stencil3_iterators.cpp index 413450e30006..f5493233c0f8 100644 --- a/libs/core/iterator_support/tests/performance/stencil3_iterators.cpp +++ b/libs/core/iterator_support/tests/performance/stencil3_iterators.cpp @@ -154,12 +154,12 @@ namespace hpx::experimental { left_transformer; typedef next_transformer right_transformer; - typedef util::transform_iterator + typedef ::hpx::util::transform_iterator left_iterator; - typedef util::transform_iterator + typedef ::hpx::util::transform_iterator right_iterator; - typedef util::detail::zip_iterator_base< + typedef ::hpx::util::detail::zip_iterator_base< hpx::tuple, stencil3_iterator_full> @@ -172,15 +172,16 @@ namespace hpx::experimental { auto prev = make_previous_transformer(begin, begin_val); auto next = make_next_transformer(end, end_val); - return type(hpx::make_tuple(util::transform_iterator(it, prev), - it, util::transform_iterator(it, next))); + return type( + hpx::make_tuple(::hpx::util::transform_iterator(it, prev), + it, ::hpx::util::transform_iterator(it, next))); } static type create(Iterator const& it) { return type(hpx::make_tuple( - util::transform_iterator(it, left_transformer()), it, - util::transform_iterator(it, right_transformer()))); + ::hpx::util::transform_iterator(it, left_transformer()), it, + ::hpx::util::transform_iterator(it, right_transformer()))); } }; } // namespace detail @@ -284,12 +285,12 @@ std::uint64_t bench_stencil3_iterator_full() namespace hpx::experimental { template class stencil3_iterator_v1 - : public util::detail::zip_iterator_base< + : public ::hpx::util::detail::zip_iterator_base< hpx::tuple, stencil3_iterator_v1> { private: - typedef util::detail::zip_iterator_base< + typedef ::hpx::util::detail::zip_iterator_base< hpx::tuple, stencil3_iterator_v1> base_type; @@ -385,10 +386,11 @@ namespace hpx::experimental { template class stencil3_iterator_v2 - : public hpx::util::transform_iterator + : public ::hpx::util::transform_iterator { private: - typedef hpx::util::transform_iterator base_type; + typedef ::hpx::util::transform_iterator + base_type; public: stencil3_iterator_v2() {} From a43f622e97e08e586633905931e6a717280affa9 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Sun, 10 Sep 2023 17:00:02 +0300 Subject: [PATCH 08/11] move is_contiguous_iterator to type_support, support array iterators --- .../algorithms/traits/pointer_category.hpp | 1 + .../include/hpx/parallel/util/transfer.hpp | 1 + .../iterator_support/traits/is_iterator.hpp | 48 ----------- libs/core/type_support/CMakeLists.txt | 1 + .../type_support/is_contiguous_iterator.hpp | 80 +++++++++++++++++++ .../uninitialized_relocate_n_primitive.hpp | 7 +- .../type_support/tests/unit/CMakeLists.txt | 4 +- .../tests/unit/is_contiguous_iterator.cpp | 49 ++++++++++++ 8 files changed, 139 insertions(+), 52 deletions(-) create mode 100644 libs/core/type_support/include/hpx/type_support/is_contiguous_iterator.hpp create mode 100644 libs/core/type_support/tests/unit/is_contiguous_iterator.cpp diff --git a/libs/core/algorithms/include/hpx/algorithms/traits/pointer_category.hpp b/libs/core/algorithms/include/hpx/algorithms/traits/pointer_category.hpp index 0d59da372ae9..3550596b9507 100644 --- a/libs/core/algorithms/include/hpx/algorithms/traits/pointer_category.hpp +++ b/libs/core/algorithms/include/hpx/algorithms/traits/pointer_category.hpp @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include diff --git a/libs/core/algorithms/include/hpx/parallel/util/transfer.hpp b/libs/core/algorithms/include/hpx/parallel/util/transfer.hpp index 69f2556ebb26..e63be42abf0d 100644 --- a/libs/core/algorithms/include/hpx/parallel/util/transfer.hpp +++ b/libs/core/algorithms/include/hpx/parallel/util/transfer.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/libs/core/iterator_support/include/hpx/iterator_support/traits/is_iterator.hpp b/libs/core/iterator_support/include/hpx/iterator_support/traits/is_iterator.hpp index c7a6a010358e..ab579147a272 100644 --- a/libs/core/iterator_support/include/hpx/iterator_support/traits/is_iterator.hpp +++ b/libs/core/iterator_support/include/hpx/iterator_support/traits/is_iterator.hpp @@ -564,52 +564,4 @@ namespace hpx::traits { template inline constexpr bool is_zip_iterator_v = is_zip_iterator::value; - /////////////////////////////////////////////////////////////////////////// - // Iterators are contiguous if they are pointers (without concepts we have - // no generic way of determining whether an iterator is contiguous) - - namespace detail { - - // Iterators returned from std::vector are contiguous (by definition) - - // different versions of clang-format disagree - // clang-format off - template > - struct is_vector_iterator - : std::integral_constant&>().begin()), Iter> || - std::is_same_v< - decltype(std::declval&>().cbegin()), Iter>> - { - }; - // clang-format on - } // namespace detail - - template ::value -#endif - > - struct is_contiguous_iterator : std::is_pointer::type - { - }; - - template - struct is_contiguous_iterator : std::true_type - { - }; - - template - using is_contiguous_iterator_t = - typename is_contiguous_iterator::type; - - template - inline constexpr bool is_contiguous_iterator_v = - is_contiguous_iterator::value; } // namespace hpx::traits diff --git a/libs/core/type_support/CMakeLists.txt b/libs/core/type_support/CMakeLists.txt index 5bf549a16256..ba11cca32dd8 100644 --- a/libs/core/type_support/CMakeLists.txt +++ b/libs/core/type_support/CMakeLists.txt @@ -18,6 +18,7 @@ set(type_support_headers hpx/type_support/identity.hpp hpx/type_support/is_relocatable.hpp hpx/type_support/is_trivially_relocatable.hpp + hpx/type_support/is_contiguous_iterator.hpp hpx/type_support/lazy_conditional.hpp hpx/type_support/lazy_enable_if.hpp hpx/type_support/pack.hpp diff --git a/libs/core/type_support/include/hpx/type_support/is_contiguous_iterator.hpp b/libs/core/type_support/include/hpx/type_support/is_contiguous_iterator.hpp new file mode 100644 index 000000000000..f1d5f5b4f509 --- /dev/null +++ b/libs/core/type_support/include/hpx/type_support/is_contiguous_iterator.hpp @@ -0,0 +1,80 @@ +// Copyright (c) 2007-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::traits { + /////////////////////////////////////////////////////////////////////////// + // Iterators are contiguous if they are pointers (without concepts we have + // no generic way of determining whether an iterator is contiguous) + + namespace detail { + + template + using iter_value_type_t = + typename std::iterator_traits::value_type; + + template + struct is_known_contiguous_iterator : std::false_type + { + }; + + template + struct is_known_contiguous_iterator>> + : std::bool_constant< + std::is_same_v< // for std::vector + typename std::vector>::iterator, + Iter> || + std::is_same_v>::const_iterator, + Iter> || // for std::array + std::is_same_v< + typename std::array, 1>::iterator, + Iter> || + std::is_same_v, + 1>::const_iterator, + Iter> || // for std::string + std::is_same_v || + std::is_same_v> + { + }; + } // namespace detail + + template ::value +#endif + > + struct is_contiguous_iterator : std::is_pointer::type + { + }; + + template + struct is_contiguous_iterator : std::true_type + { + }; + + template + using is_contiguous_iterator_t = + typename is_contiguous_iterator::type; + + template + inline constexpr bool is_contiguous_iterator_v = + is_contiguous_iterator::value; +} // namespace hpx::traits diff --git a/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp index 7d1b682feea6..ebd4bfb9ef12 100644 --- a/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp +++ b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate_n_primitive.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -165,6 +166,7 @@ namespace hpx::experimental::util { FwdIter uninitialized_relocate_n_primitive(InIter first, Size n, FwdIter dst, iterators_are_contiguous_t) noexcept( detail::relocation_traits::is_noexcept_relocatable_v) + // clang-format on { static_assert( detail::relocation_traits::valid_relocation, @@ -176,7 +178,6 @@ namespace hpx::experimental::util { return detail::uninitialized_relocate_n_primitive_helper( first, n, dst, implementation_tag{}); } - // clang-format on template FwdIter uninitialized_relocate_n_primitive(InIter first, Size n, @@ -184,8 +185,8 @@ namespace hpx::experimental::util { FwdIter>::is_noexcept_relocatable_v) { using iterators_are_contiguous_default_t = - std::bool_constant && - std::is_pointer_v>; + std::bool_constant && + hpx::traits::is_contiguous_iterator_v>; return uninitialized_relocate_n_primitive( first, n, dst, iterators_are_contiguous_default_t{}); diff --git a/libs/core/type_support/tests/unit/CMakeLists.txt b/libs/core/type_support/tests/unit/CMakeLists.txt index ae53059b2564..10a2ef767386 100644 --- a/libs/core/type_support/tests/unit/CMakeLists.txt +++ b/libs/core/type_support/tests/unit/CMakeLists.txt @@ -32,7 +32,9 @@ endforeach() if(HPX_WITH_COMPILE_ONLY_TESTS) # add compile time tests - set(compile_tests is_relocatable is_trivially_relocatable) + set(compile_tests is_relocatable is_trivially_relocatable + is_contiguous_iterator + ) if(HPX_WITH_FAIL_COMPILE_TESTS) set(fail_compile_tests fail_relocate_at fail_uninitialized_relocate) diff --git a/libs/core/type_support/tests/unit/is_contiguous_iterator.cpp b/libs/core/type_support/tests/unit/is_contiguous_iterator.cpp new file mode 100644 index 000000000000..add047d47069 --- /dev/null +++ b/libs/core/type_support/tests/unit/is_contiguous_iterator.cpp @@ -0,0 +1,49 @@ +// Copyright (c) 2023 Isidoros Tsaousis-Seiras +// +// 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 + +using hpx::traits::is_contiguous_iterator_v; + +// std::vector::iterator is a contiguous iterator +static_assert(is_contiguous_iterator_v::iterator>); +static_assert(is_contiguous_iterator_v::const_iterator>); +// reverse_iterator is not a contiguous iterator +static_assert(!is_contiguous_iterator_v::reverse_iterator>); +static_assert( + !is_contiguous_iterator_v::const_reverse_iterator>); + +// std::array::iterator is a contiguous iterator +static_assert(is_contiguous_iterator_v::iterator>); +static_assert(is_contiguous_iterator_v::const_iterator>); +// reverse_iterator is not a contiguous iterator +static_assert(!is_contiguous_iterator_v::reverse_iterator>); +static_assert( + !is_contiguous_iterator_v::const_reverse_iterator>); + +// pointers are contiguous iterators +static_assert(is_contiguous_iterator_v); +static_assert(is_contiguous_iterator_v); +static_assert(is_contiguous_iterator_v); +static_assert(is_contiguous_iterator_v); +static_assert(is_contiguous_iterator_v); +static_assert(is_contiguous_iterator_v); + +// arrays are not contiguous iterators +static_assert(!is_contiguous_iterator_v); +static_assert(!is_contiguous_iterator_v); +static_assert(!is_contiguous_iterator_v); +static_assert(!is_contiguous_iterator_v); + +// std::string::iterator is a contiguous iterator +static_assert(is_contiguous_iterator_v); +static_assert(is_contiguous_iterator_v); + +int main(int, char*[]) {} From b894f5eb9e7265fca86ffd31d1cdc6b56a4473c8 Mon Sep 17 00:00:00 2001 From: Hartmut Kaiser Date: Thu, 14 Sep 2023 08:53:28 -0500 Subject: [PATCH 09/11] Adding --exclusive to launching tests on rostam --- .jenkins/lsu-perftests/entry.sh | 1 + .jenkins/lsu-test-coverage/entry.sh | 1 + .jenkins/lsu/entry.sh | 1 + 3 files changed, 3 insertions(+) diff --git a/.jenkins/lsu-perftests/entry.sh b/.jenkins/lsu-perftests/entry.sh index f87d9ffd7cbc..e183a368fef9 100755 --- a/.jenkins/lsu-perftests/entry.sh +++ b/.jenkins/lsu-perftests/entry.sh @@ -34,6 +34,7 @@ sleep $[(RANDOM % 10) + 1].$[(RANDOM % 10)]s set +e sbatch \ --verbose --verbose --verbose --verbose \ + --exclusive \ --job-name="${job_name}" \ --nodes="1" \ --partition="${configuration_slurm_partition}" \ diff --git a/.jenkins/lsu-test-coverage/entry.sh b/.jenkins/lsu-test-coverage/entry.sh index 77450cc69c70..2105be9f309c 100755 --- a/.jenkins/lsu-test-coverage/entry.sh +++ b/.jenkins/lsu-test-coverage/entry.sh @@ -46,6 +46,7 @@ fi set +e sbatch \ --verbose --verbose --verbose --verbose \ + --exclusive \ --job-name="${job_name}" \ --nodes="1" \ --partition="${configuration_slurm_partition}" \ diff --git a/.jenkins/lsu/entry.sh b/.jenkins/lsu/entry.sh index 9554137a7fa9..5f8fa038064f 100755 --- a/.jenkins/lsu/entry.sh +++ b/.jenkins/lsu/entry.sh @@ -43,6 +43,7 @@ sleep $[(RANDOM % 10) + 1].$[(RANDOM % 10)]s set +e sbatch \ --verbose --verbose --verbose --verbose \ + --exclusive \ --job-name="${job_name}" \ --nodes="${configuration_slurm_num_nodes}" \ --partition="${configuration_slurm_partition}" \ From 496f4ba336ea27dbc118970234dcf0155e22409f Mon Sep 17 00:00:00 2001 From: Hartmut Kaiser Date: Thu, 14 Sep 2023 10:18:58 -0500 Subject: [PATCH 10/11] Moving hpx::threads::run_as_xxx to namespace hpx --- .../process/src/server/child_component.cpp | 2 +- examples/quickstart/init_globally.cpp | 6 +++--- .../hpx/runtime_local/run_as_hpx_thread.hpp | 18 ++++++++++++++-- .../hpx/runtime_local/run_as_os_thread.hpp | 21 +++++++++++++++---- libs/full/collectives/src/barrier.cpp | 2 +- .../run_as_hpx_thread_exceptions_3304.cpp | 2 +- .../threads/run_as_os_thread_lockup_2991.cpp | 2 +- 7 files changed, 40 insertions(+), 13 deletions(-) diff --git a/components/process/src/server/child_component.cpp b/components/process/src/server/child_component.cpp index 9f3d186a40c4..716dc922583a 100644 --- a/components/process/src/server/child_component.cpp +++ b/components/process/src/server/child_component.cpp @@ -34,7 +34,7 @@ namespace hpx { namespace components { namespace process { namespace server { int (*f)(process::util::child const&) = &process::util::wait_for_exit; - return hpx::threads::run_as_os_thread(f, std::ref(child_)).get(); + return hpx::run_as_os_thread(f, std::ref(child_)).get(); } }}}} diff --git a/examples/quickstart/init_globally.cpp b/examples/quickstart/init_globally.cpp index 75d941b581e7..a38e7fda8ef1 100644 --- a/examples/quickstart/init_globally.cpp +++ b/examples/quickstart/init_globally.cpp @@ -270,12 +270,12 @@ void thread_func() // Create an HPX thread (returning an int) and wait for it to run to // completion. - int result = hpx::threads::run_as_hpx_thread(&hpx_thread_func2, 42); + int result = hpx::run_as_hpx_thread(&hpx_thread_func2, 42); // Create an HPX thread (returning void) and wait for it to run to // completion. if (result == 42) - hpx::threads::run_as_hpx_thread(&hpx_thread_func1); + hpx::run_as_hpx_thread(&hpx_thread_func1); } /////////////////////////////////////////////////////////////////////////////// @@ -286,7 +286,7 @@ int main() // The main thread was automatically registered with the HPX runtime, // no explicit registration for this thread is necessary. - hpx::threads::run_as_hpx_thread(&hpx_thread_func1); + hpx::run_as_hpx_thread(&hpx_thread_func1); // wait for the (kernel) thread to run to completion t.join(); diff --git a/libs/core/runtime_local/include/hpx/runtime_local/run_as_hpx_thread.hpp b/libs/core/runtime_local/include/hpx/runtime_local/run_as_hpx_thread.hpp index 3d917d40158a..75f7f2c98543 100644 --- a/libs/core/runtime_local/include/hpx/runtime_local/run_as_hpx_thread.hpp +++ b/libs/core/runtime_local/include/hpx/runtime_local/run_as_hpx_thread.hpp @@ -26,7 +26,8 @@ #include #include -namespace hpx { namespace threads { +namespace hpx { + /////////////////////////////////////////////////////////////////////////// namespace detail { // This is the overload for running functions which return a value. @@ -148,4 +149,17 @@ namespace hpx { namespace threads { return detail::run_as_hpx_thread( result_is_void(), f, HPX_FORWARD(Ts, vs)...); } -}} // namespace hpx::threads +} // namespace hpx + +namespace hpx::threads { + + template + HPX_DEPRECATED_V(1, 10, + "hpx::threads::run_as_hpx_thread is deprecated, use " + "hpx::run_as_hpx_thread instead") + decltype(auto) run_as_hpx_thread(F const& f, Ts&&... ts) + { + return hpx::run_as_hpx_thread( + HPX_FORWARD(F, f), HPX_FORWARD(Ts, ts)...); + } +} // namespace hpx::threads diff --git a/libs/core/runtime_local/include/hpx/runtime_local/run_as_os_thread.hpp b/libs/core/runtime_local/include/hpx/runtime_local/run_as_os_thread.hpp index 1f9524a9c7d2..e894b8f78feb 100644 --- a/libs/core/runtime_local/include/hpx/runtime_local/run_as_os_thread.hpp +++ b/libs/core/runtime_local/include/hpx/runtime_local/run_as_os_thread.hpp @@ -16,17 +16,30 @@ #include #include -namespace hpx { namespace threads { +namespace hpx { + /////////////////////////////////////////////////////////////////////////// template - hpx::future::type> run_as_os_thread( + hpx::future> run_as_os_thread( F&& f, Ts&&... vs) { - HPX_ASSERT(get_self_ptr() != nullptr); + HPX_ASSERT(threads::get_self_ptr() != nullptr); parallel::execution::io_pool_executor executor; auto result = parallel::execution::async_execute( executor, HPX_FORWARD(F, f), HPX_FORWARD(Ts, vs)...); return result; } -}} // namespace hpx::threads +} // namespace hpx + +namespace hpx::threads { + + template + HPX_DEPRECATED_V(1, 10, + "hpx::threads::run_as_os_thread is deprecated, use " + "hpx::run_as_os_thread instead") + decltype(auto) run_as_os_thread(F const& f, Ts&&... ts) + { + return hpx::run_as_os_thread(HPX_FORWARD(F, f), HPX_FORWARD(Ts, ts)...); + } +} // namespace hpx::threads diff --git a/libs/full/collectives/src/barrier.cpp b/libs/full/collectives/src/barrier.cpp index 5251f3a3ee65..7fccf4363d4b 100644 --- a/libs/full/collectives/src/barrier.cpp +++ b/libs/full/collectives/src/barrier.cpp @@ -139,7 +139,7 @@ namespace hpx { namespace distributed { // make sure this runs as an HPX thread if (hpx::threads::get_self_ptr() == nullptr) { - hpx::threads::run_as_hpx_thread(&barrier::release, this); + hpx::run_as_hpx_thread(&barrier::release, this); } hpx::future f; diff --git a/tests/regressions/threads/run_as_hpx_thread_exceptions_3304.cpp b/tests/regressions/threads/run_as_hpx_thread_exceptions_3304.cpp index 0495f868c4a6..06a54b604639 100644 --- a/tests/regressions/threads/run_as_hpx_thread_exceptions_3304.cpp +++ b/tests/regressions/threads/run_as_hpx_thread_exceptions_3304.cpp @@ -65,7 +65,7 @@ int main(int argc, char** argv) bool exception_caught = false; try { - hpx::threads::run_as_hpx_thread(&hpx_thread_func); + hpx::run_as_hpx_thread(&hpx_thread_func); HPX_TEST(false); // this should not be executed } catch (...) diff --git a/tests/regressions/threads/run_as_os_thread_lockup_2991.cpp b/tests/regressions/threads/run_as_os_thread_lockup_2991.cpp index 70374ca7655d..09ada4d8b72a 100644 --- a/tests/regressions/threads/run_as_os_thread_lockup_2991.cpp +++ b/tests/regressions/threads/run_as_os_thread_lockup_2991.cpp @@ -29,7 +29,7 @@ int hpx_main() std::cout << std::this_thread::get_id() << ": about to run on io thread\n"; - hpx::threads::run_as_os_thread(locker); + hpx::run_as_os_thread(locker); //sleep(2); } std::cout << std::this_thread::get_id() << ": exiting\n"; From 82f290779c5c735ce091a451ded3e63559c58a00 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Tue, 19 Sep 2023 10:22:45 +0300 Subject: [PATCH 11/11] detect vec, arr, string w/out causing hard errors --- .../type_support/is_contiguous_iterator.hpp | 92 +++++++++++++------ .../tests/unit/is_contiguous_iterator.cpp | 35 ++++++- 2 files changed, 95 insertions(+), 32 deletions(-) diff --git a/libs/core/type_support/include/hpx/type_support/is_contiguous_iterator.hpp b/libs/core/type_support/include/hpx/type_support/is_contiguous_iterator.hpp index f1d5f5b4f509..4badf1d82b8c 100644 --- a/libs/core/type_support/include/hpx/type_support/is_contiguous_iterator.hpp +++ b/libs/core/type_support/include/hpx/type_support/is_contiguous_iterator.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2007-2022 Hartmut Kaiser +// Copyright (c) 2023 Isidoros Tsaousis-Seiras // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -24,49 +25,86 @@ namespace hpx::traits { using iter_value_type_t = typename std::iterator_traits::value_type; + template + inline constexpr bool has_valid_array_v = + std::is_copy_assignable_v && !std::is_function_v; + + template + struct is_std_array_iterator : std::false_type + { + }; + + template + struct is_std_array_iterator>>> + : std::bool_constant<( + std::is_same_v< + typename std::array, 1>::iterator, + Iter> || + std::is_same_v, + 1>::const_iterator, + Iter>)> + { + }; + + template + inline constexpr bool has_valid_vector_v = + std::is_copy_assignable_v && !std::is_function_v; + template - struct is_known_contiguous_iterator : std::false_type + struct is_std_vector_iterator : std::false_type { }; template - struct is_known_contiguous_iterator>> + struct is_std_vector_iterator>>> : std::bool_constant< - std::is_same_v< // for std::vector + std::is_same_v< typename std::vector>::iterator, Iter> || std::is_same_v>::const_iterator, - Iter> || // for std::array - std::is_same_v< - typename std::array, 1>::iterator, + Iter>> + { + }; + + template + inline constexpr bool has_valid_basic_string_v = + std::is_copy_assignable_v && !std::is_function_v && + std::is_trivial_v; + + template + struct is_std_basic_string_iterator : std::false_type + { + }; + + template + struct is_std_basic_string_iterator>>> + : std::bool_constant< + std::is_same_v>::iterator, Iter> || - std::is_same_v, - 1>::const_iterator, - Iter> || // for std::string - std::is_same_v || - std::is_same_v> + std::is_same_v>::const_iterator, + Iter>> { }; - } // namespace detail - template ::value -#endif - > - struct is_contiguous_iterator : std::is_pointer::type - { - }; + template + struct is_known_contiguous_iterator + : std::bool_constant::value || + is_std_vector_iterator::value || + is_std_basic_string_iterator::value> + { + }; + } // namespace detail template - struct is_contiguous_iterator : std::true_type + struct is_contiguous_iterator + : std::bool_constant || + detail::is_known_contiguous_iterator::value> { }; diff --git a/libs/core/type_support/tests/unit/is_contiguous_iterator.cpp b/libs/core/type_support/tests/unit/is_contiguous_iterator.cpp index add047d47069..fa8a210481f6 100644 --- a/libs/core/type_support/tests/unit/is_contiguous_iterator.cpp +++ b/libs/core/type_support/tests/unit/is_contiguous_iterator.cpp @@ -6,15 +6,24 @@ #include #include +#include +#include #include #include -using hpx::traits::is_contiguous_iterator_v; +using namespace hpx::traits::detail; +using namespace hpx::traits; // std::vector::iterator is a contiguous iterator +static_assert(has_valid_vector_v); + static_assert(is_contiguous_iterator_v::iterator>); +static_assert(is_std_vector_iterator::iterator>::value); + static_assert(is_contiguous_iterator_v::const_iterator>); +static_assert(is_std_vector_iterator::const_iterator>::value); + // reverse_iterator is not a contiguous iterator static_assert(!is_contiguous_iterator_v::reverse_iterator>); static_assert( @@ -31,19 +40,35 @@ static_assert( // pointers are contiguous iterators static_assert(is_contiguous_iterator_v); static_assert(is_contiguous_iterator_v); + +// std::string::iterator is a contiguous iterator +static_assert(is_contiguous_iterator_v); +static_assert(is_contiguous_iterator_v); + +// Pointers to arrays are still pointers static_assert(is_contiguous_iterator_v); static_assert(is_contiguous_iterator_v); static_assert(is_contiguous_iterator_v); static_assert(is_contiguous_iterator_v); -// arrays are not contiguous iterators +// Pointers to functions are still pointers +static_assert(is_contiguous_iterator_v); +static_assert(is_contiguous_iterator_v); + +// c-style arrays are not contiguous iterators static_assert(!is_contiguous_iterator_v); static_assert(!is_contiguous_iterator_v); static_assert(!is_contiguous_iterator_v); static_assert(!is_contiguous_iterator_v); -// std::string::iterator is a contiguous iterator -static_assert(is_contiguous_iterator_v); -static_assert(is_contiguous_iterator_v); +// Unknown type iterators to "weird" +// types should not cause compile errors +using function_iterator = + std::iterator; +using empty_array_iterator = + std::iterator; + +static_assert(!is_contiguous_iterator_v); +static_assert(!is_contiguous_iterator_v); int main(int, char*[]) {}