//------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2023 Ripple Labs Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #ifndef RIPPLE_BASICS_INTRUSIVEPOINTER_IPP_INCLUDED #define RIPPLE_BASICS_INTRUSIVEPOINTER_IPP_INCLUDED #include #include #include namespace ripple { template template SharedIntrusive::SharedIntrusive(T* p, TAdoptTag) noexcept : ptr_{p} { if constexpr (std::is_same_v< TAdoptTag, SharedIntrusiveAdoptIncrementStrongTag>) { if (p) p->addStrongRef(); } } template SharedIntrusive::SharedIntrusive(SharedIntrusive const& rhs) : ptr_{[&] { auto p = rhs.unsafeGetRawPtr(); if (p) p->addStrongRef(); return p; }()} { } template template requires std::convertible_to SharedIntrusive::SharedIntrusive(SharedIntrusive const& rhs) : ptr_{[&] { auto p = rhs.unsafeGetRawPtr(); if (p) p->addStrongRef(); return p; }()} { } template SharedIntrusive::SharedIntrusive(SharedIntrusive&& rhs) : ptr_{rhs.unsafeExchange(nullptr)} { } template template requires std::convertible_to SharedIntrusive::SharedIntrusive(SharedIntrusive&& rhs) : ptr_{rhs.unsafeExchange(nullptr)} { } template SharedIntrusive& SharedIntrusive::operator=(SharedIntrusive const& rhs) { if (this == &rhs) return *this; auto p = rhs.unsafeGetRawPtr(); if (p) p->addStrongRef(); unsafeReleaseAndStore(p); return *this; } template template // clang-format off requires std::convertible_to // clang-format on SharedIntrusive& SharedIntrusive::operator=(SharedIntrusive const& rhs) { if constexpr (std::is_same_v) { // This case should never be hit. The operator above will run instead. // (The normal operator= is needed or it will be marked `deleted`) if (this == &rhs) return *this; } auto p = rhs.unsafeGetRawPtr(); if (p) p->addStrongRef(); unsafeReleaseAndStore(p); return *this; } template SharedIntrusive& SharedIntrusive::operator=(SharedIntrusive&& rhs) { if (this == &rhs) return *this; unsafeReleaseAndStore(rhs.unsafeExchange(nullptr)); return *this; } template template // clang-format off requires std::convertible_to // clang-format on SharedIntrusive& SharedIntrusive::operator=(SharedIntrusive&& rhs) { static_assert( !std::is_same_v, "This overload should not be instantiated for T == TT"); unsafeReleaseAndStore(rhs.unsafeExchange(nullptr)); return *this; } template bool SharedIntrusive::operator!=(std::nullptr_t) const { return this->get() != nullptr; } template bool SharedIntrusive::operator==(std::nullptr_t) const { return this->get() == nullptr; } template template void SharedIntrusive::adopt(T* p) { if constexpr (std::is_same_v< TAdoptTag, SharedIntrusiveAdoptIncrementStrongTag>) { if (p) p->addStrongRef(); } unsafeReleaseAndStore(p); } template SharedIntrusive::~SharedIntrusive() { unsafeReleaseAndStore(nullptr); }; template template SharedIntrusive::SharedIntrusive( StaticCastTagSharedIntrusive, SharedIntrusive const& rhs) : ptr_{[&] { auto p = static_cast(rhs.unsafeGetRawPtr()); if (p) p->addStrongRef(); return p; }()} { } template template SharedIntrusive::SharedIntrusive( StaticCastTagSharedIntrusive, SharedIntrusive&& rhs) : ptr_{static_cast(rhs.unsafeExchange(nullptr))} { } template template SharedIntrusive::SharedIntrusive( DynamicCastTagSharedIntrusive, SharedIntrusive const& rhs) : ptr_{[&] { auto p = dynamic_cast(rhs.unsafeGetRawPtr()); if (p) p->addStrongRef(); return p; }()} { } template template SharedIntrusive::SharedIntrusive( DynamicCastTagSharedIntrusive, SharedIntrusive&& rhs) { // This can be simplified without the `exchange`, but the `exchange` is kept // in anticipation of supporting atomic operations. auto toSet = rhs.unsafeExchange(nullptr); if (toSet) { ptr_ = dynamic_cast(toSet); if (!ptr_) // need to set the pointer back or will leak rhs.unsafeExchange(toSet); } } template T& SharedIntrusive::operator*() const noexcept { return *unsafeGetRawPtr(); } template T* SharedIntrusive::operator->() const noexcept { return unsafeGetRawPtr(); } template SharedIntrusive::operator bool() const noexcept { return bool(unsafeGetRawPtr()); } template void SharedIntrusive::reset() { unsafeReleaseAndStore(nullptr); } template T* SharedIntrusive::get() const { return unsafeGetRawPtr(); } template std::size_t SharedIntrusive::use_count() const { if (auto p = unsafeGetRawPtr()) return p->use_count(); return 0; } template T* SharedIntrusive::unsafeGetRawPtr() const { return ptr_; } template void SharedIntrusive::unsafeSetRawPtr(T* p) { ptr_ = p; } template T* SharedIntrusive::unsafeExchange(T* p) { return std::exchange(ptr_, p); } template void SharedIntrusive::unsafeReleaseAndStore(T* next) { auto prev = unsafeExchange(next); if (!prev) return; using enum ReleaseStrongRefAction; auto action = prev->releaseStrongRef(); switch (action) { case noop: break; case destroy: delete prev; break; case partialDestroy: prev->partialDestructor(); partialDestructorFinished(&prev); // prev is null and may no longer be used break; } } //------------------------------------------------------------------------------ template WeakIntrusive::WeakIntrusive(WeakIntrusive const& rhs) : ptr_{rhs.ptr_} { if (ptr_) ptr_->addWeakRef(); } template WeakIntrusive::WeakIntrusive(WeakIntrusive&& rhs) : ptr_{rhs.ptr_} { rhs.ptr_ = nullptr; } template WeakIntrusive::WeakIntrusive(SharedIntrusive const& rhs) : ptr_{rhs.unsafeGetRawPtr()} { if (ptr_) ptr_->addWeakRef(); } template template // clang-format off requires std::convertible_to // clang-format on WeakIntrusive& WeakIntrusive::operator=(SharedIntrusive const& rhs) { unsafeReleaseNoStore(); auto p = rhs.unsafeGetRawPtr(); if (p) p->addWeakRef(); return *this; } template void WeakIntrusive::adopt(T* ptr) { unsafeReleaseNoStore(); if (ptr) ptr->addWeakRef(); ptr_ = ptr; } template WeakIntrusive::~WeakIntrusive() { unsafeReleaseNoStore(); } template SharedIntrusive WeakIntrusive::lock() const { if (ptr_ && ptr_->checkoutStrongRefFromWeak()) { return SharedIntrusive{ptr_, SharedIntrusiveAdoptNoIncrementTag{}}; } return {}; } template bool WeakIntrusive::expired() const { return (!ptr_ || ptr_->expired()); } template void WeakIntrusive::reset() { unsafeReleaseNoStore(); ptr_ = nullptr; } template void WeakIntrusive::unsafeReleaseNoStore() { if (!ptr_) return; using enum ReleaseWeakRefAction; auto action = ptr_->releaseWeakRef(); switch (action) { case noop: break; case destroy: delete ptr_; break; } } //------------------------------------------------------------------------------ template SharedWeakUnion::SharedWeakUnion(SharedWeakUnion const& rhs) : tp_{rhs.tp_} { auto p = rhs.unsafeGetRawPtr(); if (!p) return; if (rhs.isStrong()) p->addStrongRef(); else p->addWeakRef(); } template template requires std::convertible_to SharedWeakUnion::SharedWeakUnion(SharedIntrusive const& rhs) { auto p = rhs.unsafeGetRawPtr(); if (p) p->addStrongRef(); unsafeSetRawPtr(p, RefStrength::strong); } template SharedWeakUnion::SharedWeakUnion(SharedWeakUnion&& rhs) : tp_{rhs.tp_} { rhs.unsafeSetRawPtr(nullptr); } template template requires std::convertible_to SharedWeakUnion::SharedWeakUnion(SharedIntrusive&& rhs) { auto p = rhs.unsafeGetRawPtr(); if (p) unsafeSetRawPtr(p, RefStrength::strong); rhs.unsafeSetRawPtr(nullptr); } template SharedWeakUnion& SharedWeakUnion::operator=(SharedWeakUnion const& rhs) { if (this == &rhs) return *this; unsafeReleaseNoStore(); if (auto p = rhs.unsafeGetRawPtr()) { if (rhs.isStrong()) { p->addStrongRef(); unsafeSetRawPtr(p, RefStrength::strong); } else { p->addWeakRef(); unsafeSetRawPtr(p, RefStrength::weak); } } else { unsafeSetRawPtr(nullptr); } return *this; } template template // clang-format off requires std::convertible_to // clang-format on SharedWeakUnion& SharedWeakUnion::operator=(SharedIntrusive const& rhs) { unsafeReleaseNoStore(); auto p = rhs.unsafeGetRawPtr(); if (p) p->addStrongRef(); unsafeSetRawPtr(p, RefStrength::strong); return *this; } template template // clang-format off requires std::convertible_to // clang-format on SharedWeakUnion& SharedWeakUnion::operator=(SharedIntrusive&& rhs) { unsafeReleaseNoStore(); unsafeSetRawPtr(rhs.unsafeGetRawPtr(), RefStrength::strong); rhs.unsafeSetRawPtr(nullptr); return *this; } template SharedWeakUnion::~SharedWeakUnion() { unsafeReleaseNoStore(); }; // Return a strong pointer if this is already a strong pointer (i.e. don't // lock the weak pointer. Use the `lock` method if that's what's needed) template SharedIntrusive SharedWeakUnion::getStrong() const { SharedIntrusive result; auto p = unsafeGetRawPtr(); if (p && isStrong()) { result.template adopt(p); } return result; } template SharedWeakUnion::operator bool() const noexcept { return bool(get()); } template void SharedWeakUnion::reset() { unsafeReleaseNoStore(); unsafeSetRawPtr(nullptr); } template T* SharedWeakUnion::get() const { return isStrong() ? unsafeGetRawPtr() : nullptr; } template std::size_t SharedWeakUnion::use_count() const { if (auto p = get()) return p->use_count(); return 0; } template bool SharedWeakUnion::expired() const { auto p = unsafeGetRawPtr(); return (!p || p->expired()); } template SharedIntrusive SharedWeakUnion::lock() const { SharedIntrusive result; auto p = unsafeGetRawPtr(); if (!p) return result; if (isStrong()) { result.template adopt(p); return result; } if (p->checkoutStrongRefFromWeak()) { result.template adopt(p); return result; } return result; } template bool SharedWeakUnion::isStrong() const { return !(tp_ & tagMask); } template bool SharedWeakUnion::isWeak() const { return tp_ & tagMask; } template bool SharedWeakUnion::convertToStrong() { if (isStrong()) return true; auto p = unsafeGetRawPtr(); if (p && p->checkoutStrongRefFromWeak()) { [[maybe_unused]] auto action = p->releaseWeakRef(); XRPL_ASSERT( (action == ReleaseWeakRefAction::noop), "ripple::SharedWeakUnion::convertToStrong : " "action is noop"); unsafeSetRawPtr(p, RefStrength::strong); return true; } return false; } template bool SharedWeakUnion::convertToWeak() { if (isWeak()) return true; auto p = unsafeGetRawPtr(); if (!p) return false; using enum ReleaseStrongRefAction; auto action = p->addWeakReleaseStrongRef(); switch (action) { case noop: break; case destroy: // We just added a weak ref. How could we destroy? // LCOV_EXCL_START UNREACHABLE( "ripple::SharedWeakUnion::convertToWeak : destroying freshly " "added ref"); delete p; unsafeSetRawPtr(nullptr); return true; // Should never happen // LCOV_EXCL_STOP case partialDestroy: // This is a weird case. We just converted the last strong // pointer to a weak pointer. p->partialDestructor(); partialDestructorFinished(&p); // p is null and may no longer be used break; } unsafeSetRawPtr(p, RefStrength::weak); return true; } template T* SharedWeakUnion::unsafeGetRawPtr() const { return reinterpret_cast(tp_ & ptrMask); } template void SharedWeakUnion::unsafeSetRawPtr(T* p, RefStrength rs) { tp_ = reinterpret_cast(p); if (tp_ && rs == RefStrength::weak) tp_ |= tagMask; } template void SharedWeakUnion::unsafeSetRawPtr(std::nullptr_t) { tp_ = 0; } template void SharedWeakUnion::unsafeReleaseNoStore() { auto p = unsafeGetRawPtr(); if (!p) return; if (isStrong()) { using enum ReleaseStrongRefAction; auto strongAction = p->releaseStrongRef(); switch (strongAction) { case noop: break; case destroy: delete p; break; case partialDestroy: p->partialDestructor(); partialDestructorFinished(&p); // p is null and may no longer be used break; } } else { using enum ReleaseWeakRefAction; auto weakAction = p->releaseWeakRef(); switch (weakAction) { case noop: break; case destroy: delete p; break; } } } } // namespace ripple #endif