зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
5d0ac0088b
Коммит
2cf629c5cd
|
@ -7,6 +7,7 @@
|
|||
#include "nsAtomTable.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsMemoryReporterManager.h"
|
||||
|
@ -757,9 +758,72 @@ nsMemoryReporterManager::Init()
|
|||
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()
|
||||
: mMutex("nsMemoryReporterManager::mMutex")
|
||||
{
|
||||
mReporters.Init();
|
||||
mMultiReporters.Init();
|
||||
}
|
||||
|
||||
nsMemoryReporterManager::~nsMemoryReporterManager()
|
||||
|
@ -769,60 +833,123 @@ nsMemoryReporterManager::~nsMemoryReporterManager()
|
|||
NS_IMETHODIMP
|
||||
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);
|
||||
rv = NS_NewArrayEnumerator(result, mReporters);
|
||||
return rv;
|
||||
|
||||
nsRefPtr<HashtableEnumerator> enumerator =
|
||||
new HashtableEnumerator(mReporters);
|
||||
enumerator.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
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);
|
||||
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
|
||||
nsMemoryReporterManager::RegisterReporter(nsIMemoryReporter *reporter)
|
||||
{
|
||||
// This method is thread-safe.
|
||||
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;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryReporterManager::RegisterMultiReporter(nsIMemoryMultiReporter *reporter)
|
||||
{
|
||||
// This method is thread-safe.
|
||||
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;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryReporterManager::UnregisterReporter(nsIMemoryReporter *reporter)
|
||||
{
|
||||
// This method is thread-safe.
|
||||
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;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryReporterManager::UnregisterMultiReporter(nsIMemoryMultiReporter *reporter)
|
||||
{
|
||||
// This method is thread-safe.
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
using mozilla::Mutex;
|
||||
|
||||
|
@ -22,8 +23,8 @@ public:
|
|||
virtual ~nsMemoryReporterManager();
|
||||
|
||||
private:
|
||||
nsCOMArray<nsIMemoryReporter> mReporters;
|
||||
nsCOMArray<nsIMemoryMultiReporter> mMultiReporters;
|
||||
nsTHashtable<nsISupportsHashKey> mReporters;
|
||||
nsTHashtable<nsISupportsHashKey> mMultiReporters;
|
||||
Mutex mMutex;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче