From 115ac17f08d01b5741b4f5f205fd6695620b06af Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:09:36 +0300 Subject: [PATCH 01/42] similar type detection --- .../type_support/is_trivially_relocatable.hpp | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) 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 5ece0827cf8d..ad9d74cbf115 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 @@ -42,6 +42,60 @@ namespace hpx { { }; + // Constness, Volatility, References, Arrays are ignored + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable + : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable + : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable + : is_trivially_relocatable + { + }; + template inline constexpr bool is_trivially_relocatable_v = is_trivially_relocatable::value; From c58ad81bb396e1c28012a79c3a3abfaeb85e42b1 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:09:56 +0300 Subject: [PATCH 02/42] is_relocatable_from --- .../include/hpx/type_support/is_relocatable.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 d31a92dd31a2..05e881bfc69c 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 @@ -15,6 +15,19 @@ namespace hpx { { }; + // ToTp(FromTp&&) must be well-formed + template + struct is_relocatable_from + : std::bool_constant< + std::is_constructible_v, FromTp> && + std::is_same_v, std::decay_t>> + { + }; + template inline constexpr bool is_relocatable_v = is_relocatable::value; + + template + inline constexpr bool is_relocatable_from_v = + is_relocatable_from::value; } // namespace hpx From aba288b30cba0e8a8a26a7db4404701ba63a4f6b Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:10:10 +0300 Subject: [PATCH 03/42] relocate_at algorithm --- .../include/hpx/type_support/relocate_at.hpp | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 libs/core/type_support/include/hpx/type_support/relocate_at.hpp 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 new file mode 100644 index 000000000000..036c4324910a --- /dev/null +++ b/libs/core/type_support/include/hpx/type_support/relocate_at.hpp @@ -0,0 +1,119 @@ +// 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 { + + namespace detail { + template + struct destroy_guard + { + T* t; + explicit destroy_guard(T* t) + : t(t) + { + } + ~destroy_guard() + { + std::destroy_at(t); + } + }; + } // namespace detail + +#if defined(HPX_HAVE_P1144_STD_RELOCATE_AT) + using std::relocate; + using std::relocate_at; +#else + + namespace detail { + + /* + The condition to use memmove is: + hpx::is_trivially_relocatable_v && + !std::is_volatile_v + + The reason for the volatile check is that memmove is not allowed to + change the value of a volatile object. + */ + + template + constexpr bool relocate_using_memmove = + hpx::is_trivially_relocatable_v && !std::is_volatile_v; + + template , int> = 0> + T* relocate_at_helper(T* src, T* dst) noexcept + { + void* dst_void = const_cast(static_cast(dst)); + void* src_void = const_cast(static_cast(src)); + + std::memmove(dst_void, src_void, sizeof(T)); + + return std::launder(dst); + }; + + // this is move and destroy + template , int> = 0> + T* relocate_at_helper(T* src, T* dst) noexcept( + // has non-throwing move constructor + std::is_nothrow_move_constructible_v) + { + destroy_guard g(src); + return hpx::construct_at(dst, HPX_MOVE(*src)); + }; + } // namespace detail + + template + T* relocate_at(T* src, T* dst) noexcept( + // 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, + "new (dst) T(std::move(*src)) must be well-formed"); + + return detail::relocate_at_helper(src, dst); + } + + template + T relocate(T* src) noexcept(std::is_nothrow_move_constructible_v) + { + static_assert( + hpx::is_relocatable_v, "T(std::move(*src)) must be well-formed"); + + detail::destroy_guard g(src); + return HPX_MOVE(*src); + } + + /* + Memmove codegen. This part relies on UB, so it's not used. It's here for + reference. More info on this: + + https://quuxplusone.github.io/blog/2022/05/18/std-relocate/ + + template + T relocate(T* source) + { + auto magic = (T(*)(void*, size_t)) memcpy; + return magic(source, sizeof(T)); + } + */ + +#endif // defined(HPX_HAVE_P1144_STD_RELOCATE_AT) + +} // namespace hpx From 21d9722b85e74cdd8bb94c73f98c3f9fb6c52e72 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:10:31 +0300 Subject: [PATCH 04/42] uninitialized_relocate algorithm --- libs/core/type_support/CMakeLists.txt | 2 + .../type_support/uninitialized_relocate.hpp | 179 ++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp diff --git a/libs/core/type_support/CMakeLists.txt b/libs/core/type_support/CMakeLists.txt index 74ef2795d0d2..1e931219e277 100644 --- a/libs/core/type_support/CMakeLists.txt +++ b/libs/core/type_support/CMakeLists.txt @@ -22,7 +22,9 @@ set(type_support_headers hpx/type_support/lazy_enable_if.hpp hpx/type_support/pack.hpp 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/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 new file mode 100644 index 000000000000..38242c3efead --- /dev/null +++ b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp @@ -0,0 +1,179 @@ +// 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 { + +#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::remove_reference_t< + // decltype(*std::declval())>; + // using out_type = typename std::remove_reference_t< + // decltype(*std::declval())>; + + using in_type = typename std::iterator_traits::value_type; + using out_type = typename std::iterator_traits::value_type; + + constexpr static bool valid_relocation = + hpx::is_relocatable_from_v; + + constexpr static bool is_buffer_memcpyable = + hpx::is_trivially_relocatable_v && + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The important check + std::is_same_v, std::decay_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) + { + void* first_void = const_cast( + static_cast(std::addressof(*first))); + void* last_void = const_cast( + static_cast(std::addressof(*last))); + + void* dst_void = const_cast( + static_cast(std::addressof(*dst))); + + std::byte* first_byte = + reinterpret_cast(first_void); + std::byte* last_byte = reinterpret_cast(last_void); + + 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_void, first_void, n_bytes); + + dst += n_objects; + } + + return dst; + } + + template ::value == + relocate_strategy::for_loop_nothrow, + int> = 0> + FwdIter uninitialized_relocate_helper( + InIter first, InIter last, FwdIter dst) noexcept + { + for (; first != last; ++first, ++dst) + { + // the move + destroy version will be used + hpx::relocate_at(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 + hpx::relocate_at( + std::addressof(*first), std::addressof(*dst)); + } + catch (...) + { + // destroy all objects other that the one + // that caused the exception + + // 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 From d9dfe227c38ddf0462d45c04162c634170857d03 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:10:49 +0300 Subject: [PATCH 05/42] tests --- .../type_support/tests/unit/CMakeLists.txt | 30 +- .../tests/unit/fail_relocate_at.cpp | 20 ++ .../unit/fail_uninitialized_relocate.cpp | 20 ++ .../tests/unit/is_trivially_relocatable.cpp | 35 +++ .../type_support/tests/unit/relocate_at.cpp | 134 +++++++++ .../tests/unit/uninitialized_relocate.cpp | 275 ++++++++++++++++++ 6 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 libs/core/type_support/tests/unit/fail_relocate_at.cpp create mode 100644 libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp create mode 100644 libs/core/type_support/tests/unit/relocate_at.cpp create mode 100644 libs/core/type_support/tests/unit/uninitialized_relocate.cpp diff --git a/libs/core/type_support/tests/unit/CMakeLists.txt b/libs/core/type_support/tests/unit/CMakeLists.txt index 29519a4d3852..a84d995b157e 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 is_relocatable is_trivially_relocatable) +set(tests relocate_at uninitialized_relocate) if(HPX_WITH_CXX20_COROUTINES) set(tests ${tests} generator) @@ -29,3 +29,31 @@ foreach(test ${tests}) add_hpx_unit_test("modules.type_support" ${test} ${${test}_PARAMETERS}) endforeach() + +if(HPX_WITH_COMPILE_ONLY_TESTS) + # add compile time tests + set(compile_tests is_relocatable is_trivially_relocatable) + + if(HPX_WITH_FAIL_COMPILE_TESTS) + set(fail_compile_tests fail_relocate_at fail_uninitialized_relocate) + foreach(fail_compile_test ${fail_compile_tests}) + set(${fail_compile_test}_FLAGS FAILURE_EXPECTED) + endforeach() + + set(compile_tests ${compile_tests} ${fail_compile_tests}) + endif() + + foreach(compile_test ${compile_tests}) + set(sources ${compile_test}.cpp) + + source_group("Source Files" FILES ${sources}) + + add_hpx_unit_compile_test( + "modules.type_support" ${compile_test} + SOURCES ${sources} ${${compile_test}_FLAGS} + FOLDER "Tests/Unit/Modules/Core/TypeSupport/CompileOnly" + ) + + endforeach() + +endif() diff --git a/libs/core/type_support/tests/unit/fail_relocate_at.cpp b/libs/core/type_support/tests/unit/fail_relocate_at.cpp new file mode 100644 index 000000000000..95a5dffa1aab --- /dev/null +++ b/libs/core/type_support/tests/unit/fail_relocate_at.cpp @@ -0,0 +1,20 @@ +// 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 + +int main(int argc, char* argv[]) +{ + int a[10]; + int b[10]; + + int(*p)[10] = &a; + int(*q)[10] = &b; + + hpx::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 new file mode 100644 index 000000000000..4f3b12ec3104 --- /dev/null +++ b/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp @@ -0,0 +1,20 @@ +// 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 + +int main(int argc, char* argv[]) +{ + int a[10]; + int b[10]; + + int(*p)[10] = &a; + int(*q)[10] = &b; + + hpx::uninitialized_relocate(p, p + 1, q); +} 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 050d4f5bdb2d..8609dc4136d6 100644 --- a/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp +++ b/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp @@ -125,6 +125,41 @@ static_assert( static_assert( hpx::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>); +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); + +// Chain of c-v-ref-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< + explicitly_trivially_relocatable_1 volatile[10]>); +static_assert(hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 const volatile[10]>); +static_assert(hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 (&)[10]>); +static_assert( + hpx::is_trivially_relocatable_v); +static_assert(hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 const volatile&>); + // Trivial relocatability is not inherited struct derived_from_explicitly_trivially_relocatable : explicitly_trivially_relocatable_1 diff --git a/libs/core/type_support/tests/unit/relocate_at.cpp b/libs/core/type_support/tests/unit/relocate_at.cpp new file mode 100644 index 000000000000..a4791c5f97b0 --- /dev/null +++ b/libs/core/type_support/tests/unit/relocate_at.cpp @@ -0,0 +1,134 @@ +// 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 + +struct non_trivially_relocatable_struct +{ + static int count; + int data; + + explicit non_trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + non_trivially_relocatable_struct(non_trivially_relocatable_struct&& other) + : data(other.data) + { + count++; + } + ~non_trivially_relocatable_struct() + { + count--; + } + + // making sure the address is never directly accessed + friend void operator&(non_trivially_relocatable_struct) = delete; +}; +int non_trivially_relocatable_struct::count = 0; + +static_assert( + !hpx::is_trivially_relocatable_v); + +struct trivially_relocatable_struct +{ + static int count; + int data; + + explicit trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + trivially_relocatable_struct(trivially_relocatable_struct&& other) + : data(other.data) + { + count++; + } + ~trivially_relocatable_struct() + { + count--; + } + + friend void operator&(trivially_relocatable_struct) = delete; +}; +int trivially_relocatable_struct::count = 0; + +HPX_DECLARE_TRIVIALLY_RELOCATABLE(trivially_relocatable_struct); +static_assert(hpx::is_trivially_relocatable_v); + +int hpx_main() +{ + { + void* mem1 = std::malloc(sizeof(non_trivially_relocatable_struct)); + void* mem2 = std::malloc(sizeof(non_trivially_relocatable_struct)); + + HPX_TEST(mem1 && mem2); + + HPX_TEST(non_trivially_relocatable_struct::count == 0); + + non_trivially_relocatable_struct* ptr1 = hpx::construct_at( + static_cast(mem1), 1234); + + non_trivially_relocatable_struct* ptr2 = + static_cast(mem2); + + // a single object was constructed + HPX_TEST(non_trivially_relocatable_struct::count == 1); + + hpx::relocate_at(ptr1, ptr2); + + // count = 1 + 1 (from the move construction) - 1 (from the destruction) + HPX_TEST(non_trivially_relocatable_struct::count == 1); + HPX_TEST(ptr2->data == 1234); + + std::destroy_at(ptr2); + + std::free(mem1); + std::free(mem2); + } + { + void* mem1 = std::malloc(sizeof(trivially_relocatable_struct)); + void* mem2 = std::malloc(sizeof(trivially_relocatable_struct)); + + HPX_TEST(mem1 && mem2); + + HPX_TEST(trivially_relocatable_struct::count == 0); + + trivially_relocatable_struct* ptr1 = hpx::construct_at( + static_cast(mem1), 1234); + + trivially_relocatable_struct* ptr2 = + static_cast(mem2); + + // a single object was constructed + HPX_TEST(trivially_relocatable_struct::count == 1); + + hpx::relocate_at(ptr1, ptr2); + + // count = 1 + 0 (relocation on trivially relocatable + // objects does not trigger move constructors + // or destructors); no object is destroyed or created + HPX_TEST(trivially_relocatable_struct::count == 1); + HPX_TEST(ptr2->data == 1234); + + std::destroy_at(ptr2); + + 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/type_support/tests/unit/uninitialized_relocate.cpp b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp new file mode 100644 index 000000000000..bd1b7103e248 --- /dev/null +++ b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp @@ -0,0 +1,275 @@ +// 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 + +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(hpx::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( + !hpx::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(!hpx::is_trivially_relocatable_v< + non_trivially_relocatable_struct_throwing>); + +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 + hpx::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 + hpx::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 + { + hpx::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + HPX_TEST(false); // should never reach this + } + catch (int forty_two) + { + HPX_TEST(forty_two == 42); + } + + // 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 b556c0217e484f99fa247dc441e58cfa3103ff95 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:55:07 +0300 Subject: [PATCH 06/42] remove commented code --- .../include/hpx/type_support/uninitialized_relocate.hpp | 5 ----- 1 file changed, 5 deletions(-) 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 38242c3efead..7c8821d7ca09 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 @@ -35,11 +35,6 @@ namespace hpx { template struct choose_uninitialized_relocate_helper { - // using in_type = typename std::remove_reference_t< - // decltype(*std::declval())>; - // using out_type = typename std::remove_reference_t< - // decltype(*std::declval())>; - using in_type = typename std::iterator_traits::value_type; using out_type = typename std::iterator_traits::value_type; From dda851928f2b815add2be721e906ad56ce3abb1d Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 21:18:35 +0300 Subject: [PATCH 07/42] reject failing destroy_at types --- .../type_support/include/hpx/type_support/relocate_at.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 036c4324910a..0af625db38d1 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 @@ -20,7 +20,13 @@ namespace hpx { namespace detail { - template +#if __cplusplus <= 201703L +// pre c++17 std::destroy_at can be used only on non-array types + template && !std::is_array_v)> +#else +// c++17 std::destroy_at can be used on array types, destructing each element + template )> +#endif struct destroy_guard { T* t; From 04111378ecc815d4403a3d6942671fa60c5b176b Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 22:26:56 +0300 Subject: [PATCH 08/42] move to namespace experimental --- .../include/hpx/type_support/relocate_at.hpp | 54 ++++++++++--------- .../type_support/uninitialized_relocate.hpp | 6 +-- .../tests/unit/fail_relocate_at.cpp | 2 +- .../unit/fail_uninitialized_relocate.cpp | 2 +- .../type_support/tests/unit/relocate_at.cpp | 4 +- .../tests/unit/uninitialized_relocate.cpp | 6 +-- 6 files changed, 40 insertions(+), 34 deletions(-) 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 0af625db38d1..aed949fd5642 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 @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -17,35 +18,36 @@ #include #endif -namespace hpx { - - namespace detail { +namespace hpx::detail { #if __cplusplus <= 201703L -// pre c++17 std::destroy_at can be used only on non-array types - template && !std::is_array_v)> + // pre c++17 std::destroy_at can be used only on non-array types + template && !std::is_array_v)> #else -// c++17 std::destroy_at can be used on array types, destructing each element - template )> + // c++17 std::destroy_at can be used on array types, destructing each element + template )> #endif - struct destroy_guard + struct destroy_guard + { + T* t; + explicit destroy_guard(T* t) + : t(t) { - T* t; - explicit destroy_guard(T* t) - : t(t) - { - } - ~destroy_guard() - { - std::destroy_at(t); - } - }; - } // namespace detail + } + ~destroy_guard() + { + std::destroy_at(t); + } + }; +} // namespace hpx::detail #if defined(HPX_HAVE_P1144_STD_RELOCATE_AT) - using std::relocate; - using std::relocate_at; +using std::relocate; +using std::relocate_at; #else +namespace hpx::experimental { + namespace detail { /* @@ -80,6 +82,8 @@ namespace hpx { // has non-throwing move constructor std::is_nothrow_move_constructible_v) { + using hpx::detail::destroy_guard; + destroy_guard g(src); return hpx::construct_at(dst, HPX_MOVE(*src)); }; @@ -102,7 +106,9 @@ namespace hpx { static_assert( hpx::is_relocatable_v, "T(std::move(*src)) must be well-formed"); - detail::destroy_guard g(src); + using hpx::detail::destroy_guard; + + destroy_guard g(src); return HPX_MOVE(*src); } @@ -120,6 +126,6 @@ namespace hpx { } */ -#endif // defined(HPX_HAVE_P1144_STD_RELOCATE_AT) +#endif // !defined(HPX_HAVE_P1144_STD_RELOCATE_AT) -} // namespace hpx +} // namespace hpx::experimental 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 7c8821d7ca09..af00b73926b0 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 @@ -17,7 +17,7 @@ #include #endif -namespace hpx { +namespace hpx::experimental { #if defined(HPX_HAVE_P1144_STD_RELOCATE_AT) using std::uninitialized_relocate; @@ -114,7 +114,7 @@ namespace hpx { for (; first != last; ++first, ++dst) { // the move + destroy version will be used - hpx::relocate_at(std::addressof(*first), std::addressof(*dst)); + hpx::experimental::relocate_at(std::addressof(*first), std::addressof(*dst)); } return dst; @@ -135,7 +135,7 @@ namespace hpx { try { // the move + destroy version will be used - hpx::relocate_at( + hpx::experimental::relocate_at( std::addressof(*first), std::addressof(*dst)); } catch (...) 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 95a5dffa1aab..875443d1e705 100644 --- a/libs/core/type_support/tests/unit/fail_relocate_at.cpp +++ b/libs/core/type_support/tests/unit/fail_relocate_at.cpp @@ -16,5 +16,5 @@ int main(int argc, char* argv[]) int(*p)[10] = &a; int(*q)[10] = &b; - hpx::relocate_at(p, q); + hpx::experimental::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 4f3b12ec3104..ea0c533f1cbe 100644 --- a/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp @@ -16,5 +16,5 @@ int main(int argc, char* argv[]) int(*p)[10] = &a; int(*q)[10] = &b; - hpx::uninitialized_relocate(p, p + 1, q); + hpx::experimental::uninitialized_relocate(p, p + 1, q); } diff --git a/libs/core/type_support/tests/unit/relocate_at.cpp b/libs/core/type_support/tests/unit/relocate_at.cpp index a4791c5f97b0..7e99db32ec5f 100644 --- a/libs/core/type_support/tests/unit/relocate_at.cpp +++ b/libs/core/type_support/tests/unit/relocate_at.cpp @@ -83,7 +83,7 @@ int hpx_main() // a single object was constructed HPX_TEST(non_trivially_relocatable_struct::count == 1); - hpx::relocate_at(ptr1, ptr2); + hpx::experimental::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 +111,7 @@ int hpx_main() // a single object was constructed HPX_TEST(trivially_relocatable_struct::count == 1); - hpx::relocate_at(ptr1, ptr2); + hpx::experimental::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 bd1b7103e248..fec880557ff5 100644 --- a/libs/core/type_support/tests/unit/uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp @@ -150,7 +150,7 @@ int hpx_main() HPX_TEST(trivially_relocatable_struct::count == N); // relocate them to ptr2 - hpx::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + hpx::experimental::uninitialized_relocate(ptr1, ptr1 + N, ptr2); // All creations - destructions balance out HPX_TEST(trivially_relocatable_struct::count == N); @@ -193,7 +193,7 @@ int hpx_main() HPX_TEST(non_trivially_relocatable_struct::count == N); // relocate them to ptr2 - hpx::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + hpx::experimental::uninitialized_relocate(ptr1, ptr1 + N, ptr2); // All creations - destructions balance out HPX_TEST(non_trivially_relocatable_struct::count == N); @@ -240,7 +240,7 @@ int hpx_main() // relocate them to ptr2 try { - hpx::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + hpx::experimental::uninitialized_relocate(ptr1, ptr1 + N, ptr2); HPX_TEST(false); // should never reach this } catch (int forty_two) From b2951d3c3e509d09754ae50f32c1b1b1ff86c88c Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Sat, 19 Aug 2023 17:10:58 +0300 Subject: [PATCH 09/42] make relocate's format like relocate_at --- .../include/hpx/type_support/relocate_at.hpp | 56 +++++++++++++------ 1 file changed, 38 insertions(+), 18 deletions(-) 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 aed949fd5642..baa7696ccda9 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 @@ -87,6 +87,43 @@ namespace hpx::experimental { destroy_guard g(src); return hpx::construct_at(dst, HPX_MOVE(*src)); }; + + template + T relocate_helper(T* src) noexcept( + std::is_nothrow_move_constructible_v) + { + using hpx::detail::destroy_guard; + + destroy_guard g(src); + return HPX_MOVE(*src); + } + + /* + P1144 also proposes a version of relocate that does not call the + move constructor and instead memmoves the bytes of src to dest. + + Giving an interface like: + + T dest = relocate(std::addressof(src)); + + That results in a valid T object (dest) without calling any + constructor or destructor. + + This is not possible to do with the current C++ standard. + + One of the proposed ways to implement this uses a hypothetical + attribute "do_not_construct" and NRVO. + + Implementation: + + template , int> = 0> + T relocate(T* source) + { + __attribute__((do_not_construct)) T t; // hypothetical attribute + std::memmove(std::addressof(t), source, sizeof(T)); + return t; // NRVO + } + */ } // namespace detail template @@ -106,25 +143,8 @@ namespace hpx::experimental { static_assert( hpx::is_relocatable_v, "T(std::move(*src)) must be well-formed"); - using hpx::detail::destroy_guard; - - destroy_guard g(src); - return HPX_MOVE(*src); - } - - /* - Memmove codegen. This part relies on UB, so it's not used. It's here for - reference. More info on this: - - https://quuxplusone.github.io/blog/2022/05/18/std-relocate/ - - template - T relocate(T* source) - { - auto magic = (T(*)(void*, size_t)) memcpy; - return magic(source, sizeof(T)); + return detail::relocate_helper(src); } - */ #endif // !defined(HPX_HAVE_P1144_STD_RELOCATE_AT) From aa09ce45ee1304b7611dc440ef9294f0f9ad9244 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Sat, 19 Aug 2023 17:13:18 +0300 Subject: [PATCH 10/42] relocate test --- .../type_support/tests/unit/CMakeLists.txt | 2 +- .../core/type_support/tests/unit/relocate.cpp | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 libs/core/type_support/tests/unit/relocate.cpp diff --git a/libs/core/type_support/tests/unit/CMakeLists.txt b/libs/core/type_support/tests/unit/CMakeLists.txt index a84d995b157e..83886f941586 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_at uninitialized_relocate) +set(tests relocate relocate_at uninitialized_relocate) if(HPX_WITH_CXX20_COROUTINES) set(tests ${tests} generator) diff --git a/libs/core/type_support/tests/unit/relocate.cpp b/libs/core/type_support/tests/unit/relocate.cpp new file mode 100644 index 000000000000..3871598b40e2 --- /dev/null +++ b/libs/core/type_support/tests/unit/relocate.cpp @@ -0,0 +1,74 @@ +// 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) + +/* + This test checks that the relocate_at function works correctly + for non-trivially relocatable types. + + The trivially relocatable optimization can not be implemented yet + so it is not tested separately. +*/ + +#include +#include +#include +#include + +struct non_trivially_relocatable_struct +{ + static int count; + int data; + + explicit non_trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + non_trivially_relocatable_struct(non_trivially_relocatable_struct&& other) + : data(other.data) + { + count++; + } + ~non_trivially_relocatable_struct() + { + 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 hpx_main() +{ + void* mem1 = std::malloc(sizeof(non_trivially_relocatable_struct)); + + HPX_TEST(mem1); + + HPX_TEST(non_trivially_relocatable_struct::count == 0); + + non_trivially_relocatable_struct* ptr1 = hpx::construct_at( + static_cast(mem1), 1234); + + // a single object was constructed + HPX_TEST(non_trivially_relocatable_struct::count == 1); + + non_trivially_relocatable_struct obj2 = hpx::experimental::relocate(ptr1); + + // count = 1 + 1 (from the move construction) - 1 (from the destruction) + HPX_TEST(non_trivially_relocatable_struct::count == 1); + HPX_TEST(obj2.data == 1234); + + std::free(mem1); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + hpx::local::init(hpx_main, argc, argv); + return hpx::util::report_errors(); +} From 2b55ebfbe86e1d1f7281127fca27a3114c303c66 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Sat, 19 Aug 2023 17:14:13 +0300 Subject: [PATCH 11/42] clang-format and comments --- .../include/hpx/type_support/uninitialized_relocate.hpp | 5 +++-- libs/core/type_support/tests/unit/fail_relocate_at.cpp | 2 ++ .../type_support/tests/unit/fail_uninitialized_relocate.cpp | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) 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 af00b73926b0..7e6d6044d4b4 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 @@ -114,7 +114,8 @@ namespace hpx::experimental { for (; first != last; ++first, ++dst) { // the move + destroy version will be used - hpx::experimental::relocate_at(std::addressof(*first), std::addressof(*dst)); + hpx::experimental::relocate_at( + std::addressof(*first), std::addressof(*dst)); } return dst; @@ -171,4 +172,4 @@ namespace hpx::experimental { #endif // defined(HPX_HAVE_P1144_STD_RELOCATE_AT) -} // namespace hpx +} // namespace hpx::experimental 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 875443d1e705..f4d9881fc8cf 100644 --- a/libs/core/type_support/tests/unit/fail_relocate_at.cpp +++ b/libs/core/type_support/tests/unit/fail_relocate_at.cpp @@ -4,6 +4,8 @@ // 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) +// This test should fail to compile + #include #include #include 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 ea0c533f1cbe..9c4110838a28 100644 --- a/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp @@ -4,6 +4,8 @@ // 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) +// This test should fail to compile + #include #include #include From 57d4264f3a079a7b38c85abf8205d892191e6418 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Sat, 19 Aug 2023 18:22:59 +0300 Subject: [PATCH 12/42] remove whitespaces for inspect --- .../type_support/include/hpx/type_support/relocate_at.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 baa7696ccda9..86da478e9f62 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 @@ -99,16 +99,16 @@ namespace hpx::experimental { } /* - P1144 also proposes a version of relocate that does not call the + P1144 also proposes a version of relocate that does not call the move constructor and instead memmoves the bytes of src to dest. Giving an interface like: T dest = relocate(std::addressof(src)); - That results in a valid T object (dest) without calling any + That results in a valid T object (dest) without calling any constructor or destructor. - + This is not possible to do with the current C++ standard. One of the proposed ways to implement this uses a hypothetical From 799fc3dc383f0863899d876a18a7f48b5c904f0c Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 13:28:58 +0300 Subject: [PATCH 13/42] fix fail test --- libs/core/type_support/tests/unit/fail_relocate_at.cpp | 1 - .../core/type_support/tests/unit/fail_uninitialized_relocate.cpp | 1 - 2 files changed, 2 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 f4d9881fc8cf..be88f03d36ee 100644 --- a/libs/core/type_support/tests/unit/fail_relocate_at.cpp +++ b/libs/core/type_support/tests/unit/fail_relocate_at.cpp @@ -8,7 +8,6 @@ #include #include -#include int main(int argc, char* argv[]) { 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 9c4110838a28..affcb672fd67 100644 --- a/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp @@ -8,7 +8,6 @@ #include #include -#include int main(int argc, char* argv[]) { From b80f7b931febcb30125df5aa04f6545b9db85b8a Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 13:32:53 +0300 Subject: [PATCH 14/42] & and && are nor triv. rel. --- .../type_support/is_trivially_relocatable.hpp | 29 ++++++++++--------- .../tests/unit/is_trivially_relocatable.cpp | 27 ++++++++++------- 2 files changed, 33 insertions(+), 23 deletions(-) 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 ad9d74cbf115..a5587a963bfa 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 @@ -42,50 +42,53 @@ namespace hpx { { }; - // Constness, Volatility, References, Arrays are ignored + // References are not trivially relocatable template - struct is_trivially_relocatable : is_trivially_relocatable + struct is_trivially_relocatable : std::false_type { }; + // Temporary objects are not trivially relocatable template - struct is_trivially_relocatable : is_trivially_relocatable + struct is_trivially_relocatable : std::false_type { }; + // Constness, Volatility, Arrays are ignored template - struct is_trivially_relocatable - : is_trivially_relocatable + struct is_trivially_relocatable : is_trivially_relocatable { }; template - struct is_trivially_relocatable : is_trivially_relocatable + struct is_trivially_relocatable : is_trivially_relocatable { }; template - struct is_trivially_relocatable : is_trivially_relocatable + struct is_trivially_relocatable + : is_trivially_relocatable { }; + template struct is_trivially_relocatable : is_trivially_relocatable { }; - template - struct is_trivially_relocatable : is_trivially_relocatable + template + struct is_trivially_relocatable : is_trivially_relocatable { }; - template - struct is_trivially_relocatable : is_trivially_relocatable + template + struct is_trivially_relocatable : is_trivially_relocatable { }; - template - struct is_trivially_relocatable + template + struct is_trivially_relocatable : is_trivially_relocatable { }; 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 8609dc4136d6..76606e261878 100644 --- a/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp +++ b/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp @@ -134,16 +134,12 @@ 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>); -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); -// Chain of c-v-ref-array qualifiers are supported +// 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< @@ -152,13 +148,19 @@ static_assert(hpx::is_trivially_relocatable_v< explicitly_trivially_relocatable_1 volatile[10]>); static_assert(hpx::is_trivially_relocatable_v< explicitly_trivially_relocatable_1 const volatile[10]>); -static_assert(hpx::is_trivially_relocatable_v< - explicitly_trivially_relocatable_1 (&)[10]>); + +// References and temporaries 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< + explicitly_trivially_relocatable_1 (&)[10]>); +static_assert( + !hpx::is_trivially_relocatable_v); -static_assert(hpx::is_trivially_relocatable_v< - explicitly_trivially_relocatable_1 const volatile&>); +static_assert(!hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 const volatile&>); // Trivial relocatability is not inherited struct derived_from_explicitly_trivially_relocatable @@ -276,4 +278,9 @@ static_assert(hpx::is_trivially_relocatable_v, static_assert(hpx::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); + int main(int, char*[]) {} From d78947069bfc048950a784b337d9a9a90d9603ac Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 13:33:13 +0300 Subject: [PATCH 15/42] unneeded void cast --- .../type_support/uninitialized_relocate.hpp | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) 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 7e6d6044d4b4..650cc255279d 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 @@ -77,17 +77,13 @@ namespace hpx::experimental { if (n_objects != 0) { - void* first_void = const_cast( - static_cast(std::addressof(*first))); - void* last_void = const_cast( - static_cast(std::addressof(*last))); + std::byte const* first_byte = + reinterpret_cast(std::addressof(*first)); + std::byte const* last_byte = + reinterpret_cast(std::addressof(*last)); - void* dst_void = const_cast( - static_cast(std::addressof(*dst))); - - std::byte* first_byte = - reinterpret_cast(first_void); - std::byte* last_byte = reinterpret_cast(last_void); + std::byte* dst_byte = const_cast( + reinterpret_cast(std::addressof(*dst))); auto n_bytes = std::distance(first_byte, last_byte); @@ -95,7 +91,7 @@ namespace hpx::experimental { // That the new buffer actually contains objects // within their lifetime. But this is not possible // with current language features. - std::memmove(dst_void, first_void, n_bytes); + std::memmove(dst_byte, first_byte, n_bytes); dst += n_objects; } @@ -142,7 +138,8 @@ namespace hpx::experimental { catch (...) { // destroy all objects other that the one - // that caused the exception + // that caused the exception + // (relocate_at already destroyed that one) // destroy all objects constructed so far std::destroy(original_dst, dst); From 8d218e526ee7858d2e2a77bcd3715315cb8164b4 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 13:34:26 +0300 Subject: [PATCH 16/42] addition to a test --- libs/core/type_support/tests/unit/uninitialized_relocate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/core/type_support/tests/unit/uninitialized_relocate.cpp b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp index fec880557ff5..c2d55b671b67 100644 --- a/libs/core/type_support/tests/unit/uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp @@ -165,6 +165,7 @@ int hpx_main() } std::destroy(ptr2, ptr2 + N); + HPX_TEST(trivially_relocatable_struct::dtor_count == N); std::free(mem1); std::free(mem2); From f1e142e30ac2a7110c46568ad35db65a1c34969e Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 17:12:15 +0300 Subject: [PATCH 17/42] only objects are relocatable --- .../hpx/type_support/is_relocatable.hpp | 6 +- .../tests/unit/is_relocatable.cpp | 62 ++++++++++++++++--- 2 files changed, 57 insertions(+), 11 deletions(-) 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 05e881bfc69c..2d0c6d919ccb 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 @@ -11,7 +11,9 @@ namespace hpx { template - struct is_relocatable : std::is_move_constructible + struct is_relocatable + : std::bool_constant && + std::is_object_v> { }; @@ -20,7 +22,7 @@ namespace hpx { struct is_relocatable_from : std::bool_constant< std::is_constructible_v, FromTp> && - std::is_same_v, std::decay_t>> + 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 5b0c982e146a..f0a09884493c 100644 --- a/libs/core/type_support/tests/unit/is_relocatable.cpp +++ b/libs/core/type_support/tests/unit/is_relocatable.cpp @@ -8,18 +8,23 @@ #include +#include // for std::shared_ptr, std::unique_ptr #include // Integral types are relocatable static_assert(hpx::is_relocatable_v); static_assert(hpx::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); // 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(!hpx::is_relocatable_v); // Function types are not move-constructible and thus not relocatable @@ -56,13 +61,52 @@ struct not_copy_constructible static_assert(hpx::is_relocatable_v); -// reference 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(hpx::is_relocatable_v); -static_assert(hpx::is_relocatable_v); -static_assert(hpx::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); + +/* + Tests for is_relocatable_from +*/ + +// clang-format off + +// Reference types are not relocatable +static_assert(!hpx::is_relocatable_from_v< + int (&)[], int (&)[4]>); + +// Array types are not move constructible +static_assert(!hpx::is_relocatable_from_v< + int[4], int[4]>); + +// This is a simple pointer +static_assert(hpx::is_relocatable_from_v< + int (*)[4], int (*)[4]>); + +// Can move from const shared_ptr +static_assert(hpx::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< + std::unique_ptr, const std::unique_ptr>); + +// Can move away from a non-const unique_ptr +static_assert(hpx::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< + 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< + const std::unique_ptr, const std::unique_ptr>); +// clang-format on int main(int, char*[]) {} From 3801e8b5cdfb7ac0d184126da23dc05bb898bd14 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 17:23:10 +0300 Subject: [PATCH 18/42] comment correction --- .../include/hpx/type_support/relocate_at.hpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) 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 86da478e9f62..8aa1750097ff 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 @@ -19,12 +19,12 @@ #endif namespace hpx::detail { -#if __cplusplus <= 201703L - // pre c++17 std::destroy_at can be used only on non-array types +#if __cplusplus < 202002L + // until c++20 std::destroy_at can be used only on non-array types template && !std::is_array_v)> #else - // c++17 std::destroy_at can be used on array types, destructing each element + // since c++20 std::destroy_at can be used on array types, destructing each element template )> #endif struct destroy_guard @@ -79,12 +79,9 @@ namespace hpx::experimental { template , int> = 0> T* relocate_at_helper(T* src, T* dst) noexcept( - // has non-throwing move constructor std::is_nothrow_move_constructible_v) { - using hpx::detail::destroy_guard; - - destroy_guard g(src); + hpx::detail::destroy_guard g(src); return hpx::construct_at(dst, HPX_MOVE(*src)); }; @@ -92,9 +89,7 @@ namespace hpx::experimental { T relocate_helper(T* src) noexcept( std::is_nothrow_move_constructible_v) { - using hpx::detail::destroy_guard; - - destroy_guard g(src); + hpx::detail::destroy_guard g(src); return HPX_MOVE(*src); } @@ -138,7 +133,7 @@ namespace hpx::experimental { } template - T relocate(T* src) noexcept(std::is_nothrow_move_constructible_v) + T relocate(T* src) noexcept(noexcept(detail::relocate_helper(src))) { static_assert( hpx::is_relocatable_v, "T(std::move(*src)) must be well-formed"); From 3261583186861b8425142a9af1131d6d80704e92 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 17:31:38 +0300 Subject: [PATCH 19/42] clang-format --- .../type_support/is_trivially_relocatable.hpp | 4 +--- .../hpx/type_support/uninitialized_relocate.hpp | 2 +- .../tests/unit/is_trivially_relocatable.cpp | 17 +++++++++-------- 3 files changed, 11 insertions(+), 12 deletions(-) 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 a5587a963bfa..a29546d2ae8e 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 @@ -71,7 +71,6 @@ namespace hpx { { }; - template struct is_trivially_relocatable : is_trivially_relocatable { @@ -88,8 +87,7 @@ namespace hpx { }; template - struct is_trivially_relocatable - : is_trivially_relocatable + struct is_trivially_relocatable : is_trivially_relocatable { }; 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 650cc255279d..babb9f26aa33 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 @@ -138,7 +138,7 @@ namespace hpx::experimental { catch (...) { // destroy all objects other that the one - // that caused the exception + // that caused the exception // (relocate_at already destroyed that one) // destroy all objects constructed so far 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 76606e261878..c20bb642b208 100644 --- a/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp +++ b/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp @@ -150,17 +150,18 @@ static_assert(hpx::is_trivially_relocatable_v< explicitly_trivially_relocatable_1 const volatile[10]>); // References and temporaries are not trivially relocatable -static_assert( - !hpx::is_trivially_relocatable_v); -static_assert( - !hpx::is_trivially_relocatable_v); +// clang-format off static_assert(!hpx::is_trivially_relocatable_v< - explicitly_trivially_relocatable_1 (&)[10]>); -static_assert( - !hpx::is_trivially_relocatable_v); + explicitly_trivially_relocatable_1&>); +static_assert(!hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1&&>); +static_assert(!hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 (&)[10]>); +static_assert(!hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 (&&)[10]>); static_assert(!hpx::is_trivially_relocatable_v< explicitly_trivially_relocatable_1 const volatile&>); +// clang-format on // Trivial relocatability is not inherited struct derived_from_explicitly_trivially_relocatable From 3a5d8d2e443688777f9ab8ad93835666637a4296 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 17:32:34 +0300 Subject: [PATCH 20/42] template arg name size -> N --- .../include/hpx/type_support/is_trivially_relocatable.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 a29546d2ae8e..1a777ed11986 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 @@ -91,8 +91,8 @@ namespace hpx { { }; - template - struct is_trivially_relocatable + template + struct is_trivially_relocatable : is_trivially_relocatable { }; From bf1a01a5a2490159d5a8aea09794119058e3de98 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Thu, 24 Aug 2023 12:42:15 +0300 Subject: [PATCH 21/42] decay -> remove_cv --- .../hpx/type_support/uninitialized_relocate.hpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) 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 babb9f26aa33..750dc601d660 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 @@ -44,7 +44,8 @@ namespace hpx::experimental { constexpr static bool is_buffer_memcpyable = hpx::is_trivially_relocatable_v && // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The important check - std::is_same_v, std::decay_t> && + 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 @@ -103,14 +104,17 @@ namespace hpx::experimental { std::enable_if_t< choose_uninitialized_relocate_helper::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) { - // the move + destroy version will be used - hpx::experimental::relocate_at( + // 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)); } @@ -132,7 +136,7 @@ namespace hpx::experimental { try { // the move + destroy version will be used - hpx::experimental::relocate_at( + relocate_at_helper( std::addressof(*first), std::addressof(*dst)); } catch (...) From 1c0973292c497b7f43ae71cd6532e6a7d9993095 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:09:36 +0300 Subject: [PATCH 22/42] similar type detection --- .../type_support/is_trivially_relocatable.hpp | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) 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 5ece0827cf8d..ad9d74cbf115 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 @@ -42,6 +42,60 @@ namespace hpx { { }; + // Constness, Volatility, References, Arrays are ignored + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable + : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable + : is_trivially_relocatable + { + }; + + template + struct is_trivially_relocatable + : is_trivially_relocatable + { + }; + template inline constexpr bool is_trivially_relocatable_v = is_trivially_relocatable::value; From 168aad6cc5cc5b5d4a39d4a4d106063c7d0fa4bb Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:09:56 +0300 Subject: [PATCH 23/42] is_relocatable_from --- .../include/hpx/type_support/is_relocatable.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 d31a92dd31a2..05e881bfc69c 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 @@ -15,6 +15,19 @@ namespace hpx { { }; + // ToTp(FromTp&&) must be well-formed + template + struct is_relocatable_from + : std::bool_constant< + std::is_constructible_v, FromTp> && + std::is_same_v, std::decay_t>> + { + }; + template inline constexpr bool is_relocatable_v = is_relocatable::value; + + template + inline constexpr bool is_relocatable_from_v = + is_relocatable_from::value; } // namespace hpx From 9dff12aefb72211605143d5a0adcb938a1e137aa Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:10:10 +0300 Subject: [PATCH 24/42] relocate_at algorithm --- .../include/hpx/type_support/relocate_at.hpp | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 libs/core/type_support/include/hpx/type_support/relocate_at.hpp 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 new file mode 100644 index 000000000000..036c4324910a --- /dev/null +++ b/libs/core/type_support/include/hpx/type_support/relocate_at.hpp @@ -0,0 +1,119 @@ +// 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 { + + namespace detail { + template + struct destroy_guard + { + T* t; + explicit destroy_guard(T* t) + : t(t) + { + } + ~destroy_guard() + { + std::destroy_at(t); + } + }; + } // namespace detail + +#if defined(HPX_HAVE_P1144_STD_RELOCATE_AT) + using std::relocate; + using std::relocate_at; +#else + + namespace detail { + + /* + The condition to use memmove is: + hpx::is_trivially_relocatable_v && + !std::is_volatile_v + + The reason for the volatile check is that memmove is not allowed to + change the value of a volatile object. + */ + + template + constexpr bool relocate_using_memmove = + hpx::is_trivially_relocatable_v && !std::is_volatile_v; + + template , int> = 0> + T* relocate_at_helper(T* src, T* dst) noexcept + { + void* dst_void = const_cast(static_cast(dst)); + void* src_void = const_cast(static_cast(src)); + + std::memmove(dst_void, src_void, sizeof(T)); + + return std::launder(dst); + }; + + // this is move and destroy + template , int> = 0> + T* relocate_at_helper(T* src, T* dst) noexcept( + // has non-throwing move constructor + std::is_nothrow_move_constructible_v) + { + destroy_guard g(src); + return hpx::construct_at(dst, HPX_MOVE(*src)); + }; + } // namespace detail + + template + T* relocate_at(T* src, T* dst) noexcept( + // 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, + "new (dst) T(std::move(*src)) must be well-formed"); + + return detail::relocate_at_helper(src, dst); + } + + template + T relocate(T* src) noexcept(std::is_nothrow_move_constructible_v) + { + static_assert( + hpx::is_relocatable_v, "T(std::move(*src)) must be well-formed"); + + detail::destroy_guard g(src); + return HPX_MOVE(*src); + } + + /* + Memmove codegen. This part relies on UB, so it's not used. It's here for + reference. More info on this: + + https://quuxplusone.github.io/blog/2022/05/18/std-relocate/ + + template + T relocate(T* source) + { + auto magic = (T(*)(void*, size_t)) memcpy; + return magic(source, sizeof(T)); + } + */ + +#endif // defined(HPX_HAVE_P1144_STD_RELOCATE_AT) + +} // namespace hpx From d4a3a4a04ae0fa61624ecd5155241ea36efd8ac8 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:10:31 +0300 Subject: [PATCH 25/42] uninitialized_relocate algorithm --- libs/core/type_support/CMakeLists.txt | 2 + .../type_support/uninitialized_relocate.hpp | 179 ++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp diff --git a/libs/core/type_support/CMakeLists.txt b/libs/core/type_support/CMakeLists.txt index 74ef2795d0d2..1e931219e277 100644 --- a/libs/core/type_support/CMakeLists.txt +++ b/libs/core/type_support/CMakeLists.txt @@ -22,7 +22,9 @@ set(type_support_headers hpx/type_support/lazy_enable_if.hpp hpx/type_support/pack.hpp 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/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 new file mode 100644 index 000000000000..38242c3efead --- /dev/null +++ b/libs/core/type_support/include/hpx/type_support/uninitialized_relocate.hpp @@ -0,0 +1,179 @@ +// 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 { + +#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::remove_reference_t< + // decltype(*std::declval())>; + // using out_type = typename std::remove_reference_t< + // decltype(*std::declval())>; + + using in_type = typename std::iterator_traits::value_type; + using out_type = typename std::iterator_traits::value_type; + + constexpr static bool valid_relocation = + hpx::is_relocatable_from_v; + + constexpr static bool is_buffer_memcpyable = + hpx::is_trivially_relocatable_v && + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The important check + std::is_same_v, std::decay_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) + { + void* first_void = const_cast( + static_cast(std::addressof(*first))); + void* last_void = const_cast( + static_cast(std::addressof(*last))); + + void* dst_void = const_cast( + static_cast(std::addressof(*dst))); + + std::byte* first_byte = + reinterpret_cast(first_void); + std::byte* last_byte = reinterpret_cast(last_void); + + 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_void, first_void, n_bytes); + + dst += n_objects; + } + + return dst; + } + + template ::value == + relocate_strategy::for_loop_nothrow, + int> = 0> + FwdIter uninitialized_relocate_helper( + InIter first, InIter last, FwdIter dst) noexcept + { + for (; first != last; ++first, ++dst) + { + // the move + destroy version will be used + hpx::relocate_at(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 + hpx::relocate_at( + std::addressof(*first), std::addressof(*dst)); + } + catch (...) + { + // destroy all objects other that the one + // that caused the exception + + // 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 From e0449d53ed7e643daa6bc28b521267cd764ad9cc Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:10:49 +0300 Subject: [PATCH 26/42] tests --- .../type_support/tests/unit/CMakeLists.txt | 30 +- .../tests/unit/fail_relocate_at.cpp | 20 ++ .../unit/fail_uninitialized_relocate.cpp | 20 ++ .../tests/unit/is_trivially_relocatable.cpp | 35 +++ .../type_support/tests/unit/relocate_at.cpp | 134 +++++++++ .../tests/unit/uninitialized_relocate.cpp | 275 ++++++++++++++++++ 6 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 libs/core/type_support/tests/unit/fail_relocate_at.cpp create mode 100644 libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp create mode 100644 libs/core/type_support/tests/unit/relocate_at.cpp create mode 100644 libs/core/type_support/tests/unit/uninitialized_relocate.cpp diff --git a/libs/core/type_support/tests/unit/CMakeLists.txt b/libs/core/type_support/tests/unit/CMakeLists.txt index 29519a4d3852..a84d995b157e 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 is_relocatable is_trivially_relocatable) +set(tests relocate_at uninitialized_relocate) if(HPX_WITH_CXX20_COROUTINES) set(tests ${tests} generator) @@ -29,3 +29,31 @@ foreach(test ${tests}) add_hpx_unit_test("modules.type_support" ${test} ${${test}_PARAMETERS}) endforeach() + +if(HPX_WITH_COMPILE_ONLY_TESTS) + # add compile time tests + set(compile_tests is_relocatable is_trivially_relocatable) + + if(HPX_WITH_FAIL_COMPILE_TESTS) + set(fail_compile_tests fail_relocate_at fail_uninitialized_relocate) + foreach(fail_compile_test ${fail_compile_tests}) + set(${fail_compile_test}_FLAGS FAILURE_EXPECTED) + endforeach() + + set(compile_tests ${compile_tests} ${fail_compile_tests}) + endif() + + foreach(compile_test ${compile_tests}) + set(sources ${compile_test}.cpp) + + source_group("Source Files" FILES ${sources}) + + add_hpx_unit_compile_test( + "modules.type_support" ${compile_test} + SOURCES ${sources} ${${compile_test}_FLAGS} + FOLDER "Tests/Unit/Modules/Core/TypeSupport/CompileOnly" + ) + + endforeach() + +endif() diff --git a/libs/core/type_support/tests/unit/fail_relocate_at.cpp b/libs/core/type_support/tests/unit/fail_relocate_at.cpp new file mode 100644 index 000000000000..95a5dffa1aab --- /dev/null +++ b/libs/core/type_support/tests/unit/fail_relocate_at.cpp @@ -0,0 +1,20 @@ +// 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 + +int main(int argc, char* argv[]) +{ + int a[10]; + int b[10]; + + int(*p)[10] = &a; + int(*q)[10] = &b; + + hpx::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 new file mode 100644 index 000000000000..4f3b12ec3104 --- /dev/null +++ b/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp @@ -0,0 +1,20 @@ +// 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 + +int main(int argc, char* argv[]) +{ + int a[10]; + int b[10]; + + int(*p)[10] = &a; + int(*q)[10] = &b; + + hpx::uninitialized_relocate(p, p + 1, q); +} 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 050d4f5bdb2d..8609dc4136d6 100644 --- a/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp +++ b/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp @@ -125,6 +125,41 @@ static_assert( static_assert( hpx::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>); +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); + +// Chain of c-v-ref-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< + explicitly_trivially_relocatable_1 volatile[10]>); +static_assert(hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 const volatile[10]>); +static_assert(hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 (&)[10]>); +static_assert( + hpx::is_trivially_relocatable_v); +static_assert(hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 const volatile&>); + // Trivial relocatability is not inherited struct derived_from_explicitly_trivially_relocatable : explicitly_trivially_relocatable_1 diff --git a/libs/core/type_support/tests/unit/relocate_at.cpp b/libs/core/type_support/tests/unit/relocate_at.cpp new file mode 100644 index 000000000000..a4791c5f97b0 --- /dev/null +++ b/libs/core/type_support/tests/unit/relocate_at.cpp @@ -0,0 +1,134 @@ +// 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 + +struct non_trivially_relocatable_struct +{ + static int count; + int data; + + explicit non_trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + non_trivially_relocatable_struct(non_trivially_relocatable_struct&& other) + : data(other.data) + { + count++; + } + ~non_trivially_relocatable_struct() + { + count--; + } + + // making sure the address is never directly accessed + friend void operator&(non_trivially_relocatable_struct) = delete; +}; +int non_trivially_relocatable_struct::count = 0; + +static_assert( + !hpx::is_trivially_relocatable_v); + +struct trivially_relocatable_struct +{ + static int count; + int data; + + explicit trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + trivially_relocatable_struct(trivially_relocatable_struct&& other) + : data(other.data) + { + count++; + } + ~trivially_relocatable_struct() + { + count--; + } + + friend void operator&(trivially_relocatable_struct) = delete; +}; +int trivially_relocatable_struct::count = 0; + +HPX_DECLARE_TRIVIALLY_RELOCATABLE(trivially_relocatable_struct); +static_assert(hpx::is_trivially_relocatable_v); + +int hpx_main() +{ + { + void* mem1 = std::malloc(sizeof(non_trivially_relocatable_struct)); + void* mem2 = std::malloc(sizeof(non_trivially_relocatable_struct)); + + HPX_TEST(mem1 && mem2); + + HPX_TEST(non_trivially_relocatable_struct::count == 0); + + non_trivially_relocatable_struct* ptr1 = hpx::construct_at( + static_cast(mem1), 1234); + + non_trivially_relocatable_struct* ptr2 = + static_cast(mem2); + + // a single object was constructed + HPX_TEST(non_trivially_relocatable_struct::count == 1); + + hpx::relocate_at(ptr1, ptr2); + + // count = 1 + 1 (from the move construction) - 1 (from the destruction) + HPX_TEST(non_trivially_relocatable_struct::count == 1); + HPX_TEST(ptr2->data == 1234); + + std::destroy_at(ptr2); + + std::free(mem1); + std::free(mem2); + } + { + void* mem1 = std::malloc(sizeof(trivially_relocatable_struct)); + void* mem2 = std::malloc(sizeof(trivially_relocatable_struct)); + + HPX_TEST(mem1 && mem2); + + HPX_TEST(trivially_relocatable_struct::count == 0); + + trivially_relocatable_struct* ptr1 = hpx::construct_at( + static_cast(mem1), 1234); + + trivially_relocatable_struct* ptr2 = + static_cast(mem2); + + // a single object was constructed + HPX_TEST(trivially_relocatable_struct::count == 1); + + hpx::relocate_at(ptr1, ptr2); + + // count = 1 + 0 (relocation on trivially relocatable + // objects does not trigger move constructors + // or destructors); no object is destroyed or created + HPX_TEST(trivially_relocatable_struct::count == 1); + HPX_TEST(ptr2->data == 1234); + + std::destroy_at(ptr2); + + 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/type_support/tests/unit/uninitialized_relocate.cpp b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp new file mode 100644 index 000000000000..bd1b7103e248 --- /dev/null +++ b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp @@ -0,0 +1,275 @@ +// 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 + +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(hpx::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( + !hpx::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(!hpx::is_trivially_relocatable_v< + non_trivially_relocatable_struct_throwing>); + +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 + hpx::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 + hpx::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 + { + hpx::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + HPX_TEST(false); // should never reach this + } + catch (int forty_two) + { + HPX_TEST(forty_two == 42); + } + + // 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 0b14f6f27e536525d59df5dd2828b85c6b208d45 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 20:55:07 +0300 Subject: [PATCH 27/42] remove commented code --- .../include/hpx/type_support/uninitialized_relocate.hpp | 5 ----- 1 file changed, 5 deletions(-) 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 38242c3efead..7c8821d7ca09 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 @@ -35,11 +35,6 @@ namespace hpx { template struct choose_uninitialized_relocate_helper { - // using in_type = typename std::remove_reference_t< - // decltype(*std::declval())>; - // using out_type = typename std::remove_reference_t< - // decltype(*std::declval())>; - using in_type = typename std::iterator_traits::value_type; using out_type = typename std::iterator_traits::value_type; From 5b96c29d23f31345c2a7688a311848619ea82d41 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 21:18:35 +0300 Subject: [PATCH 28/42] reject failing destroy_at types --- .../type_support/include/hpx/type_support/relocate_at.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 036c4324910a..0af625db38d1 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 @@ -20,7 +20,13 @@ namespace hpx { namespace detail { - template +#if __cplusplus <= 201703L +// pre c++17 std::destroy_at can be used only on non-array types + template && !std::is_array_v)> +#else +// c++17 std::destroy_at can be used on array types, destructing each element + template )> +#endif struct destroy_guard { T* t; From 8fecec033d7a684935147b9d7ebb21394ebf7183 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Fri, 18 Aug 2023 22:26:56 +0300 Subject: [PATCH 29/42] move to namespace experimental --- .../include/hpx/type_support/relocate_at.hpp | 54 ++++++++++--------- .../type_support/uninitialized_relocate.hpp | 6 +-- .../tests/unit/fail_relocate_at.cpp | 2 +- .../unit/fail_uninitialized_relocate.cpp | 2 +- .../type_support/tests/unit/relocate_at.cpp | 4 +- .../tests/unit/uninitialized_relocate.cpp | 6 +-- 6 files changed, 40 insertions(+), 34 deletions(-) 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 0af625db38d1..aed949fd5642 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 @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -17,35 +18,36 @@ #include #endif -namespace hpx { - - namespace detail { +namespace hpx::detail { #if __cplusplus <= 201703L -// pre c++17 std::destroy_at can be used only on non-array types - template && !std::is_array_v)> + // pre c++17 std::destroy_at can be used only on non-array types + template && !std::is_array_v)> #else -// c++17 std::destroy_at can be used on array types, destructing each element - template )> + // c++17 std::destroy_at can be used on array types, destructing each element + template )> #endif - struct destroy_guard + struct destroy_guard + { + T* t; + explicit destroy_guard(T* t) + : t(t) { - T* t; - explicit destroy_guard(T* t) - : t(t) - { - } - ~destroy_guard() - { - std::destroy_at(t); - } - }; - } // namespace detail + } + ~destroy_guard() + { + std::destroy_at(t); + } + }; +} // namespace hpx::detail #if defined(HPX_HAVE_P1144_STD_RELOCATE_AT) - using std::relocate; - using std::relocate_at; +using std::relocate; +using std::relocate_at; #else +namespace hpx::experimental { + namespace detail { /* @@ -80,6 +82,8 @@ namespace hpx { // has non-throwing move constructor std::is_nothrow_move_constructible_v) { + using hpx::detail::destroy_guard; + destroy_guard g(src); return hpx::construct_at(dst, HPX_MOVE(*src)); }; @@ -102,7 +106,9 @@ namespace hpx { static_assert( hpx::is_relocatable_v, "T(std::move(*src)) must be well-formed"); - detail::destroy_guard g(src); + using hpx::detail::destroy_guard; + + destroy_guard g(src); return HPX_MOVE(*src); } @@ -120,6 +126,6 @@ namespace hpx { } */ -#endif // defined(HPX_HAVE_P1144_STD_RELOCATE_AT) +#endif // !defined(HPX_HAVE_P1144_STD_RELOCATE_AT) -} // namespace hpx +} // namespace hpx::experimental 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 7c8821d7ca09..af00b73926b0 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 @@ -17,7 +17,7 @@ #include #endif -namespace hpx { +namespace hpx::experimental { #if defined(HPX_HAVE_P1144_STD_RELOCATE_AT) using std::uninitialized_relocate; @@ -114,7 +114,7 @@ namespace hpx { for (; first != last; ++first, ++dst) { // the move + destroy version will be used - hpx::relocate_at(std::addressof(*first), std::addressof(*dst)); + hpx::experimental::relocate_at(std::addressof(*first), std::addressof(*dst)); } return dst; @@ -135,7 +135,7 @@ namespace hpx { try { // the move + destroy version will be used - hpx::relocate_at( + hpx::experimental::relocate_at( std::addressof(*first), std::addressof(*dst)); } catch (...) 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 95a5dffa1aab..875443d1e705 100644 --- a/libs/core/type_support/tests/unit/fail_relocate_at.cpp +++ b/libs/core/type_support/tests/unit/fail_relocate_at.cpp @@ -16,5 +16,5 @@ int main(int argc, char* argv[]) int(*p)[10] = &a; int(*q)[10] = &b; - hpx::relocate_at(p, q); + hpx::experimental::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 4f3b12ec3104..ea0c533f1cbe 100644 --- a/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp @@ -16,5 +16,5 @@ int main(int argc, char* argv[]) int(*p)[10] = &a; int(*q)[10] = &b; - hpx::uninitialized_relocate(p, p + 1, q); + hpx::experimental::uninitialized_relocate(p, p + 1, q); } diff --git a/libs/core/type_support/tests/unit/relocate_at.cpp b/libs/core/type_support/tests/unit/relocate_at.cpp index a4791c5f97b0..7e99db32ec5f 100644 --- a/libs/core/type_support/tests/unit/relocate_at.cpp +++ b/libs/core/type_support/tests/unit/relocate_at.cpp @@ -83,7 +83,7 @@ int hpx_main() // a single object was constructed HPX_TEST(non_trivially_relocatable_struct::count == 1); - hpx::relocate_at(ptr1, ptr2); + hpx::experimental::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 +111,7 @@ int hpx_main() // a single object was constructed HPX_TEST(trivially_relocatable_struct::count == 1); - hpx::relocate_at(ptr1, ptr2); + hpx::experimental::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 bd1b7103e248..fec880557ff5 100644 --- a/libs/core/type_support/tests/unit/uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp @@ -150,7 +150,7 @@ int hpx_main() HPX_TEST(trivially_relocatable_struct::count == N); // relocate them to ptr2 - hpx::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + hpx::experimental::uninitialized_relocate(ptr1, ptr1 + N, ptr2); // All creations - destructions balance out HPX_TEST(trivially_relocatable_struct::count == N); @@ -193,7 +193,7 @@ int hpx_main() HPX_TEST(non_trivially_relocatable_struct::count == N); // relocate them to ptr2 - hpx::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + hpx::experimental::uninitialized_relocate(ptr1, ptr1 + N, ptr2); // All creations - destructions balance out HPX_TEST(non_trivially_relocatable_struct::count == N); @@ -240,7 +240,7 @@ int hpx_main() // relocate them to ptr2 try { - hpx::uninitialized_relocate(ptr1, ptr1 + N, ptr2); + hpx::experimental::uninitialized_relocate(ptr1, ptr1 + N, ptr2); HPX_TEST(false); // should never reach this } catch (int forty_two) From 6c583147b8558346ca69c6b61dda9e2cd2e7af7a Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Sat, 19 Aug 2023 17:10:58 +0300 Subject: [PATCH 30/42] make relocate's format like relocate_at --- .../include/hpx/type_support/relocate_at.hpp | 56 +++++++++++++------ 1 file changed, 38 insertions(+), 18 deletions(-) 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 aed949fd5642..baa7696ccda9 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 @@ -87,6 +87,43 @@ namespace hpx::experimental { destroy_guard g(src); return hpx::construct_at(dst, HPX_MOVE(*src)); }; + + template + T relocate_helper(T* src) noexcept( + std::is_nothrow_move_constructible_v) + { + using hpx::detail::destroy_guard; + + destroy_guard g(src); + return HPX_MOVE(*src); + } + + /* + P1144 also proposes a version of relocate that does not call the + move constructor and instead memmoves the bytes of src to dest. + + Giving an interface like: + + T dest = relocate(std::addressof(src)); + + That results in a valid T object (dest) without calling any + constructor or destructor. + + This is not possible to do with the current C++ standard. + + One of the proposed ways to implement this uses a hypothetical + attribute "do_not_construct" and NRVO. + + Implementation: + + template , int> = 0> + T relocate(T* source) + { + __attribute__((do_not_construct)) T t; // hypothetical attribute + std::memmove(std::addressof(t), source, sizeof(T)); + return t; // NRVO + } + */ } // namespace detail template @@ -106,25 +143,8 @@ namespace hpx::experimental { static_assert( hpx::is_relocatable_v, "T(std::move(*src)) must be well-formed"); - using hpx::detail::destroy_guard; - - destroy_guard g(src); - return HPX_MOVE(*src); - } - - /* - Memmove codegen. This part relies on UB, so it's not used. It's here for - reference. More info on this: - - https://quuxplusone.github.io/blog/2022/05/18/std-relocate/ - - template - T relocate(T* source) - { - auto magic = (T(*)(void*, size_t)) memcpy; - return magic(source, sizeof(T)); + return detail::relocate_helper(src); } - */ #endif // !defined(HPX_HAVE_P1144_STD_RELOCATE_AT) From 03344574c408004d6a18aadb2acd683de4922868 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Sat, 19 Aug 2023 17:13:18 +0300 Subject: [PATCH 31/42] relocate test --- .../type_support/tests/unit/CMakeLists.txt | 2 +- .../core/type_support/tests/unit/relocate.cpp | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 libs/core/type_support/tests/unit/relocate.cpp diff --git a/libs/core/type_support/tests/unit/CMakeLists.txt b/libs/core/type_support/tests/unit/CMakeLists.txt index a84d995b157e..83886f941586 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_at uninitialized_relocate) +set(tests relocate relocate_at uninitialized_relocate) if(HPX_WITH_CXX20_COROUTINES) set(tests ${tests} generator) diff --git a/libs/core/type_support/tests/unit/relocate.cpp b/libs/core/type_support/tests/unit/relocate.cpp new file mode 100644 index 000000000000..3871598b40e2 --- /dev/null +++ b/libs/core/type_support/tests/unit/relocate.cpp @@ -0,0 +1,74 @@ +// 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) + +/* + This test checks that the relocate_at function works correctly + for non-trivially relocatable types. + + The trivially relocatable optimization can not be implemented yet + so it is not tested separately. +*/ + +#include +#include +#include +#include + +struct non_trivially_relocatable_struct +{ + static int count; + int data; + + explicit non_trivially_relocatable_struct(int data) + : data(data) + { + count++; + } + non_trivially_relocatable_struct(non_trivially_relocatable_struct&& other) + : data(other.data) + { + count++; + } + ~non_trivially_relocatable_struct() + { + 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 hpx_main() +{ + void* mem1 = std::malloc(sizeof(non_trivially_relocatable_struct)); + + HPX_TEST(mem1); + + HPX_TEST(non_trivially_relocatable_struct::count == 0); + + non_trivially_relocatable_struct* ptr1 = hpx::construct_at( + static_cast(mem1), 1234); + + // a single object was constructed + HPX_TEST(non_trivially_relocatable_struct::count == 1); + + non_trivially_relocatable_struct obj2 = hpx::experimental::relocate(ptr1); + + // count = 1 + 1 (from the move construction) - 1 (from the destruction) + HPX_TEST(non_trivially_relocatable_struct::count == 1); + HPX_TEST(obj2.data == 1234); + + std::free(mem1); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + hpx::local::init(hpx_main, argc, argv); + return hpx::util::report_errors(); +} From 3797e55ffa54afb13e8d7225f6103a509638d7bd Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Sat, 19 Aug 2023 17:14:13 +0300 Subject: [PATCH 32/42] clang-format and comments --- .../include/hpx/type_support/uninitialized_relocate.hpp | 5 +++-- libs/core/type_support/tests/unit/fail_relocate_at.cpp | 2 ++ .../type_support/tests/unit/fail_uninitialized_relocate.cpp | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) 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 af00b73926b0..7e6d6044d4b4 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 @@ -114,7 +114,8 @@ namespace hpx::experimental { for (; first != last; ++first, ++dst) { // the move + destroy version will be used - hpx::experimental::relocate_at(std::addressof(*first), std::addressof(*dst)); + hpx::experimental::relocate_at( + std::addressof(*first), std::addressof(*dst)); } return dst; @@ -171,4 +172,4 @@ namespace hpx::experimental { #endif // defined(HPX_HAVE_P1144_STD_RELOCATE_AT) -} // namespace hpx +} // namespace hpx::experimental 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 875443d1e705..f4d9881fc8cf 100644 --- a/libs/core/type_support/tests/unit/fail_relocate_at.cpp +++ b/libs/core/type_support/tests/unit/fail_relocate_at.cpp @@ -4,6 +4,8 @@ // 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) +// This test should fail to compile + #include #include #include 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 ea0c533f1cbe..9c4110838a28 100644 --- a/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp @@ -4,6 +4,8 @@ // 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) +// This test should fail to compile + #include #include #include From b940642f97021d758cbddba9e0019960639c12d2 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Sat, 19 Aug 2023 18:22:59 +0300 Subject: [PATCH 33/42] remove whitespaces for inspect --- .../type_support/include/hpx/type_support/relocate_at.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 baa7696ccda9..86da478e9f62 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 @@ -99,16 +99,16 @@ namespace hpx::experimental { } /* - P1144 also proposes a version of relocate that does not call the + P1144 also proposes a version of relocate that does not call the move constructor and instead memmoves the bytes of src to dest. Giving an interface like: T dest = relocate(std::addressof(src)); - That results in a valid T object (dest) without calling any + That results in a valid T object (dest) without calling any constructor or destructor. - + This is not possible to do with the current C++ standard. One of the proposed ways to implement this uses a hypothetical From 639f7fed758a1ebbc0d82d3501e06d40aabc5407 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 13:28:58 +0300 Subject: [PATCH 34/42] fix fail test --- libs/core/type_support/tests/unit/fail_relocate_at.cpp | 1 - .../core/type_support/tests/unit/fail_uninitialized_relocate.cpp | 1 - 2 files changed, 2 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 f4d9881fc8cf..be88f03d36ee 100644 --- a/libs/core/type_support/tests/unit/fail_relocate_at.cpp +++ b/libs/core/type_support/tests/unit/fail_relocate_at.cpp @@ -8,7 +8,6 @@ #include #include -#include int main(int argc, char* argv[]) { 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 9c4110838a28..affcb672fd67 100644 --- a/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/fail_uninitialized_relocate.cpp @@ -8,7 +8,6 @@ #include #include -#include int main(int argc, char* argv[]) { From 9f73777eb76a8f370f7ac549774bb45639e1b4bb Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 13:32:53 +0300 Subject: [PATCH 35/42] & and && are nor triv. rel. --- .../type_support/is_trivially_relocatable.hpp | 29 ++++++++++--------- .../tests/unit/is_trivially_relocatable.cpp | 27 ++++++++++------- 2 files changed, 33 insertions(+), 23 deletions(-) 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 ad9d74cbf115..a5587a963bfa 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 @@ -42,50 +42,53 @@ namespace hpx { { }; - // Constness, Volatility, References, Arrays are ignored + // References are not trivially relocatable template - struct is_trivially_relocatable : is_trivially_relocatable + struct is_trivially_relocatable : std::false_type { }; + // Temporary objects are not trivially relocatable template - struct is_trivially_relocatable : is_trivially_relocatable + struct is_trivially_relocatable : std::false_type { }; + // Constness, Volatility, Arrays are ignored template - struct is_trivially_relocatable - : is_trivially_relocatable + struct is_trivially_relocatable : is_trivially_relocatable { }; template - struct is_trivially_relocatable : is_trivially_relocatable + struct is_trivially_relocatable : is_trivially_relocatable { }; template - struct is_trivially_relocatable : is_trivially_relocatable + struct is_trivially_relocatable + : is_trivially_relocatable { }; + template struct is_trivially_relocatable : is_trivially_relocatable { }; - template - struct is_trivially_relocatable : is_trivially_relocatable + template + struct is_trivially_relocatable : is_trivially_relocatable { }; - template - struct is_trivially_relocatable : is_trivially_relocatable + template + struct is_trivially_relocatable : is_trivially_relocatable { }; - template - struct is_trivially_relocatable + template + struct is_trivially_relocatable : is_trivially_relocatable { }; 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 8609dc4136d6..76606e261878 100644 --- a/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp +++ b/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp @@ -134,16 +134,12 @@ 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>); -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); -// Chain of c-v-ref-array qualifiers are supported +// 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< @@ -152,13 +148,19 @@ static_assert(hpx::is_trivially_relocatable_v< explicitly_trivially_relocatable_1 volatile[10]>); static_assert(hpx::is_trivially_relocatable_v< explicitly_trivially_relocatable_1 const volatile[10]>); -static_assert(hpx::is_trivially_relocatable_v< - explicitly_trivially_relocatable_1 (&)[10]>); + +// References and temporaries 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< + explicitly_trivially_relocatable_1 (&)[10]>); +static_assert( + !hpx::is_trivially_relocatable_v); -static_assert(hpx::is_trivially_relocatable_v< - explicitly_trivially_relocatable_1 const volatile&>); +static_assert(!hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 const volatile&>); // Trivial relocatability is not inherited struct derived_from_explicitly_trivially_relocatable @@ -276,4 +278,9 @@ static_assert(hpx::is_trivially_relocatable_v, static_assert(hpx::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); + int main(int, char*[]) {} From e30dc0ef4124124a95596d864ea24c025e236829 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 13:33:13 +0300 Subject: [PATCH 36/42] unneeded void cast --- .../type_support/uninitialized_relocate.hpp | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) 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 7e6d6044d4b4..650cc255279d 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 @@ -77,17 +77,13 @@ namespace hpx::experimental { if (n_objects != 0) { - void* first_void = const_cast( - static_cast(std::addressof(*first))); - void* last_void = const_cast( - static_cast(std::addressof(*last))); + std::byte const* first_byte = + reinterpret_cast(std::addressof(*first)); + std::byte const* last_byte = + reinterpret_cast(std::addressof(*last)); - void* dst_void = const_cast( - static_cast(std::addressof(*dst))); - - std::byte* first_byte = - reinterpret_cast(first_void); - std::byte* last_byte = reinterpret_cast(last_void); + std::byte* dst_byte = const_cast( + reinterpret_cast(std::addressof(*dst))); auto n_bytes = std::distance(first_byte, last_byte); @@ -95,7 +91,7 @@ namespace hpx::experimental { // That the new buffer actually contains objects // within their lifetime. But this is not possible // with current language features. - std::memmove(dst_void, first_void, n_bytes); + std::memmove(dst_byte, first_byte, n_bytes); dst += n_objects; } @@ -142,7 +138,8 @@ namespace hpx::experimental { catch (...) { // destroy all objects other that the one - // that caused the exception + // that caused the exception + // (relocate_at already destroyed that one) // destroy all objects constructed so far std::destroy(original_dst, dst); From a33c1bd8fc902757c12a2e472cfc7afabeec739e Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 13:34:26 +0300 Subject: [PATCH 37/42] addition to a test --- libs/core/type_support/tests/unit/uninitialized_relocate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/core/type_support/tests/unit/uninitialized_relocate.cpp b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp index fec880557ff5..c2d55b671b67 100644 --- a/libs/core/type_support/tests/unit/uninitialized_relocate.cpp +++ b/libs/core/type_support/tests/unit/uninitialized_relocate.cpp @@ -165,6 +165,7 @@ int hpx_main() } std::destroy(ptr2, ptr2 + N); + HPX_TEST(trivially_relocatable_struct::dtor_count == N); std::free(mem1); std::free(mem2); From 445d7795e66ce5e861552d2ca10f21da8375a05b Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 17:12:15 +0300 Subject: [PATCH 38/42] only objects are relocatable --- .../hpx/type_support/is_relocatable.hpp | 6 +- .../tests/unit/is_relocatable.cpp | 62 ++++++++++++++++--- 2 files changed, 57 insertions(+), 11 deletions(-) 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 05e881bfc69c..2d0c6d919ccb 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 @@ -11,7 +11,9 @@ namespace hpx { template - struct is_relocatable : std::is_move_constructible + struct is_relocatable + : std::bool_constant && + std::is_object_v> { }; @@ -20,7 +22,7 @@ namespace hpx { struct is_relocatable_from : std::bool_constant< std::is_constructible_v, FromTp> && - std::is_same_v, std::decay_t>> + 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 5b0c982e146a..f0a09884493c 100644 --- a/libs/core/type_support/tests/unit/is_relocatable.cpp +++ b/libs/core/type_support/tests/unit/is_relocatable.cpp @@ -8,18 +8,23 @@ #include +#include // for std::shared_ptr, std::unique_ptr #include // Integral types are relocatable static_assert(hpx::is_relocatable_v); static_assert(hpx::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); // 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(!hpx::is_relocatable_v); // Function types are not move-constructible and thus not relocatable @@ -56,13 +61,52 @@ struct not_copy_constructible static_assert(hpx::is_relocatable_v); -// reference 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(hpx::is_relocatable_v); -static_assert(hpx::is_relocatable_v); -static_assert(hpx::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); + +/* + Tests for is_relocatable_from +*/ + +// clang-format off + +// Reference types are not relocatable +static_assert(!hpx::is_relocatable_from_v< + int (&)[], int (&)[4]>); + +// Array types are not move constructible +static_assert(!hpx::is_relocatable_from_v< + int[4], int[4]>); + +// This is a simple pointer +static_assert(hpx::is_relocatable_from_v< + int (*)[4], int (*)[4]>); + +// Can move from const shared_ptr +static_assert(hpx::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< + std::unique_ptr, const std::unique_ptr>); + +// Can move away from a non-const unique_ptr +static_assert(hpx::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< + 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< + const std::unique_ptr, const std::unique_ptr>); +// clang-format on int main(int, char*[]) {} From 737883441e9ba3d7550ff64b318d2e3e0993ae32 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 17:23:10 +0300 Subject: [PATCH 39/42] comment correction --- .../include/hpx/type_support/relocate_at.hpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) 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 86da478e9f62..8aa1750097ff 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 @@ -19,12 +19,12 @@ #endif namespace hpx::detail { -#if __cplusplus <= 201703L - // pre c++17 std::destroy_at can be used only on non-array types +#if __cplusplus < 202002L + // until c++20 std::destroy_at can be used only on non-array types template && !std::is_array_v)> #else - // c++17 std::destroy_at can be used on array types, destructing each element + // since c++20 std::destroy_at can be used on array types, destructing each element template )> #endif struct destroy_guard @@ -79,12 +79,9 @@ namespace hpx::experimental { template , int> = 0> T* relocate_at_helper(T* src, T* dst) noexcept( - // has non-throwing move constructor std::is_nothrow_move_constructible_v) { - using hpx::detail::destroy_guard; - - destroy_guard g(src); + hpx::detail::destroy_guard g(src); return hpx::construct_at(dst, HPX_MOVE(*src)); }; @@ -92,9 +89,7 @@ namespace hpx::experimental { T relocate_helper(T* src) noexcept( std::is_nothrow_move_constructible_v) { - using hpx::detail::destroy_guard; - - destroy_guard g(src); + hpx::detail::destroy_guard g(src); return HPX_MOVE(*src); } @@ -138,7 +133,7 @@ namespace hpx::experimental { } template - T relocate(T* src) noexcept(std::is_nothrow_move_constructible_v) + T relocate(T* src) noexcept(noexcept(detail::relocate_helper(src))) { static_assert( hpx::is_relocatable_v, "T(std::move(*src)) must be well-formed"); From 00aed1c7b89dd0782b080e9e32fb05a781e84fd5 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 17:31:38 +0300 Subject: [PATCH 40/42] clang-format --- .../type_support/is_trivially_relocatable.hpp | 4 +--- .../hpx/type_support/uninitialized_relocate.hpp | 2 +- .../tests/unit/is_trivially_relocatable.cpp | 17 +++++++++-------- 3 files changed, 11 insertions(+), 12 deletions(-) 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 a5587a963bfa..a29546d2ae8e 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 @@ -71,7 +71,6 @@ namespace hpx { { }; - template struct is_trivially_relocatable : is_trivially_relocatable { @@ -88,8 +87,7 @@ namespace hpx { }; template - struct is_trivially_relocatable - : is_trivially_relocatable + struct is_trivially_relocatable : is_trivially_relocatable { }; 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 650cc255279d..babb9f26aa33 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 @@ -138,7 +138,7 @@ namespace hpx::experimental { catch (...) { // destroy all objects other that the one - // that caused the exception + // that caused the exception // (relocate_at already destroyed that one) // destroy all objects constructed so far 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 76606e261878..c20bb642b208 100644 --- a/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp +++ b/libs/core/type_support/tests/unit/is_trivially_relocatable.cpp @@ -150,17 +150,18 @@ static_assert(hpx::is_trivially_relocatable_v< explicitly_trivially_relocatable_1 const volatile[10]>); // References and temporaries are not trivially relocatable -static_assert( - !hpx::is_trivially_relocatable_v); -static_assert( - !hpx::is_trivially_relocatable_v); +// clang-format off static_assert(!hpx::is_trivially_relocatable_v< - explicitly_trivially_relocatable_1 (&)[10]>); -static_assert( - !hpx::is_trivially_relocatable_v); + explicitly_trivially_relocatable_1&>); +static_assert(!hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1&&>); +static_assert(!hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 (&)[10]>); +static_assert(!hpx::is_trivially_relocatable_v< + explicitly_trivially_relocatable_1 (&&)[10]>); static_assert(!hpx::is_trivially_relocatable_v< explicitly_trivially_relocatable_1 const volatile&>); +// clang-format on // Trivial relocatability is not inherited struct derived_from_explicitly_trivially_relocatable From 6e781714c99515ef32b2f72128b4a64a1eea96a3 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Wed, 23 Aug 2023 17:32:34 +0300 Subject: [PATCH 41/42] template arg name size -> N --- .../include/hpx/type_support/is_trivially_relocatable.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 a29546d2ae8e..1a777ed11986 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 @@ -91,8 +91,8 @@ namespace hpx { { }; - template - struct is_trivially_relocatable + template + struct is_trivially_relocatable : is_trivially_relocatable { }; From af1f6f66e4e14f8a67f4c6e743ecc95c71368725 Mon Sep 17 00:00:00 2001 From: isidorostsa Date: Thu, 24 Aug 2023 12:42:15 +0300 Subject: [PATCH 42/42] decay -> remove_cv --- .../hpx/type_support/uninitialized_relocate.hpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) 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 babb9f26aa33..750dc601d660 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 @@ -44,7 +44,8 @@ namespace hpx::experimental { constexpr static bool is_buffer_memcpyable = hpx::is_trivially_relocatable_v && // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The important check - std::is_same_v, std::decay_t> && + 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 @@ -103,14 +104,17 @@ namespace hpx::experimental { std::enable_if_t< choose_uninitialized_relocate_helper::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) { - // the move + destroy version will be used - hpx::experimental::relocate_at( + // 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)); } @@ -132,7 +136,7 @@ namespace hpx::experimental { try { // the move + destroy version will be used - hpx::experimental::relocate_at( + relocate_at_helper( std::addressof(*first), std::addressof(*dst)); } catch (...)