diff --git a/Utilities/typemap.h b/Utilities/typemap.h index c8a9019e2f..8105d1fb13 100644 --- a/Utilities/typemap.h +++ b/Utilities/typemap.h @@ -353,6 +353,121 @@ namespace utils } } + // An object of type T paired with atomic refcounter + template + class refctr final + { + atomic_t m_ref{1}; + + public: + T object; + + template + refctr(Args&&... args) + : object(std::forward(args)...) + { + } + + void add_ref() noexcept + { + m_ref++; + } + + std::size_t remove_ref() noexcept + { + return --m_ref; + } + }; + + // Simplified "shared" ptr making use of refctr class + template + class refptr final + { + refctr* m_ptr = nullptr; + + void destroy() + { + if (m_ptr && !m_ptr->remove_ref()) + delete m_ptr; + } + + public: + constexpr refptr() = default; + + // Construct directly from refctr pointer + explicit refptr(refctr* ptr) noexcept + : m_ptr(ptr) + { + } + + refptr(const refptr& rhs) noexcept + : m_ptr(rhs.m_ptr) + { + if (m_ptr) + m_ptr->add_ref(); + } + + refptr(refptr&& rhs) noexcept + : m_ptr(rhs.m_ptr) + { + rhs.m_ptr = nullptr; + } + + ~refptr() + { + destroy(); + } + + refptr& operator =(const refptr& rhs) noexcept + { + destroy(); + m_ptr = rhs.m_ptr; + if (m_ptr) + m_ptr->add_ref(); + } + + refptr& operator =(refptr&& rhs) noexcept + { + std::swap(m_ptr, rhs.m_ptr); + } + + void reset() noexcept + { + destroy(); + m_ptr = nullptr; + } + + refctr* release() noexcept + { + return std::exchange(m_ptr, nullptr); + } + + void swap(refptr&& rhs) noexcept + { + std::swap(m_ptr, rhs.m_ptr); + } + + refctr* get() const noexcept + { + return m_ptr; + } + + T& operator *() const noexcept + { + return m_ptr->object; + } + + T* operator ->() const noexcept + { + return &m_ptr->object; + } + + explicit operator bool() const noexcept + { + return !!m_ptr; + } + }; + // Internal, typemap control block for a particular type struct alignas(64) typemap_head { @@ -1168,44 +1283,48 @@ namespace utils return true; } + // Transform T&& into refptr, moving const qualifier from T to refptr + template > + using decode_t = std::conditional_t, T, + std::conditional_t, const refptr>, refptr>>; + public: // Lock any objects by their identifiers, special tags id_new/id_any/id_always, or search predicates template > auto lock(Args&&... ids) const { static_assert(((!std::is_lvalue_reference_v == !typeinfo_poly::is_poly) && ...)); - static_assert(((!std::is_rvalue_reference_v) && ...)); static_assert(((!std::is_array_v) && ...)); static_assert(((!std::is_void_v) && ...)); // Initialize pointers - std::array result{this->init_ptr(std::forward(ids))...}; + std::array result{this->init_ptr>(std::forward(ids))...}; // Whether requires locking after init_ptr - using locks_t = std::integer_sequence()...>; + using locks_t = std::integer_sequence, Args>()...>; // Array index helper - using seq_t = std::index_sequence_for; + using seq_t = std::index_sequence_for...>; // Lock any number of objects in safe manner while (true) { - const uint locked = lock_array(result, seq_t{}, locks_t{}); - if (LIKELY(try_lock<0, Types...>(result, locked, locks_t{}))) + const uint locked = lock_array...>(result, seq_t{}, locks_t{}); + if (LIKELY(try_lock<0, decode_t...>(result, locked, locks_t{}))) break; } // Verify object types - check_array(result, seq_t{}, std::forward(ids)...); + check_array...>(result, seq_t{}, std::forward(ids)...); // Return tuple of possibly locked pointers, or a single pointer if constexpr (sizeof...(Types) != 1) { - return array_to_tuple(result, seq_t{}); + return array_to_tuple...>(result, seq_t{}); } else { - return typeptr(result[0]); + return typeptr...>(result[0]); } } @@ -1214,17 +1333,16 @@ namespace utils ullong apply(F&& func) { static_assert(!std::is_lvalue_reference_v == !typeinfo_poly::is_poly); - static_assert(!std::is_rvalue_reference_v); static_assert(!std::is_array_v); static_assert(!std::is_void_v); - const uint type_id = g_typeinfo>.type; + const uint type_id = g_typeinfo>>.type; - typemap_head* head = get_head(); + typemap_head* head = get_head>(); const ullong ix = head->m_create_count; - for (std::size_t j = 0; j < (typeinfo_count::max_count != 1 ? +head->m_limit : 1); j++) + for (std::size_t j = 0; j < (typeinfo_count>::max_count != 1 ? +head->m_limit : 1); j++) { const auto block = reinterpret_cast(head->m_ptr + j * head->m_ssize); @@ -1240,7 +1358,7 @@ namespace utils } else { - std::invoke(std::forward(func), *block->get_ptr()); + std::invoke(std::forward(func), *block->get_ptr>()); } } }