#ifndef ATOMIC_INTRUSIVE_PTR_INL_H_ #error "Direct inclusion of this file is not allowed, include atomic_intrusive_ptr.h" // For the sake of sane code completion. #include "atomic_intrusive_ptr.h" #endif #undef ATOMIC_INTRUSIVE_PTR_INL_H_ #include namespace NYT { //////////////////////////////////////////////////////////////////////////////// template TAtomicIntrusivePtr::TAtomicIntrusivePtr(std::nullptr_t) { } template TAtomicIntrusivePtr::TAtomicIntrusivePtr(TIntrusivePtr other) : Ptr_(AcquireObject(other.Release(), true)) { } template TAtomicIntrusivePtr::TAtomicIntrusivePtr(TAtomicIntrusivePtr&& other) : Ptr_(other.Ptr_.load(std::memory_order::relaxed)) { other.Ptr_.store(nullptr, std::memory_order::relaxed); } template TAtomicIntrusivePtr::~TAtomicIntrusivePtr() { ReleaseObject(Ptr_.load()); } template TAtomicIntrusivePtr& TAtomicIntrusivePtr::operator=(TIntrusivePtr other) { Store(std::move(other)); return *this; } template TAtomicIntrusivePtr& TAtomicIntrusivePtr::operator=(std::nullptr_t) { Reset(); return *this; } template TIntrusivePtr TAtomicIntrusivePtr::Acquire() const { auto ptr = Ptr_.load(); while (true) { auto [obj, localRefs] = TTaggedPtr::Unpack(ptr); if (!obj) { return {}; } YT_VERIFY(localRefs < ReservedRefCount); auto newLocalRefs = localRefs + 1; if (newLocalRefs == ReservedRefCount) { SpinLockPause(); ptr = Ptr_.load(); continue; } // Can not Ref(obj) here because it can be destroyed. if (Ptr_.compare_exchange_weak(ptr, TTaggedPtr(obj, newLocalRefs).Pack())) { if (Y_UNLIKELY(newLocalRefs > ReservedRefCount / 2)) { Ref(obj, ReservedRefCount / 2); // Decrease local ref count. while (true) { auto [currentObj, localRefs] = TTaggedPtr::Unpack(ptr); if (currentObj != obj || localRefs <= ReservedRefCount / 2) { Unref(obj, ReservedRefCount / 2); break; } if (Ptr_.compare_exchange_weak(ptr, TTaggedPtr(obj, localRefs - ReservedRefCount / 2).Pack())) { break; } } } return TIntrusivePtr(obj, false); } } } template TIntrusivePtr TAtomicIntrusivePtr::Exchange(TIntrusivePtr other) { auto [obj, localRefs] = TTaggedPtr::Unpack(Ptr_.exchange(AcquireObject(other.Release(), true))); DoRelease(obj, localRefs + 1); return TIntrusivePtr(obj, false); } template void TAtomicIntrusivePtr::Store(TIntrusivePtr other) { ReleaseObject(Ptr_.exchange(AcquireObject(other.Release(), true))); } template void TAtomicIntrusivePtr::Reset() { ReleaseObject(Ptr_.exchange(0)); } template bool TAtomicIntrusivePtr::CompareAndSwap(TRawPtr& comparePtr, T* target) { auto* targetPtr = AcquireObject(target, false); auto currentPtr = Ptr_.load(); if (UnpackPointer(currentPtr).Ptr == comparePtr && Ptr_.compare_exchange_strong(currentPtr, targetPtr)) { ReleaseObject(currentPtr); return true; } comparePtr = UnpackPointer(currentPtr).Ptr; ReleaseObject(targetPtr); return false; } template bool TAtomicIntrusivePtr::CompareAndSwap(TRawPtr& comparePtr, TIntrusivePtr target) { // TODO(lukyan): Make helper for packed owning ptr? auto targetPtr = AcquireObject(target.Release(), true); auto currentPtr = Ptr_.load(); if (TTaggedPtr::Unpack(currentPtr).Ptr == comparePtr && Ptr_.compare_exchange_strong(currentPtr, targetPtr)) { ReleaseObject(currentPtr); return true; } comparePtr = TTaggedPtr::Unpack(currentPtr).Ptr; ReleaseObject(targetPtr); return false; } template typename TAtomicIntrusivePtr::TRawPtr TAtomicIntrusivePtr::Get() const { return TTaggedPtr::Unpack(Ptr_.load()).Ptr; } template TAtomicIntrusivePtr::operator bool() const { return Get(); } template TPackedPtr TAtomicIntrusivePtr::AcquireObject(T* obj, bool consumeRef) { if (obj) { Ref(obj, static_cast(ReservedRefCount - consumeRef)); } return TTaggedPtr(obj).Pack(); } template void TAtomicIntrusivePtr::ReleaseObject(TPackedPtr packedPtr) { auto [obj, localRefs] = TTaggedPtr::Unpack(packedPtr); DoRelease(obj, localRefs); } template void TAtomicIntrusivePtr::DoRelease(T* obj, int refs) { if (obj) { Unref(obj, static_cast(ReservedRefCount - refs)); } } //////////////////////////////////////////////////////////////////////////////// template bool operator==(const TAtomicIntrusivePtr& lhs, const TIntrusivePtr& rhs) { return lhs.Get() == rhs.Get(); } template bool operator==(const TIntrusivePtr& lhs, const TAtomicIntrusivePtr& rhs) { return lhs.Get() == rhs.Get(); } template bool operator!=(const TAtomicIntrusivePtr& lhs, const TIntrusivePtr& rhs) { return lhs.Get() != rhs.Get(); } template bool operator!=(const TIntrusivePtr& lhs, const TAtomicIntrusivePtr& rhs) { return lhs.Get() != rhs.Get(); } //////////////////////////////////////////////////////////////////////////////// } // namespace NYT