Bug 1385438 - make mozilla::RefCounted use the strongest memory consistency necessary; r=jrmuizel
authorNathan Froyd <froydnj@mozilla.com>
Fri, 27 Oct 2017 13:05:51 -0400
changeset 388773 cde4f36e7c2372a61bab8ac0709febe754897bb1
parent 388772 2ef70de6105b6b0041ef9665a2ec00b9e314e78b
child 388774 2182bb31daa6360adcda87e1aab6a09f4902d308
push id32758
push userarchaeopteryx@coole-files.de
push dateFri, 27 Oct 2017 21:31:24 +0000
treeherdermozilla-central@d58424c244c3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1385438
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1385438 - make mozilla::RefCounted use the strongest memory consistency necessary; r=jrmuizel We don't need to use sequential consistency for everything, we can get by with using weaker memory consistency models depending on the operation.
mfbt/RefCounted.h
--- a/mfbt/RefCounted.h
+++ b/mfbt/RefCounted.h
@@ -12,16 +12,18 @@
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 #include "mozilla/RefCountType.h"
 #include "mozilla/TypeTraits.h"
 
+#include <atomic>
+
 #if defined(MOZILLA_INTERNAL_API)
 #include "nsXPCOM.h"
 #endif
 
 #if defined(MOZILLA_INTERNAL_API) && \
     (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
 #define MOZ_REFCOUNTED_LEAK_CHECKING
 #endif
@@ -85,16 +87,84 @@ public:
 // This is used WeakPtr.h as well as this file.
 enum RefCountAtomicity
 {
   AtomicRefCount,
   NonAtomicRefCount
 };
 
 template<typename T, RefCountAtomicity Atomicity>
+class RC
+{
+public:
+  explicit RC(T aCount) : mValue(aCount) {}
+
+  T operator++() { return ++mValue; }
+  T operator--() { return --mValue; }
+
+  void operator=(const T& aValue) { mValue = aValue; }
+
+  operator T() const { return mValue; }
+
+private:
+  T mValue;
+};
+
+template<typename T>
+class RC<T, AtomicRefCount>
+{
+public:
+  explicit RC(T aCount) : mValue(aCount) {}
+
+  T operator++()
+  {
+    // Memory synchronization is not required when incrementing a
+    // reference count.  The first increment of a reference count on a
+    // thread is not important, since the first use of the object on a
+    // thread can happen before it.  What is important is the transfer
+    // of the pointer to that thread, which may happen prior to the
+    // first increment on that thread.  The necessary memory
+    // synchronization is done by the mechanism that transfers the
+    // pointer between threads.
+    return mValue.fetch_add(1, std::memory_order_relaxed) + 1;
+  }
+
+  T operator--()
+  {
+    // Since this may be the last release on this thread, we need
+    // release semantics so that prior writes on this thread are visible
+    // to the thread that destroys the object when it reads mValue with
+    // acquire semantics.
+    T result = mValue.fetch_sub(1, std::memory_order_release) - 1;
+    if (result == 0) {
+      // We're going to destroy the object on this thread, so we need
+      // acquire semantics to synchronize with the memory released by
+      // the last release on other threads, that is, to ensure that
+      // writes prior to that release are now visible on this thread.
+      std::atomic_thread_fence(std::memory_order_acquire);
+    }
+    return result;
+  }
+
+  // This method is only called in debug builds, so we're not too concerned
+  // about its performance.
+  void operator=(const T& aValue) { mValue.store(aValue, std::memory_order_seq_cst); }
+
+  operator T() const
+  {
+    // Use acquire semantics since we're not sure what the caller is
+    // doing.
+    return mValue.load(std::memory_order_acquire);
+  }
+
+private:
+  std::atomic<T> mValue;
+};
+
+template<typename T, RefCountAtomicity Atomicity>
 class RefCounted
 {
 protected:
   RefCounted() : mRefCnt(0) {}
   ~RefCounted() { MOZ_ASSERT(mRefCnt == detail::DEAD); }
 
 public:
   // Compatibility with nsRefPtr.
@@ -145,19 +215,17 @@ public:
   MozRefCountType refCount() const { return mRefCnt; }
   bool hasOneRef() const
   {
     MOZ_ASSERT(mRefCnt > 0);
     return mRefCnt == 1;
   }
 
 private:
-  mutable typename Conditional<Atomicity == AtomicRefCount,
-                               Atomic<MozRefCountType>,
-                               MozRefCountType>::Type mRefCnt;
+  mutable RC<MozRefCountType, Atomicity> mRefCnt;
 };
 
 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
 // Passing override for the optional argument marks the typeName and
 // typeSize functions defined by this macro as overrides.
 #define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T, ...) \
   virtual const char* typeName() const __VA_ARGS__ { return #T; } \
   virtual size_t typeSize() const __VA_ARGS__ { return sizeof(*this); }