#pragma once #include "ref_counted.h" #include namespace NYT { //////////////////////////////////////////////////////////////////////////////// template class TWeakPtr { public: using TUnderlying = T; using element_type = T; //! Empty constructor. TWeakPtr() = default; TWeakPtr(std::nullptr_t) { } //! Constructor from an unqualified reference. /*! * Note that this constructor could be racy due to unsynchronized operations * on the object and on the counter. */ explicit TWeakPtr(T* p) noexcept : T_(p) { #if defined(_tsan_enabled_) if (T_) { RefCounter_ = GetRefCounter(T_); } #endif AcquireRef(); } //! Constructor from a strong reference. TWeakPtr(const TIntrusivePtr& ptr) noexcept : TWeakPtr(ptr.Get()) { } //! Constructor from a strong reference with an upcast. template >> TWeakPtr(const TIntrusivePtr& ptr) noexcept : TWeakPtr(ptr.Get()) { static_assert( std::derived_from, "Cast allowed only for types derived from TRefCountedBase"); } //! Copy constructor. TWeakPtr(const TWeakPtr& other) noexcept : TWeakPtr(other.T_) { } //! Copy constructor with an upcast. template >> TWeakPtr(const TWeakPtr& other) noexcept : TWeakPtr(other.Lock()) { static_assert( std::derived_from, "Cast allowed only for types derived from TRefCountedBase"); } //! Move constructor. TWeakPtr(TWeakPtr&& other) noexcept { other.Swap(*this); } //! Move constructor with an upcast. template >> TWeakPtr(TWeakPtr&& other) noexcept { static_assert( std::derived_from, "Cast allowed only for types derived from TRefCountedBase"); TIntrusivePtr strongOther = other.Lock(); if (strongOther) { T_ = other.T_; other.T_ = nullptr; #if defined(_tsan_enabled_) RefCounter_ = other.RefCounter_; other.RefCounter_ = nullptr; #endif } } //! Destructor. ~TWeakPtr() { ReleaseRef(); } //! Assignment operator from a strong reference. template TWeakPtr& operator=(const TIntrusivePtr& ptr) noexcept { static_assert( std::is_convertible_v, "U* must be convertible to T*"); TWeakPtr(ptr).Swap(*this); return *this; } //! Copy assignment operator. TWeakPtr& operator=(const TWeakPtr& other) noexcept { TWeakPtr(other).Swap(*this); return *this; } //! Copy assignment operator with an upcast. template TWeakPtr& operator=(const TWeakPtr& other) noexcept { static_assert( std::is_convertible_v, "U* must be convertible to T*"); TWeakPtr(other).Swap(*this); return *this; } //! Move assignment operator. TWeakPtr& operator=(TWeakPtr&& other) noexcept { other.Swap(*this); return *this; } //! Move assignment operator with an upcast. template TWeakPtr& operator=(TWeakPtr&& other) noexcept { static_assert( std::is_convertible_v, "U* must be convertible to T*"); TWeakPtr(std::move(other)).Swap(*this); return *this; } //! Drop the pointer. void Reset() // noexcept { TWeakPtr().Swap(*this); } //! Replace the pointer with a specified one. void Reset(T* p) // noexcept { TWeakPtr(p).Swap(*this); } //! Replace the pointer with a specified one. template void Reset(const TIntrusivePtr& ptr) // noexcept { static_assert( std::is_convertible_v, "U* must be convertible to T*"); TWeakPtr(ptr).Swap(*this); } //! Swap the pointer with the other one. void Swap(TWeakPtr& other) noexcept { DoSwap(T_, other.T_); #if defined(_tsan_enabled_) DoSwap(RefCounter_, other.RefCounter_); #endif } //! Acquire a strong reference to the pointee and return a strong pointer. TIntrusivePtr Lock() const noexcept { return T_ && RefCounter()->TryRef() ? TIntrusivePtr(T_, false) : TIntrusivePtr(); } bool IsExpired() const noexcept { return !T_ || (RefCounter()->GetRefCount() == 0); } const TRefCounter* TryGetRefCounter() const { return T_ ? RefCounter() : nullptr; } private: void AcquireRef() { if (T_) { RefCounter()->WeakRef(); } } void ReleaseRef() { if (T_) { // Support incomplete type. if (RefCounter()->WeakUnref()) { DeallocateRefCounted(T_); } } } template friend class TWeakPtr; template friend struct ::THash; T* T_ = nullptr; #if defined(_tsan_enabled_) const TRefCounter* RefCounter_ = nullptr; const TRefCounter* RefCounter() const { return RefCounter_; } #else const TRefCounter* RefCounter() const { return GetRefCounter(T_); } #endif }; //////////////////////////////////////////////////////////////////////////////// //! Creates a weak pointer wrapper for a given raw pointer. //! Compared to |TWeakPtr::ctor|, type inference enables omitting |T|. template TWeakPtr MakeWeak(T* p) { return TWeakPtr(p); } //! Creates a weak pointer wrapper for a given intrusive pointer. //! Compared to |TWeakPtr::ctor|, type inference enables omitting |T|. template TWeakPtr MakeWeak(const TIntrusivePtr& p) { return TWeakPtr(p); } //! A helper for acquiring weak pointer for pointee, resetting intrusive pointer and then //! returning the pointee reference count using the acquired weak pointer. //! This helper is designed for best effort in checking that the object is not leaked after //! destructing (what seems to be) the last pointer to it. //! NB: it is possible to rewrite this helper making it working event with intrinsic refcounted objects, //! but it requires much nastier integration with the intrusive pointer destruction routines. template int ResetAndGetResidualRefCount(TIntrusivePtr& pointer) { auto weakPointer = MakeWeak(pointer); pointer.Reset(); pointer = weakPointer.Lock(); if (pointer) { // This _may_ return 0 if we are again the only holder of the pointee. return pointer->GetRefCount() - 1; } else { return 0; } } //////////////////////////////////////////////////////////////////////////////// template bool operator==(const TWeakPtr& lhs, const TWeakPtr& rhs) { static_assert( std::is_convertible_v, "U* must be convertible to T*"); return lhs.TryGetRefCounter() == rhs.TryGetRefCounter(); } template bool operator!=(const TWeakPtr& lhs, const TWeakPtr& rhs) { static_assert( std::is_convertible_v, "U* must be convertible to T*"); return lhs.TryGetRefCounter() != rhs.TryGetRefCounter(); } template bool operator==(std::nullptr_t, const TWeakPtr& rhs) { return nullptr == rhs.TryGetRefCounter(); } template bool operator!=(std::nullptr_t, const TWeakPtr& rhs) { return nullptr != rhs.TryGetRefCounter(); } template bool operator==(const TWeakPtr& lhs, std::nullptr_t) { return nullptr == lhs.TryGetRefCounter(); } template bool operator!=(const TWeakPtr& lhs, std::nullptr_t) { return nullptr != lhs.TryGetRefCounter(); } //////////////////////////////////////////////////////////////////////////////// } // namespace NYT //! A hasher for TWeakPtr. template struct THash> { size_t operator () (const NYT::TWeakPtr& ptr) const { return THash()(ptr.T_); } };