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