mfbt/RefPtr.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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 */

mercurial