//==- llvm/ADT/IntrusiveRefCntPtr.h - Smart Refcounting Pointer --*- C++ -*-==// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file defines the RefCountedBase, ThreadSafeRefCountedBase, and /// IntrusiveRefCntPtr classes. /// /// IntrusiveRefCntPtr is a smart pointer to an object which maintains a /// reference count. (ThreadSafe)RefCountedBase is a mixin class that adds a /// refcount member variable and methods for updating the refcount. An object /// that inherits from (ThreadSafe)RefCountedBase deletes itself when its /// refcount hits zero. /// /// For example: /// /// ``` /// class MyClass : public RefCountedBase {}; /// /// void foo() { /// // Constructing an IntrusiveRefCntPtr increases the pointee's refcount /// // by 1 (from 0 in this case). /// IntrusiveRefCntPtr Ptr1(new MyClass()); /// /// // Copying an IntrusiveRefCntPtr increases the pointee's refcount by 1. /// IntrusiveRefCntPtr Ptr2(Ptr1); /// /// // Constructing an IntrusiveRefCntPtr has no effect on the object's /// // refcount. After a move, the moved-from pointer is null. /// IntrusiveRefCntPtr Ptr3(std::move(Ptr1)); /// assert(Ptr1 == nullptr); /// /// // Clearing an IntrusiveRefCntPtr decreases the pointee's refcount by 1. /// Ptr2.reset(); /// /// // The object deletes itself when we return from the function, because /// // Ptr3's destructor decrements its refcount to 0. /// } /// ``` /// /// You can use IntrusiveRefCntPtr with isa(), dyn_cast(), etc.: /// /// ``` /// IntrusiveRefCntPtr Ptr(new MyClass()); /// OtherClass *Other = dyn_cast(Ptr); // Ptr.get() not required /// ``` /// /// IntrusiveRefCntPtr works with any class that /// /// - inherits from (ThreadSafe)RefCountedBase, /// - has Retain() and Release() methods, or /// - specializes IntrusiveRefCntPtrInfo. /// //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_INTRUSIVEREFCNTPTR_H #define LLVM_ADT_INTRUSIVEREFCNTPTR_H #include #include #include #include namespace llvm { /// A CRTP mixin class that adds reference counting to a type. /// /// The lifetime of an object which inherits from RefCountedBase is managed by /// calls to Release() and Retain(), which increment and decrement the object's /// refcount, respectively. When a Release() call decrements the refcount to 0, /// the object deletes itself. template class RefCountedBase { mutable unsigned RefCount = 0; protected: RefCountedBase() = default; RefCountedBase(const RefCountedBase &) {} RefCountedBase &operator=(const RefCountedBase &) = delete; #ifndef NDEBUG ~RefCountedBase() { assert(RefCount == 0 && "Destruction occurred when there are still references to this."); } #else // Default the destructor in release builds, A trivial destructor may enable // better codegen. ~RefCountedBase() = default; #endif public: void Retain() const { ++RefCount; } void Release() const { assert(RefCount > 0 && "Reference count is already zero."); if (--RefCount == 0) delete static_cast(this); } }; /// A thread-safe version of \c RefCountedBase. template class ThreadSafeRefCountedBase { mutable std::atomic RefCount{0}; protected: ThreadSafeRefCountedBase() = default; ThreadSafeRefCountedBase(const ThreadSafeRefCountedBase &) {} ThreadSafeRefCountedBase & operator=(const ThreadSafeRefCountedBase &) = delete; #ifndef NDEBUG ~ThreadSafeRefCountedBase() { assert(RefCount == 0 && "Destruction occurred when there are still references to this."); } #else // Default the destructor in release builds, A trivial destructor may enable // better codegen. ~ThreadSafeRefCountedBase() = default; #endif public: void Retain() const { RefCount.fetch_add(1, std::memory_order_relaxed); } void Release() const { int NewRefCount = RefCount.fetch_sub(1, std::memory_order_acq_rel) - 1; assert(NewRefCount >= 0 && "Reference count was already zero."); if (NewRefCount == 0) delete static_cast(this); } }; /// Class you can specialize to provide custom retain/release functionality for /// a type. /// /// Usually specializing this class is not necessary, as IntrusiveRefCntPtr /// works with any type which defines Retain() and Release() functions -- you /// can define those functions yourself if RefCountedBase doesn't work for you. /// /// One case when you might want to specialize this type is if you have /// - Foo.h defines type Foo and includes Bar.h, and /// - Bar.h uses IntrusiveRefCntPtr in inline functions. /// /// Because Foo.h includes Bar.h, Bar.h can't include Foo.h in order to pull in /// the declaration of Foo. Without the declaration of Foo, normally Bar.h /// wouldn't be able to use IntrusiveRefCntPtr, which wants to call /// T::Retain and T::Release. /// /// To resolve this, Bar.h could include a third header, FooFwd.h, which /// forward-declares Foo and specializes IntrusiveRefCntPtrInfo. Then /// Bar.h could use IntrusiveRefCntPtr, although it still couldn't call any /// functions on Foo itself, because Foo would be an incomplete type. template struct IntrusiveRefCntPtrInfo { static void retain(T *obj) { obj->Retain(); } static void release(T *obj) { obj->Release(); } }; /// A smart pointer to a reference-counted object that inherits from /// RefCountedBase or ThreadSafeRefCountedBase. /// /// This class increments its pointee's reference count when it is created, and /// decrements its refcount when it's destroyed (or is changed to point to a /// different object). template class IntrusiveRefCntPtr { T *Obj = nullptr; public: using element_type = T; explicit IntrusiveRefCntPtr() = default; IntrusiveRefCntPtr(T *obj) : Obj(obj) { retain(); } IntrusiveRefCntPtr(const IntrusiveRefCntPtr &S) : Obj(S.Obj) { retain(); } IntrusiveRefCntPtr(IntrusiveRefCntPtr &&S) : Obj(S.Obj) { S.Obj = nullptr; } template ::value, bool> = true> IntrusiveRefCntPtr(IntrusiveRefCntPtr S) : Obj(S.get()) { S.Obj = nullptr; } template ::value, bool> = true> IntrusiveRefCntPtr(std::unique_ptr S) : Obj(S.release()) { retain(); } ~IntrusiveRefCntPtr() { release(); } IntrusiveRefCntPtr &operator=(IntrusiveRefCntPtr S) { swap(S); return *this; } T &operator*() const { return *Obj; } T *operator->() const { return Obj; } T *get() const { return Obj; } explicit operator bool() const { return Obj; } void swap(IntrusiveRefCntPtr &other) { T *tmp = other.Obj; other.Obj = Obj; Obj = tmp; } void reset() { release(); Obj = nullptr; } void resetWithoutRelease() { Obj = nullptr; } private: void retain() { if (Obj) IntrusiveRefCntPtrInfo::retain(Obj); } void release() { if (Obj) IntrusiveRefCntPtrInfo::release(Obj); } template friend class IntrusiveRefCntPtr; }; template inline bool operator==(const IntrusiveRefCntPtr &A, const IntrusiveRefCntPtr &B) { return A.get() == B.get(); } template inline bool operator!=(const IntrusiveRefCntPtr &A, const IntrusiveRefCntPtr &B) { return A.get() != B.get(); } template inline bool operator==(const IntrusiveRefCntPtr &A, U *B) { return A.get() == B; } template inline bool operator!=(const IntrusiveRefCntPtr &A, U *B) { return A.get() != B; } template inline bool operator==(T *A, const IntrusiveRefCntPtr &B) { return A == B.get(); } template inline bool operator!=(T *A, const IntrusiveRefCntPtr &B) { return A != B.get(); } template bool operator==(std::nullptr_t, const IntrusiveRefCntPtr &B) { return !B; } template bool operator==(const IntrusiveRefCntPtr &A, std::nullptr_t B) { return B == A; } template bool operator!=(std::nullptr_t A, const IntrusiveRefCntPtr &B) { return !(A == B); } template bool operator!=(const IntrusiveRefCntPtr &A, std::nullptr_t B) { return !(A == B); } // Make IntrusiveRefCntPtr work with dyn_cast, isa, and the other idioms from // Casting.h. template struct simplify_type; template struct simplify_type> { using SimpleType = T *; static SimpleType getSimplifiedValue(IntrusiveRefCntPtr &Val) { return Val.get(); } }; template struct simplify_type> { using SimpleType = /*const*/ T *; static SimpleType getSimplifiedValue(const IntrusiveRefCntPtr &Val) { return Val.get(); } }; /// Factory function for creating intrusive ref counted pointers. template IntrusiveRefCntPtr makeIntrusiveRefCnt(Args &&...A) { return IntrusiveRefCntPtr(new T(std::forward(A)...)); } } // end namespace llvm #endif // LLVM_ADT_INTRUSIVEREFCNTPTR_H