Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/RefCountType.h"
16 #include "mozilla/TypeTraits.h"
17 #if defined(MOZILLA_INTERNAL_API)
18 #include "nsXPCOM.h"
19 #endif
21 #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
22 #define MOZ_REFCOUNTED_LEAK_CHECKING
23 #endif
25 namespace mozilla {
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>&);
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
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 }
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
85 // This is used WeakPtr.h as well as this file.
86 enum RefCountAtomicity
87 {
88 AtomicRefCount,
89 NonAtomicRefCount
90 };
92 template<typename T, RefCountAtomicity Atomicity>
93 class RefCounted
94 {
95 friend class RefPtr<T>;
97 protected:
98 RefCounted() : refCnt(0) { }
99 ~RefCounted() {
100 MOZ_ASSERT(refCnt == detail::DEAD);
101 }
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 }
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 }
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 }
153 private:
154 mutable typename Conditional<Atomicity == AtomicRefCount, Atomic<MozRefCountType>, MozRefCountType>::Type refCnt;
155 };
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); }
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
170 }
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 };
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 };
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>;
213 struct DontRef {};
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)) {}
221 template<typename U>
222 RefPtr(const RefPtr<U>& o) : ptr(ref(o.get())) {}
224 ~RefPtr() { unref(ptr); }
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 }
239 template<typename U>
240 RefPtr& operator=(const RefPtr<U>& o) {
241 assign(ref(o.get()));
242 return *this;
243 }
245 TemporaryRef<T> forget() {
246 T* tmp = ptr;
247 ptr = 0;
248 return TemporaryRef<T>(tmp, DontRef());
249 }
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); }
258 private:
259 void assign(T* t) {
260 unref(ptr);
261 ptr = t;
262 }
264 T* ptr;
266 static MOZ_ALWAYS_INLINE T* ref(T* t) {
267 if (t)
268 t->AddRef();
269 return t;
270 }
272 static MOZ_ALWAYS_INLINE void unref(T* t) {
273 if (t)
274 t->Release();
275 }
276 };
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>;
290 typedef typename RefPtr<T>::DontRef DontRef;
292 public:
293 TemporaryRef(T* t) : ptr(RefPtr<T>::ref(t)) {}
294 TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {}
296 template<typename U>
297 TemporaryRef(const TemporaryRef<U>& o) : ptr(o.drop()) {}
299 ~TemporaryRef() { RefPtr<T>::unref(ptr); }
301 T* drop() const {
302 T* tmp = ptr;
303 ptr = 0;
304 return tmp;
305 }
307 private:
308 TemporaryRef(T* t, const DontRef&) : ptr(t) {}
310 mutable T* ptr;
312 TemporaryRef() MOZ_DELETE;
313 void operator=(const TemporaryRef&) MOZ_DELETE;
314 };
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>&);
335 public:
336 ~OutParamRef() {
337 RefPtr<T>::unref(refPtr.ptr);
338 refPtr.ptr = tmp;
339 }
341 operator T**() { return &tmp; }
343 private:
344 OutParamRef(RefPtr<T>& p) : refPtr(p), tmp(p.get()) {}
346 RefPtr<T>& refPtr;
347 T* tmp;
349 OutParamRef() MOZ_DELETE;
350 OutParamRef& operator=(const OutParamRef&) MOZ_DELETE;
351 };
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 }
363 } // namespace mozilla
365 #if 0
367 // Command line that builds these tests
368 //
369 // cp RefPtr.h test.cc && g++ -g -Wall -pedantic -DDEBUG -o test test.cc && ./test
371 using namespace mozilla;
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 }
383 bool dead;
384 static int numDestroyed;
385 };
386 int Foo::numDestroyed;
388 struct Bar : public Foo { };
390 TemporaryRef<Foo>
391 NewFoo()
392 {
393 return RefPtr<Foo>(new Foo());
394 }
396 TemporaryRef<Foo>
397 NewBar()
398 {
399 return new Bar();
400 }
402 void
403 GetNewFoo(Foo** f)
404 {
405 *f = new Bar();
406 // Kids, don't try this at home
407 (*f)->AddRef();
408 }
410 void
411 GetPassedFoo(Foo** f)
412 {
413 // Kids, don't try this at home
414 (*f)->AddRef();
415 }
417 void
418 GetNewFoo(RefPtr<Foo>* f)
419 {
420 *f = new Bar();
421 }
423 void
424 GetPassedFoo(RefPtr<Foo>* f)
425 {}
427 TemporaryRef<Foo>
428 GetNullFoo()
429 {
430 return 0;
431 }
433 int
434 main(int argc, char** argv)
435 {
436 // This should blow up
437 // Foo* f = new Foo(); delete f;
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);
446 {
447 RefPtr<Foo> f1 = NewFoo();
448 RefPtr<Foo> f2(NewFoo());
449 MOZ_ASSERT(1 == Foo::numDestroyed);
450 }
451 MOZ_ASSERT(3 == Foo::numDestroyed);
453 {
454 RefPtr<Foo> b = NewBar();
455 MOZ_ASSERT(3 == Foo::numDestroyed);
456 }
457 MOZ_ASSERT(4 == Foo::numDestroyed);
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);
471 {
472 RefPtr<Foo> f = new Foo();
473 f.forget();
474 MOZ_ASSERT(6 == Foo::numDestroyed);
475 }
477 {
478 RefPtr<Foo> f = new Foo();
479 GetNewFoo(byRef(f));
480 MOZ_ASSERT(7 == Foo::numDestroyed);
481 }
482 MOZ_ASSERT(8 == Foo::numDestroyed);
484 {
485 RefPtr<Foo> f = new Foo();
486 GetPassedFoo(byRef(f));
487 MOZ_ASSERT(8 == Foo::numDestroyed);
488 }
489 MOZ_ASSERT(9 == Foo::numDestroyed);
491 {
492 RefPtr<Foo> f = new Foo();
493 GetNewFoo(&f);
494 MOZ_ASSERT(10 == Foo::numDestroyed);
495 }
496 MOZ_ASSERT(11 == Foo::numDestroyed);
498 {
499 RefPtr<Foo> f = new Foo();
500 GetPassedFoo(&f);
501 MOZ_ASSERT(11 == Foo::numDestroyed);
502 }
503 MOZ_ASSERT(12 == Foo::numDestroyed);
505 {
506 RefPtr<Foo> f1 = new Bar();
507 }
508 MOZ_ASSERT(13 == Foo::numDestroyed);
510 {
511 RefPtr<Foo> f = GetNullFoo();
512 MOZ_ASSERT(13 == Foo::numDestroyed);
513 }
514 MOZ_ASSERT(13 == Foo::numDestroyed);
516 return 0;
517 }
519 #endif
521 #endif /* mozilla_RefPtr_h */