Bug 1310880 - Allow a BackgroundHangMonitor to have its own private BackgroundHangThread. r?jchen draft
authorMike Conley <mconley@mozilla.com>
Wed, 19 Oct 2016 14:32:43 -0400
changeset 427126 60a3edb5d289d4d1d23e84e6bc4fa75fbb8a3e90
parent 427097 a98896d3d39a5e31a370ccfda018d9d65ce4856f
child 427127 3bba3b7d683a77d7468a32162d4d72522e73e64d
push id32932
push usermconley@mozilla.com
push dateWed, 19 Oct 2016 20:17:24 +0000
reviewersjchen
bugs1310880
milestone52.0a1
Bug 1310880 - Allow a BackgroundHangMonitor to have its own private BackgroundHangThread. r?jchen MozReview-Commit-ID: L32E19FVhv
xpcom/threads/BackgroundHangMonitor.cpp
xpcom/threads/BackgroundHangMonitor.h
--- 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();
 
   /**