Bug 847236 - Simplify the dead nsEventTargetChainItem objects cache (2nd attempt). r=smaug.

--HG--
extra : rebase_source : c46d667fbcc56bdc1826ea61bc62f656f79c3919
This commit is contained in:
Nicholas Nethercote 2013-03-04 20:59:29 -08:00
Родитель 2620400c66
Коммит 7a41aa92ce
3 изменённых файлов: 55 добавлений и 103 удалений

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

@ -24,7 +24,6 @@
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsReadableUtils.h" #include "nsReadableUtils.h"
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include NEW_H
#include "prprf.h" #include "prprf.h"
#include "nsIDocument.h" #include "nsIDocument.h"
#include "nsGkAtoms.h" #include "nsGkAtoms.h"

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

@ -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 /* 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -12,7 +13,6 @@
#include "nsError.h" #include "nsError.h"
#include "nsMutationEvent.h" #include "nsMutationEvent.h"
#include NEW_H #include NEW_H
#include "nsFixedSizeAllocator.h"
#include "nsINode.h" #include "nsINode.h"
#include "nsPIDOMWindow.h" #include "nsPIDOMWindow.h"
#include "nsFrameLoader.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_WANTS_WILL_HANDLE_EVENT (1 << 1)
#define NS_TARGET_CHAIN_MAY_HAVE_MANAGER (1 << 2) #define NS_TARGET_CHAIN_MAY_HAVE_MANAGER (1 << 2)
static nsEventTargetChainItem* gCachedETCI = nullptr;
// nsEventTargetChainItem represents a single item in the event target chain. // nsEventTargetChainItem represents a single item in the event target chain.
class nsEventTargetChainItem class nsEventTargetChainItem
{ {
@ -36,25 +34,32 @@ private:
nsEventTargetChainItem(nsIDOMEventTarget* aTarget, nsEventTargetChainItem(nsIDOMEventTarget* aTarget,
nsEventTargetChainItem* aChild = nullptr); 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: public:
static nsEventTargetChainItem* Create(nsFixedSizeAllocator* aAllocator, static nsEventTargetChainItem* Create(nsIDOMEventTarget* aTarget,
nsIDOMEventTarget* aTarget,
nsEventTargetChainItem* aChild = nullptr) nsEventTargetChainItem* aChild = nullptr)
{ {
// Allocate from the ETCI recycle pool if possible.
void* place = nullptr; void* place = nullptr;
if (gCachedETCI) { if (sNumRecycledEtcis > 0) {
place = gCachedETCI; MOZ_ASSERT(sEtciRecyclePool);
gCachedETCI = gCachedETCI->mNext; place = sEtciRecyclePool;
sEtciRecyclePool = sEtciRecyclePool->mNext;
--sNumRecycledEtcis;
} else { } else {
place = aAllocator->Alloc(sizeof(nsEventTargetChainItem)); place = malloc(sizeof(nsEventTargetChainItem));
} }
return place return place
? ::new (place) nsEventTargetChainItem(aTarget, aChild) ? ::new (place) nsEventTargetChainItem(aTarget, aChild)
: nullptr; : nullptr;
} }
static void Destroy(nsFixedSizeAllocator* aAllocator, static void Destroy(nsEventTargetChainItem* aItem)
nsEventTargetChainItem* aItem)
{ {
// ::Destroy deletes ancestor chain. // ::Destroy deletes ancestor chain.
nsEventTargetChainItem* item = aItem; nsEventTargetChainItem* item = aItem;
@ -62,16 +67,30 @@ public:
item->mChild->mParent = nullptr; item->mChild->mParent = nullptr;
item->mChild = nullptr; item->mChild = nullptr;
} }
// Put destroyed ETCIs into the recycle pool if it's not already full.
while (item) { while (item) {
nsEventTargetChainItem* parent = item->mParent; nsEventTargetChainItem* parent = item->mParent;
item->~nsEventTargetChainItem(); item->~nsEventTargetChainItem();
item->mNext = gCachedETCI; if (sNumRecycledEtcis < kMaxNumRecycledEtcis) {
gCachedETCI = item; item->mNext = sEtciRecyclePool;
--sCurrentEtciCount; sEtciRecyclePool = item;
++sNumRecycledEtcis;
} else {
free(item);
}
item = parent; item = parent;
} }
} }
static void ShutdownRecyclePool()
{
while (sEtciRecyclePool) {
nsEventTargetChainItem* tmp = sEtciRecyclePool;
sEtciRecyclePool = sEtciRecyclePool->mNext;
free(tmp);
}
}
bool IsValid() bool IsValid()
{ {
NS_WARN_IF_FALSE(!!(mTarget), "Event target is not valid!"); NS_WARN_IF_FALSE(!!(mTarget), "Event target is not valid!");
@ -205,7 +224,7 @@ public:
nsEventTargetChainItem* mChild; nsEventTargetChainItem* mChild;
union { union {
nsEventTargetChainItem* mParent; nsEventTargetChainItem* mParent;
// This is used only when caching ETCI objects. // This is used only when recycling ETCIs.
nsEventTargetChainItem* mNext; nsEventTargetChainItem* mNext;
}; };
uint16_t mFlags; uint16_t mFlags;
@ -220,8 +239,8 @@ public:
static uint32_t sCurrentEtciCount; static uint32_t sCurrentEtciCount;
}; };
uint32_t nsEventTargetChainItem::sMaxEtciCount = 0; nsEventTargetChainItem* nsEventTargetChainItem::sEtciRecyclePool = nullptr;
uint32_t nsEventTargetChainItem::sCurrentEtciCount = 0; uint32_t nsEventTargetChainItem::sNumRecycledEtcis = 0;
nsEventTargetChainItem::nsEventTargetChainItem(nsIDOMEventTarget* aTarget, nsEventTargetChainItem::nsEventTargetChainItem(nsIDOMEventTarget* aTarget,
nsEventTargetChainItem* aChild) nsEventTargetChainItem* aChild)
@ -231,10 +250,6 @@ nsEventTargetChainItem::nsEventTargetChainItem(nsIDOMEventTarget* aTarget,
if (mChild) { if (mChild) {
mChild->mParent = this; mChild->mParent = this;
} }
if (++sCurrentEtciCount > sMaxEtciCount) {
sMaxEtciCount = sCurrentEtciCount;
}
} }
nsresult nsresult
@ -378,69 +393,13 @@ nsEventTargetChainItem::HandleEventTargetChain(
return NS_OK; return NS_OK;
} }
#define NS_CHAIN_POOL_SIZE 128 void NS_ShutdownEventTargetChainItemRecyclePool()
{
class ChainItemPool { nsEventTargetChainItem::ShutdownRecyclePool();
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* nsEventTargetChainItem*
EventTargetChainItemForChromeTarget(ChainItemPool& aPool, EventTargetChainItemForChromeTarget(nsINode* aNode,
nsINode* aNode,
nsEventTargetChainItem* aChild = nullptr) nsEventTargetChainItem* aChild = nullptr)
{ {
if (!aNode->IsInDoc()) { if (!aNode->IsInDoc()) {
@ -451,12 +410,11 @@ EventTargetChainItemForChromeTarget(ChainItemPool& aPool,
NS_ENSURE_TRUE(piTarget, nullptr); NS_ENSURE_TRUE(piTarget, nullptr);
nsEventTargetChainItem* etci = nsEventTargetChainItem* etci =
nsEventTargetChainItem::Create(aPool.GetPool(), nsEventTargetChainItem::Create(piTarget->GetTargetForEventTargetChain(),
piTarget->GetTargetForEventTargetChain(),
aChild); aChild);
NS_ENSURE_TRUE(etci, nullptr); NS_ENSURE_TRUE(etci, nullptr);
if (!etci->IsValid()) { if (!etci->IsValid()) {
nsEventTargetChainItem::Destroy(aPool.GetPool(), etci); nsEventTargetChainItem::Destroy(etci);
return nullptr; return nullptr;
} }
return etci; return etci;
@ -551,16 +509,13 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
// If we have a PresContext, make sure it doesn't die before // If we have a PresContext, make sure it doesn't die before
// event dispatching is finished. // event dispatching is finished.
nsRefPtr<nsPresContext> kungFuDeathGrip(aPresContext); nsRefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
ChainItemPool pool;
NS_ENSURE_TRUE(pool.GetPool(), NS_ERROR_OUT_OF_MEMORY);
// Create the event target chain item for the event target. // Create the event target chain item for the event target.
nsEventTargetChainItem* targetEtci = nsEventTargetChainItem* targetEtci =
nsEventTargetChainItem::Create(pool.GetPool(), nsEventTargetChainItem::Create(target->GetTargetForEventTargetChain());
target->GetTargetForEventTargetChain());
NS_ENSURE_TRUE(targetEtci, NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_TRUE(targetEtci, NS_ERROR_OUT_OF_MEMORY);
if (!targetEtci->IsValid()) { if (!targetEtci->IsValid()) {
nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci); nsEventTargetChainItem::Destroy(targetEtci);
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -603,8 +558,8 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
if (!preVisitor.mCanHandle && preVisitor.mAutomaticChromeDispatch && content) { if (!preVisitor.mCanHandle && preVisitor.mAutomaticChromeDispatch && content) {
// Event target couldn't handle the event. Try to propagate to chrome. // Event target couldn't handle the event. Try to propagate to chrome.
nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci); nsEventTargetChainItem::Destroy(targetEtci);
targetEtci = EventTargetChainItemForChromeTarget(pool, content); targetEtci = EventTargetChainItemForChromeTarget(content);
NS_ENSURE_STATE(targetEtci); NS_ENSURE_STATE(targetEtci);
targetEtci->PreHandleEvent(preVisitor); targetEtci->PreHandleEvent(preVisitor);
} }
@ -617,8 +572,7 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
while (preVisitor.mParentTarget) { while (preVisitor.mParentTarget) {
nsIDOMEventTarget* parentTarget = preVisitor.mParentTarget; nsIDOMEventTarget* parentTarget = preVisitor.mParentTarget;
nsEventTargetChainItem* parentEtci = nsEventTargetChainItem* parentEtci =
nsEventTargetChainItem::Create(pool.GetPool(), preVisitor.mParentTarget, nsEventTargetChainItem::Create(preVisitor.mParentTarget, topEtci);
topEtci);
if (!parentEtci) { if (!parentEtci) {
rv = NS_ERROR_OUT_OF_MEMORY; rv = NS_ERROR_OUT_OF_MEMORY;
break; break;
@ -640,15 +594,14 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
if (preVisitor.mCanHandle) { if (preVisitor.mCanHandle) {
topEtci = parentEtci; topEtci = parentEtci;
} else { } else {
nsEventTargetChainItem::Destroy(pool.GetPool(), parentEtci); nsEventTargetChainItem::Destroy(parentEtci);
parentEtci = nullptr; parentEtci = nullptr;
if (preVisitor.mAutomaticChromeDispatch && content) { if (preVisitor.mAutomaticChromeDispatch && content) {
// Even if the current target can't handle the event, try to // Even if the current target can't handle the event, try to
// propagate to chrome. // propagate to chrome.
nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget); nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget);
if (disabledTarget) { if (disabledTarget) {
parentEtci = EventTargetChainItemForChromeTarget(pool, parentEtci = EventTargetChainItemForChromeTarget(disabledTarget,
disabledTarget,
topEtci); topEtci);
if (parentEtci) { if (parentEtci) {
parentEtci->PreHandleEvent(preVisitor); parentEtci->PreHandleEvent(preVisitor);
@ -689,7 +642,7 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
} }
} }
nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci); nsEventTargetChainItem::Destroy(targetEtci);
targetEtci = nullptr; targetEtci = nullptr;
aEvent->mFlags.mIsBeingDispatched = false; aEvent->mFlags.mIsBeingDispatched = false;

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

@ -112,7 +112,7 @@ using namespace mozilla::system;
#include "mozilla/dom/time/DateCacheCleaner.h" #include "mozilla/dom/time/DateCacheCleaner.h"
#include "nsIMEStateManager.h" #include "nsIMEStateManager.h"
extern void NS_ShutdownChainItemPool(); extern void NS_ShutdownEventTargetChainItemRecyclePool();
using namespace mozilla; using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
@ -368,7 +368,7 @@ nsLayoutStatics::Shutdown()
nsRegion::ShutdownStatic(); nsRegion::ShutdownStatic();
NS_ShutdownChainItemPool(); NS_ShutdownEventTargetChainItemRecyclePool();
nsFrameList::Shutdown(); nsFrameList::Shutdown();