backout 29799f914cab, Bug 917642 - [Helix] Please update the helix blobs
[gecko.git] / mfbt / RefPtr.h
blob72c79045546c3ddcc3ca8ca790cc2e30edbf9780
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/. */
7 /* Helpers for defining and using refcounted objects. */
9 #ifndef mozilla_RefPtr_h
10 #define mozilla_RefPtr_h
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Atomics.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/TypeTraits.h"
17 namespace mozilla {
19 template<typename T> class RefCounted;
20 template<typename T> class RefPtr;
21 template<typename T> class TemporaryRef;
22 template<typename T> class OutParamRef;
23 template<typename T> OutParamRef<T> byRef(RefPtr<T>&);
25 /**
26 * RefCounted<T> is a sort of a "mixin" for a class T. RefCounted
27 * manages, well, refcounting for T, and because RefCounted is
28 * parameterized on T, RefCounted<T> can call T's destructor directly.
29 * This means T doesn't need to have a virtual dtor and so doesn't
30 * need a vtable.
32 * RefCounted<T> is created with refcount == 0. Newly-allocated
33 * RefCounted<T> must immediately be assigned to a RefPtr to make the
34 * refcount > 0. It's an error to allocate and free a bare
35 * RefCounted<T>, i.e. outside of the RefPtr machinery. Attempts to
36 * do so will abort DEBUG builds.
38 * Live RefCounted<T> have refcount > 0. The lifetime (refcounts) of
39 * live RefCounted<T> are controlled by RefPtr<T> and
40 * RefPtr<super/subclass of T>. Upon a transition from refcounted==1
41 * to 0, the RefCounted<T> "dies" and is destroyed. The "destroyed"
42 * state is represented in DEBUG builds by refcount==0xffffdead. This
43 * state distinguishes use-before-ref (refcount==0) from
44 * use-after-destroy (refcount==0xffffdead).
46 namespace detail {
47 #ifdef DEBUG
48 static const int DEAD = 0xffffdead;
49 #endif
51 // This is used WeakPtr.h as well as this file.
52 enum RefCountAtomicity
54 AtomicRefCount,
55 NonAtomicRefCount
58 template<typename T, RefCountAtomicity Atomicity>
59 class RefCounted
61 friend class RefPtr<T>;
63 protected:
64 RefCounted() : refCnt(0) { }
65 ~RefCounted() {
66 MOZ_ASSERT(refCnt == detail::DEAD);
69 public:
70 // Compatibility with nsRefPtr.
71 void AddRef() const {
72 MOZ_ASSERT(refCnt >= 0);
73 ++refCnt;
76 void Release() const {
77 MOZ_ASSERT(refCnt > 0);
78 if (0 == --refCnt) {
79 #ifdef DEBUG
80 refCnt = detail::DEAD;
81 #endif
82 delete static_cast<const T*>(this);
86 // Compatibility with wtf::RefPtr.
87 void ref() { AddRef(); }
88 void deref() { Release(); }
89 int refCount() const { return refCnt; }
90 bool hasOneRef() const {
91 MOZ_ASSERT(refCnt > 0);
92 return refCnt == 1;
95 private:
96 mutable typename Conditional<Atomicity == AtomicRefCount, Atomic<int>, int>::Type refCnt;
101 template<typename T>
102 class RefCounted : public detail::RefCounted<T, detail::NonAtomicRefCount>
104 public:
105 ~RefCounted() {
106 static_assert(IsBaseOf<RefCounted, T>::value,
107 "T must derive from RefCounted<T>");
112 * AtomicRefCounted<T> is like RefCounted<T>, with an atomically updated
113 * reference counter.
115 template<typename T>
116 class AtomicRefCounted : public detail::RefCounted<T, detail::AtomicRefCount>
118 public:
119 ~AtomicRefCounted() {
120 static_assert(IsBaseOf<AtomicRefCounted, T>::value,
121 "T must derive from AtomicRefCounted<T>");
126 * RefPtr points to a refcounted thing that has AddRef and Release
127 * methods to increase/decrease the refcount, respectively. After a
128 * RefPtr<T> is assigned a T*, the T* can be used through the RefPtr
129 * as if it were a T*.
131 * A RefPtr can forget its underlying T*, which results in the T*
132 * being wrapped in a temporary object until the T* is either
133 * re-adopted from or released by the temporary.
135 template<typename T>
136 class RefPtr
138 // To allow them to use unref()
139 friend class TemporaryRef<T>;
140 friend class OutParamRef<T>;
142 struct DontRef {};
144 public:
145 RefPtr() : ptr(0) { }
146 RefPtr(const RefPtr& o) : ptr(ref(o.ptr)) {}
147 RefPtr(const TemporaryRef<T>& o) : ptr(o.drop()) {}
148 RefPtr(T* t) : ptr(ref(t)) {}
150 template<typename U>
151 RefPtr(const RefPtr<U>& o) : ptr(ref(o.get())) {}
153 ~RefPtr() { unref(ptr); }
155 RefPtr& operator=(const RefPtr& o) {
156 assign(ref(o.ptr));
157 return *this;
159 RefPtr& operator=(const TemporaryRef<T>& o) {
160 assign(o.drop());
161 return *this;
163 RefPtr& operator=(T* t) {
164 assign(ref(t));
165 return *this;
168 template<typename U>
169 RefPtr& operator=(const RefPtr<U>& o) {
170 assign(ref(o.get()));
171 return *this;
174 TemporaryRef<T> forget() {
175 T* tmp = ptr;
176 ptr = 0;
177 return TemporaryRef<T>(tmp, DontRef());
180 T* get() const { return ptr; }
181 operator T*() const { return ptr; }
182 T* operator->() const { return ptr; }
183 T& operator*() const { return *ptr; }
184 template<typename U>
185 operator TemporaryRef<U>() { return TemporaryRef<U>(ptr); }
187 private:
188 void assign(T* t) {
189 unref(ptr);
190 ptr = t;
193 T* ptr;
195 static MOZ_ALWAYS_INLINE T* ref(T* t) {
196 if (t)
197 t->AddRef();
198 return t;
201 static MOZ_ALWAYS_INLINE void unref(T* t) {
202 if (t)
203 t->Release();
208 * TemporaryRef<T> represents an object that holds a temporary
209 * reference to a T. TemporaryRef objects can't be manually ref'd or
210 * unref'd (being temporaries, not lvalues), so can only relinquish
211 * references to other objects, or unref on destruction.
213 template<typename T>
214 class TemporaryRef
216 // To allow it to construct TemporaryRef from a bare T*
217 friend class RefPtr<T>;
219 typedef typename RefPtr<T>::DontRef DontRef;
221 public:
222 TemporaryRef(T* t) : ptr(RefPtr<T>::ref(t)) {}
223 TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {}
225 template<typename U>
226 TemporaryRef(const TemporaryRef<U>& o) : ptr(o.drop()) {}
228 ~TemporaryRef() { RefPtr<T>::unref(ptr); }
230 T* drop() const {
231 T* tmp = ptr;
232 ptr = 0;
233 return tmp;
236 private:
237 TemporaryRef(T* t, const DontRef&) : ptr(t) {}
239 mutable T* ptr;
241 TemporaryRef() MOZ_DELETE;
242 void operator=(const TemporaryRef&) MOZ_DELETE;
246 * OutParamRef is a wrapper that tracks a refcounted pointer passed as
247 * an outparam argument to a function. OutParamRef implements COM T**
248 * outparam semantics: this requires the callee to AddRef() the T*
249 * returned through the T** outparam on behalf of the caller. This
250 * means the caller (through OutParamRef) must Release() the old
251 * object contained in the tracked RefPtr. It's OK if the callee
252 * returns the same T* passed to it through the T** outparam, as long
253 * as the callee obeys the COM discipline.
255 * Prefer returning TemporaryRef<T> from functions over creating T**
256 * outparams and passing OutParamRef<T> to T**. Prefer RefPtr<T>*
257 * outparams over T** outparams.
259 template<typename T>
260 class OutParamRef
262 friend OutParamRef byRef<T>(RefPtr<T>&);
264 public:
265 ~OutParamRef() {
266 RefPtr<T>::unref(refPtr.ptr);
267 refPtr.ptr = tmp;
270 operator T**() { return &tmp; }
272 private:
273 OutParamRef(RefPtr<T>& p) : refPtr(p), tmp(p.get()) {}
275 RefPtr<T>& refPtr;
276 T* tmp;
278 OutParamRef() MOZ_DELETE;
279 OutParamRef& operator=(const OutParamRef&) MOZ_DELETE;
283 * byRef cooperates with OutParamRef to implement COM outparam semantics.
285 template<typename T>
286 OutParamRef<T>
287 byRef(RefPtr<T>& ptr)
289 return OutParamRef<T>(ptr);
292 } // namespace mozilla
294 #if 0
296 // Command line that builds these tests
298 // cp RefPtr.h test.cc && g++ -g -Wall -pedantic -DDEBUG -o test test.cc && ./test
300 using namespace mozilla;
302 struct Foo : public RefCounted<Foo>
304 Foo() : dead(false) { }
305 ~Foo() {
306 MOZ_ASSERT(!dead);
307 dead = true;
308 numDestroyed++;
311 bool dead;
312 static int numDestroyed;
314 int Foo::numDestroyed;
316 struct Bar : public Foo { };
318 TemporaryRef<Foo>
319 NewFoo()
321 return RefPtr<Foo>(new Foo());
324 TemporaryRef<Foo>
325 NewBar()
327 return new Bar();
330 void
331 GetNewFoo(Foo** f)
333 *f = new Bar();
334 // Kids, don't try this at home
335 (*f)->AddRef();
338 void
339 GetPassedFoo(Foo** f)
341 // Kids, don't try this at home
342 (*f)->AddRef();
345 void
346 GetNewFoo(RefPtr<Foo>* f)
348 *f = new Bar();
351 void
352 GetPassedFoo(RefPtr<Foo>* f)
355 TemporaryRef<Foo>
356 GetNullFoo()
358 return 0;
362 main(int argc, char** argv)
364 // This should blow up
365 // Foo* f = new Foo(); delete f;
367 MOZ_ASSERT(0 == Foo::numDestroyed);
369 RefPtr<Foo> f = new Foo();
370 MOZ_ASSERT(f->refCount() == 1);
372 MOZ_ASSERT(1 == Foo::numDestroyed);
375 RefPtr<Foo> f1 = NewFoo();
376 RefPtr<Foo> f2(NewFoo());
377 MOZ_ASSERT(1 == Foo::numDestroyed);
379 MOZ_ASSERT(3 == Foo::numDestroyed);
382 RefPtr<Foo> b = NewBar();
383 MOZ_ASSERT(3 == Foo::numDestroyed);
385 MOZ_ASSERT(4 == Foo::numDestroyed);
388 RefPtr<Foo> f1;
390 f1 = new Foo();
391 RefPtr<Foo> f2(f1);
392 RefPtr<Foo> f3 = f2;
393 MOZ_ASSERT(4 == Foo::numDestroyed);
395 MOZ_ASSERT(4 == Foo::numDestroyed);
397 MOZ_ASSERT(5 == Foo::numDestroyed);
400 RefPtr<Foo> f = new Foo();
401 f.forget();
402 MOZ_ASSERT(6 == Foo::numDestroyed);
406 RefPtr<Foo> f = new Foo();
407 GetNewFoo(byRef(f));
408 MOZ_ASSERT(7 == Foo::numDestroyed);
410 MOZ_ASSERT(8 == Foo::numDestroyed);
413 RefPtr<Foo> f = new Foo();
414 GetPassedFoo(byRef(f));
415 MOZ_ASSERT(8 == Foo::numDestroyed);
417 MOZ_ASSERT(9 == Foo::numDestroyed);
420 RefPtr<Foo> f = new Foo();
421 GetNewFoo(&f);
422 MOZ_ASSERT(10 == Foo::numDestroyed);
424 MOZ_ASSERT(11 == Foo::numDestroyed);
427 RefPtr<Foo> f = new Foo();
428 GetPassedFoo(&f);
429 MOZ_ASSERT(11 == Foo::numDestroyed);
431 MOZ_ASSERT(12 == Foo::numDestroyed);
434 RefPtr<Foo> f1 = new Bar();
436 MOZ_ASSERT(13 == Foo::numDestroyed);
439 RefPtr<Foo> f = GetNullFoo();
440 MOZ_ASSERT(13 == Foo::numDestroyed);
442 MOZ_ASSERT(13 == Foo::numDestroyed);
444 return 0;
447 #endif
449 #endif /* mozilla_RefPtr_h */