Bug 1177627 - Back out most of 7fcf6bf43eda for causing Gaia unit test crashes. r=smaug
authorAndrew McCreight <continuation@gmail.com>
Thu, 02 Jul 2015 12:34:00 +0200
changeset 251456 036eff95f08b7a2440d305bb31e164515df1ae9a
parent 251455 04420ed3c03678d0276c739df3b7e0830eae56e3
child 251457 50c233bcb32aa2ac4981fb2b1240dc57b461c733
push id61863
push usercbook@mozilla.com
push dateMon, 06 Jul 2015 08:21:02 +0000
treeherdermozilla-inbound@a04ce0f96418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1177627
milestone42.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 1177627 - Back out most of 7fcf6bf43eda for causing Gaia unit test crashes. r=smaug
dom/base/FragmentOrElement.cpp
dom/base/FragmentOrElement.h
dom/base/nsCCUncollectableMarker.cpp
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -8,17 +8,16 @@
  * Base class for all element classes; this provides an implementation
  * of DOM Core's nsIDOMElement, implements nsIContent, provides
  * utility methods for subclasses, and so forth.
  */
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/SegmentedVector.h"
 #include "mozilla/StaticPtr.h"
 
 #include "mozilla/dom/FragmentOrElement.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
@@ -1222,22 +1221,34 @@ FragmentOrElement::FireNodeInserted(nsID
       mozAutoSubtreeModified subtree(aDoc, aParent);
       (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe();
     }
   }
 }
 
 //----------------------------------------------------------------------
 
-class ContentUnbinder
+// nsISupports implementation
+
+#define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
+
+class ContentUnbinder : public nsRunnable
 {
-  static const size_t kSegmentSize = sizeof(void*) * 512;
-  typedef SegmentedVector<nsCOMPtr<nsIContent>, kSegmentSize, InfallibleAllocPolicy> ContentArray;
-
-  static void UnbindSubtree(nsIContent* aNode)
+public:
+  ContentUnbinder()
+  {
+    mLast = this;
+  }
+
+  ~ContentUnbinder()
+  {
+    Run();
+  }
+
+  void UnbindSubtree(nsIContent* aNode)
   {
     if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE &&
         aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
       return;
     }
     FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
     uint32_t childCount = container->mAttrsAndChildren.ChildCount();
     if (childCount) {
@@ -1253,66 +1264,83 @@ class ContentUnbinder
           container->mFirstChild = nullptr;
         }
         UnbindSubtree(child);
         child->UnbindFromTree();
       }
     }
   }
 
-  // These two methods are based on DeferredFinalizerImpl.
-
-  static void*
-  AppendContentUnbinderPointer(void* aData, void* aObject)
+  NS_IMETHOD Run()
   {
-    ContentArray* contentArray = static_cast<ContentArray*>(aData);
-    if (!contentArray) {
-      contentArray = new ContentArray();
+    nsAutoScriptBlocker scriptBlocker;
+    uint32_t len = mSubtreeRoots.Length();
+    if (len) {
+      for (uint32_t i = 0; i < len; ++i) {
+        UnbindSubtree(mSubtreeRoots[i]);
+      }
+      mSubtreeRoots.Clear();
     }
-
-    contentArray->InfallibleAppend(dont_AddRef(static_cast<nsIContent*>(aObject)));
-    return contentArray;
+    nsCycleCollector_dispatchDeferredDeletion();
+    if (this == sContentUnbinder) {
+      sContentUnbinder = nullptr;
+      if (mNext) {
+        nsRefPtr<ContentUnbinder> next;
+        next.swap(mNext);
+        sContentUnbinder = next;
+        next->mLast = mLast;
+        mLast = nullptr;
+        NS_DispatchToMainThread(next);
+      }
+    }
+    return NS_OK;
   }
 
-  static bool
-  DeferredFinalize(uint32_t aSliceBudget, void* aData)
+  static void UnbindAll()
   {
-    MOZ_ASSERT(aSliceBudget > 0, "nonsensical/useless call with aSliceBudget == 0");
-    nsAutoScriptBlocker scriptBlocker;
-    ContentArray* contentArray = static_cast<ContentArray*>(aData);
-
-    size_t numToRemove = contentArray->Length();
-    if (aSliceBudget < numToRemove) {
-      numToRemove = aSliceBudget;
+    nsRefPtr<ContentUnbinder> ub = sContentUnbinder;
+    sContentUnbinder = nullptr;
+    while (ub) {
+      ub->Run();
+      ub = ub->mNext;
     }
-
-    for (size_t i = 0; i < numToRemove; ++i) {
-      nsCOMPtr<nsIContent> element = contentArray->GetLast().forget();
-      contentArray->PopLast();
-      UnbindSubtree(element);
+  }
+
+  static void Append(nsIContent* aSubtreeRoot)
+  {
+    if (!sContentUnbinder) {
+      sContentUnbinder = new ContentUnbinder();
+      nsCOMPtr<nsIRunnable> e = sContentUnbinder;
+      NS_DispatchToMainThread(e);
+    }
+
+    if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
+        SUBTREE_UNBINDINGS_PER_RUNNABLE) {
+      sContentUnbinder->mLast->mNext = new ContentUnbinder();
+      sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
     }
-
-    nsCycleCollector_dispatchDeferredDeletion();
-
-    if (contentArray->Length() == 0) {
-      delete contentArray;
-      return true;
-    }
-    return false;
+    sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
   }
 
-public:
-  static void
-  Append(nsIContent* aSubtreeRoot)
-  {
-    nsCOMPtr<nsIContent> root = aSubtreeRoot;
-    mozilla::DeferredFinalize(AppendContentUnbinderPointer, DeferredFinalize, root.forget().take());
-  }
+private:
+  nsAutoTArray<nsCOMPtr<nsIContent>,
+               SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
+  nsRefPtr<ContentUnbinder>                     mNext;
+  ContentUnbinder*                              mLast;
+  static ContentUnbinder*                       sContentUnbinder;
 };
 
+ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
+
+void
+FragmentOrElement::ClearContentUnbinder()
+{
+  ContentUnbinder::UnbindAll();
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
   nsINode::Unlink(tmp);
 
   // The XBL binding is removed by RemoveFromBindingManagerRunnable
   // which is dispatched in UnbindFromTree.
 
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -210,16 +210,17 @@ public:
     return mRefCnt.IsPurple();
   }
 
   virtual void RemovePurple() override
   {
     mRefCnt.RemovePurple();
   }
 
+  static void ClearContentUnbinder();
   static bool CanSkip(nsINode* aNode, bool aRemovingAllowed);
   static bool CanSkipInCC(nsINode* aNode);
   static bool CanSkipThis(nsINode* aNode);
   static void RemoveBlackMarkedNode(nsINode* aNode);
   static void MarkNodeChildren(nsINode* aNode);
   static void InitCCCallbacks();
   static void MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
                            void *aData);
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -313,16 +313,18 @@ MarkWindowList(nsISimpleEnumerator* aWin
   }
 }
 
 nsresult
 nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
                                  const char16_t* aData)
 {
   if (!strcmp(aTopic, "xpcom-shutdown")) {
+    Element::ClearContentUnbinder();
+
     nsCOMPtr<nsIObserverService> obs =
       mozilla::services::GetObserverService();
     if (!obs)
       return NS_ERROR_FAILURE;
 
     // No need for kungFuDeathGrip here, yay observerservice!
     obs->RemoveObserver(this, "xpcom-shutdown");
     obs->RemoveObserver(this, "cycle-collector-begin");
@@ -337,16 +339,19 @@ nsCCUncollectableMarker::Observe(nsISupp
                !strcmp(aTopic, "cycle-collector-forget-skippable"), "wrong topic");
 
   // JS cleanup can be slow. Do it only if there has been a GC.
   bool cleanupJS =
     nsJSContext::CleanupsSinceLastGC() == 0 &&
     !strcmp(aTopic, "cycle-collector-forget-skippable");
 
   bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin");
+  if (prepareForCC) {
+    Element::ClearContentUnbinder();
+  }
 
   // Increase generation to effectively unmark all current objects
   if (!++sGeneration) {
     ++sGeneration;
   }
 
   nsFocusManager::MarkUncollectableForCCGeneration(sGeneration);