Bug 730581, unbind content tree lazily after unlink, r=jst

This commit is contained in:
Olli Pettay 2012-03-01 15:09:00 +02:00
Родитель ae146ef230
Коммит 0d972e78e7
3 изменённых файлов: 108 добавлений и 7 удалений

Просмотреть файл

@ -157,6 +157,8 @@
#include "nsCycleCollector.h" #include "nsCycleCollector.h"
#include "xpcpublic.h" #include "xpcpublic.h"
#include "xpcprivate.h" #include "xpcprivate.h"
#include "nsLayoutStatics.h"
#include "mozilla/Telemetry.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
@ -4374,6 +4376,102 @@ nsINode::IsEqualNode(nsIDOMNode* aOther, bool* aReturn)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericElement) NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericElement)
#define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
class ContentUnbinder : public nsRunnable
{
public:
ContentUnbinder()
{
nsLayoutStatics::AddRef();
mLast = this;
}
~ContentUnbinder()
{
Run();
nsLayoutStatics::Release();
}
void UnbindSubtree(nsIContent* aNode)
{
if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE &&
aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
return;
}
nsGenericElement* container = static_cast<nsGenericElement*>(aNode);
PRUint32 childCount = container->mAttrsAndChildren.ChildCount();
if (childCount) {
while (childCount-- > 0) {
// Hold a strong ref to the node when we remove it, because we may be
// the last reference to it. We need to call TakeChildAt() and
// update mFirstChild before calling UnbindFromTree, since this last
// can notify various observers and they should really see consistent
// tree state.
nsCOMPtr<nsIContent> child =
container->mAttrsAndChildren.TakeChildAt(childCount);
if (childCount == 0) {
container->mFirstChild = nsnull;
}
UnbindSubtree(child);
child->UnbindFromTree();
}
}
}
NS_IMETHOD Run()
{
nsAutoScriptBlocker scriptBlocker;
PRUint32 len = mSubtreeRoots.Length();
if (len) {
PRTime start = PR_Now();
for (PRUint32 i = 0; i < len; ++i) {
UnbindSubtree(mSubtreeRoots[i]);
}
mSubtreeRoots.Clear();
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND,
PRUint32(PR_Now() - start) / PR_USEC_PER_MSEC);
}
if (this == sContentUnbinder) {
sContentUnbinder = nsnull;
if (mNext) {
nsRefPtr<ContentUnbinder> next;
next.swap(mNext);
sContentUnbinder = next;
next->mLast = mLast;
mLast = nsnull;
NS_DispatchToMainThread(next);
}
}
return NS_OK;
}
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;
}
sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
}
private:
nsAutoTArray<nsCOMPtr<nsIContent>,
SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
nsRefPtr<ContentUnbinder> mNext;
ContentUnbinder* mLast;
static ContentUnbinder* sContentUnbinder;
};
ContentUnbinder* ContentUnbinder::sContentUnbinder = nsnull;
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
nsINode::Unlink(tmp); nsINode::Unlink(tmp);
@ -4383,16 +4481,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
} }
// Unlink child content (and unbind our subtree). // Unlink child content (and unbind our subtree).
{ if (UnoptimizableCCNode(tmp) || !nsCCUncollectableMarker::sGeneration) {
PRUint32 childCount = tmp->mAttrsAndChildren.ChildCount(); PRUint32 childCount = tmp->mAttrsAndChildren.ChildCount();
if (childCount) { if (childCount) {
// Don't allow script to run while we're unbinding everything. // Don't allow script to run while we're unbinding everything.
nsAutoScriptBlocker scriptBlocker; nsAutoScriptBlocker scriptBlocker;
while (childCount-- > 0) { while (childCount-- > 0) {
// Once we have XPCOMGC we shouldn't need to call UnbindFromTree.
// We could probably do a non-deep unbind here when IsInDoc is false
// for better performance.
// Hold a strong ref to the node when we remove it, because we may be // Hold a strong ref to the node when we remove it, because we may be
// the last reference to it. We need to call TakeChildAt() and // the last reference to it. We need to call TakeChildAt() and
// update mFirstChild before calling UnbindFromTree, since this last // update mFirstChild before calling UnbindFromTree, since this last
@ -4405,7 +4499,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
child->UnbindFromTree(); child->UnbindFromTree();
} }
} }
} } else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) {
ContentUnbinder::Append(tmp);
} /* else {
The subtree root will end up to a ContentUnbinder, and that will
unbind the child nodes.
} */
// Unlink any DOM slots of interest. // Unlink any DOM slots of interest.
{ {

Просмотреть файл

@ -82,6 +82,7 @@ class nsIScrollableFrame;
class nsAttrValueOrString; class nsAttrValueOrString;
class nsContentList; class nsContentList;
class nsDOMTokenList; class nsDOMTokenList;
class ContentUnbinder;
struct nsRect; struct nsRect;
typedef PRUptrdiff PtrBits; typedef PRUptrdiff PtrBits;
@ -956,6 +957,7 @@ protected:
*/ */
virtual void GetLinkTarget(nsAString& aTarget); virtual void GetLinkTarget(nsAString& aTarget);
friend class ContentUnbinder;
/** /**
* Array containing all attributes and children for this element * Array containing all attributes and children for this element
*/ */

Просмотреть файл

@ -70,7 +70,7 @@ HISTOGRAM(CYCLE_COLLECTOR_VISITED_GCED, 1, 300000, 50, EXPONENTIAL, "Number of J
HISTOGRAM(CYCLE_COLLECTOR_COLLECTED, 1, 100000, 50, EXPONENTIAL, "Number of objects collected by the cycle collector") HISTOGRAM(CYCLE_COLLECTOR_COLLECTED, 1, 100000, 50, EXPONENTIAL, "Number of objects collected by the cycle collector")
HISTOGRAM_BOOLEAN(CYCLE_COLLECTOR_NEED_GC, "Needed garbage collection before cycle collection.") HISTOGRAM_BOOLEAN(CYCLE_COLLECTOR_NEED_GC, "Needed garbage collection before cycle collection.")
HISTOGRAM(CYCLE_COLLECTOR_TIME_BETWEEN, 1, 120, 50, EXPONENTIAL, "Time spent in between cycle collections (seconds)") HISTOGRAM(CYCLE_COLLECTOR_TIME_BETWEEN, 1, 120, 50, EXPONENTIAL, "Time spent in between cycle collections (seconds)")
HISTOGRAM(CYCLE_COLLECTOR_CONTENT_UNBIND, 1, 10000, 50, EXPONENTIAL, "Time spent on one ContentUnbinder (ms)")
HISTOGRAM(FORGET_SKIPPABLE_MAX, 1, 10000, 50, EXPONENTIAL, "Max time spent on one forget skippable (ms)") HISTOGRAM(FORGET_SKIPPABLE_MAX, 1, 10000, 50, EXPONENTIAL, "Max time spent on one forget skippable (ms)")
/** /**