Bug 970307: Part 1: Report status of each thread to find when all the threads are idle. r=nfroyd
authorChih-Kai (Patrick) Wang <kk1fff@ckwang.info>
Thu, 18 Dec 2014 17:00:39 +0800
changeset 246214 b71ccef9c674a03a616630e1e2d93b66f63c83cb
parent 246213 119ce28762a9000109b4c0b96bb2e771a757232d
child 246215 adae9be2294d816d260a6c9e01f6a220fe7b25bc
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd
bugs970307
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 970307: Part 1: Report status of each thread to find when all the threads are idle. r=nfroyd
dom/indexedDB/TransactionThreadPool.cpp
dom/wifi/WifiProxyService.cpp
dom/workers/WorkerPrivate.cpp
netwerk/base/src/nsSocketTransportService2.cpp
widget/gonk/GonkMemoryPressureMonitoring.cpp
xpcom/glue/nsThreadUtils.cpp
xpcom/glue/nsThreadUtils.h
xpcom/threads/TimerThread.cpp
xpcom/threads/nsThread.cpp
xpcom/threads/nsThread.h
xpcom/threads/nsThreadManager.cpp
xpcom/threads/nsThreadManager.h
xpcom/threads/nsThreadPool.cpp
xpcom/threads/nsTimerImpl.cpp
--- a/dom/indexedDB/TransactionThreadPool.cpp
+++ b/dom/indexedDB/TransactionThreadPool.cpp
@@ -14,16 +14,17 @@
 #include "nsIEventTarget.h"
 #include "nsIRunnable.h"
 #include "nsISupportsPriority.h"
 #include "nsIThreadPool.h"
 #include "nsThreadUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsXPCOMCIDInternal.h"
 #include "ProfilerHelpers.h"
+#include "nsThread.h"
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 using mozilla::ipc::AssertIsOnBackgroundThread;
 
 namespace {
@@ -171,16 +172,20 @@ public:
   void Dispatch(nsIRunnable* aRunnable);
 
   void Finish(FinishCallback* aFinishCallback);
 
 private:
   ~TransactionQueue()
   { }
 
+#ifdef MOZ_NUWA_PROCESS
+  nsThread* mThread;
+#endif
+
   NS_DECL_NSIRUNNABLE
 };
 
 struct TransactionThreadPool::TransactionInfo MOZ_FINAL
 {
   uint64_t transactionId;
   nsCString databaseId;
   nsRefPtr<TransactionQueue> queue;
@@ -784,16 +789,19 @@ TransactionQueue::TransactionQueue(Trans
   , mOwningThreadPool(aThreadPool)
   , mTransactionId(aTransactionId)
   , mBackgroundChildLoggingId(aBackgroundChildLoggingId)
   , mLoggingSerialNumber(aLoggingSerialNumber)
   , mDatabaseId(aDatabaseId)
   , mObjectStoreNames(aObjectStoreNames)
   , mMode(aMode)
   , mShouldFinish(false)
+#ifdef MOZ_NUWA_PROCESS
+, mThread(nullptr)
+#endif
 {
   MOZ_ASSERT(aThreadPool);
   aThreadPool->AssertIsOnOwningThread();
 }
 
 void
 TransactionThreadPool::TransactionQueue::Unblock()
 {
@@ -809,16 +817,22 @@ void
 TransactionThreadPool::TransactionQueue::Dispatch(nsIRunnable* aRunnable)
 {
   MonitorAutoLock lock(mMonitor);
 
   NS_ASSERTION(!mShouldFinish, "Dispatch called after Finish!");
 
   mQueue.AppendElement(aRunnable);
 
+#ifdef MOZ_NUWA_PROCESS
+  if (mThread) {
+    mThread->SetWorking();
+  }
+#endif
+
   mMonitor.Notify();
 }
 
 void
 TransactionThreadPool::TransactionQueue::Finish(FinishCallback* aFinishCallback)
 {
   MonitorAutoLock lock(mMonitor);
 
@@ -844,23 +858,32 @@ TransactionThreadPool::TransactionQueue:
                  "Beginning database work",
                "IndexedDB %s: P T[%lld]: DB Start",
                IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
                mLoggingSerialNumber);
 
   nsAutoTArray<nsCOMPtr<nsIRunnable>, 10> queue;
   nsRefPtr<FinishCallback> finishCallback;
   bool shouldFinish = false;
+#ifdef MOZ_NUWA_PROCESS
+  mThread = static_cast<nsThread*>(NS_GetCurrentThread());
+  // Set ourself as working thread. We can reset later if we found
+  // our queue is empty.
+  mThread->SetWorking();
+#endif
 
   do {
     NS_ASSERTION(queue.IsEmpty(), "Should have cleared this!");
 
     {
       MonitorAutoLock lock(mMonitor);
       while (!mShouldFinish && mQueue.IsEmpty()) {
+#ifdef MOZ_NUWA_PROCESS
+        mThread->SetIdle();
+#endif
         if (NS_FAILED(mMonitor.Wait())) {
           NS_ERROR("Failed to wait!");
         }
       }
 
       mQueue.SwapElements(queue);
       if (mShouldFinish) {
         mFinishCallback.swap(finishCallback);
@@ -883,16 +906,20 @@ TransactionThreadPool::TransactionQueue:
       runnable = nullptr;
     }
 
     if (count) {
       queue.Clear();
     }
   } while (!shouldFinish);
 
+#ifdef MOZ_NUWA_PROCESS
+  mThread = nullptr;
+#endif
+
 #ifdef DEBUG
   if (kDEBUGThreadSleepMS) {
     MOZ_ALWAYS_TRUE(
       PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) == PR_SUCCESS);
   }
 #endif // DEBUG
 
   IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
--- a/dom/wifi/WifiProxyService.cpp
+++ b/dom/wifi/WifiProxyService.cpp
@@ -60,16 +60,19 @@ public:
     : mInterface(aInterface)
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
+#ifdef MOZ_NUWA_PROCESS
+    NS_SetIgnoreStatusOfCurrentThread();
+#endif
     nsAutoString event;
     gWpaSupplicant->WaitForEvent(event, mInterface);
     if (!event.IsEmpty()) {
 #ifdef MOZ_TASK_TRACER
       // Make wifi initialization events to be the source events of TaskTracer,
       // and originate the rest correlation tasks from here.
       AutoSourceEvent taskTracerEvent(SourceEventType::Wifi);
       AddLabel("%s %s", mInterface.get(), NS_ConvertUTF16toUTF8(event).get());
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -68,16 +68,17 @@
 #include "nsHostObjectProtocolHandler.h"
 #include "nsJSEnvironment.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
 #include "nsProxyRelease.h"
 #include "nsSandboxFlags.h"
 #include "prthread.h"
+#include "nsThread.h"
 #include "xpcpublic.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 #ifdef DEBUG
 #include "nsThreadManager.h"
@@ -4327,16 +4328,27 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
     }
 
     Status currentStatus;
     bool normalRunnablesPending = false;
 
     {
       MutexAutoLock lock(mMutex);
 
+#ifdef MOZ_NUWA_PROCESS
+      {
+        nsThread *thr = static_cast<nsThread*>(NS_GetCurrentThread());
+        ReentrantMonitorAutoEnter mon(thr->ThreadStatusMonitor());
+        if (mControlQueue.IsEmpty() &&
+            !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
+          thr->SetIdle();
+        }
+      }
+#endif // MOZ_NUWA_PROCESS
+
       while (mControlQueue.IsEmpty() &&
              !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
         WaitForWorkerEvents();
       }
 
       ProcessAllControlRunnablesLocked();
 
       currentStatus = mStatus;
--- a/netwerk/base/src/nsSocketTransportService2.cpp
+++ b/netwerk/base/src/nsSocketTransportService2.cpp
@@ -694,16 +694,17 @@ NS_IMETHODIMP
 nsSocketTransportService::Run()
 {
     PR_SetCurrentThreadName("Socket Thread");
 
 #ifdef MOZ_NUWA_PROCESS
     if (IsNuwaProcess()) {
         NuwaMarkCurrentThread(nullptr, nullptr);
     }
+    NS_SetIgnoreStatusOfCurrentThread();
 #endif
 
     SOCKET_LOG(("STS thread init\n"));
 
     psm::InitializeSSLServerCertVerificationThreads();
 
     gSocketThread = PR_GetCurrentThread();
 
--- a/widget/gonk/GonkMemoryPressureMonitoring.cpp
+++ b/widget/gonk/GonkMemoryPressureMonitoring.cpp
@@ -122,16 +122,18 @@ public:
 #ifdef MOZ_NUWA_PROCESS
     if (IsNuwaProcess()) {
       NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
                    "NuwaMarkCurrentThread is undefined!");
       NuwaMarkCurrentThread(nullptr, nullptr);
     }
 #endif
 
+    NS_SetIgnoreStatusOfCurrentThread();
+
     int lowMemFd = open("/sys/kernel/mm/lowmemkiller/notify_trigger_active",
                         O_RDONLY | O_CLOEXEC);
     NS_ENSURE_STATE(lowMemFd != -1);
     ScopedClose autoClose(lowMemFd);
 
     nsresult rv = CheckForMemoryPressure(lowMemFd, nullptr);
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/xpcom/glue/nsThreadUtils.cpp
+++ b/xpcom/glue/nsThreadUtils.cpp
@@ -370,8 +370,16 @@ nsAutoLowPriorityIO::~nsAutoLowPriorityI
   }
 #elif defined(XP_MACOSX)
   if (MOZ_LIKELY(lowIOPrioritySet)) {
     setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, oldPriority);
   }
 #endif
 }
 
+#ifdef MOZ_NUWA_PROCESS
+#ifdef MOZILLA_INTERNAL_API
+void
+NS_SetIgnoreStatusOfCurrentThread() {
+  nsThreadManager::get()->SetIgnoreThreadStatus();
+}
+#endif // MOZILLA_INTERNAL_API
+#endif // MOZ_NUWA_PROCESS
--- a/xpcom/glue/nsThreadUtils.h
+++ b/xpcom/glue/nsThreadUtils.h
@@ -560,9 +560,24 @@ private:
 #if defined(XP_MACOSX)
   int oldPriority;
 #endif
 };
 
 void
 NS_SetMainThread();
 
+/**
+ * Helpers for thread to report their status when compiled with Nuwa.
+ */
+#ifdef MOZILLA_INTERNAL_API
+#ifdef MOZ_NUWA_PROCESS
+extern void
+NS_SetIgnoreStatusOfCurrentThread();
+#else // MOZ_NUWA_PROCESS
+inline void
+NS_SetIgnoreStatusOfCurrentThread()
+{
+}
+#endif // MOZ_NUWA_PROCESS
+#endif // MOZILLA_INTERNAL_API
+
 #endif  // nsThreadUtils_h__
--- a/xpcom/threads/TimerThread.cpp
+++ b/xpcom/threads/TimerThread.cpp
@@ -200,16 +200,17 @@ TimerThread::Run()
   PR_SetCurrentThreadName("Timer");
 
 #ifdef MOZ_NUWA_PROCESS
   if (IsNuwaProcess()) {
     NuwaMarkCurrentThread(nullptr, nullptr);
   }
 #endif
 
+  NS_SetIgnoreStatusOfCurrentThread();
   MonitorAutoLock lock(mMonitor);
 
   // We need to know how many microseconds give a positive PRIntervalTime. This
   // is platform-dependent and we calculate it at runtime, finding a value |v|
   // such that |PR_MicrosecondsToInterval(v) > 0| and then binary-searching in
   // the range [0, v) to find the ms-to-interval scale.
   uint32_t usForPosInterval = 1;
   while (PR_MicrosecondsToInterval(usForPosInterval) == 0) {
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -8,17 +8,16 @@
 
 #include "base/message_loop.h"
 
 // Chromium's logging can sometimes leak through...
 #ifdef LOG
 #undef LOG
 #endif
 
-#include "mozilla/ReentrantMonitor.h"
 #include "nsMemoryPressure.h"
 #include "nsThreadManager.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "pratom.h"
 #include "prlog.h"
@@ -327,16 +326,20 @@ nsThread::ThreadFunc(void* aArg)
   using mozilla::ipc::BackgroundChild;
 
   nsThread* self = static_cast<nsThread*>(aArg);  // strong reference
   self->mThread = PR_GetCurrentThread();
   SetupCurrentThreadForChaosMode();
 
   // Inform the ThreadManager
   nsThreadManager::get()->RegisterCurrentThread(self);
+#ifdef MOZ_NUWA_PROCESS
+  self->mThreadStatusInfo =
+    static_cast<void*>(nsThreadManager::get()->GetCurrentThreadStatusInfo());
+#endif
 
   mozilla::IOInterposer::RegisterCurrentThread();
 
   // Wait for and process startup event
   nsCOMPtr<nsIRunnable> event;
   if (!self->GetEvent(true, getter_AddRefs(event))) {
     NS_WARNING("failed waiting for thread startup event");
     return;
@@ -434,16 +437,20 @@ nsThread::nsThread(MainThreadFlag aMainT
   , mPriority(PRIORITY_NORMAL)
   , mThread(nullptr)
   , mRunningEvent(0)
   , mStackSize(aStackSize)
   , mShutdownContext(nullptr)
   , mShutdownRequired(false)
   , mEventsAreDoomed(false)
   , mIsMainThread(aMainThread)
+#ifdef MOZ_NUWA_PROCESS
+  , mThreadStatusMonitor("nsThread.mThreadStatusLock")
+  , mThreadStatusInfo(nullptr)
+#endif
 {
 }
 
 nsThread::~nsThread()
 {
 }
 
 nsresult
@@ -481,32 +488,45 @@ nsThread::Init()
 
 nsresult
 nsThread::InitCurrentThread()
 {
   mThread = PR_GetCurrentThread();
   SetupCurrentThreadForChaosMode();
 
   nsThreadManager::get()->RegisterCurrentThread(this);
+#ifdef MOZ_NUWA_PROCESS
+  mThreadStatusInfo =
+    static_cast<void*>(nsThreadManager::get()->GetCurrentThreadStatusInfo());
+#endif
+
   return NS_OK;
 }
 
 nsresult
 nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget)
 {
   nsCOMPtr<nsIThreadObserver> obs;
 
   {
     MutexAutoLock lock(mLock);
     nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot;
     if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
       NS_WARNING("An event was posted to a thread that will never run it (rejected)");
       return NS_ERROR_UNEXPECTED;
     }
-    queue->PutEvent(aEvent);
+#ifdef MOZ_NUWA_PROCESS
+    {
+      ReentrantMonitorAutoEnter mon(mThreadStatusMonitor);
+      SetWorking();
+#endif // MOZ_NUWA_PROCESS
+      queue->PutEvent(aEvent);
+#ifdef MOZ_NUWA_PROCESS
+    }
+#endif // MOZ_NUWA_PROCESS
 
     // Make sure to grab the observer before dropping the lock, otherwise the
     // event that we just placed into the queue could run and eventually delete
     // this nsThread before the calling thread is scheduled again. We would then
     // crash while trying to access a dead nsThread.
     obs = mObserver;
   }
 
@@ -837,16 +857,37 @@ nsThread::ProcessNextEvent(bool aMayWait
       MOZ_ASSERT(ShuttingDown(),
                  "This should only happen when shutting down");
       rv = NS_ERROR_UNEXPECTED;
     }
   }
 
   --mRunningEvent;
 
+#ifdef MOZ_NUWA_PROCESS
+  nsCOMPtr<nsIRunnable> notifyAllIdleRunnable;
+  {
+    ReentrantMonitorAutoEnter mon(mThreadStatusMonitor);
+    if ((!mEvents->GetEvent(false, nullptr)) && (mRunningEvent == 0)) {
+      nsThreadManager::get()->SetThreadIsWorking(
+        static_cast<nsThreadManager::ThreadStatusInfo*>(mThreadStatusInfo),
+        false, getter_AddRefs(notifyAllIdleRunnable));
+    }
+  }
+  if (notifyAllIdleRunnable) {
+    // Dispatching a task leads us to acquire |mLock| of the thread. If we
+    // dispatch to main thread while holding main thread's
+    // |mThreadStatusMonitor|, deadlock could happen if other thread is
+    // blocked by main thread's |mThreadStatusMonitor| and is holding
+    // main thread's |mLock|.
+    Dispatch(notifyAllIdleRunnable, NS_DISPATCH_NORMAL);
+    nsThreadManager::get()->ResetIsDispatchingToMainThread();
+  }
+#endif // MOZ_NUWA_PROCESS
+
   NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent,
                          (this, mRunningEvent, *aResult));
 
   if (obs) {
     obs->AfterProcessNextEvent(this, mRunningEvent, *aResult);
   }
 
   if (notifyMainThreadObserver && sMainThreadObserver) {
@@ -1046,16 +1087,34 @@ nsThread::SetMainThreadObserver(nsIThrea
   if (!NS_IsMainThread()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsThread::sMainThreadObserver = aObserver;
   return NS_OK;
 }
 
+#ifdef MOZ_NUWA_PROCESS
+void
+nsThread::SetWorking()
+{
+  nsThreadManager::get()->SetThreadIsWorking(
+    static_cast<nsThreadManager::ThreadStatusInfo*>(mThreadStatusInfo),
+    true, nullptr);
+}
+
+void
+nsThread::SetIdle()
+{
+  nsThreadManager::get()->SetThreadIsWorking(
+    static_cast<nsThreadManager::ThreadStatusInfo*>(mThreadStatusInfo),
+    false, nullptr);
+}
+#endif
+
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsThreadSyncDispatch::Run()
 {
   if (mSyncTask) {
     mResult = mSyncTask->Run();
     mSyncTask = nullptr;
--- a/xpcom/threads/nsThread.h
+++ b/xpcom/threads/nsThread.h
@@ -11,16 +11,17 @@
 #include "nsIThreadInternal.h"
 #include "nsISupportsPriority.h"
 #include "nsEventQueue.h"
 #include "nsThreadUtils.h"
 #include "nsString.h"
 #include "nsTObserverArray.h"
 #include "mozilla/Attributes.h"
 #include "nsAutoPtr.h"
+#include "mozilla/ReentrantMonitor.h"
 
 // A native thread
 class nsThread
   : public nsIThreadInternal
   , public nsISupportsPriority
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
@@ -60,16 +61,24 @@ public:
   void ClearObservers()
   {
     mEventObservers.Clear();
   }
 
   static nsresult
   SetMainThreadObserver(nsIThreadObserver* aObserver);
 
+#ifdef MOZ_NUWA_PROCESS
+  void SetWorking();
+  void SetIdle();
+  mozilla::ReentrantMonitor& ThreadStatusMonitor() {
+    return mThreadStatusMonitor;
+  }
+#endif
+
 protected:
   static nsIThreadObserver* sMainThreadObserver;
 
   class nsChainedEventQueue;
 
   class nsNestedEventTarget;
   friend class nsNestedEventTarget;
 
@@ -177,16 +186,22 @@ protected:
   uint32_t  mStackSize;
 
   struct nsThreadShutdownContext* mShutdownContext;
 
   bool mShutdownRequired;
   // Set to true when events posted to this thread will never run.
   bool mEventsAreDoomed;
   MainThreadFlag mIsMainThread;
+#ifdef MOZ_NUWA_PROCESS
+  mozilla::ReentrantMonitor mThreadStatusMonitor;
+  // The actual type is defined in nsThreadManager.h which is not exposed to
+  // file out of thread module.
+  void* mThreadStatusInfo;
+#endif
 };
 
 //-----------------------------------------------------------------------------
 
 class nsThreadSyncDispatch : public nsRunnable
 {
 public:
   nsThreadSyncDispatch(nsIThread* aOrigin, nsIRunnable* aTask)
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -6,16 +6,17 @@
 
 #include "nsThreadManager.h"
 #include "nsThread.h"
 #include "nsThreadUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "mozilla/ThreadLocal.h"
+#include "mozilla/ReentrantMonitor.h"
 #ifdef MOZ_CANARY
 #include <fcntl.h>
 #include <unistd.h>
 #endif
 
 using namespace mozilla;
 
 #ifdef XP_WIN
@@ -42,24 +43,81 @@ NS_SetMainThread()
     }
     sTLSIsMainThread.set(true);
   }
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 typedef nsTArray<nsRefPtr<nsThread>> nsThreadArray;
 
+#ifdef MOZ_NUWA_PROCESS
+class NotifyAllThreadsWereIdle: public nsRunnable
+{
+public:
+
+  NotifyAllThreadsWereIdle(
+    nsTArray<nsRefPtr<nsThreadManager::AllThreadsWereIdleListener>>* aListeners)
+    : mListeners(aListeners)
+  {
+  }
+
+  virtual NS_IMETHODIMP
+  Run() {
+    // Copy listener array, which may be modified during call back.
+    nsTArray<nsRefPtr<nsThreadManager::AllThreadsWereIdleListener>> arr(*mListeners);
+    for (size_t i = 0; i < arr.Length(); i++) {
+      arr[i]->OnAllThreadsWereIdle();
+    }
+    return NS_OK;
+  }
+
+private:
+  // Raw pointer, since it's pointing to a  member of thread manager.
+  nsTArray<nsRefPtr<nsThreadManager::AllThreadsWereIdleListener>>* mListeners;
+};
+
+struct nsThreadManager::ThreadStatusInfo {
+  Atomic<bool> mWorking;
+  Atomic<bool> mWillBeWorking;
+  bool mIgnored;
+  ThreadStatusInfo()
+    : mWorking(false)
+    , mWillBeWorking(false)
+    , mIgnored(false)
+  {
+  }
+};
+#endif // MOZ_NUWA_PROCESS
+
 //-----------------------------------------------------------------------------
 
 static void
 ReleaseObject(void* aData)
 {
   static_cast<nsISupports*>(aData)->Release();
 }
 
+#ifdef MOZ_NUWA_PROCESS
+void
+nsThreadManager::DeleteThreadStatusInfo(void* aData)
+{
+  nsThreadManager* mgr = nsThreadManager::get();
+  nsThreadManager::ThreadStatusInfo* thrInfo =
+    static_cast<nsThreadManager::ThreadStatusInfo*>(aData);
+  {
+    ReentrantMonitorAutoEnter mon(*(mgr->mMonitor));
+    mgr->mThreadStatusInfos.RemoveElement(thrInfo);
+    if (NS_IsMainThread()) {
+      mgr->mMainThreadStatusInfo = nullptr;
+    }
+  }
+  delete thrInfo;
+}
+#endif
+
 static PLDHashOperator
 AppendAndRemoveThread(PRThread* aKey, nsRefPtr<nsThread>& aThread, void* aArg)
 {
   nsThreadArray* threads = static_cast<nsThreadArray*>(aArg);
   threads->AppendElement(aThread);
   return PL_DHASH_REMOVE;
 }
 
@@ -91,17 +149,28 @@ nsThreadManager::Init()
   if (mInitialized) {
     return NS_OK;
   }
 
   if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE) {
     return NS_ERROR_FAILURE;
   }
 
+#ifdef MOZ_NUWA_PROCESS
+  if (PR_NewThreadPrivateIndex(
+      &mThreadStatusInfoIndex,
+      nsThreadManager::DeleteThreadStatusInfo) == PR_FAILURE) {
+    return NS_ERROR_FAILURE;
+  }
+#endif // MOZ_NUWA_PROCESS
+
   mLock = new Mutex("nsThreadManager.mLock");
+#ifdef MOZ_NUWA_PROCESS
+  mMonitor = MakeUnique<ReentrantMonitor>("nsThreadManager.mMonitor");
+#endif // MOZ_NUWA_PROCESS
 
 #ifdef MOZ_CANARY
   const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
   const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
   char* env_var_flag = getenv("MOZ_KILL_CANARIES");
   sCanaryOutputFD =
     env_var_flag ? (env_var_flag[0] ? open(env_var_flag, flags, mode) :
                                       STDERR_FILENO) :
@@ -189,16 +258,19 @@ nsThreadManager::Shutdown()
   mMainThread->ClearObservers();
 
   // Release main thread object.
   mMainThread = nullptr;
   mLock = nullptr;
 
   // Remove the TLS entry for the main thread.
   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
+#ifdef MOZ_NUWA_PROCESS
+  PR_SetThreadPrivate(mThreadStatusInfoIndex, nullptr);
+#endif
 }
 
 void
 nsThreadManager::RegisterCurrentThread(nsThread* aThread)
 {
   MOZ_ASSERT(aThread->GetPRThread() == PR_GetCurrentThread(), "bad aThread");
 
   MutexAutoLock lock(*mLock);
@@ -221,16 +293,19 @@ nsThreadManager::UnregisterCurrentThread
 
   MutexAutoLock lock(*mLock);
 
   --mCurrentNumberOfThreads;
   mThreadsByPRThread.Remove(aThread->GetPRThread());
 
   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
   // Ref-count balanced via ReleaseObject
+#ifdef MOZ_NUWA_PROCESS
+  PR_SetThreadPrivate(mThreadStatusInfoIndex, nullptr);
+#endif
 }
 
 nsThread*
 nsThreadManager::GetCurrentThread()
 {
   // read thread local storage
   void* data = PR_GetThreadPrivate(mCurThreadIndex);
   if (data) {
@@ -245,16 +320,37 @@ nsThreadManager::GetCurrentThread()
   nsRefPtr<nsThread> thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0);
   if (!thread || NS_FAILED(thread->InitCurrentThread())) {
     return nullptr;
   }
 
   return thread.get();  // reference held in TLS
 }
 
+#ifdef MOZ_NUWA_PROCESS
+nsThreadManager::ThreadStatusInfo*
+nsThreadManager::GetCurrentThreadStatusInfo()
+{
+  void* data = PR_GetThreadPrivate(mThreadStatusInfoIndex);
+  if (!data) {
+    ThreadStatusInfo *thrInfo = new ThreadStatusInfo();
+    PR_SetThreadPrivate(mThreadStatusInfoIndex, thrInfo);
+    data = thrInfo;
+
+    ReentrantMonitorAutoEnter mon(*mMonitor);
+    mThreadStatusInfos.AppendElement(thrInfo);
+    if (NS_IsMainThread()) {
+      mMainThreadStatusInfo = thrInfo;
+    }
+  }
+
+  return static_cast<ThreadStatusInfo*>(data);
+}
+#endif
+
 NS_IMETHODIMP
 nsThreadManager::NewThread(uint32_t aCreationFlags,
                            uint32_t aStackSize,
                            nsIThread** aResult)
 {
   // No new threads during Shutdown
   if (NS_WARN_IF(!mInitialized)) {
     return NS_ERROR_NOT_INITIALIZED;
@@ -337,8 +433,162 @@ nsThreadManager::GetIsMainThread(bool* a
 }
 
 uint32_t
 nsThreadManager::GetHighestNumberOfThreads()
 {
   MutexAutoLock lock(*mLock);
   return mHighestNumberOfThreads;
 }
+
+#ifdef MOZ_NUWA_PROCESS
+void
+nsThreadManager::SetIgnoreThreadStatus()
+{
+  GetCurrentThreadStatusInfo()->mIgnored = true;
+}
+
+void
+nsThreadManager::SetThreadIdle(nsIRunnable **aReturnRunnable)
+{
+  SetThreadIsWorking(GetCurrentThreadStatusInfo(), false, aReturnRunnable);
+}
+
+void
+nsThreadManager::SetThreadWorking()
+{
+  SetThreadIsWorking(GetCurrentThreadStatusInfo(), true, nullptr);
+}
+
+void
+nsThreadManager::SetThreadIsWorking(ThreadStatusInfo* aInfo,
+                                    bool aIsWorking,
+                                    nsIRunnable **aReturnRunnable)
+{
+  aInfo->mWillBeWorking = aIsWorking;
+  if (mThreadsIdledListeners.Length() > 0) {
+
+    // A race condition occurs since we don't want threads to try to enter the
+    // monitor (nsThreadManager::mMonitor) when no one cares about their status.
+    // And thus the race can happen when we put the first listener into
+    // |mThreadsIdledListeners|:
+    //
+    // (1) Thread A wants to dispatch a task to Thread B.
+    // (2) Thread A checks |mThreadsIdledListeners|, and nothing is in the
+    //     list. So Thread A decides not to enter |mMonitor| when updating B's
+    //     status.
+    // (3) Thread A is suspended just before it changed status of B.
+    // (4) A listener is added to |mThreadsIdledListeners|
+    // (5) Now is Thread C's turn to run. Thread C finds there's something in
+    //     |mThreadsIdledListeners|, so it enters |mMonitor| and check all
+    //     thread info structs in |mThreadStatusInfos| while A is in the middle
+    //     of changing B's status.
+    //
+    // Then C may find Thread B is an idle thread (which is not correct, because
+    // A attempted to change B's status prior to C starting to walk throught
+    // |mThreadStatusInfo|), but the fact that thread A is working (thread A
+    // hasn't finished dispatching a task to thread B) can prevent thread C from
+    // firing a bogus notification.
+    //
+    // If the state transition that happens outside the monitor is in the other
+    // direction, the race condition could be:
+    //
+    // (1) Thread D has just finished its jobs and wants to set its status to idle.
+    // (2) Thread D checks |mThreadsIdledListeners|, and nothing is in the list.
+    //     So Thread D decides not to enter |mMonitor|.
+    // (3) Thread D is is suspended before it updates its own status.
+    // (4) A listener is put into |mThreadsIdledListeners|.
+    // (5) Thread C wants to changes status of itself. It checks
+    //     |mThreadsIdledListeners| and finds something inside the list. Thread C
+    //     then enters |mMonitor|, updates its status and checks thread info in
+    //     |mThreadStatusInfos| while D is changing status of itself out of monitor.
+    //
+    // Thread C will find that thread D is working (D actually wants to change its
+    // status to idle before C starting to check), then C returns without firing
+    // any notification. Finding that thread D is working can make our checking
+    // mechanism miss a chance to fire a notification: because thread D thought
+    // there's nothing in |mThreadsIdledListeners| and thus won't check the
+    // |mThreadStatusInfos| after changing the status of itself.
+    //
+    // |mWillBeWorking| can be used to address this problem. We require each
+    // thread to put the value that is going to be set to |mWorking| to
+    // |mWillBeWorking| before the thread decide whether it should enter
+    // |mMonitor| to change status or not. Thus C finds that D is working while
+    // D's |mWillBeWorking| is false, and C realizes that D is just updating and
+    // can treat D as an idle thread.
+    //
+    // It doesn't matter whether D will check thread status after changing its
+    // own status or not. If D checks, which means D will enter the monitor
+    // before updating status, thus D must be blocked until C has finished
+    // dispatching the notification task to main thread, and D will find that main
+    // thread is working and will not fire an additional event. On the other hand,
+    // if D doesn't check |mThreadStatusInfos|, it's still ok, because C has
+    // treated D as an idle thread already.
+
+    bool hasWorkingThread = false;
+    nsRefPtr<NotifyAllThreadsWereIdle> runnable;
+    {
+      ReentrantMonitorAutoEnter mon(*mMonitor);
+      // Get data structure of thread info.
+      aInfo->mWorking = aIsWorking;
+      if (aIsWorking) {
+        // We are working, so there's no need to check futher.
+        return;
+      }
+
+      for (size_t i = 0; i < mThreadStatusInfos.Length(); i++) {
+        ThreadStatusInfo *info = mThreadStatusInfos[i];
+        if (!info->mIgnored) {
+          if (info->mWorking) {
+            if (info->mWillBeWorking) {
+              hasWorkingThread = true;
+              break;
+            }
+          }
+        }
+      }
+      if (!hasWorkingThread && !mDispatchingToMainThread) {
+        runnable = new NotifyAllThreadsWereIdle(&mThreadsIdledListeners);
+        mDispatchingToMainThread = true;
+      }
+    }
+
+    if (runnable) {
+      if (NS_IsMainThread()) {
+        // We are holding the main thread's |nsThread::mThreadStatusMonitor|.
+        // If we dispatch a task to ourself, then we are in danger of causing
+        // deadlock. Instead, return the task, and let the caller dispatch it
+        // for us.
+        MOZ_ASSERT(aReturnRunnable,
+                   "aReturnRunnable must be provided on main thread");
+        runnable.forget(aReturnRunnable);
+      } else {
+        NS_DispatchToMainThread(runnable);
+        ResetIsDispatchingToMainThread();
+      }
+    }
+  } else {
+    // Update thread info without holding any lock.
+    aInfo->mWorking = aIsWorking;
+  }
+}
+
+void
+nsThreadManager::ResetIsDispatchingToMainThread()
+{
+  ReentrantMonitorAutoEnter mon(*mMonitor);
+  mDispatchingToMainThread = false;
+}
+
+void
+nsThreadManager::AddAllThreadsWereIdleListener(AllThreadsWereIdleListener *listener)
+{
+  MOZ_ASSERT(GetCurrentThreadStatusInfo()->mWorking);
+  mThreadsIdledListeners.AppendElement(listener);
+}
+
+void
+nsThreadManager::RemoveAllThreadsWereIdleListener(AllThreadsWereIdleListener *listener)
+{
+  mThreadsIdledListeners.RemoveElement(listener);
+}
+
+#endif // MOZ_NUWA_PROCESS
--- a/xpcom/threads/nsThreadManager.h
+++ b/xpcom/threads/nsThreadManager.h
@@ -9,19 +9,36 @@
 
 #include "mozilla/Mutex.h"
 #include "nsIThreadManager.h"
 #include "nsRefPtrHashtable.h"
 #include "nsThread.h"
 
 class nsIRunnable;
 
+namespace mozilla {
+class ReentrantMonitor;
+}
+
 class nsThreadManager : public nsIThreadManager
 {
 public:
+#ifdef MOZ_NUWA_PROCESS
+  struct ThreadStatusInfo;
+  class AllThreadsWereIdleListener {
+  public:
+    NS_INLINE_DECL_REFCOUNTING(AllThreadsWereIdleListener);
+    virtual void OnAllThreadsWereIdle() = 0;
+  protected:
+    virtual ~AllThreadsWereIdleListener()
+    {
+    }
+  };
+#endif // MOZ_NUWA_PROCESS
+
   NS_DECL_ISUPPORTS
   NS_DECL_NSITHREADMANAGER
 
   static nsThreadManager* get()
   {
     static nsThreadManager sInstance;
     return &sInstance;
   }
@@ -49,40 +66,83 @@ public:
   uint32_t GetHighestNumberOfThreads();
 
   // This needs to be public in order to support static instantiation of this
   // class with older compilers (e.g., egcs-2.91.66).
   ~nsThreadManager()
   {
   }
 
+#ifdef MOZ_NUWA_PROCESS
+  void SetIgnoreThreadStatus();
+
+  // |SetThreadWorking| and |SetThreadIdle| set status of thread that is
+  // currently running. They get thread status information from TLS and pass
+  // the information to |SetThreadIsWorking|.
+  void SetThreadIdle(nsIRunnable** aReturnRunnable);
+  void SetThreadWorking();
+
+  // |SetThreadIsWorking| is where is status actually changed. Thread status
+  // information is passed as a argument so caller must obtain the structure
+  // by itself. If this method is invoked on main thread, |aReturnRunnable|
+  // should be provided to receive the runnable of notifying listeners.
+  // |ResetIsDispatchingToMainThread| should be invoked after caller on main
+  // thread dispatched the task to main thread's queue.
+  void SetThreadIsWorking(ThreadStatusInfo* aInfo,
+                          bool aIsWorking,
+                          nsIRunnable** aReturnRunnable);
+  void ResetIsDispatchingToMainThread();
+
+  void AddAllThreadsWereIdleListener(AllThreadsWereIdleListener *listener);
+  void RemoveAllThreadsWereIdleListener(AllThreadsWereIdleListener *listener);
+  ThreadStatusInfo* GetCurrentThreadStatusInfo();
+#endif // MOZ_NUWA_PROCESS
+
 private:
   nsThreadManager()
     : mCurThreadIndex(0)
     , mMainPRThread(nullptr)
     , mLock(nullptr)
     , mInitialized(false)
     , mCurrentNumberOfThreads(1)
     , mHighestNumberOfThreads(1)
+#ifdef MOZ_NUWA_PROCESS
+    , mMonitor(nullptr)
+    , mMainThreadStatusInfo(nullptr)
+    , mDispatchingToMainThread(nullptr)
+#endif
   {
   }
 
   nsRefPtrHashtable<nsPtrHashKey<PRThread>, nsThread> mThreadsByPRThread;
   unsigned             mCurThreadIndex;  // thread-local-storage index
   nsRefPtr<nsThread>  mMainThread;
   PRThread*           mMainPRThread;
   // This is a pointer in order to allow creating nsThreadManager from
   // the static context in debug builds.
   nsAutoPtr<mozilla::Mutex> mLock;  // protects tables
   bool                mInitialized;
 
   // The current number of threads
   uint32_t            mCurrentNumberOfThreads;
   // The highest number of threads encountered so far during the session
   uint32_t            mHighestNumberOfThreads;
+
+#ifdef MOZ_NUWA_PROCESS
+  static void DeleteThreadStatusInfo(void *aData);
+  unsigned mThreadStatusInfoIndex;
+  nsTArray<nsRefPtr<AllThreadsWereIdleListener>> mThreadsIdledListeners;
+  nsTArray<ThreadStatusInfo*> mThreadStatusInfos;
+  mozilla::UniquePtr<mozilla::ReentrantMonitor> mMonitor;
+  ThreadStatusInfo* mMainThreadStatusInfo;
+  // |mDispatchingToMainThread| is set when all thread are found to be idle
+  // before task of notifying all listeners are dispatched to main thread.
+  // The flag is protected by |mMonitor|.
+  bool mDispatchingToMainThread;
+#endif // MOZ_NUWA_PROCESS
 };
 
 #define NS_THREADMANAGER_CID                       \
 { /* 7a4204c6-e45a-4c37-8ebb-6709a22c917c */       \
   0x7a4204c6,                                      \
   0xe45a,                                          \
   0x4c37,                                          \
   {0x8e, 0xbb, 0x67, 0x09, 0xa2, 0x2c, 0x91, 0x7c} \
--- a/xpcom/threads/nsThreadPool.cpp
+++ b/xpcom/threads/nsThreadPool.cpp
@@ -145,17 +145,16 @@ nsThreadPool::ShutdownThread(nsIThread* 
   nsRefPtr<nsIRunnable> r = NS_NewRunnableMethod(aThread, &nsIThread::Shutdown);
   NS_DispatchToMainThread(r);
 }
 
 NS_IMETHODIMP
 nsThreadPool::Run()
 {
   LOG(("THRD-P(%p) enter\n", this));
-
   mThreadNaming.SetThreadPoolName(mName);
 
   nsCOMPtr<nsIThread> current;
   nsThreadManager::get()->GetCurrentThread(getter_AddRefs(current));
 
   bool shutdownThreadOnExit = false;
   bool exitThread = false;
   bool wasIdle = false;
@@ -203,25 +202,31 @@ nsThreadPool::Run()
         if (exitThread) {
           if (wasIdle) {
             --mIdleCount;
           }
           shutdownThreadOnExit = mThreads.RemoveObject(current);
         } else {
           PRIntervalTime delta = timeout - (now - idleSince);
           LOG(("THRD-P(%p) waiting [%d]\n", this, delta));
+#ifdef MOZ_NUWA_PROCESS
+          nsThreadManager::get()->SetThreadIdle(nullptr);
+#endif // MOZ_NUWA_PROCESS
           mon.Wait(delta);
         }
       } else if (wasIdle) {
         wasIdle = false;
         --mIdleCount;
       }
     }
     if (event) {
       LOG(("THRD-P(%p) running [%p]\n", this, event.get()));
+#ifdef MOZ_NUWA_PROCESS
+      nsThreadManager::get()->SetThreadWorking();
+#endif // MOZ_NUWA_PROCESS
       event->Run();
     }
   } while (!exitThread);
 
   if (listener) {
     listener->OnThreadShuttingDown();
   }
 
--- a/xpcom/threads/nsTimerImpl.cpp
+++ b/xpcom/threads/nsTimerImpl.cpp
@@ -554,16 +554,23 @@ nsTimerImpl::SetTarget(nsIEventTarget* a
 
 void
 nsTimerImpl::Fire()
 {
   if (mCanceled) {
     return;
   }
 
+#ifdef MOZ_NUWA_PROCESS
+  if (IsNuwaProcess() && IsNuwaReady()) {
+    // A timer event fired after Nuwa frozen can freeze main thread.
+    return;
+  }
+#endif
+
   PROFILER_LABEL("Timer", "Fire",
                  js::ProfileEntry::Category::OTHER);
 
 #ifdef MOZ_TASK_TRACER
   // mTracedTask is an instance of FakeTracedTask created by
   // DispatchTracedTask(). AutoRunFakeTracedTask logs the begin/end time of the
   // timer/FakeTracedTask instance in ctor/dtor.
   mozilla::tasktracer::AutoRunFakeTracedTask runTracedTask(mTracedTask);