author | Andrew McCreight <continuation@gmail.com> |
Wed, 13 May 2015 12:48:52 -0700 | |
changeset 274525 | 7fcf6bf43eda78d42b098cc2b24c032ebd29b34e |
parent 274524 | 8e75b3eb23f44c52b6e22e2783bee7aba10c2823 |
child 274526 | 82ddfd3ddce00175739c74cdf32863886c8abc50 |
push id | 4932 |
push user | jlund@mozilla.com |
push date | Mon, 10 Aug 2015 18:23:06 +0000 |
treeherder | mozilla-beta@6dd5a4f5f745 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | smaug |
bugs | 866681 |
milestone | 41.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
|
--- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -8,16 +8,17 @@ * 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" @@ -1220,34 +1221,22 @@ FragmentOrElement::FireNodeInserted(nsID mozAutoSubtreeModified subtree(aDoc, aParent); (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe(); } } } //---------------------------------------------------------------------- -// nsISupports implementation - -#define SUBTREE_UNBINDINGS_PER_RUNNABLE 500 - -class ContentUnbinder : public nsRunnable +class ContentUnbinder { -public: - ContentUnbinder() - { - mLast = this; - } - - ~ContentUnbinder() - { - Run(); - } - - void UnbindSubtree(nsIContent* aNode) + static const size_t kSegmentSize = sizeof(void*) * 512; + typedef SegmentedVector<nsCOMPtr<nsIContent>, kSegmentSize, InfallibleAllocPolicy> ContentArray; + + static 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) { @@ -1263,86 +1252,66 @@ public: container->mFirstChild = nullptr; } UnbindSubtree(child); child->UnbindFromTree(); } } } - NS_IMETHOD Run() + // These two methods are based on DeferredFinalizerImpl. + + static void* + AppendContentUnbinderPointer(void* aData, void* aObject) { - nsAutoScriptBlocker scriptBlocker; - uint32_t len = mSubtreeRoots.Length(); - if (len) { - PRTime start = PR_Now(); - for (uint32_t i = 0; i < len; ++i) { - UnbindSubtree(mSubtreeRoots[i]); - } - mSubtreeRoots.Clear(); - Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND, - uint32_t(PR_Now() - start) / PR_USEC_PER_MSEC); + ContentArray* contentArray = static_cast<ContentArray*>(aData); + if (!contentArray) { + contentArray = new 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; + + contentArray->InfallibleAppend(dont_AddRef(static_cast<nsIContent*>(aObject))); + return contentArray; } - static void UnbindAll() + static bool + DeferredFinalize(uint32_t aSliceBudget, void* aData) { - nsRefPtr<ContentUnbinder> ub = sContentUnbinder; - sContentUnbinder = nullptr; - while (ub) { - ub->Run(); - ub = ub->mNext; + 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; } - } - - static void Append(nsIContent* aSubtreeRoot) - { - if (!sContentUnbinder) { - sContentUnbinder = new ContentUnbinder(); - nsCOMPtr<nsIRunnable> e = sContentUnbinder; - NS_DispatchToMainThread(e); + + for (size_t i = 0; i < numToRemove; ++i) { + nsCOMPtr<nsIContent> element = contentArray->GetLast().forget(); + contentArray->PopLast(); + UnbindSubtree(element); } - 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; } - sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot); + return false; } -private: - nsAutoTArray<nsCOMPtr<nsIContent>, - SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots; - nsRefPtr<ContentUnbinder> mNext; - ContentUnbinder* mLast; - static ContentUnbinder* sContentUnbinder; +public: + static void + Append(nsIContent* aSubtreeRoot) + { + nsCOMPtr<nsIContent> root = aSubtreeRoot; + mozilla::DeferredFinalize(AppendContentUnbinderPointer, DeferredFinalize, root.forget().take()); + } }; -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,17 +210,16 @@ 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,18 +312,16 @@ 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"); @@ -338,19 +336,16 @@ 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);
--- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -203,32 +203,32 @@ private: static void OutOfMemoryCallback(JSContext* aContext, void* aData); static void LargeAllocationFailureCallback(void* aData); static bool ContextCallback(JSContext* aCx, unsigned aOperation, void* aData); virtual void TraceNativeBlackRoots(JSTracer* aTracer) { }; void TraceNativeGrayRoots(JSTracer* aTracer); +public: enum DeferredFinalizeType { FinalizeIncrementally, FinalizeNow, }; void FinalizeDeferredThings(DeferredFinalizeType aType); -public: // Two conditions, JSOutOfMemory and JSLargeAllocationFailure, are noted in // crash reports. Here are the values that can appear in the reports: enum class OOMState : uint32_t { // The condition has never happened. No entry appears in the crash report. OK, // We are currently reporting the given condition. - // + // // Suppose a crash report contains "JSLargeAllocationFailure: // Reporting". This means we crashed while executing memory-pressure // observers, trying to shake loose some memory. The large allocation in // question did not return null: it is still on the stack. Had we not // crashed, it would have been retried. Reporting, // The condition has been reported since the last GC.
--- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -3536,16 +3536,19 @@ nsCycleCollector::CleanupAfterCollection CC_TELEMETRY( , interval); CC_TELEMETRY(_VISITED_REF_COUNTED, mResults.mVisitedRefCounted); CC_TELEMETRY(_VISITED_GCED, mResults.mVisitedGCed); CC_TELEMETRY(_COLLECTED, mWhiteNodeCount); timeLog.Checkpoint("CleanupAfterCollection::telemetry"); if (mJSRuntime) { + mJSRuntime->FinalizeDeferredThings(mResults.mAnyManual + ? CycleCollectedJSRuntime::FinalizeNow + : CycleCollectedJSRuntime::FinalizeIncrementally); mJSRuntime->EndCycleCollectionCallback(mResults); timeLog.Checkpoint("CleanupAfterCollection::EndCycleCollectionCallback()"); } mIncrementalPhase = IdlePhase; } void nsCycleCollector::ShutdownCollect()