#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::operator=(TIntrusivePtr other) { Store(std::move(other)); return *this; } template TAtomicIntrusivePtr& TAtomicIntrusivePtr::operator=(std::nullptr_t) { Reset(); return *this; } template TAtomicIntrusivePtr::operator bool() const { return Get(); } #ifdef _lsan_enabled_ template TAtomicIntrusivePtr::TAtomicIntrusivePtr(TIntrusivePtr other) : Ptr_(std::move(other)) { } template TAtomicIntrusivePtr::TAtomicIntrusivePtr(TAtomicIntrusivePtr&& other) : Ptr_(std::move(other)) { } template TAtomicIntrusivePtr::~TAtomicIntrusivePtr() = default; template TIntrusivePtr TAtomicIntrusivePtr::Acquire() const { auto guard = Guard(Lock_); return Ptr_; } template TIntrusivePtr TAtomicIntrusivePtr::Exchange(TIntrusivePtr other) { auto guard = Guard(Lock_); Ptr_.Swap(other); return other; } template void TAtomicIntrusivePtr::Store(TIntrusivePtr other) { Exchange(std::move(other)); } template void TAtomicIntrusivePtr::Reset() { Exchange(nullptr); } template bool TAtomicIntrusivePtr::CompareAndSwap(TRawPtr& comparePtr, T* target) { auto guard = Guard(Lock_); if (Ptr_.Get() != comparePtr) { comparePtr = Ptr_.Get(); return false; } auto targetPtr = TIntrusivePtr(target, /*addReference*/ false); Ptr_.Swap(targetPtr); guard.Release(); // targetPtr will die here. return true; } template bool TAtomicIntrusivePtr::CompareAndSwap(TRawPtr& comparePtr, TIntrusivePtr target) { return CompareAndSwap(comparePtr, target.Release()); } template typename TAtomicIntrusivePtr::TRawPtr TAtomicIntrusivePtr::Get() const { auto guard = Guard(Lock_); return Ptr_.Get(); } #else template TAtomicIntrusivePtr::TAtomicIntrusivePtr(TIntrusivePtr other) : Ptr_(AcquireObject(other.Release(), /*consumeRef*/ 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 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. auto newPtr = TTaggedPtr(obj, newLocalRefs).Pack(); if (Ptr_.compare_exchange_weak(ptr, newPtr)) { ptr = newPtr; 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(), /*consumeRef*/ true))); DoRelease(obj, localRefs + 1); return TIntrusivePtr(obj, false); } template void TAtomicIntrusivePtr::Store(TIntrusivePtr other) { ReleaseObject(Ptr_.exchange(AcquireObject(other.Release(), /*consumeRef*/ true))); } template void TAtomicIntrusivePtr::Reset() { ReleaseObject(Ptr_.exchange(0)); } template bool TAtomicIntrusivePtr::CompareAndSwap(TRawPtr& comparePtr, T* target) { auto* targetPtr = AcquireObject(target, /*consumeRef*/ 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(), /*consumeRef*/ 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 TPackedPtr TAtomicIntrusivePtr::AcquireObject(T* obj, bool consumeRef) { if (obj) { Ref(obj, ReservedRefCount - static_cast(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, ReservedRefCount - refs); } } #endif //////////////////////////////////////////////////////////////////////////////// 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