diff --git a/content/events/src/nsEventDispatcher.cpp b/content/events/src/nsEventDispatcher.cpp index 60bb3ae79b3..203eb1e6248 100644 --- a/content/events/src/nsEventDispatcher.cpp +++ b/content/events/src/nsEventDispatcher.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ @@ -12,7 +13,6 @@ #include "nsError.h" #include "nsMutationEvent.h" #include NEW_H -#include "nsFixedSizeAllocator.h" #include "nsINode.h" #include "nsPIDOMWindow.h" #include "nsFrameLoader.h" @@ -27,8 +27,6 @@ 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 { @@ -36,25 +34,32 @@ 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(nsFixedSizeAllocator* aAllocator, - nsIDOMEventTarget* aTarget, + static nsEventTargetChainItem* Create(nsIDOMEventTarget* aTarget, nsEventTargetChainItem* aChild = nullptr) { + // Allocate from the ETCI recycle pool if possible. void* place = nullptr; - if (gCachedETCI) { - place = gCachedETCI; - gCachedETCI = gCachedETCI->mNext; + if (sNumRecycledEtcis > 0) { + MOZ_ASSERT(sEtciRecyclePool); + place = sEtciRecyclePool; + sEtciRecyclePool = sEtciRecyclePool->mNext; + --sNumRecycledEtcis; } else { - place = aAllocator->Alloc(sizeof(nsEventTargetChainItem)); + place = malloc(sizeof(nsEventTargetChainItem)); } return place ? ::new (place) nsEventTargetChainItem(aTarget, aChild) : nullptr; } - static void Destroy(nsFixedSizeAllocator* aAllocator, - nsEventTargetChainItem* aItem) + static void Destroy(nsEventTargetChainItem* aItem) { // ::Destroy deletes ancestor chain. nsEventTargetChainItem* item = aItem; @@ -62,16 +67,30 @@ 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(); - item->mNext = gCachedETCI; - gCachedETCI = item; - --sCurrentEtciCount; + if (sNumRecycledEtcis < kMaxNumRecycledEtcis) { + item->mNext = sEtciRecyclePool; + sEtciRecyclePool = item; + ++sNumRecycledEtcis; + } else { + free(item); + } 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!"); @@ -205,7 +224,7 @@ public: nsEventTargetChainItem* mChild; union { nsEventTargetChainItem* mParent; - // This is used only when caching ETCI objects. + // This is used only when recycling ETCIs. nsEventTargetChainItem* mNext; }; uint16_t mFlags; @@ -220,8 +239,8 @@ public: static uint32_t sCurrentEtciCount; }; -uint32_t nsEventTargetChainItem::sMaxEtciCount = 0; -uint32_t nsEventTargetChainItem::sCurrentEtciCount = 0; +nsEventTargetChainItem* nsEventTargetChainItem::sEtciRecyclePool = nullptr; +uint32_t nsEventTargetChainItem::sNumRecycledEtcis = 0; nsEventTargetChainItem::nsEventTargetChainItem(nsIDOMEventTarget* aTarget, nsEventTargetChainItem* aChild) @@ -231,10 +250,6 @@ nsEventTargetChainItem::nsEventTargetChainItem(nsIDOMEventTarget* aTarget, if (mChild) { mChild->mParent = this; } - - if (++sCurrentEtciCount > sMaxEtciCount) { - sMaxEtciCount = sCurrentEtciCount; - } } nsresult @@ -378,69 +393,13 @@ nsEventTargetChainItem::HandleEventTargetChain( return NS_OK; } -#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(); } +void NS_ShutdownEventTargetChainItemRecyclePool() +{ + nsEventTargetChainItem::ShutdownRecyclePool(); +} nsEventTargetChainItem* -EventTargetChainItemForChromeTarget(ChainItemPool& aPool, - nsINode* aNode, +EventTargetChainItemForChromeTarget(nsINode* aNode, nsEventTargetChainItem* aChild = nullptr) { if (!aNode->IsInDoc()) { @@ -451,12 +410,11 @@ EventTargetChainItemForChromeTarget(ChainItemPool& aPool, NS_ENSURE_TRUE(piTarget, nullptr); nsEventTargetChainItem* etci = - nsEventTargetChainItem::Create(aPool.GetPool(), - piTarget->GetTargetForEventTargetChain(), + nsEventTargetChainItem::Create(piTarget->GetTargetForEventTargetChain(), aChild); NS_ENSURE_TRUE(etci, nullptr); if (!etci->IsValid()) { - nsEventTargetChainItem::Destroy(aPool.GetPool(), etci); + nsEventTargetChainItem::Destroy(etci); return nullptr; } return etci; @@ -551,16 +509,13 @@ 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(pool.GetPool(), - target->GetTargetForEventTargetChain()); + nsEventTargetChainItem::Create(target->GetTargetForEventTargetChain()); NS_ENSURE_TRUE(targetEtci, NS_ERROR_OUT_OF_MEMORY); if (!targetEtci->IsValid()) { - nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci); + nsEventTargetChainItem::Destroy(targetEtci); return NS_ERROR_FAILURE; } @@ -603,8 +558,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(pool.GetPool(), targetEtci); - targetEtci = EventTargetChainItemForChromeTarget(pool, content); + nsEventTargetChainItem::Destroy(targetEtci); + targetEtci = EventTargetChainItemForChromeTarget(content); NS_ENSURE_STATE(targetEtci); targetEtci->PreHandleEvent(preVisitor); } @@ -617,8 +572,7 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget, while (preVisitor.mParentTarget) { nsIDOMEventTarget* parentTarget = preVisitor.mParentTarget; nsEventTargetChainItem* parentEtci = - nsEventTargetChainItem::Create(pool.GetPool(), preVisitor.mParentTarget, - topEtci); + nsEventTargetChainItem::Create(preVisitor.mParentTarget, topEtci); if (!parentEtci) { rv = NS_ERROR_OUT_OF_MEMORY; break; @@ -640,15 +594,14 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget, if (preVisitor.mCanHandle) { topEtci = parentEtci; } else { - nsEventTargetChainItem::Destroy(pool.GetPool(), parentEtci); + nsEventTargetChainItem::Destroy(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(pool, - disabledTarget, + parentEtci = EventTargetChainItemForChromeTarget(disabledTarget, topEtci); if (parentEtci) { parentEtci->PreHandleEvent(preVisitor); @@ -679,7 +632,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) { @@ -689,7 +642,7 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget, } } - nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci); + nsEventTargetChainItem::Destroy(targetEtci); targetEtci = nullptr; aEvent->mFlags.mIsBeingDispatched = false; diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index 1b1d7ce5250..0ef327cc584 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_ShutdownChainItemPool(); +extern void NS_ShutdownEventTargetChainItemRecyclePool(); using namespace mozilla; using namespace mozilla::dom; @@ -368,7 +368,7 @@ nsLayoutStatics::Shutdown() nsRegion::ShutdownStatic(); - NS_ShutdownChainItemPool(); + NS_ShutdownEventTargetChainItemRecyclePool(); nsFrameList::Shutdown();