Bug 1310880 - Allow a BackgroundHangMonitor to have its own private BackgroundHangThread. r?jchen
MozReview-Commit-ID: L32E19FVhv
--- a/xpcom/threads/BackgroundHangMonitor.cpp
+++ b/xpcom/threads/BackgroundHangMonitor.cpp
@@ -136,65 +136,89 @@ private:
~BackgroundHangThread();
/* Keep a reference to the manager, so we can keep going even
after BackgroundHangManager::Shutdown is called. */
const RefPtr<BackgroundHangManager> mManager;
// Unique thread ID for identification
const PRThread* mThreadID;
+ void Update();
+
public:
NS_INLINE_DECL_REFCOUNTING(BackgroundHangThread)
+ /**
+ * Returns the BackgroundHangThread associated with the
+ * running thread. Note that this will not find private
+ * BackgroundHangThread threads.
+ *
+ * @return BackgroundHangThread*, or nullptr if no thread
+ * is found.
+ */
static BackgroundHangThread* FindThread();
static void Startup()
{
/* We can tolerate init() failing. */
- (void)!sTlsKey.init();
+ sTlsKeyInitialized = sTlsKey.init();
}
// Hang timeout in ticks
const PRIntervalTime mTimeout;
// PermaHang timeout in ticks
const PRIntervalTime mMaxTimeout;
// Time at last activity
PRIntervalTime mInterval;
// Time when a hang started
PRIntervalTime mHangStart;
// Is the thread in a hang
bool mHanging;
// Is the thread in a waiting state
bool mWaiting;
+ // Is the thread dedicated to a single BackgroundHangMonitor
+ BackgroundHangMonitor::ThreadType mThreadType;
// Platform-specific helper to get hang stacks
ThreadStackHelper mStackHelper;
// Stack of current hang
Telemetry::HangStack mHangStack;
// Statistics for telemetry
Telemetry::ThreadHangStats mStats;
// Annotations for the current hang
UniquePtr<HangMonitor::HangAnnotations> mAnnotations;
// Annotators registered for this thread
HangMonitor::Observer::Annotators mAnnotators;
BackgroundHangThread(const char* aName,
uint32_t aTimeoutMs,
- uint32_t aMaxTimeoutMs);
+ uint32_t aMaxTimeoutMs,
+ BackgroundHangMonitor::ThreadType aThreadType = BackgroundHangMonitor::THREAD_SHARED);
// Report a hang; aManager->mLock IS locked
Telemetry::HangHistogram& ReportHang(PRIntervalTime aHangTime);
// Report a permanent hang; aManager->mLock IS locked
void ReportPermaHang();
// Called by BackgroundHangMonitor::NotifyActivity
- void NotifyActivity();
+ void NotifyActivity()
+ {
+ MonitorAutoLock autoLock(mManager->mLock);
+ Update();
+ }
// Called by BackgroundHangMonitor::NotifyWait
void NotifyWait()
{
- NotifyActivity();
+ MonitorAutoLock autoLock(mManager->mLock);
+ Update();
mWaiting = true;
}
+
+ // Returns true if this thread is (or might be) shared between other
+ // BackgroundHangMonitors for the monitored thread.
+ bool IsShared() {
+ return mThreadType == BackgroundHangMonitor::THREAD_SHARED;
+ }
};
StaticRefPtr<BackgroundHangManager> BackgroundHangManager::sInstance;
bool BackgroundHangManager::sDisabled = false;
MOZ_THREAD_LOCAL(BackgroundHangThread*) BackgroundHangThread::sTlsKey;
bool BackgroundHangThread::sTlsKeyInitialized;
@@ -342,32 +366,34 @@ BackgroundHangManager::RunMonitorThread(
while (!mHangThreads.isEmpty()) {
autoLock.Wait(PR_INTERVAL_NO_TIMEOUT);
}
}
BackgroundHangThread::BackgroundHangThread(const char* aName,
uint32_t aTimeoutMs,
- uint32_t aMaxTimeoutMs)
+ uint32_t aMaxTimeoutMs,
+ BackgroundHangMonitor::ThreadType aThreadType)
: mManager(BackgroundHangManager::sInstance)
, mThreadID(PR_GetCurrentThread())
, mTimeout(aTimeoutMs == BackgroundHangMonitor::kNoTimeout
? PR_INTERVAL_NO_TIMEOUT
: PR_MillisecondsToInterval(aTimeoutMs))
, mMaxTimeout(aMaxTimeoutMs == BackgroundHangMonitor::kNoTimeout
? PR_INTERVAL_NO_TIMEOUT
: PR_MillisecondsToInterval(aMaxTimeoutMs))
, mInterval(mManager->mIntervalNow)
, mHangStart(mInterval)
, mHanging(false)
, mWaiting(true)
+ , mThreadType(aThreadType)
, mStats(aName)
{
- if (sTlsKeyInitialized) {
+ if (sTlsKeyInitialized && IsShared()) {
sTlsKey.set(this);
}
// Lock here because LinkedList is not thread-safe
MonitorAutoLock autoLock(mManager->mLock);
// Add to thread list
mManager->mHangThreads.insertBack(this);
// Wake up monitor thread to process new thread
autoLock.Notify();
@@ -378,17 +404,17 @@ BackgroundHangThread::~BackgroundHangThr
// Lock here because LinkedList is not thread-safe
MonitorAutoLock autoLock(mManager->mLock);
// Remove from thread list
remove();
// Wake up monitor thread to process removed thread
autoLock.Notify();
// We no longer have a thread
- if (sTlsKeyInitialized) {
+ if (sTlsKeyInitialized && IsShared()) {
sTlsKey.set(nullptr);
}
// Move our copy of ThreadHangStats to Telemetry storage
Telemetry::RecordThreadHangStats(mStats);
}
Telemetry::HangHistogram&
@@ -447,17 +473,17 @@ BackgroundHangThread::ReportPermaHang()
Telemetry::HangHistogram& hang = ReportHang(mMaxTimeout);
Telemetry::HangStack& stack = hang.GetNativeStack();
if (stack.empty()) {
mStackHelper.GetNativeStack(stack);
}
}
MOZ_ALWAYS_INLINE void
-BackgroundHangThread::NotifyActivity()
+BackgroundHangThread::Update()
{
PRIntervalTime intervalNow = mManager->mIntervalNow;
if (mWaiting) {
mInterval = intervalNow;
mWaiting = false;
/* We have to wake up the manager thread because when all threads
are waiting, the manager thread waits indefinitely as well. */
mManager->Wakeup();
@@ -490,17 +516,17 @@ BackgroundHangThread::FindThread()
RefPtr<BackgroundHangManager> manager(BackgroundHangManager::sInstance);
MOZ_ASSERT(manager, "Creating BackgroundHangMonitor after shutdown");
PRThread* threadID = PR_GetCurrentThread();
// Lock thread list for traversal
MonitorAutoLock autoLock(manager->mLock);
for (BackgroundHangThread* thread = manager->mHangThreads.getFirst();
thread; thread = thread->getNext()) {
- if (thread->mThreadID == threadID) {
+ if (thread->mThreadID == threadID && !thread->IsShared()) {
return thread;
}
}
#endif
// Current thread is not initialized
return nullptr;
}
@@ -582,22 +608,24 @@ BackgroundHangMonitor::Shutdown()
BackgroundHangManager::sInstance = nullptr;
ThreadStackHelper::Shutdown();
BackgroundHangManager::sDisabled = true;
#endif
}
BackgroundHangMonitor::BackgroundHangMonitor(const char* aName,
uint32_t aTimeoutMs,
- uint32_t aMaxTimeoutMs)
- : mThread(BackgroundHangThread::FindThread())
+ uint32_t aMaxTimeoutMs,
+ ThreadType aThreadType)
+ : mThread(aThreadType == THREAD_SHARED ? BackgroundHangThread::FindThread() : nullptr)
{
#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
if (!BackgroundHangManager::sDisabled && !mThread) {
- mThread = new BackgroundHangThread(aName, aTimeoutMs, aMaxTimeoutMs);
+ mThread = new BackgroundHangThread(aName, aTimeoutMs, aMaxTimeoutMs,
+ aThreadType);
}
#endif
}
BackgroundHangMonitor::BackgroundHangMonitor()
: mThread(BackgroundHangThread::FindThread())
{
#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
--- a/xpcom/threads/BackgroundHangMonitor.h
+++ b/xpcom/threads/BackgroundHangMonitor.h
@@ -111,16 +111,26 @@ private:
RefPtr<BackgroundHangThread> mThread;
static bool ShouldDisableOnBeta(const nsCString &);
static bool DisableOnBeta();
public:
static const uint32_t kNoTimeout = 0;
+ enum ThreadType {
+ // For a new BackgroundHangMonitor for thread T, only create a new
+ // monitoring thread for T if onen doesn't already exist. If one does,
+ // share that pre-existing monitoring thread.
+ THREAD_SHARED,
+ // For a new BackgroundHangMonitor for thread T, create a new
+ // monitoring thread for T even if there are other, pre-existing
+ // monitoring threads for T.
+ THREAD_PRIVATE
+ };
/**
* ThreadHangStatsIterator is used to iterate through the ThreadHangStats
* associated with each active monitored thread. Because of an internal
* lock while this object is alive, a thread must use only one instance
* of this class at a time and must iterate through the list as fast as
* possible. The following example shows using the iterator:
*
@@ -175,20 +185,24 @@ public:
/**
* Start monitoring hangs for the current thread.
*
* @param aName Name to identify the thread with
* @param aTimeoutMs Amount of time in milliseconds without
* activity before registering a hang
* @param aMaxTimeoutMs Amount of time in milliseconds without
* activity before registering a permanent hang
+ * @param aThreadType
+ * The ThreadType type of monitoring thread that should be created
+ * for this monitor. See the documentation for ThreadType.
*/
BackgroundHangMonitor(const char* aName,
uint32_t aTimeoutMs,
- uint32_t aMaxTimeoutMs);
+ uint32_t aMaxTimeoutMs,
+ ThreadType aThreadType = THREAD_SHARED);
/**
* Monitor hangs using an existing monitor
* associated with the current thread.
*/
BackgroundHangMonitor();
/**