try: -b do -p all -u all -t all draft
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Fri, 18 Aug 2017 20:16:42 +0300
changeset 1264887 ab9127aaa432
parent 1260483 de339d0ca73b
child 1265707 2893c0ecfa01
push id214355
push useropettay@mozilla.com
push dateFri, 18 Aug 2017 17:17:18 +0000
treeherdertry@ab9127aaa432 [default view] [failures only]
milestone57.0a1
try: -b do -p all -u all -t all
dom/base/FragmentOrElement.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsISupportsImpl.h
xpcom/build/nsXPCOM.h
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -2139,19 +2139,19 @@ NS_INTERFACE_MAP_BEGIN(FragmentOrElement
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
                                  new nsNodeSupportsWeakRefTearoff(this))
   // DOM bindings depend on the identity pointer being the
   // same as nsINode (which nsIContent inherits).
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentOrElement)
-NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement,
-                                                   nsNodeUtils::LastRelease(this))
+NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(FragmentOrElement)
+NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement,
+                                                                    nsNodeUtils::LastRelease(this))
 
 //----------------------------------------------------------------------
 
 nsresult
 FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst,
                                bool aPreallocateChildren)
 {
   nsresult rv = aDst->mAttrsAndChildren.EnsureCapacityToClone(mAttrsAndChildren,
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -192,16 +192,47 @@
 #include "mozilla/ThreadLocal.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 using namespace mozilla;
 
+struct NurseryPurpleBufferEntry
+{
+  void* mPtr;
+  nsCycleCollectionParticipant* mParticipant;
+  nsCycleCollectingAutoRefCnt* mRefCnt;
+};
+
+#define NURSERY_PURPLE_BUFFER_SIZE 2048
+bool gNurseryPurpleBufferEnabled = true;
+NurseryPurpleBufferEntry gNurseryPurpleBufferEntry[NURSERY_PURPLE_BUFFER_SIZE];
+uint32_t gNurseryPurpleBufferEntryCount = 0;
+
+void ClearNurseryPurpleBuffer();
+
+void SuspectUsingNurseryPurpleBuffer(void* aPtr,
+                                     nsCycleCollectionParticipant* aCp,
+                                     nsCycleCollectingAutoRefCnt* aRefCnt)
+{
+  MOZ_ASSERT(gNurseryPurpleBufferEnabled);
+  if (gNurseryPurpleBufferEntryCount == NURSERY_PURPLE_BUFFER_SIZE) {
+    ClearNurseryPurpleBuffer();
+  }
+
+  NurseryPurpleBufferEntry& entry =
+    gNurseryPurpleBufferEntry[gNurseryPurpleBufferEntryCount];
+  entry.mPtr = aPtr;
+  entry.mParticipant = aCp;
+  entry.mRefCnt = aRefCnt;
+  ++gNurseryPurpleBufferEntryCount;
+}
+
 //#define COLLECT_TIME_DEBUG
 
 // Enable assertions that are useful for diagnosing errors in graph construction.
 //#define DEBUG_CC_GRAPH
 
 #define DEFAULT_SHUTDOWN_COLLECTIONS 5
 
 // One to do the freeing, then another to detect there is no more work to do.
@@ -1034,16 +1065,23 @@ public:
   ~nsPurpleBuffer()
   {
   }
 
   // This method compacts mEntries.
   template<class PurpleVisitor>
   void VisitEntries(PurpleVisitor& aVisitor)
   {
+    Maybe<AutoRestore<bool>> ar;
+    if (NS_IsMainThread()) {
+      ar.emplace(gNurseryPurpleBufferEnabled);
+      gNurseryPurpleBufferEnabled = false;
+      ClearNurseryPurpleBuffer();
+    }
+
     if (mEntries.IsEmpty()) {
       return;
     }
 
     uint32_t oldLength = mEntries.Length();
     uint32_t newLength = 0;
     auto revIter = mEntries.IterFromLast();
     auto iter = mEntries.Iter();
@@ -1284,16 +1322,17 @@ public:
   void SetForgetSkippableCallback(CC_ForgetSkippableCallback aForgetSkippableCB)
   {
     CheckThreadSafety();
     mForgetSkippableCB = aForgetSkippableCB;
   }
 
   void Suspect(void* aPtr, nsCycleCollectionParticipant* aCp,
                nsCycleCollectingAutoRefCnt* aRefCnt);
+  void SuspectNurseryEntries();
   uint32_t SuspectedCount();
   void ForgetSkippable(js::SliceBudget& aBudget, bool aRemoveChildlessNodes,
                        bool aAsyncSnowWhiteFreeing);
   bool FreeSnowWhite(bool aUntilNoSWInPurpleBuffer);
 
   // This method assumes its argument is already canonicalized.
   void RemoveObjectFromGraph(void* aPtr);
 
@@ -3487,16 +3526,27 @@ nsCycleCollector::Suspect(void* aPtr, ns
 
   MOZ_ASSERT(aParti || CanonicalizeXPCOMParticipant(static_cast<nsISupports*>(aPtr)) == aPtr,
              "Suspect nsISupports pointer must be canonical");
 
   mPurpleBuf.Put(aPtr, aParti, aRefCnt);
 }
 
 void
+nsCycleCollector::SuspectNurseryEntries()
+{
+  CheckThreadSafety();
+  while (gNurseryPurpleBufferEntryCount) {
+    NurseryPurpleBufferEntry& entry =
+      gNurseryPurpleBufferEntry[--gNurseryPurpleBufferEntryCount];
+    mPurpleBuf.Put(entry.mPtr, entry.mParticipant, entry.mRefCnt);
+  }
+}
+
+void
 nsCycleCollector::CheckThreadSafety()
 {
 #ifdef DEBUG
   MOZ_ASSERT(mEventTarget->IsOnCurrentThread());
 #endif
 }
 
 // The cycle collector uses the mark bitmap to discover what JS objects
@@ -3887,24 +3937,32 @@ nsCycleCollector::BeginCollection(ccType
   mBuilder->DoneAddingRoots();
   mIncrementalPhase = GraphBuildingPhase;
 }
 
 uint32_t
 nsCycleCollector::SuspectedCount()
 {
   CheckThreadSafety();
+  if (NS_IsMainThread()) {
+    return gNurseryPurpleBufferEntryCount + mPurpleBuf.Count();
+  }
+
   return mPurpleBuf.Count();
 }
 
 void
 nsCycleCollector::Shutdown(bool aDoCollect)
 {
   CheckThreadSafety();
 
+  if (NS_IsMainThread()) {
+    gNurseryPurpleBufferEnabled = false;
+  }
+
   // Always delete snow white objects.
   FreeSnowWhite(true);
 
   if (aDoCollect) {
     ShutdownCollect();
   }
 }
 
@@ -4032,16 +4090,36 @@ NS_CycleCollectorSuspect3(void* aPtr, ns
 
   if (MOZ_LIKELY(data->mCollector)) {
     data->mCollector->Suspect(aPtr, aCp, aRefCnt);
     return;
   }
   SuspectAfterShutdown(aPtr, aCp, aRefCnt, aShouldDelete);
 }
 
+void ClearNurseryPurpleBuffer()
+{
+  CollectorData* data = sCollectorData.get();
+  MOZ_ASSERT(data);
+  MOZ_ASSERT(data->mCollector);
+  data->mCollector->SuspectNurseryEntries();
+}
+
+bool
+NS_CycleCollectorSuspectUsingNursery(void* aPtr,
+                                     nsCycleCollectionParticipant* aCp,
+                                     nsCycleCollectingAutoRefCnt* aRefCnt)
+{
+  if (!gNurseryPurpleBufferEnabled) {
+    return false;
+  }
+  SuspectUsingNurseryPurpleBuffer(aPtr, aCp, aRefCnt);
+  return true;
+}
+
 uint32_t
 nsCycleCollector_suspectedCount()
 {
   CollectorData* data = sCollectorData.get();
 
   // We should have started the cycle collector by now.
   MOZ_ASSERT(data);
 
--- a/xpcom/base/nsISupportsImpl.h
+++ b/xpcom/base/nsISupportsImpl.h
@@ -216,16 +216,39 @@ public:
       mRefCntAndFlags |= NS_IN_PURPLE_BUFFER;
       // Refcount isn't zero, so Suspect won't delete anything.
       MOZ_ASSERT(get() > 0);
       NS_CycleCollectorSuspect3(aOwner, aCp, this, nullptr);
     }
     return NS_REFCOUNT_VALUE(mRefCntAndFlags);
   }
 
+  MOZ_ALWAYS_INLINE uintptr_t incrUsingNursery(nsISupports* aOwner)
+  {
+    return incrUsingNursery(aOwner, nullptr);
+  }
+
+  MOZ_ALWAYS_INLINE uintptr_t
+  incrUsingNursery(void* aOwner, nsCycleCollectionParticipant* aCp)
+  {
+    mRefCntAndFlags += NS_REFCOUNT_CHANGE;
+    mRefCntAndFlags &= ~NS_IS_PURPLE;
+    // For incremental cycle collection, use the purple buffer to track objects
+    // that have been AddRef'd.
+    if (!IsInPurpleBuffer()) {
+      mRefCntAndFlags |= NS_IN_PURPLE_BUFFER;
+      // Refcount isn't zero, so Suspect won't delete anything.
+      MOZ_ASSERT(get() > 0);
+      if (!NS_CycleCollectorSuspectUsingNursery(aOwner, aCp, this)) {
+        NS_CycleCollectorSuspect3(aOwner, aCp, this, nullptr);
+      }
+    }
+    return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+  }
+
   MOZ_ALWAYS_INLINE void stabilizeForDeletion()
   {
     // Set refcnt to 1 and mark us to be in the purple buffer.
     // This way decr won't call suspect again.
     mRefCntAndFlags = NS_REFCOUNT_CHANGE | NS_IN_PURPLE_BUFFER;
   }
 
   MOZ_ALWAYS_INLINE uintptr_t decr(nsISupports* aOwner,
@@ -247,16 +270,42 @@ public:
       NS_CycleCollectorSuspect3(aOwner, aCp, this, aShouldDelete);
       return retval;
     }
     mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
     mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
     return NS_REFCOUNT_VALUE(mRefCntAndFlags);
   }
 
+  MOZ_ALWAYS_INLINE uintptr_t decrUsingNursery(nsISupports* aOwner,
+                                               bool* aShouldDelete = nullptr)
+  {
+    return decrUsingNursery(aOwner, nullptr, aShouldDelete);
+  }
+
+  MOZ_ALWAYS_INLINE uintptr_t
+  decrUsingNursery(void* aOwner, nsCycleCollectionParticipant* aCp,
+                   bool* aShouldDelete = nullptr)
+  {
+    MOZ_ASSERT(get() > 0);
+    if (!IsInPurpleBuffer()) {
+      mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
+      mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
+      uintptr_t retval = NS_REFCOUNT_VALUE(mRefCntAndFlags);
+      if (!NS_CycleCollectorSuspectUsingNursery(aOwner, aCp, this)) {
+        // Suspect may delete 'aOwner' and 'this'!
+        NS_CycleCollectorSuspect3(aOwner, aCp, this, aShouldDelete);
+      }
+      return retval;
+    }
+    mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
+    mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
+    return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+  }
+
   MOZ_ALWAYS_INLINE void RemovePurple()
   {
     MOZ_ASSERT(IsPurple(), "must be purple");
     mRefCntAndFlags &= ~NS_IS_PURPLE;
   }
 
   MOZ_ALWAYS_INLINE void RemoveFromPurpleBuffer()
   {
@@ -705,16 +754,28 @@ NS_IMETHODIMP_(MozExternalRefCountType) 
   MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");                        \
   NS_ASSERT_OWNINGTHREAD(_class);                                             \
   nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);    \
   nsrefcnt count = mRefCnt.incr(base);                                        \
   NS_LOG_ADDREF(this, count, #_class, sizeof(*this));                         \
   return count;                                                               \
 }
 
+#define NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(_class)              \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::AddRef(void)                  \
+{                                                                             \
+  MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class)                                  \
+  MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");                        \
+  NS_ASSERT_OWNINGTHREAD(_class);                                             \
+  nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);    \
+  nsrefcnt count = mRefCnt.incrUsingNursery(base);                            \
+  NS_LOG_ADDREF(this, count, #_class, sizeof(*this));                         \
+  return count;                                                               \
+}
+
 #define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, _destroy)       \
 NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void)                 \
 {                                                                             \
   MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");                            \
   NS_ASSERT_OWNINGTHREAD(_class);                                             \
   nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);    \
   nsrefcnt count = mRefCnt.decr(base);                                        \
   NS_LOG_RELEASE(this, count, #_class);                                       \
@@ -750,16 +811,43 @@ NS_IMETHODIMP_(MozExternalRefCountType) 
   }                                                                           \
   return count;                                                               \
 }                                                                             \
 NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void)                     \
 {                                                                             \
   delete this;                                                                \
 }
 
+// _LAST_RELEASE can be useful when certain resources should be released
+// as soon as we know the object will be deleted.
+#define NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(_class, _last) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void)                 \
+{                                                                             \
+  MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");                            \
+  NS_ASSERT_OWNINGTHREAD(_class);                                             \
+  bool shouldDelete = false;                                                  \
+  nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);    \
+  nsrefcnt count = mRefCnt.decrUsingNursery(base, &shouldDelete);             \
+  NS_LOG_RELEASE(this, count, #_class);                                       \
+  if (count == 0) {                                                           \
+      mRefCnt.incrUsingNursery(base);                                         \
+      _last;                                                                  \
+      mRefCnt.decrUsingNursery(base);                                         \
+      if (shouldDelete) {                                                     \
+          mRefCnt.stabilizeForDeletion();                                     \
+          DeleteCycleCollectable();                                           \
+      }                                                                       \
+  }                                                                           \
+  return count;                                                               \
+}                                                                             \
+NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void)                     \
+{                                                                             \
+  delete this;                                                                \
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * There are two ways of implementing QueryInterface, and we use both:
  *
  * Table-driven QueryInterface uses a static table of IID->offset mappings
  * and a shared helper function. Using it tends to reduce codesize and improve
  * runtime performance (due to processor cache hits).
--- a/xpcom/build/nsXPCOM.h
+++ b/xpcom/build/nsXPCOM.h
@@ -353,16 +353,21 @@ XPCOM_API(void) NS_LogCOMPtrRelease(void
 class nsCycleCollectionParticipant;
 class nsCycleCollectingAutoRefCnt;
 
 XPCOM_API(void) NS_CycleCollectorSuspect3(void* aPtr,
                                           nsCycleCollectionParticipant* aCp,
                                           nsCycleCollectingAutoRefCnt* aRefCnt,
                                           bool* aShouldDelete);
 
+XPCOM_API(bool)
+NS_CycleCollectorSuspectUsingNursery(void* aPtr,
+                                     nsCycleCollectionParticipant* aCp,
+                                     nsCycleCollectingAutoRefCnt* aRefCnt);
+
 #endif
 
 /**
  * Categories (in the category manager service) used by XPCOM:
  */
 
 /**
  * A category which is read after component registration but before