|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* Helpers for defining and using refcounted objects. */ |
|
8 |
|
9 #ifndef mozilla_RefPtr_h |
|
10 #define mozilla_RefPtr_h |
|
11 |
|
12 #include "mozilla/Assertions.h" |
|
13 #include "mozilla/Atomics.h" |
|
14 #include "mozilla/Attributes.h" |
|
15 #include "mozilla/RefCountType.h" |
|
16 #include "mozilla/TypeTraits.h" |
|
17 #if defined(MOZILLA_INTERNAL_API) |
|
18 #include "nsXPCOM.h" |
|
19 #endif |
|
20 |
|
21 #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)) |
|
22 #define MOZ_REFCOUNTED_LEAK_CHECKING |
|
23 #endif |
|
24 |
|
25 namespace mozilla { |
|
26 |
|
27 template<typename T> class RefCounted; |
|
28 template<typename T> class RefPtr; |
|
29 template<typename T> class TemporaryRef; |
|
30 template<typename T> class OutParamRef; |
|
31 template<typename T> OutParamRef<T> byRef(RefPtr<T>&); |
|
32 |
|
33 /** |
|
34 * RefCounted<T> is a sort of a "mixin" for a class T. RefCounted |
|
35 * manages, well, refcounting for T, and because RefCounted is |
|
36 * parameterized on T, RefCounted<T> can call T's destructor directly. |
|
37 * This means T doesn't need to have a virtual dtor and so doesn't |
|
38 * need a vtable. |
|
39 * |
|
40 * RefCounted<T> is created with refcount == 0. Newly-allocated |
|
41 * RefCounted<T> must immediately be assigned to a RefPtr to make the |
|
42 * refcount > 0. It's an error to allocate and free a bare |
|
43 * RefCounted<T>, i.e. outside of the RefPtr machinery. Attempts to |
|
44 * do so will abort DEBUG builds. |
|
45 * |
|
46 * Live RefCounted<T> have refcount > 0. The lifetime (refcounts) of |
|
47 * live RefCounted<T> are controlled by RefPtr<T> and |
|
48 * RefPtr<super/subclass of T>. Upon a transition from refcounted==1 |
|
49 * to 0, the RefCounted<T> "dies" and is destroyed. The "destroyed" |
|
50 * state is represented in DEBUG builds by refcount==0xffffdead. This |
|
51 * state distinguishes use-before-ref (refcount==0) from |
|
52 * use-after-destroy (refcount==0xffffdead). |
|
53 * |
|
54 * Note that when deriving from RefCounted or AtomicRefCounted, you |
|
55 * should add MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public |
|
56 * section of your class, where ClassName is the name of your class. |
|
57 */ |
|
58 namespace detail { |
|
59 #ifdef DEBUG |
|
60 const MozRefCountType DEAD = 0xffffdead; |
|
61 #endif |
|
62 |
|
63 // When building code that gets compiled into Gecko, try to use the |
|
64 // trace-refcount leak logging facilities. |
|
65 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING |
|
66 class RefCountLogger |
|
67 { |
|
68 public: |
|
69 static void logAddRef(const void* aPointer, MozRefCountType aRefCount, |
|
70 const char* aTypeName, uint32_t aInstanceSize) |
|
71 { |
|
72 MOZ_ASSERT(aRefCount != DEAD); |
|
73 NS_LogAddRef(const_cast<void*>(aPointer), aRefCount, aTypeName, aInstanceSize); |
|
74 } |
|
75 |
|
76 static void logRelease(const void* aPointer, MozRefCountType aRefCount, |
|
77 const char* aTypeName) |
|
78 { |
|
79 MOZ_ASSERT(aRefCount != DEAD); |
|
80 NS_LogRelease(const_cast<void*>(aPointer), aRefCount, aTypeName); |
|
81 } |
|
82 }; |
|
83 #endif |
|
84 |
|
85 // This is used WeakPtr.h as well as this file. |
|
86 enum RefCountAtomicity |
|
87 { |
|
88 AtomicRefCount, |
|
89 NonAtomicRefCount |
|
90 }; |
|
91 |
|
92 template<typename T, RefCountAtomicity Atomicity> |
|
93 class RefCounted |
|
94 { |
|
95 friend class RefPtr<T>; |
|
96 |
|
97 protected: |
|
98 RefCounted() : refCnt(0) { } |
|
99 ~RefCounted() { |
|
100 MOZ_ASSERT(refCnt == detail::DEAD); |
|
101 } |
|
102 |
|
103 public: |
|
104 // Compatibility with nsRefPtr. |
|
105 void AddRef() const { |
|
106 // Note: this method must be thread safe for AtomicRefCounted. |
|
107 MOZ_ASSERT(int32_t(refCnt) >= 0); |
|
108 #ifndef MOZ_REFCOUNTED_LEAK_CHECKING |
|
109 ++refCnt; |
|
110 #else |
|
111 const char* type = static_cast<const T*>(this)->typeName(); |
|
112 uint32_t size = static_cast<const T*>(this)->typeSize(); |
|
113 const void* ptr = static_cast<const T*>(this); |
|
114 MozRefCountType cnt = ++refCnt; |
|
115 detail::RefCountLogger::logAddRef(ptr, cnt, type, size); |
|
116 #endif |
|
117 } |
|
118 |
|
119 void Release() const { |
|
120 // Note: this method must be thread safe for AtomicRefCounted. |
|
121 MOZ_ASSERT(int32_t(refCnt) > 0); |
|
122 #ifndef MOZ_REFCOUNTED_LEAK_CHECKING |
|
123 MozRefCountType cnt = --refCnt; |
|
124 #else |
|
125 const char* type = static_cast<const T*>(this)->typeName(); |
|
126 const void* ptr = static_cast<const T*>(this); |
|
127 MozRefCountType cnt = --refCnt; |
|
128 // Note: it's not safe to touch |this| after decrementing the refcount, |
|
129 // except for below. |
|
130 detail::RefCountLogger::logRelease(ptr, cnt, type); |
|
131 #endif |
|
132 if (0 == cnt) { |
|
133 // Because we have atomically decremented the refcount above, only |
|
134 // one thread can get a 0 count here, so as long as we can assume that |
|
135 // everything else in the system is accessing this object through |
|
136 // RefPtrs, it's safe to access |this| here. |
|
137 #ifdef DEBUG |
|
138 refCnt = detail::DEAD; |
|
139 #endif |
|
140 delete static_cast<const T*>(this); |
|
141 } |
|
142 } |
|
143 |
|
144 // Compatibility with wtf::RefPtr. |
|
145 void ref() { AddRef(); } |
|
146 void deref() { Release(); } |
|
147 MozRefCountType refCount() const { return refCnt; } |
|
148 bool hasOneRef() const { |
|
149 MOZ_ASSERT(refCnt > 0); |
|
150 return refCnt == 1; |
|
151 } |
|
152 |
|
153 private: |
|
154 mutable typename Conditional<Atomicity == AtomicRefCount, Atomic<MozRefCountType>, MozRefCountType>::Type refCnt; |
|
155 }; |
|
156 |
|
157 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING |
|
158 #define MOZ_DECLARE_REFCOUNTED_TYPENAME(T) \ |
|
159 const char* typeName() const { return #T; } \ |
|
160 size_t typeSize() const { return sizeof(*this); } |
|
161 |
|
162 #define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T) \ |
|
163 virtual const char* typeName() const { return #T; } \ |
|
164 virtual size_t typeSize() const { return sizeof(*this); } |
|
165 #else |
|
166 #define MOZ_DECLARE_REFCOUNTED_TYPENAME(T) |
|
167 #define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T) |
|
168 #endif |
|
169 |
|
170 } |
|
171 |
|
172 template<typename T> |
|
173 class RefCounted : public detail::RefCounted<T, detail::NonAtomicRefCount> |
|
174 { |
|
175 public: |
|
176 ~RefCounted() { |
|
177 static_assert(IsBaseOf<RefCounted, T>::value, |
|
178 "T must derive from RefCounted<T>"); |
|
179 } |
|
180 }; |
|
181 |
|
182 /** |
|
183 * AtomicRefCounted<T> is like RefCounted<T>, with an atomically updated |
|
184 * reference counter. |
|
185 */ |
|
186 template<typename T> |
|
187 class AtomicRefCounted : public detail::RefCounted<T, detail::AtomicRefCount> |
|
188 { |
|
189 public: |
|
190 ~AtomicRefCounted() { |
|
191 static_assert(IsBaseOf<AtomicRefCounted, T>::value, |
|
192 "T must derive from AtomicRefCounted<T>"); |
|
193 } |
|
194 }; |
|
195 |
|
196 /** |
|
197 * RefPtr points to a refcounted thing that has AddRef and Release |
|
198 * methods to increase/decrease the refcount, respectively. After a |
|
199 * RefPtr<T> is assigned a T*, the T* can be used through the RefPtr |
|
200 * as if it were a T*. |
|
201 * |
|
202 * A RefPtr can forget its underlying T*, which results in the T* |
|
203 * being wrapped in a temporary object until the T* is either |
|
204 * re-adopted from or released by the temporary. |
|
205 */ |
|
206 template<typename T> |
|
207 class RefPtr |
|
208 { |
|
209 // To allow them to use unref() |
|
210 friend class TemporaryRef<T>; |
|
211 friend class OutParamRef<T>; |
|
212 |
|
213 struct DontRef {}; |
|
214 |
|
215 public: |
|
216 RefPtr() : ptr(0) { } |
|
217 RefPtr(const RefPtr& o) : ptr(ref(o.ptr)) {} |
|
218 RefPtr(const TemporaryRef<T>& o) : ptr(o.drop()) {} |
|
219 RefPtr(T* t) : ptr(ref(t)) {} |
|
220 |
|
221 template<typename U> |
|
222 RefPtr(const RefPtr<U>& o) : ptr(ref(o.get())) {} |
|
223 |
|
224 ~RefPtr() { unref(ptr); } |
|
225 |
|
226 RefPtr& operator=(const RefPtr& o) { |
|
227 assign(ref(o.ptr)); |
|
228 return *this; |
|
229 } |
|
230 RefPtr& operator=(const TemporaryRef<T>& o) { |
|
231 assign(o.drop()); |
|
232 return *this; |
|
233 } |
|
234 RefPtr& operator=(T* t) { |
|
235 assign(ref(t)); |
|
236 return *this; |
|
237 } |
|
238 |
|
239 template<typename U> |
|
240 RefPtr& operator=(const RefPtr<U>& o) { |
|
241 assign(ref(o.get())); |
|
242 return *this; |
|
243 } |
|
244 |
|
245 TemporaryRef<T> forget() { |
|
246 T* tmp = ptr; |
|
247 ptr = 0; |
|
248 return TemporaryRef<T>(tmp, DontRef()); |
|
249 } |
|
250 |
|
251 T* get() const { return ptr; } |
|
252 operator T*() const { return ptr; } |
|
253 T* operator->() const { return ptr; } |
|
254 T& operator*() const { return *ptr; } |
|
255 template<typename U> |
|
256 operator TemporaryRef<U>() { return TemporaryRef<U>(ptr); } |
|
257 |
|
258 private: |
|
259 void assign(T* t) { |
|
260 unref(ptr); |
|
261 ptr = t; |
|
262 } |
|
263 |
|
264 T* ptr; |
|
265 |
|
266 static MOZ_ALWAYS_INLINE T* ref(T* t) { |
|
267 if (t) |
|
268 t->AddRef(); |
|
269 return t; |
|
270 } |
|
271 |
|
272 static MOZ_ALWAYS_INLINE void unref(T* t) { |
|
273 if (t) |
|
274 t->Release(); |
|
275 } |
|
276 }; |
|
277 |
|
278 /** |
|
279 * TemporaryRef<T> represents an object that holds a temporary |
|
280 * reference to a T. TemporaryRef objects can't be manually ref'd or |
|
281 * unref'd (being temporaries, not lvalues), so can only relinquish |
|
282 * references to other objects, or unref on destruction. |
|
283 */ |
|
284 template<typename T> |
|
285 class TemporaryRef |
|
286 { |
|
287 // To allow it to construct TemporaryRef from a bare T* |
|
288 friend class RefPtr<T>; |
|
289 |
|
290 typedef typename RefPtr<T>::DontRef DontRef; |
|
291 |
|
292 public: |
|
293 TemporaryRef(T* t) : ptr(RefPtr<T>::ref(t)) {} |
|
294 TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {} |
|
295 |
|
296 template<typename U> |
|
297 TemporaryRef(const TemporaryRef<U>& o) : ptr(o.drop()) {} |
|
298 |
|
299 ~TemporaryRef() { RefPtr<T>::unref(ptr); } |
|
300 |
|
301 T* drop() const { |
|
302 T* tmp = ptr; |
|
303 ptr = 0; |
|
304 return tmp; |
|
305 } |
|
306 |
|
307 private: |
|
308 TemporaryRef(T* t, const DontRef&) : ptr(t) {} |
|
309 |
|
310 mutable T* ptr; |
|
311 |
|
312 TemporaryRef() MOZ_DELETE; |
|
313 void operator=(const TemporaryRef&) MOZ_DELETE; |
|
314 }; |
|
315 |
|
316 /** |
|
317 * OutParamRef is a wrapper that tracks a refcounted pointer passed as |
|
318 * an outparam argument to a function. OutParamRef implements COM T** |
|
319 * outparam semantics: this requires the callee to AddRef() the T* |
|
320 * returned through the T** outparam on behalf of the caller. This |
|
321 * means the caller (through OutParamRef) must Release() the old |
|
322 * object contained in the tracked RefPtr. It's OK if the callee |
|
323 * returns the same T* passed to it through the T** outparam, as long |
|
324 * as the callee obeys the COM discipline. |
|
325 * |
|
326 * Prefer returning TemporaryRef<T> from functions over creating T** |
|
327 * outparams and passing OutParamRef<T> to T**. Prefer RefPtr<T>* |
|
328 * outparams over T** outparams. |
|
329 */ |
|
330 template<typename T> |
|
331 class OutParamRef |
|
332 { |
|
333 friend OutParamRef byRef<T>(RefPtr<T>&); |
|
334 |
|
335 public: |
|
336 ~OutParamRef() { |
|
337 RefPtr<T>::unref(refPtr.ptr); |
|
338 refPtr.ptr = tmp; |
|
339 } |
|
340 |
|
341 operator T**() { return &tmp; } |
|
342 |
|
343 private: |
|
344 OutParamRef(RefPtr<T>& p) : refPtr(p), tmp(p.get()) {} |
|
345 |
|
346 RefPtr<T>& refPtr; |
|
347 T* tmp; |
|
348 |
|
349 OutParamRef() MOZ_DELETE; |
|
350 OutParamRef& operator=(const OutParamRef&) MOZ_DELETE; |
|
351 }; |
|
352 |
|
353 /** |
|
354 * byRef cooperates with OutParamRef to implement COM outparam semantics. |
|
355 */ |
|
356 template<typename T> |
|
357 OutParamRef<T> |
|
358 byRef(RefPtr<T>& ptr) |
|
359 { |
|
360 return OutParamRef<T>(ptr); |
|
361 } |
|
362 |
|
363 } // namespace mozilla |
|
364 |
|
365 #if 0 |
|
366 |
|
367 // Command line that builds these tests |
|
368 // |
|
369 // cp RefPtr.h test.cc && g++ -g -Wall -pedantic -DDEBUG -o test test.cc && ./test |
|
370 |
|
371 using namespace mozilla; |
|
372 |
|
373 struct Foo : public RefCounted<Foo> |
|
374 { |
|
375 MOZ_DECLARE_REFCOUNTED_TYPENAME(Foo) |
|
376 Foo() : dead(false) { } |
|
377 ~Foo() { |
|
378 MOZ_ASSERT(!dead); |
|
379 dead = true; |
|
380 numDestroyed++; |
|
381 } |
|
382 |
|
383 bool dead; |
|
384 static int numDestroyed; |
|
385 }; |
|
386 int Foo::numDestroyed; |
|
387 |
|
388 struct Bar : public Foo { }; |
|
389 |
|
390 TemporaryRef<Foo> |
|
391 NewFoo() |
|
392 { |
|
393 return RefPtr<Foo>(new Foo()); |
|
394 } |
|
395 |
|
396 TemporaryRef<Foo> |
|
397 NewBar() |
|
398 { |
|
399 return new Bar(); |
|
400 } |
|
401 |
|
402 void |
|
403 GetNewFoo(Foo** f) |
|
404 { |
|
405 *f = new Bar(); |
|
406 // Kids, don't try this at home |
|
407 (*f)->AddRef(); |
|
408 } |
|
409 |
|
410 void |
|
411 GetPassedFoo(Foo** f) |
|
412 { |
|
413 // Kids, don't try this at home |
|
414 (*f)->AddRef(); |
|
415 } |
|
416 |
|
417 void |
|
418 GetNewFoo(RefPtr<Foo>* f) |
|
419 { |
|
420 *f = new Bar(); |
|
421 } |
|
422 |
|
423 void |
|
424 GetPassedFoo(RefPtr<Foo>* f) |
|
425 {} |
|
426 |
|
427 TemporaryRef<Foo> |
|
428 GetNullFoo() |
|
429 { |
|
430 return 0; |
|
431 } |
|
432 |
|
433 int |
|
434 main(int argc, char** argv) |
|
435 { |
|
436 // This should blow up |
|
437 // Foo* f = new Foo(); delete f; |
|
438 |
|
439 MOZ_ASSERT(0 == Foo::numDestroyed); |
|
440 { |
|
441 RefPtr<Foo> f = new Foo(); |
|
442 MOZ_ASSERT(f->refCount() == 1); |
|
443 } |
|
444 MOZ_ASSERT(1 == Foo::numDestroyed); |
|
445 |
|
446 { |
|
447 RefPtr<Foo> f1 = NewFoo(); |
|
448 RefPtr<Foo> f2(NewFoo()); |
|
449 MOZ_ASSERT(1 == Foo::numDestroyed); |
|
450 } |
|
451 MOZ_ASSERT(3 == Foo::numDestroyed); |
|
452 |
|
453 { |
|
454 RefPtr<Foo> b = NewBar(); |
|
455 MOZ_ASSERT(3 == Foo::numDestroyed); |
|
456 } |
|
457 MOZ_ASSERT(4 == Foo::numDestroyed); |
|
458 |
|
459 { |
|
460 RefPtr<Foo> f1; |
|
461 { |
|
462 f1 = new Foo(); |
|
463 RefPtr<Foo> f2(f1); |
|
464 RefPtr<Foo> f3 = f2; |
|
465 MOZ_ASSERT(4 == Foo::numDestroyed); |
|
466 } |
|
467 MOZ_ASSERT(4 == Foo::numDestroyed); |
|
468 } |
|
469 MOZ_ASSERT(5 == Foo::numDestroyed); |
|
470 |
|
471 { |
|
472 RefPtr<Foo> f = new Foo(); |
|
473 f.forget(); |
|
474 MOZ_ASSERT(6 == Foo::numDestroyed); |
|
475 } |
|
476 |
|
477 { |
|
478 RefPtr<Foo> f = new Foo(); |
|
479 GetNewFoo(byRef(f)); |
|
480 MOZ_ASSERT(7 == Foo::numDestroyed); |
|
481 } |
|
482 MOZ_ASSERT(8 == Foo::numDestroyed); |
|
483 |
|
484 { |
|
485 RefPtr<Foo> f = new Foo(); |
|
486 GetPassedFoo(byRef(f)); |
|
487 MOZ_ASSERT(8 == Foo::numDestroyed); |
|
488 } |
|
489 MOZ_ASSERT(9 == Foo::numDestroyed); |
|
490 |
|
491 { |
|
492 RefPtr<Foo> f = new Foo(); |
|
493 GetNewFoo(&f); |
|
494 MOZ_ASSERT(10 == Foo::numDestroyed); |
|
495 } |
|
496 MOZ_ASSERT(11 == Foo::numDestroyed); |
|
497 |
|
498 { |
|
499 RefPtr<Foo> f = new Foo(); |
|
500 GetPassedFoo(&f); |
|
501 MOZ_ASSERT(11 == Foo::numDestroyed); |
|
502 } |
|
503 MOZ_ASSERT(12 == Foo::numDestroyed); |
|
504 |
|
505 { |
|
506 RefPtr<Foo> f1 = new Bar(); |
|
507 } |
|
508 MOZ_ASSERT(13 == Foo::numDestroyed); |
|
509 |
|
510 { |
|
511 RefPtr<Foo> f = GetNullFoo(); |
|
512 MOZ_ASSERT(13 == Foo::numDestroyed); |
|
513 } |
|
514 MOZ_ASSERT(13 == Foo::numDestroyed); |
|
515 |
|
516 return 0; |
|
517 } |
|
518 |
|
519 #endif |
|
520 |
|
521 #endif /* mozilla_RefPtr_h */ |