1 //==- llvm/ADT/IntrusiveRefCntPtr.h - Smart Refcounting Pointer --*- C++ -*-==// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines the RefCountedBase, ThreadSafeRefCountedBase, and 11 // IntrusiveRefCntPtr classes. 12 // 13 // IntrusiveRefCntPtr is a smart pointer to an object which maintains a 14 // reference count. (ThreadSafe)RefCountedBase is a mixin class that adds a 15 // refcount member variable and methods for updating the refcount. An object 16 // that inherits from (ThreadSafe)RefCountedBase deletes itself when its 17 // refcount hits zero. 18 // 19 // For example: 20 // 21 // class MyClass : public RefCountedBase<MyClass> {}; 22 // 23 // void foo() { 24 // // Constructing an IntrusiveRefCntPtr increases the pointee's refcount by 25 // // 1 (from 0 in this case). 26 // IntrusiveRefCntPtr<MyClass> Ptr1(new MyClass()); 27 // 28 // // Copying an IntrusiveRefCntPtr increases the pointee's refcount by 1. 29 // IntrusiveRefCntPtr<MyClass> Ptr2(Ptr1); 30 // 31 // // Constructing an IntrusiveRefCntPtr has no effect on the object's 32 // // refcount. After a move, the moved-from pointer is null. 33 // IntrusiveRefCntPtr<MyClass> Ptr3(std::move(Ptr1)); 34 // assert(Ptr1 == nullptr); 35 // 36 // // Clearing an IntrusiveRefCntPtr decreases the pointee's refcount by 1. 37 // Ptr2.reset(); 38 // 39 // // The object deletes itself when we return from the function, because 40 // // Ptr3's destructor decrements its refcount to 0. 41 // } 42 // 43 // You can use IntrusiveRefCntPtr with isa<T>(), dyn_cast<T>(), etc.: 44 // 45 // IntrusiveRefCntPtr<MyClass> Ptr(new MyClass()); 46 // OtherClass *Other = dyn_cast<OtherClass>(Ptr); // Ptr.get() not required 47 // 48 // IntrusiveRefCntPtr works with any class that 49 // 50 // - inherits from (ThreadSafe)RefCountedBase, 51 // - has Retain() and Release() methods, or 52 // - specializes IntrusiveRefCntPtrInfo. 53 // 54 //===----------------------------------------------------------------------===// 55 56 #ifndef LLVM_ADT_INTRUSIVEREFCNTPTR_H 57 #define LLVM_ADT_INTRUSIVEREFCNTPTR_H 58 59 #include <atomic> 60 #include <cassert> 61 #include <cstddef> 62 63 namespace llvm { 64 65 /// A CRTP mixin class that adds reference counting to a type. 66 /// 67 /// The lifetime of an object which inherits from RefCountedBase is managed by 68 /// calls to Release() and Retain(), which increment and decrement the object's 69 /// refcount, respectively. When a Release() call decrements the refcount to 0, 70 /// the object deletes itself. 71 template <class Derived> class RefCountedBase { 72 mutable unsigned RefCount = 0; 73 74 public: 75 RefCountedBase() = default; 76 RefCountedBase(const RefCountedBase &) {} 77 78 void Retain() const { ++RefCount; } 79 80 void Release() const { 81 assert(RefCount > 0 && "Reference count is already zero."); 82 if (--RefCount == 0) 83 delete static_cast<const Derived *>(this); 84 } 85 }; 86 87 /// A thread-safe version of \c RefCountedBase. 88 template <class Derived> class ThreadSafeRefCountedBase { 89 mutable std::atomic<int> RefCount; 90 91 protected: 92 ThreadSafeRefCountedBase() : RefCount(0) {} 93 94 public: 95 void Retain() const { RefCount.fetch_add(1, std::memory_order_relaxed); } 96 97 void Release() const { 98 int NewRefCount = RefCount.fetch_sub(1, std::memory_order_acq_rel) - 1; 99 assert(NewRefCount >= 0 && "Reference count was already zero."); 100 if (NewRefCount == 0) 101 delete static_cast<const Derived *>(this); 102 } 103 }; 104 105 /// Class you can specialize to provide custom retain/release functionality for 106 /// a type. 107 /// 108 /// Usually specializing this class is not necessary, as IntrusiveRefCntPtr 109 /// works with any type which defines Retain() and Release() functions -- you 110 /// can define those functions yourself if RefCountedBase doesn't work for you. 111 /// 112 /// One case when you might want to specialize this type is if you have 113 /// - Foo.h defines type Foo and includes Bar.h, and 114 /// - Bar.h uses IntrusiveRefCntPtr<Foo> in inline functions. 115 /// 116 /// Because Foo.h includes Bar.h, Bar.h can't include Foo.h in order to pull in 117 /// the declaration of Foo. Without the declaration of Foo, normally Bar.h 118 /// wouldn't be able to use IntrusiveRefCntPtr<Foo>, which wants to call 119 /// T::Retain and T::Release. 120 /// 121 /// To resolve this, Bar.h could include a third header, FooFwd.h, which 122 /// forward-declares Foo and specializes IntrusiveRefCntPtrInfo<Foo>. Then 123 /// Bar.h could use IntrusiveRefCntPtr<Foo>, although it still couldn't call any 124 /// functions on Foo itself, because Foo would be an incomplete type. 125 template <typename T> struct IntrusiveRefCntPtrInfo { 126 static void retain(T *obj) { obj->Retain(); } 127 static void release(T *obj) { obj->Release(); } 128 }; 129 130 /// A smart pointer to a reference-counted object that inherits from 131 /// RefCountedBase or ThreadSafeRefCountedBase. 132 /// 133 /// This class increments its pointee's reference count when it is created, and 134 /// decrements its refcount when it's destroyed (or is changed to point to a 135 /// different object). 136 template <typename T> class IntrusiveRefCntPtr { 137 T *Obj = nullptr; 138 139 public: 140 using element_type = T; 141 142 explicit IntrusiveRefCntPtr() = default; 143 IntrusiveRefCntPtr(T *obj) : Obj(obj) { retain(); } 144 IntrusiveRefCntPtr(const IntrusiveRefCntPtr &S) : Obj(S.Obj) { retain(); } 145 IntrusiveRefCntPtr(IntrusiveRefCntPtr &&S) : Obj(S.Obj) { S.Obj = nullptr; } 146 147 template <class X> 148 IntrusiveRefCntPtr(IntrusiveRefCntPtr<X> &&S) : Obj(S.get()) { 149 S.Obj = nullptr; 150 } 151 152 template <class X> 153 IntrusiveRefCntPtr(const IntrusiveRefCntPtr<X> &S) : Obj(S.get()) { 154 retain(); 155 } 156 157 ~IntrusiveRefCntPtr() { release(); } 158 159 IntrusiveRefCntPtr &operator=(IntrusiveRefCntPtr S) { 160 swap(S); 161 return *this; 162 } 163 164 T &operator*() const { return *Obj; } 165 T *operator->() const { return Obj; } 166 T *get() const { return Obj; } 167 explicit operator bool() const { return Obj; } 168 169 void swap(IntrusiveRefCntPtr &other) { 170 T *tmp = other.Obj; 171 other.Obj = Obj; 172 Obj = tmp; 173 } 174 175 void reset() { 176 release(); 177 Obj = nullptr; 178 } 179 180 void resetWithoutRelease() { Obj = nullptr; } 181 182 private: 183 void retain() { 184 if (Obj) 185 IntrusiveRefCntPtrInfo<T>::retain(Obj); 186 } 187 188 void release() { 189 if (Obj) 190 IntrusiveRefCntPtrInfo<T>::release(Obj); 191 } 192 193 template <typename X> friend class IntrusiveRefCntPtr; 194 }; 195 196 template <class T, class U> 197 inline bool operator==(const IntrusiveRefCntPtr<T> &A, 198 const IntrusiveRefCntPtr<U> &B) { 199 return A.get() == B.get(); 200 } 201 202 template <class T, class U> 203 inline bool operator!=(const IntrusiveRefCntPtr<T> &A, 204 const IntrusiveRefCntPtr<U> &B) { 205 return A.get() != B.get(); 206 } 207 208 template <class T, class U> 209 inline bool operator==(const IntrusiveRefCntPtr<T> &A, U *B) { 210 return A.get() == B; 211 } 212 213 template <class T, class U> 214 inline bool operator!=(const IntrusiveRefCntPtr<T> &A, U *B) { 215 return A.get() != B; 216 } 217 218 template <class T, class U> 219 inline bool operator==(T *A, const IntrusiveRefCntPtr<U> &B) { 220 return A == B.get(); 221 } 222 223 template <class T, class U> 224 inline bool operator!=(T *A, const IntrusiveRefCntPtr<U> &B) { 225 return A != B.get(); 226 } 227 228 template <class T> 229 bool operator==(std::nullptr_t A, const IntrusiveRefCntPtr<T> &B) { 230 return !B; 231 } 232 233 template <class T> 234 bool operator==(const IntrusiveRefCntPtr<T> &A, std::nullptr_t B) { 235 return B == A; 236 } 237 238 template <class T> 239 bool operator!=(std::nullptr_t A, const IntrusiveRefCntPtr<T> &B) { 240 return !(A == B); 241 } 242 243 template <class T> 244 bool operator!=(const IntrusiveRefCntPtr<T> &A, std::nullptr_t B) { 245 return !(A == B); 246 } 247 248 // Make IntrusiveRefCntPtr work with dyn_cast, isa, and the other idioms from 249 // Casting.h. 250 template <typename From> struct simplify_type; 251 252 template <class T> struct simplify_type<IntrusiveRefCntPtr<T>> { 253 using SimpleType = T *; 254 255 static SimpleType getSimplifiedValue(IntrusiveRefCntPtr<T> &Val) { 256 return Val.get(); 257 } 258 }; 259 260 template <class T> struct simplify_type<const IntrusiveRefCntPtr<T>> { 261 using SimpleType = /*const*/ T *; 262 263 static SimpleType getSimplifiedValue(const IntrusiveRefCntPtr<T> &Val) { 264 return Val.get(); 265 } 266 }; 267 268 } // end namespace llvm 269 270 #endif // LLVM_ADT_INTRUSIVEREFCNTPTR_H 271