Bug 937766, part 1 - Remove dying nodes from gCCBlackMarkedNodes. r=smaug
authorAndrew McCreight <continuation@gmail.com>
Fri, 06 Dec 2013 10:17:19 -0800
changeset 174950 4dcb040e3c84e69e9c3be4cee4d5e4b9ee995285
parent 174949 f86d2d4cfadf457bef12afbedb8a8f82a5e69fca
child 174951 b015eac4566f51574e0beac410bf9db937adbc09
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs937766
milestone28.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 937766, part 1 - Remove dying nodes from gCCBlackMarkedNodes. r=smaug With ICC, arbitrary other activity can happen while nodes are in gCCBlackMarkedNodes, so the nodes can die, because gCCBlackMarkedNodes only holds a weak reference. To avoid this, remove nodes from gCCBlackMarkedNodes when they are going away.
content/base/public/FragmentOrElement.h
content/base/src/FragmentOrElement.cpp
content/base/src/nsNodeUtils.cpp
--- a/content/base/public/FragmentOrElement.h
+++ b/content/base/public/FragmentOrElement.h
@@ -258,16 +258,17 @@ public:
   {
     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);
   static void MarkUserDataHandler(void* aObject, nsIAtom* aKey, void* aChild,
                                   void* aData);
 
 protected:
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -5,19 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * 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/Likely.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/Util.h"
-#include "mozilla/Likely.h"
 
 #include "mozilla/dom/FragmentOrElement.h"
 
 #include "mozilla/dom/Attr.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIAtom.h"
 #include "nsINodeInfo.h"
 #include "nsIDocumentInlines.h"
@@ -1314,39 +1315,52 @@ FindOptimizableSubtreeRoot(nsINode* aNod
 {
   nsINode* p;
   while ((p = aNode->GetParentNode())) {
     if (aNode->UnoptimizableCCNode()) {
       return nullptr;
     }
     aNode = p;
   }
-  
+
   if (aNode->UnoptimizableCCNode()) {
     return nullptr;
   }
   return aNode;
 }
 
-nsAutoTArray<nsINode*, 1020>* gCCBlackMarkedNodes = nullptr;
+StaticAutoPtr<nsTHashtable<nsPtrHashKey<nsINode>>> gCCBlackMarkedNodes;
 
-void
+static PLDHashOperator
+VisitBlackMarkedNode(nsPtrHashKey<nsINode>* aEntry, void*)
+{
+  nsINode* n = aEntry->GetKey();
+  n->SetCCMarkedRoot(false);
+  n->SetInCCBlackTree(false);
+  return PL_DHASH_NEXT;
+}
+
+static void
 ClearBlackMarkedNodes()
 {
   if (!gCCBlackMarkedNodes) {
     return;
   }
-  uint32_t len = gCCBlackMarkedNodes->Length();
-  for (uint32_t i = 0; i < len; ++i) {
-    nsINode* n = gCCBlackMarkedNodes->ElementAt(i);
-    n->SetCCMarkedRoot(false);
-    n->SetInCCBlackTree(false);
+  gCCBlackMarkedNodes->EnumerateEntries(VisitBlackMarkedNode, nullptr);
+  gCCBlackMarkedNodes = nullptr;
+}
+
+// static
+void
+FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode)
+{
+  if (!gCCBlackMarkedNodes) {
+    return;
   }
-  delete gCCBlackMarkedNodes;
-  gCCBlackMarkedNodes = nullptr;
+  gCCBlackMarkedNodes->RemoveEntry(aNode);
 }
 
 // static
 bool
 FragmentOrElement::CanSkipInCC(nsINode* aNode)
 {
   // Don't try to optimize anything during shutdown.
   if (nsCCUncollectableMarker::sGeneration == 0) {
@@ -1366,24 +1380,24 @@ FragmentOrElement::CanSkipInCC(nsINode* 
   }
 
   nsINode* root =
     currentDoc ? static_cast<nsINode*>(currentDoc) :
                  FindOptimizableSubtreeRoot(aNode);
   if (!root) {
     return false;
   }
-  
+
   // Subtree has been traversed already.
   if (root->CCMarkedRoot()) {
     return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
   }
 
   if (!gCCBlackMarkedNodes) {
-    gCCBlackMarkedNodes = new nsAutoTArray<nsINode*, 1020>;
+    gCCBlackMarkedNodes = new nsTHashtable<nsPtrHashKey<nsINode> >(1020);
   }
 
   // nodesToUnpurple contains nodes which will be removed
   // from the purple buffer if the DOM tree is black.
   nsAutoTArray<nsIContent*, 1020> nodesToUnpurple;
   // grayNodes need script traverse, so they aren't removed from
   // the purple buffer, but are marked to be in black subtree so that
   // traverse is faster.
@@ -1417,33 +1431,33 @@ FragmentOrElement::CanSkipInCC(nsINode* 
       grayNodes.AppendElement(node);
     } else if (node->IsPurple()) {
       nodesToUnpurple.AppendElement(node);
     }
   }
 
   root->SetCCMarkedRoot(true);
   root->SetInCCBlackTree(foundBlack);
-  gCCBlackMarkedNodes->AppendElement(root);
+  gCCBlackMarkedNodes->PutEntry(root);
 
   if (!foundBlack) {
     return false;
   }
 
   if (currentDoc) {
     // Special case documents. If we know the document is black,
     // we can mark the document to be in CCGeneration.
     currentDoc->
       MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
   } else {
     for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
       nsINode* node = grayNodes[i];
       node->SetInCCBlackTree(true);
+      gCCBlackMarkedNodes->PutEntry(node);
     }
-    gCCBlackMarkedNodes->AppendElements(grayNodes);
   }
 
   // Subtree is black, we can remove non-gray purple nodes from
   // purple buffer.
   for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
     nsIContent* purple = nodesToUnpurple[i];
     // Can't remove currently handled purple node.
     if (purple != aNode) {
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -248,30 +248,32 @@ nsNodeUtils::LastRelease(nsINode* aNode)
     nsContentUtils::RemoveListenerManager(aNode);
     aNode->UnsetFlags(NODE_HAS_LISTENERMANAGER);
   }
 
   if (aNode->IsElement()) {
     nsIDocument* ownerDoc = aNode->OwnerDoc();
     Element* elem = aNode->AsElement();
     ownerDoc->ClearBoxObjectFor(elem);
-    
+
     NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) ||
                  !elem->GetXBLBinding(),
                  "Non-forced node has binding on destruction");
 
     // if NODE_FORCE_XBL_BINDINGS is set, the node might still have a binding
     // attached
     if (aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) &&
         ownerDoc->BindingManager()) {
       ownerDoc->BindingManager()->RemovedFromDocument(elem, ownerDoc);
     }
   }
 
   aNode->ReleaseWrapper(aNode);
+
+  FragmentOrElement::RemoveBlackMarkedNode(aNode);
 }
 
 struct MOZ_STACK_CLASS nsHandlerData
 {
   uint16_t mOperation;
   nsCOMPtr<nsIDOMNode> mSource;
   nsCOMPtr<nsIDOMNode> mDest;
   nsCxPusher mPusher;