Bug 1475899: Track live nsThreads and add a method of enumerating them. r?erahm
This will allow us to enumerate active threads in order to report their
memory.
MozReview-Commit-ID: IExELSkFdwB
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -371,16 +371,36 @@ namespace {
struct ThreadInitData {
nsThread* thread;
const nsACString& name;
};
}
+/* static */ mozilla::Mutex&
+nsThread::ThreadListMutex()
+{
+ static Mutex sMutex("nsThread::ThreadListMutex");
+ return sMutex;
+}
+
+/* static */ LinkedList<nsThread>&
+nsThread::ThreadList()
+{
+ static LinkedList<nsThread> sList;
+ return sList;
+}
+
+/* static */ nsThreadEnumerator
+nsThread::Enumerate()
+{
+ return {};
+}
+
/*static*/ void
nsThread::ThreadFunc(void* aArg)
{
using mozilla::ipc::BackgroundChild;
ThreadInitData* initData = static_cast<ThreadInitData*>(aArg);
nsThread* self = initData->thread; // strong reference
@@ -563,16 +583,17 @@ nsThread::nsThread(NotNull<SynchronizedE
, mCurrentPerformanceCounter(nullptr)
{
}
nsThread::~nsThread()
{
NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
"shouldn't be waiting on other threads to shutdown");
+ MOZ_ASSERT(!isInList());
#ifdef DEBUG
// We deliberately leak these so they can be tracked by the leak checker.
// If you're having nsThreadShutdownContext leaks, you can set:
// XPCOM_MEM_LOG_CLASSES=nsThreadShutdownContext
// during a test run and that will at least tell you what thread is
// requesting shutdown on another, which can be helpful for diagnosing
// the leak.
for (size_t i = 0; i < mRequestedShutdownContexts.Length(); ++i) {
@@ -596,16 +617,21 @@ nsThread::Init(const nsACString& aName)
// ThreadFunc is responsible for setting mThread
if (!PR_CreateThread(PR_USER_THREAD, ThreadFunc, &initData,
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
PR_JOINABLE_THREAD, mStackSize)) {
NS_RELEASE_THIS();
return NS_ERROR_OUT_OF_MEMORY;
}
+ {
+ MutexAutoLock mal(ThreadListMutex());
+ ThreadList().insertBack(this);
+ }
+
// ThreadFunc will wait for this event to be run before it tries to access
// mThread. By delaying insertion of this event into the queue, we ensure
// that mThread is set properly.
{
mEvents->PutEvent(do_AddRef(startup), EventPriority::Normal); // retain a reference
}
// Wait for thread to call ThreadManager::SetupCurrentThread, which completes
@@ -710,16 +736,23 @@ nsThread::ShutdownInternal(bool aSync)
return nullptr;
}
// Prevent multiple calls to this method
if (!mShutdownRequired.compareExchange(true, false)) {
return nullptr;
}
+ {
+ MutexAutoLock mal(ThreadListMutex());
+ if (isInList()) {
+ removeFrom(ThreadList());
+ }
+ }
+
NotNull<nsThread*> currentThread =
WrapNotNull(nsThreadManager::get().GetCurrentThread());
nsAutoPtr<nsThreadShutdownContext>& context =
*currentThread->mRequestedShutdownContexts.AppendElement();
context = new nsThreadShutdownContext(WrapNotNull(this), currentThread, aSync);
// Set mShutdownContext and wake up the thread in case it is waiting for
--- a/xpcom/threads/nsThread.h
+++ b/xpcom/threads/nsThread.h
@@ -10,37 +10,43 @@
#include "mozilla/Mutex.h"
#include "nsIIdlePeriod.h"
#include "nsIThreadInternal.h"
#include "nsISupportsPriority.h"
#include "nsThreadUtils.h"
#include "nsString.h"
#include "nsTObserverArray.h"
#include "mozilla/Attributes.h"
+#include "mozilla/LinkedList.h"
#include "mozilla/SynchronizedEventQueue.h"
#include "mozilla/NotNull.h"
#include "mozilla/TimeStamp.h"
#include "nsAutoPtr.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Array.h"
#include "mozilla/dom/DocGroup.h"
namespace mozilla {
class CycleCollectedJSContext;
class ThreadEventTarget;
}
using mozilla::NotNull;
+class nsThreadEnumerator;
+
// A native thread
class nsThread
: public nsIThreadInternal
, public nsISupportsPriority
+ , private mozilla::LinkedListElement<nsThread>
{
+ friend mozilla::LinkedList<nsThread>;
+ friend mozilla::LinkedListElement<nsThread>;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIEVENTTARGET_FULL
NS_DECL_NSITHREAD
NS_DECL_NSITHREADINTERNAL
NS_DECL_NSISUPPORTSPRIORITY
enum MainThreadFlag
@@ -127,36 +133,43 @@ public:
bool ShuttingDown()
{
return mShutdownContext != nullptr;
}
virtual mozilla::PerformanceCounter* GetPerformanceCounter(nsIRunnable* aEvent);
+ static nsThreadEnumerator Enumerate();
+
private:
void DoMainThreadSpecificProcessing(bool aReallyWait);
protected:
friend class nsThreadShutdownEvent;
+ friend class nsThreadEnumerator;
+
virtual ~nsThread();
static void ThreadFunc(void* aArg);
// Helper
already_AddRefed<nsIThreadObserver> GetObserver()
{
nsIThreadObserver* obs;
nsThread::GetObserver(&obs);
return already_AddRefed<nsIThreadObserver>(obs);
}
struct nsThreadShutdownContext* ShutdownInternal(bool aSync);
+ static mozilla::Mutex& ThreadListMutex();
+ static mozilla::LinkedList<nsThread>& ThreadList();
+
RefPtr<mozilla::SynchronizedEventQueue> mEvents;
RefPtr<mozilla::ThreadEventTarget> mEventTarget;
mozilla::CycleCollectedJSContext* mScriptObserver;
// Only accessed on the target thread.
nsAutoTObserverArray<NotNull<nsCOMPtr<nsIThreadObserver>>, 2> mEventObservers;
@@ -179,16 +192,30 @@ protected:
mozilla::TimeStamp mNextIdleDeadline;
// Used to track which event is being executed by ProcessNextEvent
nsCOMPtr<nsIRunnable> mCurrentEvent;
mozilla::TimeStamp mCurrentEventStart;
uint32_t mCurrentEventLoopDepth;
RefPtr<mozilla::PerformanceCounter> mCurrentPerformanceCounter;
};
+class MOZ_STACK_CLASS nsThreadEnumerator final
+{
+public:
+ nsThreadEnumerator()
+ : mMal(nsThread::ThreadListMutex())
+ {}
+
+ auto begin() { return nsThread::ThreadList().begin(); }
+ auto end() { return nsThread::ThreadList().end(); }
+
+private:
+ mozilla::MutexAutoLock mMal;
+};
+
#if defined(XP_UNIX) && !defined(ANDROID) && !defined(DEBUG) && HAVE_UALARM \
&& defined(_GNU_SOURCE)
# define MOZ_CANARY
extern int sCanaryOutputFD;
#endif
#endif // nsThread_h__