Bug 1177627 - Back out most of 7fcf6bf43eda for causing Gaia unit test crashes. r=smaug, a=ritu
authorAndrew McCreight <continuation@gmail.com>
Thu, 02 Jul 2015 12:34:00 +0200
changeset 281437 fdfc95cad1215ff59653a5f85f37c3b9bab9e330
parent 281436 6fb042f0271d014e589ffebc66a158baff3ce24c
child 281438 0d125537bfbc8387dc72cfb5b69ff20cc8cf09fd
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, ritu
bugs1177627
milestone41.0a2
Bug 1177627 - Back out most of 7fcf6bf43eda for causing Gaia unit test crashes. r=smaug, a=ritu
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
@@ -312,16 +312,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");
@@ -336,16 +338,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);