Bug 864035 - Add an atomic RefCounted and WeakPtr implementation. r=Waldo
authorMike Hommey <mh+mozilla@glandium.org>
Sat, 18 May 2013 09:52:53 +0200
changeset 132304 5ff5c972e3af55bd1190d1418f6634e665213dbc
parent 132303 be729bc526a3879a8c1bcd77d6fa0c7dd415ec93
child 132305 68ec60cf14a9f5c635eb4cd9e36bf27534094d51
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersWaldo
bugs864035
milestone24.0a1
Bug 864035 - Add an atomic RefCounted and WeakPtr implementation. r=Waldo
mfbt/RefPtr.h
mfbt/WeakPtr.h
mozglue/linker/ElfLoader.h
--- a/mfbt/RefPtr.h
+++ b/mfbt/RefPtr.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Helpers for defining and using refcounted objects. */
 
 #ifndef mozilla_RefPtr_h_
 #define mozilla_RefPtr_h_
 
 #include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TypeTraits.h"
 
 namespace mozilla {
 
 template<typename T> class RefCounted;
 template<typename T> class RefPtr;
 template<typename T> class TemporaryRef;
@@ -36,33 +37,37 @@ template<typename T> OutParamRef<T> byRe
  * Live RefCounted<T> have refcount > 0.  The lifetime (refcounts) of
  * live RefCounted<T> are controlled by RefPtr<T> and
  * RefPtr<super/subclass of T>.  Upon a transition from refcounted==1
  * to 0, the RefCounted<T> "dies" and is destroyed.  The "destroyed"
  * state is represented in DEBUG builds by refcount==0xffffdead.  This
  * state distinguishes use-before-ref (refcount==0) from
  * use-after-destroy (refcount==0xffffdead).
  */
+namespace detail {
 #ifdef DEBUG
-namespace detail {
 static const int DEAD = 0xffffdead;
-}
 #endif
 
-template<typename T>
+// This is used WeakPtr.h as well as this file.
+enum RefCountAtomicity
+{
+  AtomicRefCount,
+  NonAtomicRefCount
+};
+
+template<typename T, RefCountAtomicity Atomicity>
 class RefCounted
 {
     friend class RefPtr<T>;
 
   protected:
     RefCounted() : refCnt(0) { }
     ~RefCounted() {
       MOZ_ASSERT(refCnt == detail::DEAD);
-      MOZ_STATIC_ASSERT((IsBaseOf<RefCounted<T>, T>::value),
-                        "T must derive from RefCounted<T>");
     }
 
   public:
     // Compatibility with nsRefPtr.
     void AddRef() {
       MOZ_ASSERT(refCnt >= 0);
       ++refCnt;
     }
@@ -82,17 +87,43 @@ class RefCounted
     void deref() { Release(); }
     int refCount() const { return refCnt; }
     bool hasOneRef() const {
       MOZ_ASSERT(refCnt > 0);
       return refCnt == 1;
     }
 
   private:
-    int refCnt;
+    typename Conditional<Atomicity == AtomicRefCount, Atomic<int>, int>::Type refCnt;
+};
+
+}
+
+template<typename T>
+class RefCounted : public detail::RefCounted<T, detail::NonAtomicRefCount>
+{
+  public:
+    ~RefCounted() {
+      MOZ_STATIC_ASSERT((IsBaseOf<RefCounted, T>::value),
+                        "T must derive from RefCounted<T>");
+    }
+};
+
+/**
+ * AtomicRefCounted<T> is like RefCounted<T>, with an atomically updated
+ * reference counter.
+ */
+template<typename T>
+class AtomicRefCounted : public detail::RefCounted<T, detail::AtomicRefCount>
+{
+  public:
+    ~AtomicRefCounted() {
+      MOZ_STATIC_ASSERT((IsBaseOf<AtomicRefCounted, T>::value),
+                        "T must derive from AtomicRefCounted<T>");
+    }
 };
 
 /**
  * RefPtr points to a refcounted thing that has AddRef and Release
  * methods to increase/decrease the refcount, respectively.  After a
  * RefPtr<T> is assigned a T*, the T* can be used through the RefPtr
  * as if it were a T*.
  *
--- a/mfbt/WeakPtr.h
+++ b/mfbt/WeakPtr.h
@@ -8,16 +8,19 @@
 /**
  * SupportsWeakPtr lets you have a pointer to an object 'Foo' without affecting
  * its lifetime. It works by creating a single shared reference counted object
  * (WeakReference) that each WeakPtr will access 'Foo' through. This lets 'Foo'
  * clear the pointer in the WeakReference without having to know about all of
  * the WeakPtrs to it and allows the WeakReference to live beyond the lifetime
  * of 'Foo'.
  *
+ * AtomicSupportsWeakPtr can be used for a variant with an atomically updated
+ * reference counter.
+ *
  * The overhead of WeakPtr is that accesses to 'Foo' becomes an additional
  * dereference, and an additional heap allocated pointer sized object shared
  * between all of the WeakPtrs.
  *
  * Example of usage:
  *
  *   // To have a class C support weak pointers, inherit from SupportsWeakPtr<C>.
  *   class C : public SupportsWeakPtr<C>
@@ -54,40 +57,41 @@
  * The API was loosely inspired by Chromium's weak_ptr.h:
  * http://src.chromium.org/svn/trunk/src/base/memory/weak_ptr.h
  */
 
 #ifndef mozilla_WeakPtr_h_
 #define mozilla_WeakPtr_h_
 
 #include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/NullPtr.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TypeTraits.h"
 
 namespace mozilla {
 
 template <typename T, class WeakReference> class WeakPtrBase;
 template <typename T, class WeakReference> class SupportsWeakPtrBase;
 
 namespace detail {
 
 // This can live beyond the lifetime of the class derived from SupportsWeakPtrBase.
-template<class T>
-class WeakReference : public RefCounted<WeakReference<T> >
+template<class T, RefCountAtomicity Atomicity>
+class WeakReference : public RefCounted<WeakReference<T, Atomicity>, Atomicity>
 {
   public:
     explicit WeakReference(T* p) : ptr(p) {}
     T* get() const {
       return ptr;
     }
 
   private:
-    friend class WeakPtrBase<T, WeakReference<T> >;
-    friend class SupportsWeakPtrBase<T, WeakReference<T> >;
+    friend class WeakPtrBase<T, WeakReference>;
+    friend class SupportsWeakPtrBase<T, WeakReference>;
     void detach() {
       ptr = nullptr;
     }
     T* ptr;
 };
 
 } // namespace detail
 
@@ -111,20 +115,40 @@ class SupportsWeakPtrBase
 
   private:
     friend class WeakPtrBase<T, WeakReference>;
 
     RefPtr<WeakReference> weakRef;
 };
 
 template <typename T>
-class SupportsWeakPtr : public SupportsWeakPtrBase<T, detail::WeakReference<T> >
+class SupportsWeakPtr
+  : public SupportsWeakPtrBase<T, detail::WeakReference<T, detail::NonAtomicRefCount> >
+{
+};
+
+template <typename T>
+class AtomicSupportsWeakPtr
+  : public SupportsWeakPtrBase<T, detail::WeakReference<T, detail::AtomicRefCount> >
 {
 };
 
+namespace detail {
+
+template <typename T>
+struct WeakReferenceCount
+{
+  static const RefCountAtomicity atomicity =
+    IsBaseOf<AtomicSupportsWeakPtr<T>, T>::value
+    ? AtomicRefCount
+    : NonAtomicRefCount;
+};
+
+}
+
 template <typename T, class WeakReference>
 class WeakPtrBase
 {
   public:
     WeakPtrBase(const WeakPtrBase<T, WeakReference>& o) : ref(o.ref) {}
     // Ensure that ref is dereferenceable in the uninitialized state
     WeakPtrBase() : ref(new WeakReference(nullptr)) {}
 
@@ -147,19 +171,19 @@ class WeakPtrBase
     friend class SupportsWeakPtrBase<T, WeakReference>;
 
     explicit WeakPtrBase(const RefPtr<WeakReference> &o) : ref(o) {}
 
     RefPtr<WeakReference> ref;
 };
 
 template <typename T>
-class WeakPtr : public WeakPtrBase<T, detail::WeakReference<T> >
+class WeakPtr : public WeakPtrBase<T, detail::WeakReference<T, detail::WeakReferenceCount<T>::atomicity> >
 {
-    typedef WeakPtrBase<T, detail::WeakReference<T> > Base;
+    typedef WeakPtrBase<T, detail::WeakReference<T, detail::WeakReferenceCount<T>::atomicity> > Base;
   public:
     WeakPtr(const WeakPtr<T>& o) : Base(o) {}
     WeakPtr(const Base& o) : Base(o) {}
     WeakPtr() {}
 };
 
 } // namespace mozilla
 
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -63,24 +63,26 @@ MFBT_API void
  * Specialize RefCounted template for LibHandle. We may get references to
  * LibHandles during the execution of their destructor, so we need
  * RefCounted<LibHandle>::Release to support some reentrancy. See further
  * below.
  */
 class LibHandle;
 
 namespace mozilla {
+namespace detail {
 
-template <> inline void RefCounted<LibHandle>::Release();
+template <> inline void RefCounted<LibHandle, NonAtomicRefCount>::Release();
 
-template <> inline RefCounted<LibHandle>::~RefCounted()
+template <> inline RefCounted<LibHandle, NonAtomicRefCount>::~RefCounted()
 {
   MOZ_ASSERT(refCnt == 0x7fffdead);
 }
 
+} /* namespace detail */
 } /* namespace mozilla */
 
 /* Forward declaration */
 class Mappable;
 
 /**
  * Abstract class for loaded libraries. Libraries may be loaded through the
  * system linker or this linker, both cases will be derived from this class.
@@ -208,18 +210,19 @@ private:
  * Specialized RefCounted<LibHandle>::Release. Under normal operation, when
  * refCnt reaches 0, the LibHandle is deleted. Its refCnt is however increased
  * to 1 on normal builds, and 0x7fffdead on debug builds so that the LibHandle
  * can still be referenced while the destructor is executing. The refCnt is
  * allowed to grow > 0x7fffdead, but not to decrease under that value, which
  * would mean too many Releases from within the destructor.
  */
 namespace mozilla {
+namespace detail {
 
-template <> inline void RefCounted<LibHandle>::Release() {
+template <> inline void RefCounted<LibHandle, NonAtomicRefCount>::Release() {
 #ifdef DEBUG
   if (refCnt > 0x7fff0000)
     MOZ_ASSERT(refCnt > 0x7fffdead);
 #endif
   MOZ_ASSERT(refCnt > 0);
   if (refCnt > 0) {
     if (0 == --refCnt) {
 #ifdef DEBUG
@@ -227,16 +230,17 @@ template <> inline void RefCounted<LibHa
 #else
       refCnt = 1;
 #endif
       delete static_cast<LibHandle*>(this);
     }
   }
 }
 
+} /* namespace detail */
 } /* namespace mozilla */
 
 /**
  * Class handling libraries loaded by the system linker
  */
 class SystemElf: public LibHandle
 {
 public: