#ifndef XRPL_BASICS_INTRUSIVEPOINTER_IPP_INCLUDED #define XRPL_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