From 8c5f2b249ca3f2c19a334ba7494250df87db23b3 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Thu, 8 Apr 2021 18:45:43 +0300 Subject: [PATCH] shared_ptr.hpp: further fixes Improved is_same_ptr() trait (added third "maybe" category). Fallback to pseudo-runtime check in certain cases (if "maybe"). Fix single_ptr moving assignment operator. --- rpcs3/util/shared_ptr.hpp | 173 ++++++++++++++++++++++++++----------- rpcs3/util/typeindices.hpp | 4 +- 2 files changed, 124 insertions(+), 53 deletions(-) diff --git a/rpcs3/util/shared_ptr.hpp b/rpcs3/util/shared_ptr.hpp index f99b0414d4..8d1b3f47cd 100644 --- a/rpcs3/util/shared_ptr.hpp +++ b/rpcs3/util/shared_ptr.hpp @@ -25,16 +25,23 @@ namespace stx static thread_local const #endif fake_t> sample{}; + + template + struct can_static_cast : std::false_type {}; + + template + struct can_static_cast(std::declval()))>> : std::true_type {}; } + // Classify compile-time information available for pointers + enum class same_ptr + { + no, + yes, + maybe + }; + template - constexpr bool is_same_ptr_test(const volatile Y*, ...) - { - // Can't sample abstract class - return false; - } - - template , int> = 0> constexpr bool is_same_ptr_test(const volatile Y* ptr = std::addressof(detail::sample.data)) { return static_cast(ptr) == static_cast(ptr); @@ -42,26 +49,26 @@ namespace stx // Checks whether the cast between two types is the same pointer template - constexpr bool is_same_ptr() noexcept + constexpr same_ptr is_same_ptr() noexcept { - if constexpr (std::is_void_v || std::is_void_v || std::is_same_v) + if constexpr (std::is_void_v || std::is_void_v || std::is_same_v, std::remove_cv_t>) { - return true; + return same_ptr::yes; } - else if constexpr (std::is_convertible_v) + else if constexpr (detail::can_static_cast::value && !std::is_abstract_v) { - return is_same_ptr_test(); + return is_same_ptr_test() ? same_ptr::yes : same_ptr::no; } - else if constexpr (std::is_convertible_v) + else if constexpr (detail::can_static_cast::value && !std::is_abstract_v) { - return is_same_ptr_test(); + return is_same_ptr_test() ? same_ptr::yes : same_ptr::no; } - return !std::is_class_v && !std::is_class_v && !std::is_union_v && !std::is_union_v; + return same_ptr::maybe; } template - constexpr bool is_same_ptr_cast_v = std::is_convertible_v && is_same_ptr(); + constexpr same_ptr is_same_ptr_cast_v = std::is_convertible_v ? is_same_ptr() : same_ptr::no; template class single_ptr; @@ -166,10 +173,13 @@ namespace stx r.m_ptr = nullptr; } - template >> + template != same_ptr::no>> single_ptr(single_ptr&& r) noexcept - : m_ptr(r.m_ptr) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + + m_ptr = r.m_ptr; r.m_ptr = nullptr; } @@ -182,16 +192,17 @@ namespace stx single_ptr& operator=(single_ptr&& r) noexcept { - m_ptr = r.m_ptr; - r.m_ptr = nullptr; + single_ptr(std::move(r)).swap(*this); return *this; } - template >> + template != same_ptr::no>> single_ptr& operator=(single_ptr&& r) noexcept { - m_ptr = r.m_ptr; - r.m_ptr = nullptr; + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + + single_ptr(std::move(r)).swap(*this); return *this; } @@ -259,9 +270,12 @@ namespace stx } // "Moving" "static cast" - template (std::declval())), typename = std::enable_if_t()>> + template (std::declval())), typename = std::enable_if_t() != same_ptr::no>> explicit operator single_ptr() && noexcept { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(m_ptr)); + single_ptr r; r.m_ptr = static_cast(std::exchange(m_ptr, nullptr)); return r; @@ -424,10 +438,13 @@ namespace stx d()->refs++; } - template >> + template != same_ptr::no>> shared_ptr(const shared_ptr& r) noexcept - : m_ptr(r.m_ptr) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + + m_ptr = r.m_ptr; if (m_ptr) d()->refs++; } @@ -438,17 +455,23 @@ namespace stx r.m_ptr = nullptr; } - template >> + template != same_ptr::no>> shared_ptr(shared_ptr&& r) noexcept - : m_ptr(r.m_ptr) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + + m_ptr = r.m_ptr; r.m_ptr = nullptr; } - template >> + template != same_ptr::no>> shared_ptr(single_ptr&& r) noexcept - : m_ptr(r.m_ptr) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + + m_ptr = r.m_ptr; r.m_ptr = nullptr; } @@ -463,9 +486,12 @@ namespace stx return *this; } - template >> + template != same_ptr::no>> shared_ptr& operator=(const shared_ptr& r) noexcept { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + shared_ptr(r).swap(*this); return *this; } @@ -476,16 +502,22 @@ namespace stx return *this; } - template >> + template != same_ptr::no>> shared_ptr& operator=(shared_ptr&& r) noexcept { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + shared_ptr(std::move(r)).swap(*this); return *this; } - template >> + template != same_ptr::no>> shared_ptr& operator=(single_ptr&& r) noexcept { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + shared_ptr(std::move(r)).swap(*this); return *this; } @@ -503,9 +535,12 @@ namespace stx } // Converts to unique (single) ptr if reference is 1, otherwise returns null. Nullifies self. - template (std::declval())), typename = std::enable_if_t()>> + template (std::declval())), typename = std::enable_if_t() != same_ptr::no>> explicit operator single_ptr() && noexcept { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(m_ptr)); + const auto o = d(); if (m_ptr && !--o->refs) @@ -588,9 +623,12 @@ namespace stx } // Basic "static cast" support - template (std::declval())), typename = std::enable_if_t()>> + template (std::declval())), typename = std::enable_if_t() != same_ptr::no>> explicit operator shared_ptr() const& noexcept { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(m_ptr)); + if (m_ptr) { d()->refs++; @@ -602,9 +640,12 @@ namespace stx } // "Moving" "static cast" - template (std::declval())), typename = std::enable_if_t()>> + template (std::declval())), typename = std::enable_if_t() != same_ptr::no>> explicit operator shared_ptr() && noexcept { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(m_ptr)); + shared_ptr r; r.m_ptr = static_cast(std::exchange(m_ptr, nullptr)); return r; @@ -660,29 +701,38 @@ namespace stx d()->refs.raw() += c_ref_mask; } - template >> + template != same_ptr::no>> atomic_ptr(const shared_ptr& r) noexcept - : m_val(reinterpret_cast(r.m_ptr) << c_ref_size) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + // Obtain a ref + as many refs as an atomic_ptr can additionally reference + m_val = reinterpret_cast(r.m_ptr) << c_ref_size; if (m_val) d()->refs += c_ref_mask + 1; } - template >> + template != same_ptr::no>> atomic_ptr(shared_ptr&& r) noexcept - : m_val(reinterpret_cast(r.m_ptr) << c_ref_size) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + + m_val = reinterpret_cast(r.m_ptr) << c_ref_size; r.m_ptr = nullptr; if (m_val) d()->refs += c_ref_mask; } - template >> + template != same_ptr::no>> atomic_ptr(single_ptr&& r) noexcept - : m_val(reinterpret_cast(r.m_ptr) << c_ref_size) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + + m_val = reinterpret_cast(r.m_ptr) << c_ref_size; r.m_ptr = nullptr; if (m_val) @@ -711,23 +761,32 @@ namespace stx return *this; } - template >> + template != same_ptr::no>> atomic_ptr& operator=(const shared_ptr& r) noexcept { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + store(r); return *this; } - template >> + template != same_ptr::no>> atomic_ptr& operator=(shared_ptr&& r) noexcept { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + store(std::move(r)); return *this; } - template >> + template != same_ptr::no>> atomic_ptr& operator=(single_ptr&& r) noexcept { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(r.m_ptr)); + store(std::move(r)); return *this; } @@ -988,9 +1047,12 @@ namespace stx } // Unoptimized - template >> + template != same_ptr::no>> shared_type compare_and_swap(const shared_ptr& cmp, shared_type exch) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(cmp.m_ptr)); + shared_type old = cmp; if (compare_exchange(old, std::move(exch))) @@ -1004,9 +1066,12 @@ namespace stx } // More lightweight than compare_exchange - template >> + template != same_ptr::no>> bool compare_and_swap_test(const shared_ptr& cmp, shared_type exch) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(cmp.m_ptr)); + const uptr _old = reinterpret_cast(cmp.m_ptr); const uptr _new = reinterpret_cast(exch.m_ptr); @@ -1043,9 +1108,12 @@ namespace stx } // Unoptimized - template >> + template != same_ptr::no>> shared_type compare_and_swap(const single_ptr& cmp, shared_type exch) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(cmp.m_ptr)); + shared_type old = cmp; if (compare_exchange(old, std::move(exch))) @@ -1059,9 +1127,12 @@ namespace stx } // Supplementary - template >> + template != same_ptr::no>> bool compare_and_swap_test(const single_ptr& cmp, shared_type exch) { + if constexpr (is_same_ptr() == same_ptr::maybe) + ensure(is_same_ptr_test(cmp.m_ptr)); + return compare_and_swap_test(reinterpret_cast&>(cmp), std::move(exch)); } @@ -1111,13 +1182,13 @@ namespace stx return m_val != 0; } - template >> + template != same_ptr::no>> bool is_equal(const shared_ptr& r) const noexcept { return observe() == r.get(); } - template >> + template != same_ptr::no>> bool is_equal(const single_ptr& r) const noexcept { return observe() == r.get(); diff --git a/rpcs3/util/typeindices.hpp b/rpcs3/util/typeindices.hpp index 742310b889..768ae16df1 100644 --- a/rpcs3/util/typeindices.hpp +++ b/rpcs3/util/typeindices.hpp @@ -255,7 +255,7 @@ namespace stx else { static_assert(sizeof(As) > 0); - static_assert(is_same_ptr()); + static_assert(is_same_ptr() == same_ptr::yes); // TODO return type_counter::template dyn_type.index(); } } @@ -274,7 +274,7 @@ namespace stx ATTR_PURE inline const Info& typedata() noexcept { static_assert(sizeof(T) > 0 && sizeof(As) > 0); - static_assert(is_same_ptr()); + static_assert(is_same_ptr() == same_ptr::yes); // TODO return type_counter::template dyn_type; }