Bug 833913 - Part 3: Switch nsMemoryReporterManager to using nsTHashtable. r=khuey,njn

This makes NS_RegisterMemoryReporter() and NS_UnregisterMemoryReporter()
O(1).  Previously, they took time linear to the number of registered
reporters.
This commit is contained in:
Justin Lebar 2013-02-01 10:12:52 -05:00
Родитель 5d0ac0088b
Коммит 2cf629c5cd
2 изменённых файлов: 148 добавлений и 20 удалений

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

@ -7,6 +7,7 @@
#include "nsAtomTable.h" #include "nsAtomTable.h"
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceUtils.h"
#include "nsServiceManagerUtils.h" #include "nsServiceManagerUtils.h"
#include "nsMemoryReporterManager.h" #include "nsMemoryReporterManager.h"
@ -757,9 +758,72 @@ nsMemoryReporterManager::Init()
return NS_OK; return NS_OK;
} }
namespace {
/**
* HastableEnumerator takes an nsTHashtable<nsISupportsHashKey>& in its
* constructor and creates an nsISimpleEnumerator from its contents.
*
* The resultant enumerator works over a copy of the hashtable, so it's safe to
* mutate or destroy the hashtable after the enumerator is created.
*/
class HashtableEnumerator MOZ_FINAL : public nsISimpleEnumerator
{
public:
HashtableEnumerator(nsTHashtable<nsISupportsHashKey>& aHashtable)
: mIndex(0)
{
aHashtable.EnumerateEntries(EnumeratorFunc, this);
}
NS_DECL_ISUPPORTS
NS_DECL_NSISIMPLEENUMERATOR
private:
static PLDHashOperator
EnumeratorFunc(nsISupportsHashKey* aEntry, void* aData);
uint32_t mIndex;
nsCOMArray<nsISupports> mArray;
};
NS_IMPL_ISUPPORTS1(HashtableEnumerator, nsISimpleEnumerator)
/* static */ PLDHashOperator
HashtableEnumerator::EnumeratorFunc(nsISupportsHashKey* aElem, void* aData)
{
HashtableEnumerator* enumerator = static_cast<HashtableEnumerator*>(aData);
enumerator->mArray.AppendObject(aElem->GetKey());
return PL_DHASH_NEXT;
}
NS_IMETHODIMP HashtableEnumerator::HasMoreElements(bool* aResult)
{
*aResult = mIndex < mArray.Count();
return NS_OK;
}
NS_IMETHODIMP HashtableEnumerator::GetNext(nsISupports** aNext)
{
if (mIndex < mArray.Count()) {
nsCOMPtr<nsISupports> next = mArray.ObjectAt(mIndex);
next.forget(aNext);
mIndex++;
return NS_OK;
}
*aNext = nullptr;
return NS_ERROR_FAILURE;
}
} // anonymous namespace
nsMemoryReporterManager::nsMemoryReporterManager() nsMemoryReporterManager::nsMemoryReporterManager()
: mMutex("nsMemoryReporterManager::mMutex") : mMutex("nsMemoryReporterManager::mMutex")
{ {
mReporters.Init();
mMultiReporters.Init();
} }
nsMemoryReporterManager::~nsMemoryReporterManager() nsMemoryReporterManager::~nsMemoryReporterManager()
@ -769,60 +833,123 @@ nsMemoryReporterManager::~nsMemoryReporterManager()
NS_IMETHODIMP NS_IMETHODIMP
nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator **result) nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator **result)
{ {
nsresult rv; // Memory reporters are not necessarily threadsafe, so EnumerateReporters()
// must be called from the main thread.
if (!NS_IsMainThread()) {
MOZ_CRASH();
}
mozilla::MutexAutoLock autoLock(mMutex); mozilla::MutexAutoLock autoLock(mMutex);
rv = NS_NewArrayEnumerator(result, mReporters);
return rv; nsRefPtr<HashtableEnumerator> enumerator =
new HashtableEnumerator(mReporters);
enumerator.forget(result);
return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
nsMemoryReporterManager::EnumerateMultiReporters(nsISimpleEnumerator **result) nsMemoryReporterManager::EnumerateMultiReporters(nsISimpleEnumerator **result)
{ {
nsresult rv; // Memory multi-reporters are not necessarily threadsafe, so
// EnumerateMultiReporters() must be called from the main thread.
if (!NS_IsMainThread()) {
MOZ_CRASH();
}
mozilla::MutexAutoLock autoLock(mMutex); mozilla::MutexAutoLock autoLock(mMutex);
rv = NS_NewArrayEnumerator(result, mMultiReporters);
return rv; nsRefPtr<HashtableEnumerator> enumerator =
new HashtableEnumerator(mMultiReporters);
enumerator.forget(result);
return NS_OK;
}
static void
DebugAssertRefcountIsNonZero(nsISupports* aObj)
{
#ifdef DEBUG
// This will probably crash if the object's refcount is 0.
uint32_t refcnt = NS_ADDREF(aObj);
MOZ_ASSERT(refcnt >= 2);
NS_RELEASE(aObj);
#endif
} }
NS_IMETHODIMP NS_IMETHODIMP
nsMemoryReporterManager::RegisterReporter(nsIMemoryReporter *reporter) nsMemoryReporterManager::RegisterReporter(nsIMemoryReporter *reporter)
{ {
// This method is thread-safe.
mozilla::MutexAutoLock autoLock(mMutex); mozilla::MutexAutoLock autoLock(mMutex);
if (mReporters.IndexOf(reporter) != -1)
return NS_ERROR_FAILURE;
mReporters.AppendObject(reporter); if (mReporters.Contains(reporter)) {
return NS_ERROR_FAILURE;
}
// This method needs to be safe even if |reporter| has a refcnt of 0, so we
// take a kung fu death grip before calling PutEntry. Otherwise, if
// PutEntry addref'ed and released reporter before finally addref'ing it for
// good, it would free reporter!
//
// The kung fu death grip could itself be problematic if PutEntry didn't
// addref |reporter| (because then when the death grip goes out of scope, we
// would delete the reporter). In debug mode, we check that this doesn't
// happen.
{
nsCOMPtr<nsIMemoryReporter> kungFuDeathGrip = reporter;
mReporters.PutEntry(reporter);
}
DebugAssertRefcountIsNonZero(reporter);
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
nsMemoryReporterManager::RegisterMultiReporter(nsIMemoryMultiReporter *reporter) nsMemoryReporterManager::RegisterMultiReporter(nsIMemoryMultiReporter *reporter)
{ {
// This method is thread-safe.
mozilla::MutexAutoLock autoLock(mMutex); mozilla::MutexAutoLock autoLock(mMutex);
if (mMultiReporters.IndexOf(reporter) != -1)
return NS_ERROR_FAILURE;
mMultiReporters.AppendObject(reporter); if (mMultiReporters.Contains(reporter)) {
return NS_ERROR_FAILURE;
}
{
nsCOMPtr<nsIMemoryMultiReporter> kungFuDeathGrip = reporter;
mMultiReporters.PutEntry(reporter);
}
DebugAssertRefcountIsNonZero(reporter);
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
nsMemoryReporterManager::UnregisterReporter(nsIMemoryReporter *reporter) nsMemoryReporterManager::UnregisterReporter(nsIMemoryReporter *reporter)
{ {
// This method is thread-safe.
mozilla::MutexAutoLock autoLock(mMutex); mozilla::MutexAutoLock autoLock(mMutex);
if (!mReporters.RemoveObject(reporter))
return NS_ERROR_FAILURE;
if (!mReporters.Contains(reporter)) {
return NS_ERROR_FAILURE;
}
mReporters.RemoveEntry(reporter);
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
nsMemoryReporterManager::UnregisterMultiReporter(nsIMemoryMultiReporter *reporter) nsMemoryReporterManager::UnregisterMultiReporter(nsIMemoryMultiReporter *reporter)
{ {
// This method is thread-safe.
mozilla::MutexAutoLock autoLock(mMutex); mozilla::MutexAutoLock autoLock(mMutex);
if (!mMultiReporters.RemoveObject(reporter))
return NS_ERROR_FAILURE;
if (!mMultiReporters.Contains(reporter)) {
return NS_ERROR_FAILURE;
}
mMultiReporters.RemoveEntry(reporter);
return NS_OK; return NS_OK;
} }

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

@ -5,10 +5,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIMemoryReporter.h" #include "nsIMemoryReporter.h"
#include "nsCOMArray.h"
#include "mozilla/Mutex.h" #include "mozilla/Mutex.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "nsString.h" #include "nsString.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
using mozilla::Mutex; using mozilla::Mutex;
@ -22,9 +23,9 @@ public:
virtual ~nsMemoryReporterManager(); virtual ~nsMemoryReporterManager();
private: private:
nsCOMArray<nsIMemoryReporter> mReporters; nsTHashtable<nsISupportsHashKey> mReporters;
nsCOMArray<nsIMemoryMultiReporter> mMultiReporters; nsTHashtable<nsISupportsHashKey> mMultiReporters;
Mutex mMutex; Mutex mMutex;
}; };
#define NS_MEMORY_REPORTER_MANAGER_CID \ #define NS_MEMORY_REPORTER_MANAGER_CID \