Bug 920804 - Improve nsFrameMessageManager p=fabrice,smaug, r=smaug,fabrice

--HG--
extra : rebase_source : 1930ae4472d899c0996d78642d6ba2164dcdf9f1
This commit is contained in:
Olli Pettay 2013-10-26 00:55:16 +03:00
parent 60b12a2e89
commit 3ed9822732
2 changed files with 266 additions and 188 deletions

View File

@ -51,14 +51,26 @@ using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::dom::ipc;
static PLDHashOperator
CycleCollectorTraverseListeners(const nsAString& aKey,
nsTArray<nsMessageListenerInfo>* aListeners,
void* aCb)
{
nsCycleCollectionTraversalCallback* cb =
static_cast<nsCycleCollectionTraversalCallback*> (aCb);
uint32_t count = aListeners->Length();
for (uint32_t i = 0; i < count; ++i) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "listeners[i] mStrongListener");
cb->NoteXPCOMChild((*aListeners)[i].mStrongListener.get());
}
return PL_DHASH_NEXT;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
uint32_t count = tmp->mListeners.Length();
for (uint32_t i = 0; i < count; i++) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mStrongListener");
cb.NoteXPCOMChild(tmp->mListeners[i].mStrongListener.get());
}
tmp->mListeners.EnumerateRead(CycleCollectorTraverseListeners,
static_cast<void*>(&cb));
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@ -246,17 +258,21 @@ NS_IMETHODIMP
nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
nsIMessageListener* aListener)
{
nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
uint32_t len = mListeners.Length();
nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
if (!listeners) {
listeners = new nsTArray<nsMessageListenerInfo>();
mListeners.Put(aMessage, listeners);
} else {
uint32_t len = listeners->Length();
for (uint32_t i = 0; i < len; ++i) {
if (mListeners[i].mMessage == message &&
mListeners[i].mStrongListener == aListener) {
if ((*listeners)[i].mStrongListener == aListener) {
return NS_OK;
}
}
nsMessageListenerInfo* entry = mListeners.AppendElement();
}
nsMessageListenerInfo* entry = listeners->AppendElement();
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
entry->mMessage = message;
entry->mStrongListener = aListener;
return NS_OK;
}
@ -265,18 +281,50 @@ NS_IMETHODIMP
nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage,
nsIMessageListener* aListener)
{
nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
uint32_t len = mListeners.Length();
nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
if (!listeners) {
return NS_OK;
}
uint32_t len = listeners->Length();
for (uint32_t i = 0; i < len; ++i) {
if (mListeners[i].mMessage == message &&
mListeners[i].mStrongListener == aListener) {
mListeners.RemoveElementAt(i);
if ((*listeners)[i].mStrongListener == aListener) {
listeners->RemoveElementAt(i);
return NS_OK;
}
}
return NS_OK;
}
#ifdef DEBUG
typedef struct
{
nsCOMPtr<nsISupports> mCanonical;
nsWeakPtr mWeak;
} CanonicalCheckerParams;
static PLDHashOperator
CanonicalChecker(const nsAString& aKey,
nsTArray<nsMessageListenerInfo>* aListeners,
void* aParams)
{
CanonicalCheckerParams* params =
static_cast<CanonicalCheckerParams*> (aParams);
uint32_t count = aListeners->Length();
for (uint32_t i = 0; i < count; i++) {
if (!(*aListeners)[i].mWeakListener) {
continue;
}
nsCOMPtr<nsISupports> otherCanonical =
do_QueryReferent((*aListeners)[i].mWeakListener);
MOZ_ASSERT((params->mCanonical == otherCanonical) ==
(params->mWeak == (*aListeners)[i].mWeakListener));
}
return PL_DHASH_NEXT;
}
#endif
NS_IMETHODIMP
nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage,
nsIMessageListener* aListener)
@ -290,29 +338,26 @@ nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage,
// this to happen; it will break e.g. RemoveWeakMessageListener. So let's
// check that we're not getting ourselves into that situation.
nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
if (!mListeners[i].mWeakListener) {
continue;
}
nsCOMPtr<nsISupports> otherCanonical =
do_QueryReferent(mListeners[i].mWeakListener);
MOZ_ASSERT((canonical == otherCanonical) ==
(weak == mListeners[i].mWeakListener));
}
CanonicalCheckerParams params;
params.mCanonical = canonical;
params.mWeak = weak;
mListeners.EnumerateRead(CanonicalChecker, (void*)&params);
#endif
nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
uint32_t len = mListeners.Length();
nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
if (!listeners) {
listeners = new nsTArray<nsMessageListenerInfo>();
mListeners.Put(aMessage, listeners);
} else {
uint32_t len = listeners->Length();
for (uint32_t i = 0; i < len; ++i) {
if (mListeners[i].mMessage == message &&
mListeners[i].mWeakListener == weak) {
if ((*listeners)[i].mWeakListener == weak) {
return NS_OK;
}
}
}
nsMessageListenerInfo* entry = mListeners.AppendElement();
entry->mMessage = message;
nsMessageListenerInfo* entry = listeners->AppendElement();
entry->mWeakListener = weak;
return NS_OK;
}
@ -324,12 +369,15 @@ nsFrameMessageManager::RemoveWeakMessageListener(const nsAString& aMessage,
nsWeakPtr weak = do_GetWeakReference(aListener);
NS_ENSURE_TRUE(weak, NS_OK);
nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
uint32_t len = mListeners.Length();
nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
if (!listeners) {
return NS_OK;
}
uint32_t len = listeners->Length();
for (uint32_t i = 0; i < len; ++i) {
if (mListeners[i].mMessage == message &&
mListeners[i].mWeakListener == weak) {
mListeners.RemoveElementAt(i);
if ((*listeners)[i].mWeakListener == weak) {
listeners->RemoveElementAt(i);
return NS_OK;
}
}
@ -792,28 +840,27 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
InfallibleTArray<nsString>* aJSONRetVal)
{
AutoSafeJSContext ctx;
nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
if (listeners) {
if (mListeners.Length()) {
nsCOMPtr<nsIAtom> name = do_GetAtom(aMessage);
MMListenerRemover lr(this);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
for (uint32_t i = 0; i < listeners->Length(); ++i) {
// Remove mListeners[i] if it's an expired weak listener.
nsCOMPtr<nsISupports> weakListener;
if (mListeners[i].mWeakListener) {
weakListener = do_QueryReferent(mListeners[i].mWeakListener);
if ((*listeners)[i].mWeakListener) {
weakListener = do_QueryReferent((*listeners)[i].mWeakListener);
if (!weakListener) {
mListeners.RemoveElementAt(i--);
listeners->RemoveElementAt(i--);
continue;
}
}
if (mListeners[i].mMessage == name) {
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
if (weakListener) {
wrappedJS = do_QueryInterface(weakListener);
} else {
wrappedJS = do_QueryInterface(mListeners[i].mStrongListener);
wrappedJS = do_QueryInterface((*listeners)[i].mStrongListener);
}
if (!wrappedJS) {
@ -923,7 +970,6 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
}
}
}
}
nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage,
aIsSync, aCloneData,
@ -1011,13 +1057,14 @@ nsFrameMessageManager::Disconnect(bool aRemoveFromParent)
namespace {
struct MessageManagerReferentCount {
MessageManagerReferentCount() : strong(0), weakAlive(0), weakDead(0) {}
size_t strong;
size_t weakAlive;
size_t weakDead;
nsCOMArray<nsIAtom> suspectMessages;
nsDataHashtable<nsPtrHashKey<nsIAtom>, uint32_t> messageCounter;
struct MessageManagerReferentCount
{
MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
size_t mStrong;
size_t mWeakAlive;
size_t mWeakDead;
nsTArray<nsString> mSuspectMessages;
nsDataHashtable<nsStringHashKey, uint32_t> mMessageCounter;
};
} // anonymous namespace
@ -1030,48 +1077,67 @@ class MessageManagerReporter MOZ_FINAL : public nsIMemoryReporter
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMEMORYREPORTER
protected:
static const size_t kSuspectReferentCount = 300;
protected:
void CountReferents(nsFrameMessageManager* aMessageManager,
MessageManagerReferentCount* aReferentCount);
};
NS_IMPL_ISUPPORTS1(MessageManagerReporter, nsIMemoryReporter)
void
MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager,
MessageManagerReferentCount* aReferentCount)
static PLDHashOperator
CollectMessageListenerData(const nsAString& aKey,
nsTArray<nsMessageListenerInfo>* aListeners,
void* aData)
{
for (uint32_t i = 0; i < aMessageManager->mListeners.Length(); i++) {
const nsMessageListenerInfo& listenerInfo = aMessageManager->mListeners[i];
MessageManagerReferentCount* referentCount =
static_cast<MessageManagerReferentCount*>(aData);
uint32_t listenerCount = aListeners->Length();
if (!listenerCount) {
return PL_DHASH_NEXT;
}
nsString key(aKey);
uint32_t oldCount = 0;
referentCount->mMessageCounter.Get(key, &oldCount);
uint32_t currentCount = oldCount + listenerCount;
referentCount->mMessageCounter.Put(key, currentCount);
// Keep track of messages that have a suspiciously large
// number of referents (symptom of leak).
if (currentCount == MessageManagerReporter::kSuspectReferentCount) {
referentCount->mSuspectMessages.AppendElement(key);
}
for (uint32_t i = 0; i < listenerCount; ++i) {
const nsMessageListenerInfo& listenerInfo = (*aListeners)[i];
if (listenerInfo.mWeakListener) {
nsCOMPtr<nsISupports> referent =
do_QueryReferent(listenerInfo.mWeakListener);
if (referent) {
aReferentCount->weakAlive++;
referentCount->mWeakAlive++;
} else {
aReferentCount->weakDead++;
referentCount->mWeakDead++;
}
} else {
aReferentCount->strong++;
referentCount->mStrong++;
}
}
return PL_DHASH_NEXT;
}
uint32_t oldCount = 0;
aReferentCount->messageCounter.Get(listenerInfo.mMessage, &oldCount);
uint32_t currentCount = oldCount + 1;
aReferentCount->messageCounter.Put(listenerInfo.mMessage, currentCount);
// Keep track of messages that have a suspiciously large
// number of referents (symptom of leak).
if (currentCount == kSuspectReferentCount) {
aReferentCount->suspectMessages.AppendElement(listenerInfo.mMessage);
}
}
void
MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager,
MessageManagerReferentCount* aReferentCount)
{
aMessageManager->mListeners.EnumerateRead(CollectMessageListenerData,
aReferentCount);
// Add referent count in child managers because the listeners
// participate in messages dispatched from parent message manager.
for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); i++) {
for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) {
nsRefPtr<nsFrameMessageManager> mm =
static_cast<nsFrameMessageManager*>(aMessageManager->mChildManagers[i]);
CountReferents(mm, aReferentCount);
@ -1102,25 +1168,25 @@ ReportReferentCount(const char* aManagerType,
} while (0)
REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType),
aReferentCount.strong,
aReferentCount.mStrong,
nsPrintfCString("The number of strong referents held by the message "
"manager in the %s manager.", aManagerType));
REPORT(nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType),
aReferentCount.weakAlive,
aReferentCount.mWeakAlive,
nsPrintfCString("The number of weak referents that are still alive "
"held by the message manager in the %s manager.",
aManagerType));
REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType),
aReferentCount.weakDead,
aReferentCount.mWeakDead,
nsPrintfCString("The number of weak referents that are dead "
"held by the message manager in the %s manager.",
aManagerType));
for (uint32_t i = 0; i < aReferentCount.suspectMessages.Length(); i++) {
for (uint32_t i = 0; i < aReferentCount.mSuspectMessages.Length(); i++) {
uint32_t totalReferentCount = 0;
aReferentCount.messageCounter.Get(aReferentCount.suspectMessages[i],
aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i],
&totalReferentCount);
nsAtomCString suspect(aReferentCount.suspectMessages[i]);
NS_ConvertUTF16toUTF8 suspect(aReferentCount.mSuspectMessages[i]);
REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)",
aManagerType, suspect.get()), totalReferentCount,
nsPrintfCString("A message in the %s message manager with a "
@ -1711,15 +1777,25 @@ NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult)
return CallQueryInterface(mm, aResult);
}
static PLDHashOperator
CycleCollectorMarkListeners(const nsAString& aKey,
nsTArray<nsMessageListenerInfo>* aListeners,
void* aData)
{
uint32_t count = aListeners->Length();
for (uint32_t i = 0; i < count; i++) {
if ((*aListeners)[i].mStrongListener) {
xpc_TryUnmarkWrappedGrayObject((*aListeners)[i].mStrongListener);
}
}
return PL_DHASH_NEXT;
}
bool
nsFrameMessageManager::MarkForCC()
{
uint32_t len = mListeners.Length();
for (uint32_t i = 0; i < len; ++i) {
if (mListeners[i].mStrongListener) {
xpc_TryUnmarkWrappedGrayObject(mListeners[i].mStrongListener);
}
}
mListeners.EnumerateRead(CycleCollectorMarkListeners, nullptr);
if (mRefCnt.IsPurple()) {
mRefCnt.RemovePurple();
}

View File

@ -18,6 +18,7 @@
#include "nsIPrincipal.h"
#include "nsIXPConnect.h"
#include "nsDataHashtable.h"
#include "nsClassHashtable.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "nsThreadUtils.h"
@ -116,7 +117,6 @@ struct nsMessageListenerInfo
// Exactly one of mStrongListener and mWeakListener must be non-null.
nsCOMPtr<nsIMessageListener> mStrongListener;
nsWeakPtr mWeakListener;
nsCOMPtr<nsIAtom> mMessage;
};
class CpowHolder
@ -268,7 +268,9 @@ private:
bool aIsSync);
protected:
friend class MMListenerRemover;
nsTArray<nsMessageListenerInfo> mListeners;
// We keep the message listeners as arrays in a hastable indexed by the
// message name. That gives us fast lookups in ReceiveMessage().
nsClassHashtable<nsStringHashKey, nsTArray<nsMessageListenerInfo>> mListeners;
nsCOMArray<nsIContentFrameMessageManager> mChildManagers;
bool mChrome; // true if we're in the chrome process
bool mGlobal; // true if we're the global frame message manager