From 1d1e78217e8dfaec39bfc8a000cbbd4ff0623d10 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 4 Mar 2013 22:46:22 -0500 Subject: [PATCH] Backed out changeset 74eaeeec2b9d (bug 847236) for crashes on a CLOSED TREE. --- content/events/src/nsEventDispatcher.cpp | 153 +++++++++++++++-------- layout/build/nsLayoutStatics.cpp | 4 +- 2 files changed, 102 insertions(+), 55 deletions(-) diff --git a/content/events/src/nsEventDispatcher.cpp b/content/events/src/nsEventDispatcher.cpp index 203eb1e62486..60bb3ae79b38 100644 --- a/content/events/src/nsEventDispatcher.cpp +++ b/content/events/src/nsEventDispatcher.cpp @@ -1,5 +1,4 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -13,6 +12,7 @@ #include "nsError.h" #include "nsMutationEvent.h" #include NEW_H +#include "nsFixedSizeAllocator.h" #include "nsINode.h" #include "nsPIDOMWindow.h" #include "nsFrameLoader.h" @@ -27,6 +27,8 @@ using namespace mozilla; #define NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT (1 << 1) #define NS_TARGET_CHAIN_MAY_HAVE_MANAGER (1 << 2) +static nsEventTargetChainItem* gCachedETCI = nullptr; + // nsEventTargetChainItem represents a single item in the event target chain. class nsEventTargetChainItem { @@ -34,32 +36,25 @@ private: nsEventTargetChainItem(nsIDOMEventTarget* aTarget, nsEventTargetChainItem* aChild = nullptr); - // This is the ETCI recycle pool, which is used to avoid some malloc/free - // churn. It's implemented as a linked list. - static nsEventTargetChainItem* sEtciRecyclePool; - static uint32_t sNumRecycledEtcis; - static const uint32_t kMaxNumRecycledEtcis = 128; - public: - static nsEventTargetChainItem* Create(nsIDOMEventTarget* aTarget, + static nsEventTargetChainItem* Create(nsFixedSizeAllocator* aAllocator, + nsIDOMEventTarget* aTarget, nsEventTargetChainItem* aChild = nullptr) { - // Allocate from the ETCI recycle pool if possible. void* place = nullptr; - if (sNumRecycledEtcis > 0) { - MOZ_ASSERT(sEtciRecyclePool); - place = sEtciRecyclePool; - sEtciRecyclePool = sEtciRecyclePool->mNext; - --sNumRecycledEtcis; + if (gCachedETCI) { + place = gCachedETCI; + gCachedETCI = gCachedETCI->mNext; } else { - place = malloc(sizeof(nsEventTargetChainItem)); + place = aAllocator->Alloc(sizeof(nsEventTargetChainItem)); } return place ? ::new (place) nsEventTargetChainItem(aTarget, aChild) : nullptr; } - static void Destroy(nsEventTargetChainItem* aItem) + static void Destroy(nsFixedSizeAllocator* aAllocator, + nsEventTargetChainItem* aItem) { // ::Destroy deletes ancestor chain. nsEventTargetChainItem* item = aItem; @@ -67,30 +62,16 @@ public: item->mChild->mParent = nullptr; item->mChild = nullptr; } - // Put destroyed ETCIs into the recycle pool if it's not already full. while (item) { nsEventTargetChainItem* parent = item->mParent; item->~nsEventTargetChainItem(); - if (sNumRecycledEtcis < kMaxNumRecycledEtcis) { - item->mNext = sEtciRecyclePool; - sEtciRecyclePool = item; - ++sNumRecycledEtcis; - } else { - free(item); - } + item->mNext = gCachedETCI; + gCachedETCI = item; + --sCurrentEtciCount; item = parent; } } - static void ShutdownRecyclePool() - { - while (sEtciRecyclePool) { - nsEventTargetChainItem* tmp = sEtciRecyclePool; - sEtciRecyclePool = sEtciRecyclePool->mNext; - free(tmp); - } - } - bool IsValid() { NS_WARN_IF_FALSE(!!(mTarget), "Event target is not valid!"); @@ -224,7 +205,7 @@ public: nsEventTargetChainItem* mChild; union { nsEventTargetChainItem* mParent; - // This is used only when recycling ETCIs. + // This is used only when caching ETCI objects. nsEventTargetChainItem* mNext; }; uint16_t mFlags; @@ -239,8 +220,8 @@ public: static uint32_t sCurrentEtciCount; }; -nsEventTargetChainItem* nsEventTargetChainItem::sEtciRecyclePool = nullptr; -uint32_t nsEventTargetChainItem::sNumRecycledEtcis = 0; +uint32_t nsEventTargetChainItem::sMaxEtciCount = 0; +uint32_t nsEventTargetChainItem::sCurrentEtciCount = 0; nsEventTargetChainItem::nsEventTargetChainItem(nsIDOMEventTarget* aTarget, nsEventTargetChainItem* aChild) @@ -250,6 +231,10 @@ nsEventTargetChainItem::nsEventTargetChainItem(nsIDOMEventTarget* aTarget, if (mChild) { mChild->mParent = this; } + + if (++sCurrentEtciCount > sMaxEtciCount) { + sMaxEtciCount = sCurrentEtciCount; + } } nsresult @@ -393,13 +378,69 @@ nsEventTargetChainItem::HandleEventTargetChain( return NS_OK; } -void NS_ShutdownEventTargetChainItemRecyclePool() -{ - nsEventTargetChainItem::ShutdownRecyclePool(); -} +#define NS_CHAIN_POOL_SIZE 128 + +class ChainItemPool { +public: + ChainItemPool() { + if (!sEtciPool) { + sEtciPool = new nsFixedSizeAllocator(); + if (sEtciPool) { + static const size_t kBucketSizes[] = { sizeof(nsEventTargetChainItem) }; + static const int32_t kNumBuckets = sizeof(kBucketSizes) / sizeof(size_t); + static const int32_t kInitialPoolSize = + sizeof(nsEventTargetChainItem) * NS_CHAIN_POOL_SIZE; + nsresult rv = sEtciPool->Init("EventTargetChainItem Pool", kBucketSizes, + kNumBuckets, kInitialPoolSize); + if (NS_FAILED(rv)) { + delete sEtciPool; + sEtciPool = nullptr; + } + } + } + if (sEtciPool) { + ++sEtciPoolUsers; + } + } + + ~ChainItemPool() { + if (sEtciPool) { + --sEtciPoolUsers; + } + if (!sEtciPoolUsers) { + if (nsEventTargetChainItem::MaxEtciCount() > NS_CHAIN_POOL_SIZE) { + gCachedETCI = nullptr; + delete sEtciPool; + sEtciPool = nullptr; + nsEventTargetChainItem::ResetMaxEtciCount(); + } + } + } + + static void Shutdown() + { + if (!sEtciPoolUsers) { + gCachedETCI = nullptr; + delete sEtciPool; + sEtciPool = nullptr; + nsEventTargetChainItem::ResetMaxEtciCount(); + } + } + + nsFixedSizeAllocator* GetPool() { return sEtciPool; } + + static nsFixedSizeAllocator* sEtciPool; + static int32_t sEtciPoolUsers; +}; + +nsFixedSizeAllocator* ChainItemPool::sEtciPool = nullptr; +int32_t ChainItemPool::sEtciPoolUsers = 0; + +void NS_ShutdownChainItemPool() { ChainItemPool::Shutdown(); } nsEventTargetChainItem* -EventTargetChainItemForChromeTarget(nsINode* aNode, +EventTargetChainItemForChromeTarget(ChainItemPool& aPool, + nsINode* aNode, nsEventTargetChainItem* aChild = nullptr) { if (!aNode->IsInDoc()) { @@ -410,11 +451,12 @@ EventTargetChainItemForChromeTarget(nsINode* aNode, NS_ENSURE_TRUE(piTarget, nullptr); nsEventTargetChainItem* etci = - nsEventTargetChainItem::Create(piTarget->GetTargetForEventTargetChain(), + nsEventTargetChainItem::Create(aPool.GetPool(), + piTarget->GetTargetForEventTargetChain(), aChild); NS_ENSURE_TRUE(etci, nullptr); if (!etci->IsValid()) { - nsEventTargetChainItem::Destroy(etci); + nsEventTargetChainItem::Destroy(aPool.GetPool(), etci); return nullptr; } return etci; @@ -509,13 +551,16 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget, // If we have a PresContext, make sure it doesn't die before // event dispatching is finished. nsRefPtr kungFuDeathGrip(aPresContext); + ChainItemPool pool; + NS_ENSURE_TRUE(pool.GetPool(), NS_ERROR_OUT_OF_MEMORY); // Create the event target chain item for the event target. nsEventTargetChainItem* targetEtci = - nsEventTargetChainItem::Create(target->GetTargetForEventTargetChain()); + nsEventTargetChainItem::Create(pool.GetPool(), + target->GetTargetForEventTargetChain()); NS_ENSURE_TRUE(targetEtci, NS_ERROR_OUT_OF_MEMORY); if (!targetEtci->IsValid()) { - nsEventTargetChainItem::Destroy(targetEtci); + nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci); return NS_ERROR_FAILURE; } @@ -558,8 +603,8 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget, if (!preVisitor.mCanHandle && preVisitor.mAutomaticChromeDispatch && content) { // Event target couldn't handle the event. Try to propagate to chrome. - nsEventTargetChainItem::Destroy(targetEtci); - targetEtci = EventTargetChainItemForChromeTarget(content); + nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci); + targetEtci = EventTargetChainItemForChromeTarget(pool, content); NS_ENSURE_STATE(targetEtci); targetEtci->PreHandleEvent(preVisitor); } @@ -572,7 +617,8 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget, while (preVisitor.mParentTarget) { nsIDOMEventTarget* parentTarget = preVisitor.mParentTarget; nsEventTargetChainItem* parentEtci = - nsEventTargetChainItem::Create(preVisitor.mParentTarget, topEtci); + nsEventTargetChainItem::Create(pool.GetPool(), preVisitor.mParentTarget, + topEtci); if (!parentEtci) { rv = NS_ERROR_OUT_OF_MEMORY; break; @@ -594,14 +640,15 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget, if (preVisitor.mCanHandle) { topEtci = parentEtci; } else { - nsEventTargetChainItem::Destroy(parentEtci); + nsEventTargetChainItem::Destroy(pool.GetPool(), parentEtci); parentEtci = nullptr; if (preVisitor.mAutomaticChromeDispatch && content) { // Even if the current target can't handle the event, try to // propagate to chrome. nsCOMPtr disabledTarget = do_QueryInterface(parentTarget); if (disabledTarget) { - parentEtci = EventTargetChainItemForChromeTarget(disabledTarget, + parentEtci = EventTargetChainItemForChromeTarget(pool, + disabledTarget, topEtci); if (parentEtci) { parentEtci->PreHandleEvent(preVisitor); @@ -632,7 +679,7 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget, aCallback, false, &pusher); - + preVisitor.mEventStatus = postVisitor.mEventStatus; // If the DOM event was created during event flow. if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) { @@ -642,7 +689,7 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget, } } - nsEventTargetChainItem::Destroy(targetEtci); + nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci); targetEtci = nullptr; aEvent->mFlags.mIsBeingDispatched = false; diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index 0ef327cc5849..1b1d7ce52504 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -112,7 +112,7 @@ using namespace mozilla::system; #include "mozilla/dom/time/DateCacheCleaner.h" #include "nsIMEStateManager.h" -extern void NS_ShutdownEventTargetChainItemRecyclePool(); +extern void NS_ShutdownChainItemPool(); using namespace mozilla; using namespace mozilla::dom; @@ -368,7 +368,7 @@ nsLayoutStatics::Shutdown() nsRegion::ShutdownStatic(); - NS_ShutdownEventTargetChainItemRecyclePool(); + NS_ShutdownChainItemPool(); nsFrameList::Shutdown();