LCOV - code coverage report
Current view: top level - xpcom/threads - nsThread.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 461 618 74.6 %
Date: 2017-07-14 16:53:18 Functions: 76 104 73.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsThread.h"
       8             : 
       9             : #include "base/message_loop.h"
      10             : 
      11             : // Chromium's logging can sometimes leak through...
      12             : #ifdef LOG
      13             : #undef LOG
      14             : #endif
      15             : 
      16             : #include "mozilla/ReentrantMonitor.h"
      17             : #include "nsMemoryPressure.h"
      18             : #include "nsThreadManager.h"
      19             : #include "nsIClassInfoImpl.h"
      20             : #include "nsAutoPtr.h"
      21             : #include "nsCOMPtr.h"
      22             : #include "nsQueryObject.h"
      23             : #include "pratom.h"
      24             : #include "mozilla/CycleCollectedJSContext.h"
      25             : #include "mozilla/Logging.h"
      26             : #include "nsIObserverService.h"
      27             : #include "mozilla/HangMonitor.h"
      28             : #include "mozilla/IOInterposer.h"
      29             : #include "mozilla/ipc/MessageChannel.h"
      30             : #include "mozilla/ipc/BackgroundChild.h"
      31             : #include "mozilla/SchedulerGroup.h"
      32             : #include "mozilla/Services.h"
      33             : #include "nsXPCOMPrivate.h"
      34             : #include "mozilla/ChaosMode.h"
      35             : #include "mozilla/Telemetry.h"
      36             : #include "mozilla/TimeStamp.h"
      37             : #include "mozilla/Unused.h"
      38             : #include "mozilla/dom/ScriptSettings.h"
      39             : #include "nsIIdlePeriod.h"
      40             : #include "nsIIdleRunnable.h"
      41             : #include "nsThreadSyncDispatch.h"
      42             : #include "LeakRefPtr.h"
      43             : #include "GeckoProfiler.h"
      44             : 
      45             : #ifdef MOZ_CRASHREPORTER
      46             : #include "nsServiceManagerUtils.h"
      47             : #include "nsICrashReporter.h"
      48             : #include "mozilla/dom/ContentChild.h"
      49             : #endif
      50             : 
      51             : #ifdef XP_LINUX
      52             : #include <sys/time.h>
      53             : #include <sys/resource.h>
      54             : #include <sched.h>
      55             : #endif
      56             : 
      57             : #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 ||                 \
      58             :                       _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) &&           \
      59             :                       !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
      60             : 
      61             : #if defined(XP_LINUX) && !defined(ANDROID) && defined(_GNU_SOURCE)
      62             : #define HAVE_SCHED_SETAFFINITY
      63             : #endif
      64             : 
      65             : #ifdef XP_MACOSX
      66             : #include <mach/mach.h>
      67             : #include <mach/thread_policy.h>
      68             : #endif
      69             : 
      70             : #ifdef MOZ_CANARY
      71             : # include <unistd.h>
      72             : # include <execinfo.h>
      73             : # include <signal.h>
      74             : # include <fcntl.h>
      75             : # include "nsXULAppAPI.h"
      76             : #endif
      77             : 
      78             : #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
      79             : #include "nsTimerImpl.h"
      80             : #include "mozilla/StackWalk.h"
      81             : #endif
      82             : #ifdef NS_FUNCTION_TIMER
      83             : #include "nsCRT.h"
      84             : #endif
      85             : 
      86             : #ifdef MOZ_TASK_TRACER
      87             : #include "GeckoTaskTracer.h"
      88             : #include "TracedTaskCommon.h"
      89             : using namespace mozilla::tasktracer;
      90             : #endif
      91             : 
      92             : using namespace mozilla;
      93             : 
      94             : static LazyLogModule sThreadLog("nsThread");
      95             : #ifdef LOG
      96             : #undef LOG
      97             : #endif
      98             : #define LOG(args) MOZ_LOG(sThreadLog, mozilla::LogLevel::Debug, args)
      99             : 
     100             : NS_DECL_CI_INTERFACE_GETTER(nsThread)
     101             : 
     102             : const char* nsThread::sMainThreadRunnableName = nullptr;
     103             : 
     104             : //-----------------------------------------------------------------------------
     105             : // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
     106             : // somewhat manually.
     107             : 
     108             : class nsThreadClassInfo : public nsIClassInfo
     109             : {
     110             : public:
     111             :   NS_DECL_ISUPPORTS_INHERITED  // no mRefCnt
     112             :   NS_DECL_NSICLASSINFO
     113             : 
     114           0 :   nsThreadClassInfo()
     115           0 :   {
     116           0 :   }
     117             : };
     118             : 
     119             : NS_IMETHODIMP_(MozExternalRefCountType)
     120           0 : nsThreadClassInfo::AddRef()
     121             : {
     122           0 :   return 2;
     123             : }
     124             : NS_IMETHODIMP_(MozExternalRefCountType)
     125           0 : nsThreadClassInfo::Release()
     126             : {
     127           0 :   return 1;
     128             : }
     129           0 : NS_IMPL_QUERY_INTERFACE(nsThreadClassInfo, nsIClassInfo)
     130             : 
     131             : NS_IMETHODIMP
     132           0 : nsThreadClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
     133             : {
     134           0 :   return NS_CI_INTERFACE_GETTER_NAME(nsThread)(aCount, aArray);
     135             : }
     136             : 
     137             : NS_IMETHODIMP
     138           0 : nsThreadClassInfo::GetScriptableHelper(nsIXPCScriptable** aResult)
     139             : {
     140           0 :   *aResult = nullptr;
     141           0 :   return NS_OK;
     142             : }
     143             : 
     144             : NS_IMETHODIMP
     145           0 : nsThreadClassInfo::GetContractID(char** aResult)
     146             : {
     147           0 :   *aResult = nullptr;
     148           0 :   return NS_OK;
     149             : }
     150             : 
     151             : NS_IMETHODIMP
     152           0 : nsThreadClassInfo::GetClassDescription(char** aResult)
     153             : {
     154           0 :   *aResult = nullptr;
     155           0 :   return NS_OK;
     156             : }
     157             : 
     158             : NS_IMETHODIMP
     159           0 : nsThreadClassInfo::GetClassID(nsCID** aResult)
     160             : {
     161           0 :   *aResult = nullptr;
     162           0 :   return NS_OK;
     163             : }
     164             : 
     165             : NS_IMETHODIMP
     166           0 : nsThreadClassInfo::GetFlags(uint32_t* aResult)
     167             : {
     168           0 :   *aResult = THREADSAFE;
     169           0 :   return NS_OK;
     170             : }
     171             : 
     172             : NS_IMETHODIMP
     173           0 : nsThreadClassInfo::GetClassIDNoAlloc(nsCID* aResult)
     174             : {
     175           0 :   return NS_ERROR_NOT_AVAILABLE;
     176             : }
     177             : 
     178             : //-----------------------------------------------------------------------------
     179             : 
     180        8180 : NS_IMPL_ADDREF(nsThread)
     181        7707 : NS_IMPL_RELEASE(nsThread)
     182        3796 : NS_INTERFACE_MAP_BEGIN(nsThread)
     183        3796 :   NS_INTERFACE_MAP_ENTRY(nsIThread)
     184        1377 :   NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
     185        1341 :   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
     186          27 :   NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
     187           2 :   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
     188           2 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
     189           0 :   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
     190           0 :     static nsThreadClassInfo sThreadClassInfo;
     191           0 :     foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
     192             :   } else
     193           0 : NS_INTERFACE_MAP_END
     194           0 : NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,
     195             :                             nsIEventTarget, nsISupportsPriority)
     196             : 
     197             : //-----------------------------------------------------------------------------
     198             : 
     199             : class nsThreadStartupEvent : public Runnable
     200             : {
     201             : public:
     202          56 :   nsThreadStartupEvent()
     203          56 :     : Runnable("nsThreadStartupEvent")
     204             :     , mMon("nsThreadStartupEvent.mMon")
     205          56 :     , mInitialized(false)
     206             :   {
     207          56 :   }
     208             : 
     209             :   // This method does not return until the thread startup object is in the
     210             :   // completion state.
     211          56 :   void Wait()
     212             :   {
     213         112 :     ReentrantMonitorAutoEnter mon(mMon);
     214         168 :     while (!mInitialized) {
     215          56 :       mon.Wait();
     216             :     }
     217          56 :   }
     218             : 
     219             :   // This method needs to be public to support older compilers (xlC_r on AIX).
     220             :   // It should be called directly as this class type is reference counted.
     221         168 :   virtual ~nsThreadStartupEvent() {}
     222             : 
     223             : private:
     224          56 :   NS_IMETHOD Run() override
     225             :   {
     226         112 :     ReentrantMonitorAutoEnter mon(mMon);
     227          56 :     mInitialized = true;
     228          56 :     mon.Notify();
     229         112 :     return NS_OK;
     230             :   }
     231             : 
     232             :   ReentrantMonitor mMon;
     233             :   bool mInitialized;
     234             : };
     235             : //-----------------------------------------------------------------------------
     236             : 
     237             : namespace {
     238             : class DelayedRunnable : public Runnable,
     239             :                         public nsITimerCallback
     240             : {
     241             : public:
     242           2 :   DelayedRunnable(already_AddRefed<nsIThread> aTargetThread,
     243             :                   already_AddRefed<nsIRunnable> aRunnable,
     244             :                   uint32_t aDelay)
     245           2 :     : mozilla::Runnable("DelayedRunnable")
     246             :     , mTargetThread(aTargetThread)
     247             :     , mWrappedRunnable(aRunnable)
     248             :     , mDelayedFrom(TimeStamp::NowLoRes())
     249           2 :     , mDelay(aDelay)
     250           2 :   { }
     251             : 
     252             :   NS_DECL_ISUPPORTS_INHERITED
     253             : 
     254           2 :   nsresult Init()
     255             :   {
     256             :     nsresult rv;
     257           2 :     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
     258           2 :     NS_ENSURE_SUCCESS(rv, rv);
     259             : 
     260           2 :     MOZ_ASSERT(mTimer);
     261           2 :     rv = mTimer->SetTarget(mTargetThread);
     262             : 
     263           2 :     NS_ENSURE_SUCCESS(rv, rv);
     264           2 :     return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
     265             :   }
     266             : 
     267           2 :   nsresult DoRun()
     268             :   {
     269           4 :     nsCOMPtr<nsIRunnable> r = mWrappedRunnable.forget();
     270           4 :     return r->Run();
     271             :   }
     272             : 
     273           2 :   NS_IMETHOD Run() override
     274             :   {
     275             :     // Already ran?
     276           2 :     if (!mWrappedRunnable) {
     277           0 :       return NS_OK;
     278             :     }
     279             : 
     280             :     // Are we too early?
     281           2 :     if ((TimeStamp::NowLoRes() - mDelayedFrom).ToMilliseconds() < mDelay) {
     282           2 :       return NS_OK; // Let the nsITimer run us.
     283             :     }
     284             : 
     285           0 :     mTimer->Cancel();
     286           0 :     return DoRun();
     287             :   }
     288             : 
     289           2 :   NS_IMETHOD Notify(nsITimer* aTimer) override
     290             :   {
     291             :     // If we already ran, the timer should have been canceled.
     292           2 :     MOZ_ASSERT(mWrappedRunnable);
     293           2 :     MOZ_ASSERT(aTimer == mTimer);
     294             : 
     295           2 :     return DoRun();
     296             :   }
     297             : 
     298             : private:
     299           6 :   ~DelayedRunnable() {}
     300             : 
     301             :   nsCOMPtr<nsIThread> mTargetThread;
     302             :   nsCOMPtr<nsIRunnable> mWrappedRunnable;
     303             :   nsCOMPtr<nsITimer> mTimer;
     304             :   TimeStamp mDelayedFrom;
     305             :   uint32_t mDelay;
     306             : };
     307             : 
     308          30 : NS_IMPL_ISUPPORTS_INHERITED(DelayedRunnable, Runnable, nsITimerCallback)
     309             : 
     310             : } // anonymous namespace
     311             : 
     312             : //-----------------------------------------------------------------------------
     313             : 
     314             : struct nsThreadShutdownContext
     315             : {
     316           1 :   nsThreadShutdownContext(NotNull<nsThread*> aTerminatingThread,
     317             :                           NotNull<nsThread*> aJoiningThread,
     318             :                           bool      aAwaitingShutdownAck)
     319           1 :     : mTerminatingThread(aTerminatingThread)
     320             :     , mJoiningThread(aJoiningThread)
     321           1 :     , mAwaitingShutdownAck(aAwaitingShutdownAck)
     322             :   {
     323           1 :     MOZ_COUNT_CTOR(nsThreadShutdownContext);
     324           1 :   }
     325           1 :   ~nsThreadShutdownContext()
     326           1 :   {
     327           1 :     MOZ_COUNT_DTOR(nsThreadShutdownContext);
     328           1 :   }
     329             : 
     330             :   // NB: This will be the last reference.
     331             :   NotNull<RefPtr<nsThread>> mTerminatingThread;
     332             :   NotNull<nsThread*> MOZ_UNSAFE_REF("Thread manager is holding reference to joining thread")
     333             :     mJoiningThread;
     334             :   bool mAwaitingShutdownAck;
     335             : };
     336             : 
     337             : // This event is responsible for notifying nsThread::Shutdown that it is time
     338             : // to call PR_JoinThread. It implements nsICancelableRunnable so that it can
     339             : // run on a DOM Worker thread (where all events must implement
     340             : // nsICancelableRunnable.)
     341             : class nsThreadShutdownAckEvent : public CancelableRunnable
     342             : {
     343             : public:
     344           1 :   explicit nsThreadShutdownAckEvent(NotNull<nsThreadShutdownContext*> aCtx)
     345           1 :     : CancelableRunnable("nsThreadShutdownAckEvent")
     346           1 :     , mShutdownContext(aCtx)
     347             :   {
     348           1 :   }
     349           1 :   NS_IMETHOD Run() override
     350             :   {
     351           1 :     mShutdownContext->mTerminatingThread->ShutdownComplete(mShutdownContext);
     352           1 :     return NS_OK;
     353             :   }
     354           0 :   nsresult Cancel() override
     355             :   {
     356           0 :     return Run();
     357             :   }
     358             : private:
     359           3 :   virtual ~nsThreadShutdownAckEvent() { }
     360             : 
     361             :   NotNull<nsThreadShutdownContext*> mShutdownContext;
     362             : };
     363             : 
     364             : // This event is responsible for setting mShutdownContext
     365           3 : class nsThreadShutdownEvent : public Runnable
     366             : {
     367             : public:
     368           1 :   nsThreadShutdownEvent(NotNull<nsThread*> aThr,
     369             :                         NotNull<nsThreadShutdownContext*> aCtx)
     370           1 :     : Runnable("nsThreadShutdownEvent")
     371             :     , mThread(aThr)
     372           1 :     , mShutdownContext(aCtx)
     373             :   {
     374           1 :   }
     375           1 :   NS_IMETHOD Run() override
     376             :   {
     377           1 :     mThread->mShutdownContext = mShutdownContext;
     378           1 :     MessageLoop::current()->Quit();
     379           1 :     return NS_OK;
     380             :   }
     381             : private:
     382             :   NotNull<RefPtr<nsThread>> mThread;
     383             :   NotNull<nsThreadShutdownContext*> mShutdownContext;
     384             : };
     385             : 
     386             : //-----------------------------------------------------------------------------
     387             : 
     388             : static void
     389           0 : SetThreadAffinity(unsigned int cpu)
     390             : {
     391             : #ifdef HAVE_SCHED_SETAFFINITY
     392             :   cpu_set_t cpus;
     393           0 :   CPU_ZERO(&cpus);
     394           0 :   CPU_SET(cpu, &cpus);
     395           0 :   sched_setaffinity(0, sizeof(cpus), &cpus);
     396             :   // Don't assert sched_setaffinity's return value because it intermittently (?)
     397             :   // fails with EINVAL on Linux x64 try runs.
     398             : #elif defined(XP_MACOSX)
     399             :   // OS X does not provide APIs to pin threads to specific processors, but you
     400             :   // can tag threads as belonging to the same "affinity set" and the OS will try
     401             :   // to run them on the same processor. To run threads on different processors,
     402             :   // tag them as belonging to different affinity sets. Tag 0, the default, means
     403             :   // "no affinity" so let's pretend each CPU has its own tag `cpu+1`.
     404             :   thread_affinity_policy_data_t policy;
     405             :   policy.affinity_tag = cpu + 1;
     406             :   MOZ_ALWAYS_TRUE(thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY,
     407             :                                     &policy.affinity_tag, 1) == KERN_SUCCESS);
     408             : #elif defined(XP_WIN)
     409             :   MOZ_ALWAYS_TRUE(SetThreadIdealProcessor(GetCurrentThread(), cpu) != -1);
     410             : #endif
     411           0 : }
     412             : 
     413             : static void
     414          66 : SetupCurrentThreadForChaosMode()
     415             : {
     416          66 :   if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
     417          66 :     return;
     418             :   }
     419             : 
     420             : #ifdef XP_LINUX
     421             :   // PR_SetThreadPriority doesn't really work since priorities >
     422             :   // PR_PRIORITY_NORMAL can't be set by non-root users. Instead we'll just use
     423             :   // setpriority(2) to set random 'nice values'. In regular Linux this is only
     424             :   // a dynamic adjustment so it still doesn't really do what we want, but tools
     425             :   // like 'rr' can be more aggressive about honoring these values.
     426             :   // Some of these calls may fail due to trying to lower the priority
     427             :   // (e.g. something may have already called setpriority() for this thread).
     428             :   // This makes it hard to have non-main threads with higher priority than the
     429             :   // main thread, but that's hard to fix. Tools like rr can choose to honor the
     430             :   // requested values anyway.
     431             :   // Use just 4 priorities so there's a reasonable chance of any two threads
     432             :   // having equal priority.
     433           0 :   setpriority(PRIO_PROCESS, 0, ChaosMode::randomUint32LessThan(4));
     434             : #else
     435             :   // We should set the affinity here but NSPR doesn't provide a way to expose it.
     436             :   uint32_t priority = ChaosMode::randomUint32LessThan(PR_PRIORITY_LAST + 1);
     437             :   PR_SetThreadPriority(PR_GetCurrentThread(), PRThreadPriority(priority));
     438             : #endif
     439             : 
     440             :   // Force half the threads to CPU 0 so they compete for CPU
     441           0 :   if (ChaosMode::randomUint32LessThan(2)) {
     442           0 :     SetThreadAffinity(0);
     443             :   }
     444             : }
     445             : 
     446             : namespace {
     447             : 
     448             : struct ThreadInitData {
     449             :   nsThread* thread;
     450             :   const nsACString& name;
     451             : };
     452             : 
     453             : }
     454             : 
     455             : /*static*/ void
     456          56 : nsThread::ThreadFunc(void* aArg)
     457             : {
     458             :   using mozilla::ipc::BackgroundChild;
     459             : 
     460             :   char stackTop;
     461             : 
     462          56 :   ThreadInitData* initData = static_cast<ThreadInitData*>(aArg);
     463          56 :   nsThread* self = initData->thread;  // strong reference
     464             : 
     465          56 :   self->mThread = PR_GetCurrentThread();
     466          56 :   self->mVirtualThread = GetCurrentVirtualThread();
     467          56 :   SetupCurrentThreadForChaosMode();
     468             : 
     469          56 :   if (!initData->name.IsEmpty()) {
     470          55 :     NS_SetCurrentThreadName(initData->name.BeginReading());
     471             :   }
     472             : 
     473             :   // Inform the ThreadManager
     474          56 :   nsThreadManager::get().RegisterCurrentThread(*self);
     475             : 
     476          56 :   mozilla::IOInterposer::RegisterCurrentThread();
     477             : 
     478             :   // This must come after the call to nsThreadManager::RegisterCurrentThread(),
     479             :   // because that call is needed to properly set up this thread as an nsThread,
     480             :   // which profiler_register_thread() requires. See bug 1347007.
     481          56 :   if (!initData->name.IsEmpty()) {
     482          55 :     profiler_register_thread(initData->name.BeginReading(), &stackTop);
     483             :   }
     484             : 
     485             :   // Wait for and process startup event
     486          57 :   nsCOMPtr<nsIRunnable> event;
     487             :   {
     488         112 :     MutexAutoLock lock(self->mLock);
     489          56 :     if (!self->mEvents->GetEvent(true, getter_AddRefs(event), nullptr, lock)) {
     490           0 :       NS_WARNING("failed waiting for thread startup event");
     491           0 :       return;
     492             :     }
     493             :   }
     494             : 
     495          56 :   initData = nullptr; // clear before unblocking nsThread::Init
     496             : 
     497          56 :   event->Run();  // unblocks nsThread::Init
     498          56 :   event = nullptr;
     499             : 
     500             :   {
     501             :     // Scope for MessageLoop.
     502             :     nsAutoPtr<MessageLoop> loop(
     503          57 :       new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD, self));
     504             : 
     505             :     // Now, process incoming events...
     506          56 :     loop->Run();
     507             : 
     508           1 :     BackgroundChild::CloseForCurrentThread();
     509             : 
     510             :     // NB: The main thread does not shut down here!  It shuts down via
     511             :     // nsThreadManager::Shutdown.
     512             : 
     513             :     // Do NS_ProcessPendingEvents but with special handling to set
     514             :     // mEventsAreDoomed atomically with the removal of the last event. The key
     515             :     // invariant here is that we will never permit PutEvent to succeed if the
     516             :     // event would be left in the queue after our final call to
     517             :     // NS_ProcessPendingEvents. We also have to keep processing events as long
     518             :     // as we have outstanding mRequestedShutdownContexts.
     519             :     while (true) {
     520             :       // Check and see if we're waiting on any threads.
     521           1 :       self->WaitForAllAsynchronousShutdowns();
     522             : 
     523             :       {
     524           1 :         MutexAutoLock lock(self->mLock);
     525           1 :         if (!self->mEvents->HasPendingEvent(lock)) {
     526             :           // No events in the queue, so we will stop now. Don't let any more
     527             :           // events be added, since they won't be processed. It is critical
     528             :           // that no PutEvent can occur between testing that the event queue is
     529             :           // empty and setting mEventsAreDoomed!
     530           1 :           self->mEventsAreDoomed = true;
     531           1 :           break;
     532             :         }
     533             :       }
     534           0 :       NS_ProcessPendingEvents(self);
     535           0 :     }
     536             :   }
     537             : 
     538           1 :   mozilla::IOInterposer::UnregisterCurrentThread();
     539             : 
     540             :   // Inform the threadmanager that this thread is going away
     541           1 :   nsThreadManager::get().UnregisterCurrentThread(*self);
     542             : 
     543           1 :   profiler_unregister_thread();
     544             : 
     545             :   // Dispatch shutdown ACK
     546             :   NotNull<nsThreadShutdownContext*> context =
     547           1 :     WrapNotNull(self->mShutdownContext);
     548           1 :   MOZ_ASSERT(context->mTerminatingThread == self);
     549           2 :   event = do_QueryObject(new nsThreadShutdownAckEvent(context));
     550           1 :   context->mJoiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
     551             : 
     552             :   // Release any observer of the thread here.
     553           1 :   self->SetObserver(nullptr);
     554             : 
     555             : #ifdef MOZ_TASK_TRACER
     556             :   FreeTraceInfo();
     557             : #endif
     558             : 
     559           1 :   NS_RELEASE(self);
     560             : }
     561             : 
     562             : //-----------------------------------------------------------------------------
     563             : 
     564             : #ifdef MOZ_CRASHREPORTER
     565             : // Tell the crash reporter to save a memory report if our heuristics determine
     566             : // that an OOM failure is likely to occur soon.
     567             : // Memory usage will not be checked more than every 30 seconds or saved more
     568             : // than every 3 minutes
     569             : // If |aShouldSave == kForceReport|, a report will be saved regardless of
     570             : // whether the process is low on memory or not. However, it will still not be
     571             : // saved if a report was saved less than 3 minutes ago.
     572             : bool
     573        1230 : nsThread::SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave)
     574             : {
     575             :   // Keep an eye on memory usage (cheap, ~7ms) somewhat frequently,
     576             :   // but save memory reports (expensive, ~75ms) less frequently.
     577        1230 :   const size_t kLowMemoryCheckSeconds = 30;
     578        1230 :   const size_t kLowMemorySaveSeconds = 3 * 60;
     579             : 
     580           6 :   static TimeStamp nextCheck = TimeStamp::NowLoRes()
     581        1236 :     + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
     582             :   static bool recentlySavedReport = false; // Keeps track of whether a report
     583             :                                            // was saved last time we checked
     584             : 
     585             :   // Are we checking again too soon?
     586        1230 :   TimeStamp now = TimeStamp::NowLoRes();
     587        1230 :   if ((aShouldSave == ShouldSaveMemoryReport::kMaybeReport ||
     588        2460 :       recentlySavedReport) && now < nextCheck) {
     589        1230 :     return false;
     590             :   }
     591             : 
     592           0 :   bool needMemoryReport = (aShouldSave == ShouldSaveMemoryReport::kForceReport);
     593             : #ifdef XP_WIN // XXX implement on other platforms as needed
     594             :   // If the report is forced there is no need to check whether it is necessary
     595             :   if (aShouldSave != ShouldSaveMemoryReport::kForceReport) {
     596             :     const size_t LOWMEM_THRESHOLD_VIRTUAL = 200 * 1024 * 1024;
     597             :     MEMORYSTATUSEX statex;
     598             :     statex.dwLength = sizeof(statex);
     599             :     if (GlobalMemoryStatusEx(&statex)) {
     600             :       if (statex.ullAvailVirtual < LOWMEM_THRESHOLD_VIRTUAL) {
     601             :         needMemoryReport = true;
     602             :       }
     603             :     }
     604             :   }
     605             : #endif
     606             : 
     607           0 :   if (needMemoryReport) {
     608           0 :     if (XRE_IsContentProcess()) {
     609           0 :       dom::ContentChild* cc = dom::ContentChild::GetSingleton();
     610           0 :       if (cc) {
     611           0 :         cc->SendNotifyLowMemory();
     612             :       }
     613             :     } else {
     614             :       nsCOMPtr<nsICrashReporter> cr =
     615           0 :         do_GetService("@mozilla.org/toolkit/crash-reporter;1");
     616           0 :       if (cr) {
     617           0 :         cr->SaveMemoryReport();
     618             :       }
     619             :     }
     620           0 :     recentlySavedReport = true;
     621           0 :     nextCheck = now + TimeDuration::FromSeconds(kLowMemorySaveSeconds);
     622             :   } else {
     623           0 :     recentlySavedReport = false;
     624           0 :     nextCheck = now + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
     625             :   }
     626             : 
     627           0 :   return recentlySavedReport;
     628             : }
     629             : #endif
     630             : 
     631             : #ifdef MOZ_CANARY
     632             : int sCanaryOutputFD = -1;
     633             : #endif
     634             : 
     635          66 : nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
     636             :   : mLock("nsThread.mLock")
     637             :   , mScriptObserver(nullptr)
     638          66 :   , mEvents(WrapNotNull(&mEventsRoot))
     639             :   , mEventsRoot(mLock)
     640             :   , mIdleEventsAvailable(mLock, "[nsThread.mEventsAvailable]")
     641             :   , mIdleEvents(mIdleEventsAvailable, nsEventQueue::eNormalQueue)
     642             :   , mPriority(PRIORITY_NORMAL)
     643             :   , mThread(nullptr)
     644             :   , mNestedEventLoopDepth(0)
     645             :   , mStackSize(aStackSize)
     646             :   , mShutdownContext(nullptr)
     647             :   , mShutdownRequired(false)
     648             :   , mEventsAreDoomed(false)
     649             :   , mIsMainThread(aMainThread)
     650             :   , mLastUnlabeledRunnable(TimeStamp::Now())
     651             :   , mCanInvokeJS(false)
     652         132 :   , mHasPendingEventsPromisedIdleEvent(false)
     653             : {
     654          66 : }
     655             : 
     656           3 : nsThread::~nsThread()
     657             : {
     658           1 :   NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
     659             :                "shouldn't be waiting on other threads to shutdown");
     660             : #ifdef DEBUG
     661             :   // We deliberately leak these so they can be tracked by the leak checker.
     662             :   // If you're having nsThreadShutdownContext leaks, you can set:
     663             :   //   XPCOM_MEM_LOG_CLASSES=nsThreadShutdownContext
     664             :   // during a test run and that will at least tell you what thread is
     665             :   // requesting shutdown on another, which can be helpful for diagnosing
     666             :   // the leak.
     667           1 :   for (size_t i = 0; i < mRequestedShutdownContexts.Length(); ++i) {
     668           0 :     Unused << mRequestedShutdownContexts[i].forget();
     669             :   }
     670             : #endif
     671           3 : }
     672             : 
     673             : nsresult
     674          56 : nsThread::Init(const nsACString& aName)
     675             : {
     676             :   // spawn thread and wait until it is fully setup
     677         112 :   RefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
     678             : 
     679          56 :   NS_ADDREF_THIS();
     680             : 
     681          56 :   mIdlePeriod = new IdlePeriod();
     682             : 
     683          56 :   mShutdownRequired = true;
     684             : 
     685          56 :   ThreadInitData initData = { this, aName };
     686             : 
     687             :   // ThreadFunc is responsible for setting mThread
     688          56 :   if (!PR_CreateThread(PR_USER_THREAD, ThreadFunc, &initData,
     689             :                        PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
     690             :                        PR_JOINABLE_THREAD, mStackSize)) {
     691           0 :     NS_RELEASE_THIS();
     692           0 :     return NS_ERROR_OUT_OF_MEMORY;
     693             :   }
     694             : 
     695             :   // ThreadFunc will wait for this event to be run before it tries to access
     696             :   // mThread.  By delaying insertion of this event into the queue, we ensure
     697             :   // that mThread is set properly.
     698             :   {
     699         112 :     MutexAutoLock lock(mLock);
     700          56 :     mEventsRoot.PutEvent(startup, lock); // retain a reference
     701             :   }
     702             : 
     703             :   // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
     704             :   // initialization of ThreadFunc.
     705          56 :   startup->Wait();
     706          56 :   return NS_OK;
     707             : }
     708             : 
     709             : nsresult
     710          10 : nsThread::InitCurrentThread()
     711             : {
     712          10 :   mThread = PR_GetCurrentThread();
     713          10 :   mVirtualThread = GetCurrentVirtualThread();
     714          10 :   SetupCurrentThreadForChaosMode();
     715             : 
     716          10 :   mIdlePeriod = new IdlePeriod();
     717             : 
     718          10 :   nsThreadManager::get().RegisterCurrentThread(*this);
     719          10 :   return NS_OK;
     720             : }
     721             : 
     722             : nsresult
     723           0 : nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget)
     724             : {
     725           0 :   nsCOMPtr<nsIRunnable> event(aEvent);
     726           0 :   return PutEvent(event.forget(), aTarget);
     727             : }
     728             : 
     729             : nsresult
     730        1566 : nsThread::PutEvent(already_AddRefed<nsIRunnable> aEvent, nsNestedEventTarget* aTarget)
     731             : {
     732             :   // We want to leak the reference when we fail to dispatch it, so that
     733             :   // we won't release the event in a wrong thread.
     734        1566 :   LeakRefPtr<nsIRunnable> event(Move(aEvent));
     735        3134 :   nsCOMPtr<nsIThreadObserver> obs;
     736             : 
     737             :   {
     738        3133 :     MutexAutoLock lock(mLock);
     739        1567 :     nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot;
     740        1567 :     if (!queue || (queue == &mEventsRoot && mEventsAreDoomed)) {
     741           0 :       NS_WARNING("An event was posted to a thread that will never run it (rejected)");
     742           0 :       return NS_ERROR_UNEXPECTED;
     743             :     }
     744        1567 :     queue->PutEvent(event.take(), lock);
     745             : 
     746             :     // Make sure to grab the observer before dropping the lock, otherwise the
     747             :     // event that we just placed into the queue could run and eventually delete
     748             :     // this nsThread before the calling thread is scheduled again. We would then
     749             :     // crash while trying to access a dead nsThread.
     750        1567 :     obs = mObserver;
     751             :   }
     752             : 
     753        1567 :   if (obs) {
     754        1249 :     obs->OnDispatchedEvent(this);
     755             :   }
     756             : 
     757        1567 :   return NS_OK;
     758             : }
     759             : 
     760             : nsresult
     761        1566 : nsThread::DispatchInternal(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags,
     762             :                            nsNestedEventTarget* aTarget)
     763             : {
     764             :   // We want to leak the reference when we fail to dispatch it, so that
     765             :   // we won't release the event in a wrong thread.
     766        1566 :   LeakRefPtr<nsIRunnable> event(Move(aEvent));
     767        1566 :   if (NS_WARN_IF(!event)) {
     768           0 :     return NS_ERROR_INVALID_ARG;
     769             :   }
     770             : 
     771        1566 :   if (gXPCOMThreadsShutDown && MAIN_THREAD != mIsMainThread && !aTarget) {
     772           0 :     NS_ASSERTION(false, "Failed Dispatch after xpcom-shutdown-threads");
     773           0 :     return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
     774             :   }
     775             : 
     776             : #ifdef MOZ_TASK_TRACER
     777             :   nsCOMPtr<nsIRunnable> tracedRunnable = CreateTracedRunnable(event.take());
     778             :   (static_cast<TracedRunnable*>(tracedRunnable.get()))->DispatchTask();
     779             :   // XXX tracedRunnable will always leaked when we fail to disptch.
     780             :   event = tracedRunnable.forget();
     781             : #endif
     782             : 
     783        1566 :   if (aFlags & DISPATCH_SYNC) {
     784           0 :     nsThread* thread = nsThreadManager::get().GetCurrentThread();
     785           0 :     if (NS_WARN_IF(!thread)) {
     786           0 :       return NS_ERROR_NOT_AVAILABLE;
     787             :     }
     788             : 
     789             :     // XXX we should be able to do something better here... we should
     790             :     //     be able to monitor the slot occupied by this event and use
     791             :     //     that to tell us when the event has been processed.
     792             : 
     793             :     RefPtr<nsThreadSyncDispatch> wrapper =
     794           0 :       new nsThreadSyncDispatch(thread, event.take());
     795           0 :     nsresult rv = PutEvent(wrapper, aTarget); // hold a ref
     796             :     // Don't wait for the event to finish if we didn't dispatch it...
     797           0 :     if (NS_FAILED(rv)) {
     798             :       // PutEvent leaked the wrapper runnable object on failure, so we
     799             :       // explicitly release this object once for that. Note that this
     800             :       // object will be released again soon because it exits the scope.
     801           0 :       wrapper.get()->Release();
     802           0 :       return rv;
     803             :     }
     804             : 
     805             :     // Allows waiting; ensure no locks are held that would deadlock us!
     806           0 :     SpinEventLoopUntil([&, wrapper]() -> bool {
     807           0 :         return !wrapper->IsPending();
     808           0 :       }, thread);
     809             : 
     810           0 :     return NS_OK;
     811             :   }
     812             : 
     813        1566 :   NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL ||
     814             :                aFlags == NS_DISPATCH_AT_END, "unexpected dispatch flags");
     815        1566 :   return PutEvent(event.take(), aTarget);
     816             : }
     817             : 
     818             : bool
     819        2044 : nsThread::nsChainedEventQueue::GetEvent(bool aMayWait, nsIRunnable** aEvent,
     820             :                                         unsigned short* aPriority,
     821             :                                         mozilla::MutexAutoLock& aProofOfLock)
     822             : {
     823        2044 :   bool retVal = false;
     824           2 :   do {
     825        2046 :     if (mProcessSecondaryQueueRunnable) {
     826          73 :       MOZ_ASSERT(mSecondaryQueue->HasPendingEvent(aProofOfLock));
     827          73 :       retVal = mSecondaryQueue->GetEvent(aMayWait, aEvent, aProofOfLock);
     828          73 :       MOZ_ASSERT(*aEvent);
     829          73 :       if (aPriority) {
     830          73 :         *aPriority = nsIRunnablePriority::PRIORITY_HIGH;
     831             :       }
     832          73 :       mProcessSecondaryQueueRunnable = false;
     833          73 :       return retVal;
     834             :     }
     835             : 
     836             :     // We don't want to wait if mSecondaryQueue has some events.
     837             :     bool reallyMayWait =
     838        1973 :       aMayWait && !mSecondaryQueue->HasPendingEvent(aProofOfLock);
     839             :     retVal =
     840        1973 :       mNormalQueue->GetEvent(reallyMayWait, aEvent, aProofOfLock);
     841        1947 :     if (aPriority) {
     842        1878 :       *aPriority = nsIRunnablePriority::PRIORITY_NORMAL;
     843             :     }
     844             : 
     845             :     // Let's see if we should next time process an event from the secondary
     846             :     // queue.
     847        1947 :     mProcessSecondaryQueueRunnable =
     848        1947 :       mSecondaryQueue->HasPendingEvent(aProofOfLock);
     849             : 
     850        1947 :     if (*aEvent) {
     851             :       // We got an event, return early.
     852        1508 :       return retVal;
     853             :     }
     854         439 :   } while(aMayWait || mProcessSecondaryQueueRunnable);
     855             : 
     856         437 :   return retVal;
     857             : }
     858             : 
     859             : //-----------------------------------------------------------------------------
     860             : // nsIEventTarget
     861             : 
     862             : NS_IMETHODIMP
     863           6 : nsThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
     864             : {
     865          12 :   nsCOMPtr<nsIRunnable> event(aEvent);
     866          12 :   return Dispatch(event.forget(), aFlags);
     867             : }
     868             : 
     869             : NS_IMETHODIMP
     870        1528 : nsThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
     871             : {
     872        1528 :   LOG(("THRD(%p) Dispatch [%p %x]\n", this, /* XXX aEvent */nullptr, aFlags));
     873             : 
     874        1528 :   return DispatchInternal(Move(aEvent), aFlags, nullptr);
     875             : }
     876             : 
     877             : NS_IMETHODIMP
     878           2 : nsThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelayMs)
     879             : {
     880           2 :   NS_ENSURE_TRUE(!!aDelayMs, NS_ERROR_UNEXPECTED);
     881             : 
     882           4 :   RefPtr<DelayedRunnable> r = new DelayedRunnable(Move(do_AddRef(this)),
     883           2 :                                                   Move(aEvent),
     884           8 :                                                   aDelayMs);
     885           2 :   nsresult rv = r->Init();
     886           2 :   NS_ENSURE_SUCCESS(rv, rv);
     887             : 
     888           2 :   return DispatchInternal(r.forget(), 0, nullptr);
     889             : }
     890             : 
     891             : NS_IMETHODIMP
     892         956 : nsThread::IsOnCurrentThread(bool* aResult)
     893             : {
     894         956 :   *aResult = (PR_GetCurrentThread() == mThread);
     895         956 :   return NS_OK;
     896             : }
     897             : 
     898             : NS_IMETHODIMP_(bool)
     899           0 : nsThread::IsOnCurrentThreadInfallible()
     900             : {
     901             :   // Rely on mVirtualThread being correct.
     902           0 :   MOZ_CRASH("IsOnCurrentThreadInfallible should never be called on nsIThread");
     903             : }
     904             : 
     905             : //-----------------------------------------------------------------------------
     906             : // nsIThread
     907             : 
     908             : NS_IMETHODIMP
     909          19 : nsThread::GetPRThread(PRThread** aResult)
     910             : {
     911          19 :   *aResult = mThread;
     912          19 :   return NS_OK;
     913             : }
     914             : 
     915             : NS_IMETHODIMP
     916           0 : nsThread::GetCanInvokeJS(bool* aResult)
     917             : {
     918           0 :   *aResult = mCanInvokeJS;
     919           0 :   return NS_OK;
     920             : }
     921             : 
     922             : NS_IMETHODIMP
     923           4 : nsThread::SetCanInvokeJS(bool aCanInvokeJS)
     924             : {
     925           4 :   mCanInvokeJS = aCanInvokeJS;
     926           4 :   return NS_OK;
     927             : }
     928             : 
     929             : NS_IMETHODIMP
     930           0 : nsThread::AsyncShutdown()
     931             : {
     932           0 :   LOG(("THRD(%p) async shutdown\n", this));
     933             : 
     934             :   // XXX If we make this warn, then we hit that warning at xpcom shutdown while
     935             :   //     shutting down a thread in a thread pool.  That happens b/c the thread
     936             :   //     in the thread pool is already shutdown by the thread manager.
     937           0 :   if (!mThread) {
     938           0 :     return NS_OK;
     939             :   }
     940             : 
     941           0 :   return !!ShutdownInternal(/* aSync = */ false) ? NS_OK : NS_ERROR_UNEXPECTED;
     942             : }
     943             : 
     944             : nsThreadShutdownContext*
     945           1 : nsThread::ShutdownInternal(bool aSync)
     946             : {
     947           1 :   MOZ_ASSERT(mThread);
     948           1 :   MOZ_ASSERT(mThread != PR_GetCurrentThread());
     949           1 :   if (NS_WARN_IF(mThread == PR_GetCurrentThread())) {
     950           0 :     return nullptr;
     951             :   }
     952             : 
     953             :   // Prevent multiple calls to this method
     954             :   {
     955           2 :     MutexAutoLock lock(mLock);
     956           1 :     if (!mShutdownRequired) {
     957           0 :       return nullptr;
     958             :     }
     959           1 :     mShutdownRequired = false;
     960             :   }
     961             : 
     962             :   NotNull<nsThread*> currentThread =
     963           1 :     WrapNotNull(nsThreadManager::get().GetCurrentThread());
     964             : 
     965             :   nsAutoPtr<nsThreadShutdownContext>& context =
     966           1 :     *currentThread->mRequestedShutdownContexts.AppendElement();
     967           2 :   context = new nsThreadShutdownContext(WrapNotNull(this), currentThread, aSync);
     968             : 
     969             :   // Set mShutdownContext and wake up the thread in case it is waiting for
     970             :   // events to process.
     971             :   nsCOMPtr<nsIRunnable> event =
     972           3 :     new nsThreadShutdownEvent(WrapNotNull(this), WrapNotNull(context.get()));
     973             :   // XXXroc What if posting the event fails due to OOM?
     974           1 :   PutEvent(event.forget(), nullptr);
     975             : 
     976             :   // We could still end up with other events being added after the shutdown
     977             :   // task, but that's okay because we process pending events in ThreadFunc
     978             :   // after setting mShutdownContext just before exiting.
     979           1 :   return context;
     980             : }
     981             : 
     982             : void
     983           2 : nsThread::ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext)
     984             : {
     985           2 :   MOZ_ASSERT(mThread);
     986           2 :   MOZ_ASSERT(aContext->mTerminatingThread == this);
     987             : 
     988           2 :   if (aContext->mAwaitingShutdownAck) {
     989             :     // We're in a synchronous shutdown, so tell whatever is up the stack that
     990             :     // we're done and unwind the stack so it can call us again.
     991           1 :     aContext->mAwaitingShutdownAck = false;
     992           1 :     return;
     993             :   }
     994             : 
     995             :   // Now, it should be safe to join without fear of dead-locking.
     996             : 
     997           1 :   PR_JoinThread(mThread);
     998           1 :   mThread = nullptr;
     999             : 
    1000             :   // We hold strong references to our event observers, and once the thread is
    1001             :   // shut down the observers can't easily unregister themselves. Do it here
    1002             :   // to avoid leaking.
    1003           1 :   ClearObservers();
    1004             : 
    1005             : #ifdef DEBUG
    1006             :   {
    1007           2 :     MutexAutoLock lock(mLock);
    1008           1 :     MOZ_ASSERT(!mObserver, "Should have been cleared at shutdown!");
    1009             :   }
    1010             : #endif
    1011             : 
    1012             :   // Delete aContext.
    1013           1 :   MOZ_ALWAYS_TRUE(
    1014             :     aContext->mJoiningThread->mRequestedShutdownContexts.RemoveElement(aContext));
    1015             : }
    1016             : 
    1017             : void
    1018           1 : nsThread::WaitForAllAsynchronousShutdowns()
    1019             : {
    1020             :   // This is the motivating example for why SpinEventLoop has the template
    1021             :   // parameter we are providing here.
    1022           3 :   SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>([&]() {
    1023           1 :       return mRequestedShutdownContexts.IsEmpty();
    1024           2 :     }, this);
    1025           1 : }
    1026             : 
    1027             : NS_IMETHODIMP
    1028           1 : nsThread::Shutdown()
    1029             : {
    1030           1 :   LOG(("THRD(%p) sync shutdown\n", this));
    1031             : 
    1032             :   // XXX If we make this warn, then we hit that warning at xpcom shutdown while
    1033             :   //     shutting down a thread in a thread pool.  That happens b/c the thread
    1034             :   //     in the thread pool is already shutdown by the thread manager.
    1035           1 :   if (!mThread) {
    1036           0 :     return NS_OK;
    1037             :   }
    1038             : 
    1039           1 :   nsThreadShutdownContext* maybeContext = ShutdownInternal(/* aSync = */ true);
    1040           1 :   NS_ENSURE_TRUE(maybeContext, NS_ERROR_UNEXPECTED);
    1041           1 :   NotNull<nsThreadShutdownContext*> context = WrapNotNull(maybeContext);
    1042             : 
    1043             :   // Process events on the current thread until we receive a shutdown ACK.
    1044             :   // Allows waiting; ensure no locks are held that would deadlock us!
    1045          19 :   SpinEventLoopUntil([&, context]() {
    1046          17 :       return !context->mAwaitingShutdownAck;
    1047          19 :     }, context->mJoiningThread);
    1048             : 
    1049           1 :   ShutdownComplete(context);
    1050             : 
    1051           1 :   return NS_OK;
    1052             : }
    1053             : 
    1054             : TimeStamp
    1055         303 : nsThread::GetIdleDeadline()
    1056             : {
    1057             :   // If we are shutting down, we won't honor the idle period, and we will
    1058             :   // always process idle runnables.  This will ensure that the idle queue
    1059             :   // gets exhausted at shutdown time to prevent intermittently leaking
    1060             :   // some runnables inside that queue and even worse potentially leaving
    1061             :   // some important cleanup work unfinished.
    1062             :   // Note that we need to check both of these conditions since ShuttingDown()
    1063             :   // will never return true on the main thread, where gXPCOMThreadsShutDown
    1064             :   // performs a similar function.
    1065         303 :   if (gXPCOMThreadsShutDown || ShuttingDown()) {
    1066           0 :     return TimeStamp::Now();
    1067             :   }
    1068             : 
    1069         303 :   TimeStamp idleDeadline;
    1070             :   {
    1071             :     // Releasing the lock temporarily since getting the idle period
    1072             :     // might need to lock the timer thread. Unlocking here might make
    1073             :     // us receive an event on the main queue, but we've committed to
    1074             :     // run an idle event anyhow.
    1075         606 :     MutexAutoUnlock unlock(mLock);
    1076         303 :     mIdlePeriod->GetIdlePeriodHint(&idleDeadline);
    1077             :   }
    1078             : 
    1079             :   // If HasPendingEvents() has been called and it has returned true because of
    1080             :   // pending idle events, there is a risk that we may decide here that we aren't
    1081             :   // idle and return null, in which case HasPendingEvents() has effectively
    1082             :   // lied.  Since we can't go back and fix the past, we have to adjust what we
    1083             :   // do here and forcefully pick the idle queue task here.  Note that this means
    1084             :   // that we are choosing to run a task from the idle queue when we would
    1085             :   // normally decide that we aren't in an idle period, but this can only happen
    1086             :   // if we fall out of the idle period in between the call to HasPendingEvents()
    1087             :   // and here, which should hopefully be quite rare.  We are effectively
    1088             :   // choosing to prioritize the sanity of our API semantics over the optimal
    1089             :   // scheduling.
    1090        1207 :   if (!mHasPendingEventsPromisedIdleEvent &&
    1091         914 :       (!idleDeadline || idleDeadline < TimeStamp::Now())) {
    1092         149 :     return TimeStamp();
    1093             :   }
    1094         154 :   if (mHasPendingEventsPromisedIdleEvent && !idleDeadline) {
    1095             :     // If HasPendingEvents() has been called and it has returned true, but we're no
    1096             :     // longer in the idle period, we must return a valid timestamp to pretend that
    1097             :     // we are still in the idle period.
    1098           0 :     return TimeStamp::Now();
    1099             :   }
    1100         154 :   return idleDeadline;
    1101             : }
    1102             : 
    1103             : NS_IMETHODIMP
    1104        1709 : nsThread::HasPendingEvents(bool* aResult)
    1105             : {
    1106        1709 :   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
    1107           0 :     return NS_ERROR_NOT_SAME_THREAD;
    1108             :   }
    1109             : 
    1110             :   {
    1111        3418 :     MutexAutoLock lock(mLock);
    1112        1709 :     mHasPendingEventsPromisedIdleEvent = false;
    1113        1709 :     bool hasPendingEvent = mEvents->HasPendingEvent(lock);
    1114        1709 :     bool hasPendingIdleEvent = false;
    1115        1709 :     if (!hasPendingEvent) {
    1116             :       // Note that GetIdleDeadline() checks mHasPendingEventsPromisedIdleEvent,
    1117             :       // but that's OK since we set it to false in the beginning of this method!
    1118         297 :       TimeStamp idleDeadline = GetIdleDeadline();
    1119             : 
    1120             :       // Only examine the idle queue if we are in an idle period.
    1121         297 :       if (idleDeadline) {
    1122         148 :         hasPendingIdleEvent = mIdleEvents.HasPendingEvent(lock);
    1123         148 :         mHasPendingEventsPromisedIdleEvent = hasPendingIdleEvent;
    1124             :       }
    1125             :     }
    1126        1709 :     *aResult = hasPendingEvent || hasPendingIdleEvent;
    1127             :   }
    1128        1709 :   return NS_OK;
    1129             : }
    1130             : 
    1131             : NS_IMETHODIMP
    1132           3 : nsThread::RegisterIdlePeriod(already_AddRefed<nsIIdlePeriod> aIdlePeriod)
    1133             : {
    1134           3 :   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
    1135           0 :     return NS_ERROR_NOT_SAME_THREAD;
    1136             :   }
    1137             : 
    1138           6 :   MutexAutoLock lock(mLock);
    1139           3 :   mIdlePeriod = aIdlePeriod;
    1140           3 :   return NS_OK;
    1141             : }
    1142             : 
    1143             : NS_IMETHODIMP
    1144          14 : nsThread::IdleDispatch(already_AddRefed<nsIRunnable> aEvent)
    1145             : {
    1146             :   // Currently the only supported idle dispatch is from the same
    1147             :   // thread. To support idle dispatch from another thread we need to
    1148             :   // support waking threads that are waiting for an event queue that
    1149             :   // isn't mIdleEvents.
    1150          14 :   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
    1151             : 
    1152          28 :   MutexAutoLock lock(mLock);
    1153          14 :   LeakRefPtr<nsIRunnable> event(Move(aEvent));
    1154             : 
    1155          14 :   if (NS_WARN_IF(!event)) {
    1156           0 :     return NS_ERROR_INVALID_ARG;
    1157             :   }
    1158             : 
    1159          14 :   if (mEventsAreDoomed) {
    1160           0 :     NS_WARNING("An idle event was posted to a thread that will never run it (rejected)");
    1161           0 :     return NS_ERROR_UNEXPECTED;
    1162             :   }
    1163             : 
    1164          14 :   mIdleEvents.PutEvent(event.take(), lock);
    1165          14 :   return NS_OK;
    1166             : }
    1167             : 
    1168             : #ifdef MOZ_CANARY
    1169             : void canary_alarm_handler(int signum);
    1170             : 
    1171             : class Canary
    1172             : {
    1173             :   //XXX ToDo: support nested loops
    1174             : public:
    1175             :   Canary()
    1176             :   {
    1177             :     if (sCanaryOutputFD > 0 && EventLatencyIsImportant()) {
    1178             :       signal(SIGALRM, canary_alarm_handler);
    1179             :       ualarm(15000, 0);
    1180             :     }
    1181             :   }
    1182             : 
    1183             :   ~Canary()
    1184             :   {
    1185             :     if (sCanaryOutputFD != 0 && EventLatencyIsImportant()) {
    1186             :       ualarm(0, 0);
    1187             :     }
    1188             :   }
    1189             : 
    1190             :   static bool EventLatencyIsImportant()
    1191             :   {
    1192             :     return NS_IsMainThread() && XRE_IsParentProcess();
    1193             :   }
    1194             : };
    1195             : 
    1196             : void canary_alarm_handler(int signum)
    1197             : {
    1198             :   void* array[30];
    1199             :   const char msg[29] = "event took too long to run:\n";
    1200             :   // use write to be safe in the signal handler
    1201             :   write(sCanaryOutputFD, msg, sizeof(msg));
    1202             :   backtrace_symbols_fd(array, backtrace(array, 30), sCanaryOutputFD);
    1203             : }
    1204             : 
    1205             : #endif
    1206             : 
    1207             : #define NOTIFY_EVENT_OBSERVERS(func_, params_)                                 \
    1208             :   do {                                                                         \
    1209             :     if (!mEventObservers.IsEmpty()) {                                          \
    1210             :       nsAutoTObserverArray<NotNull<nsCOMPtr<nsIThreadObserver>>, 2>::ForwardIterator \
    1211             :         iter_(mEventObservers);                                                \
    1212             :       nsCOMPtr<nsIThreadObserver> obs_;                                        \
    1213             :       while (iter_.HasMore()) {                                                \
    1214             :         obs_ = iter_.GetNext();                                                \
    1215             :         obs_ -> func_ params_ ;                                                \
    1216             :       }                                                                        \
    1217             :     }                                                                          \
    1218             :   } while(0)
    1219             : 
    1220             : void
    1221         424 : nsThread::GetIdleEvent(nsIRunnable** aEvent, MutexAutoLock& aProofOfLock)
    1222             : {
    1223         424 :   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
    1224         424 :   MOZ_ASSERT(aEvent);
    1225             : 
    1226         424 :   if (!mIdleEvents.HasPendingEvent(aProofOfLock)) {
    1227         418 :     MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent);
    1228         418 :     aEvent = nullptr;
    1229         836 :     return;
    1230             :   }
    1231             : 
    1232           6 :   TimeStamp idleDeadline = GetIdleDeadline();
    1233           6 :   if (!idleDeadline) {
    1234           0 :     aEvent = nullptr;
    1235           0 :     return;
    1236             :   }
    1237             : 
    1238           6 :   mIdleEvents.GetEvent(false, aEvent, aProofOfLock);
    1239             : 
    1240           6 :   if (*aEvent) {
    1241          12 :     nsCOMPtr<nsIIdleRunnable> idleEvent(do_QueryInterface(*aEvent));
    1242           6 :     if (idleEvent) {
    1243           4 :       idleEvent->SetDeadline(idleDeadline);
    1244             :     }
    1245             : 
    1246             : #ifndef RELEASE_OR_BETA
    1247             :     // Store the next idle deadline to be able to determine budget use
    1248             :     // in ProcessNextEvent.
    1249           6 :     mNextIdleDeadline = idleDeadline;
    1250             : #endif
    1251             :   }
    1252             : }
    1253             : 
    1254             : void
    1255        1823 : nsThread::GetEvent(bool aWait, nsIRunnable** aEvent,
    1256             :                    unsigned short* aPriority,
    1257             :                    MutexAutoLock& aProofOfLock)
    1258             : {
    1259        1823 :   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
    1260        1823 :   MOZ_ASSERT(aEvent);
    1261             : 
    1262        1823 :   MakeScopeExit([&] {
    1263        1823 :     mHasPendingEventsPromisedIdleEvent = false;
    1264        3646 :   });
    1265             : 
    1266             : #ifndef RELEASE_OR_BETA
    1267             :   // Clear mNextIdleDeadline so that it is possible to determine that
    1268             :   // we're running an idle runnable in ProcessNextEvent.
    1269        1823 :   mNextIdleDeadline = TimeStamp();
    1270             : #endif
    1271             : 
    1272             :   // We'll try to get an event to execute in three stages.
    1273             :   // [1] First we just try to get it from the regular queue without waiting.
    1274        1823 :   mEvents->GetEvent(false, aEvent, aPriority, aProofOfLock);
    1275             : 
    1276             :   // [2] If we didn't get an event from the regular queue, try to
    1277             :   // get one from the idle queue
    1278        1823 :   if (!*aEvent) {
    1279             :     // Since events in mEvents have higher priority than idle
    1280             :     // events, we will only consider idle events when there are no
    1281             :     // pending events in mEvents. We will for the same reason never
    1282             :     // wait for an idle event, since a higher priority event might
    1283             :     // appear at any time.
    1284         424 :     GetIdleEvent(aEvent, aProofOfLock);
    1285             : 
    1286         424 :     if (*aEvent && aPriority) {
    1287             :       // Idle events count as normal priority.
    1288           6 :       *aPriority = nsIRunnablePriority::PRIORITY_NORMAL;
    1289             :     }
    1290             :   }
    1291             : 
    1292             :   // [3] If we neither got an event from the regular queue nor the
    1293             :   // idle queue, then if we should wait for events we block on the
    1294             :   // main queue until an event is available.
    1295             :   // If we are shutting down, then do not wait for new events.
    1296        1823 :   if (!*aEvent && aWait) {
    1297         152 :     mEvents->GetEvent(aWait, aEvent, aPriority, aProofOfLock);
    1298             :   }
    1299        1797 : }
    1300             : 
    1301             : #ifndef RELEASE_OR_BETA
    1302             : static bool
    1303        1157 : GetLabeledRunnableName(nsIRunnable* aEvent, nsACString& aName)
    1304             : {
    1305        1157 :   bool labeled = false;
    1306        2314 :   if (RefPtr<SchedulerGroup::Runnable> groupRunnable = do_QueryObject(aEvent)) {
    1307         129 :     labeled = true;
    1308         129 :     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(groupRunnable->GetName(aName)));
    1309        2056 :   } else if (nsCOMPtr<nsINamed> named = do_QueryInterface(aEvent)) {
    1310        1021 :     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(named->GetName(aName)));
    1311             :   } else {
    1312           7 :     aName.AssignLiteral("non-nsINamed runnable");
    1313             :   }
    1314        1157 :   if (aName.IsEmpty()) {
    1315           0 :     aName.AssignLiteral("anonymous runnable");
    1316             :   }
    1317             : 
    1318        1157 :   return labeled;
    1319             : }
    1320             : #endif
    1321             : 
    1322             : NS_IMETHODIMP
    1323        1822 : nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
    1324             : {
    1325        1822 :   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait,
    1326             :        mNestedEventLoopDepth));
    1327             : 
    1328        1822 :   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
    1329           0 :     return NS_ERROR_NOT_SAME_THREAD;
    1330             :   }
    1331             : 
    1332             :   // The toplevel event loop normally blocks waiting for the next event, but
    1333             :   // if we're trying to shut this thread down, we must exit the event loop when
    1334             :   // the event queue is empty.
    1335             :   // This only applys to the toplevel event loop! Nested event loops (e.g.
    1336             :   // during sync dispatch) are waiting for some state change and must be able
    1337             :   // to block even if something has requested shutdown of the thread. Otherwise
    1338             :   // we'll just busywait as we endlessly look for an event, fail to find one,
    1339             :   // and repeat the nested event loop since its state change hasn't happened yet.
    1340        1822 :   bool reallyWait = aMayWait && (mNestedEventLoopDepth > 0 || !ShuttingDown());
    1341             : 
    1342        3583 :   Maybe<SchedulerGroup::AutoProcessEvent> ape;
    1343        1822 :   if (mIsMainThread == MAIN_THREAD) {
    1344        1230 :     DoMainThreadSpecificProcessing(reallyWait);
    1345        1230 :     ape.emplace();
    1346             :   }
    1347             : 
    1348        1822 :   ++mNestedEventLoopDepth;
    1349             : 
    1350             :   // We only want to create an AutoNoJSAPI on threads that actually do DOM stuff
    1351             :   // (including workers).  Those are exactly the threads that have an
    1352             :   // mScriptObserver.
    1353        3583 :   Maybe<dom::AutoNoJSAPI> noJSAPI;
    1354        1823 :   bool callScriptObserver = !!mScriptObserver;
    1355        1823 :   if (callScriptObserver) {
    1356        1265 :     noJSAPI.emplace();
    1357        1265 :     mScriptObserver->BeforeProcessTask(reallyWait);
    1358             :   }
    1359             : 
    1360        3584 :   nsCOMPtr<nsIThreadObserver> obs = mObserver;
    1361        1823 :   if (obs) {
    1362        1419 :     obs->OnProcessNextEvent(this, reallyWait);
    1363             :   }
    1364             : 
    1365        1823 :   NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent, (this, reallyWait));
    1366             : 
    1367             : #ifdef MOZ_CANARY
    1368             :   Canary canary;
    1369             : #endif
    1370        1823 :   nsresult rv = NS_OK;
    1371             : 
    1372             :   {
    1373             :     // Scope for |event| to make sure that its destructor fires while
    1374             :     // mNestedEventLoopDepth has been incremented, since that destructor can
    1375             :     // also do work.
    1376        3583 :     nsCOMPtr<nsIRunnable> event;
    1377             :     unsigned short priority;
    1378             :     {
    1379        3620 :       MutexAutoLock lock(mLock);
    1380        1823 :       GetEvent(reallyWait, getter_AddRefs(event), &priority, lock);
    1381             :     }
    1382             : 
    1383        1797 :     *aResult = (event.get() != nullptr);
    1384             : 
    1385        1797 :     if (event) {
    1386        1531 :       LOG(("THRD(%p) running [%p]\n", this, event.get()));
    1387             : 
    1388        1531 :       if (MAIN_THREAD == mIsMainThread) {
    1389        1157 :         HangMonitor::NotifyActivity();
    1390             :       }
    1391             : 
    1392             : #ifndef RELEASE_OR_BETA
    1393        3026 :       Maybe<Telemetry::AutoTimer<Telemetry::MAIN_THREAD_RUNNABLE_MS>> timer;
    1394        3026 :       Maybe<Telemetry::AutoTimer<Telemetry::IDLE_RUNNABLE_BUDGET_OVERUSE_MS>> idleTimer;
    1395             : 
    1396        3026 :       nsAutoCString name;
    1397        1531 :       if ((MAIN_THREAD == mIsMainThread) || mNextIdleDeadline) {
    1398        1157 :         bool labeled = GetLabeledRunnableName(event, name);
    1399             : 
    1400        1157 :         if (MAIN_THREAD == mIsMainThread) {
    1401        1157 :           timer.emplace(name);
    1402             : 
    1403             :           // High-priority runnables are ignored here since they'll run right away
    1404             :           // even with the cooperative scheduler.
    1405        1157 :           if (!labeled && priority == nsIRunnablePriority::PRIORITY_NORMAL) {
    1406         955 :             TimeStamp now = TimeStamp::Now();
    1407         955 :             double diff = (now - mLastUnlabeledRunnable).ToMilliseconds();
    1408         955 :             Telemetry::Accumulate(Telemetry::TIME_BETWEEN_UNLABELED_RUNNABLES_MS, diff);
    1409         955 :             mLastUnlabeledRunnable = now;
    1410             :           }
    1411             :         }
    1412             : 
    1413        1157 :         if (mNextIdleDeadline) {
    1414             :           // If we construct the AutoTimer with the deadline, then we'll
    1415             :           // compute TimeStamp::Now() - mNextIdleDeadline when
    1416             :           // accumulating telemetry.  If that is positive we've
    1417             :           // overdrawn our idle budget, if it's negative it will go in
    1418             :           // the 0 bucket of the histogram.
    1419           6 :           idleTimer.emplace(name, mNextIdleDeadline);
    1420             :         }
    1421             :       }
    1422             : 
    1423             :       // If we're on the main thread, we want to record our current runnable's
    1424             :       // name in a static so that BHR can record it.
    1425        1531 :       const char* restoreRunnableName = nullptr;
    1426        1495 :       auto clear = MakeScopeExit([&] {
    1427        1495 :         if (MAIN_THREAD == mIsMainThread) {
    1428        1154 :           sMainThreadRunnableName = restoreRunnableName;
    1429             :         }
    1430        4521 :       });
    1431        1531 :       if (MAIN_THREAD == mIsMainThread) {
    1432        1157 :         restoreRunnableName = sMainThreadRunnableName;
    1433        1157 :         sMainThreadRunnableName = name.get();
    1434             :       }
    1435             : #endif
    1436             : 
    1437        1531 :       event->Run();
    1438         266 :     } else if (aMayWait) {
    1439           0 :       MOZ_ASSERT(ShuttingDown(),
    1440             :                  "This should only happen when shutting down");
    1441           0 :       rv = NS_ERROR_UNEXPECTED;
    1442             :     }
    1443             :   }
    1444             : 
    1445        1761 :   NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, (this, *aResult));
    1446             : 
    1447        1761 :   if (obs) {
    1448        1416 :     obs->AfterProcessNextEvent(this, *aResult);
    1449             :   }
    1450             : 
    1451        1761 :   if (callScriptObserver) {
    1452        1258 :     if (mScriptObserver) {
    1453        1258 :       mScriptObserver->AfterProcessTask(mNestedEventLoopDepth);
    1454             :     }
    1455        1258 :     noJSAPI.reset();
    1456             :   }
    1457             : 
    1458        1761 :   --mNestedEventLoopDepth;
    1459             : 
    1460        1761 :   return rv;
    1461             : }
    1462             : 
    1463             : //-----------------------------------------------------------------------------
    1464             : // nsISupportsPriority
    1465             : 
    1466             : NS_IMETHODIMP
    1467           0 : nsThread::GetPriority(int32_t* aPriority)
    1468             : {
    1469           0 :   *aPriority = mPriority;
    1470           0 :   return NS_OK;
    1471             : }
    1472             : 
    1473             : NS_IMETHODIMP
    1474           1 : nsThread::SetPriority(int32_t aPriority)
    1475             : {
    1476           1 :   if (NS_WARN_IF(!mThread)) {
    1477           0 :     return NS_ERROR_NOT_INITIALIZED;
    1478             :   }
    1479             : 
    1480             :   // NSPR defines the following four thread priorities:
    1481             :   //   PR_PRIORITY_LOW
    1482             :   //   PR_PRIORITY_NORMAL
    1483             :   //   PR_PRIORITY_HIGH
    1484             :   //   PR_PRIORITY_URGENT
    1485             :   // We map the priority values defined on nsISupportsPriority to these values.
    1486             : 
    1487           1 :   mPriority = aPriority;
    1488             : 
    1489             :   PRThreadPriority pri;
    1490           1 :   if (mPriority <= PRIORITY_HIGHEST) {
    1491           0 :     pri = PR_PRIORITY_URGENT;
    1492           1 :   } else if (mPriority < PRIORITY_NORMAL) {
    1493           0 :     pri = PR_PRIORITY_HIGH;
    1494           1 :   } else if (mPriority > PRIORITY_NORMAL) {
    1495           0 :     pri = PR_PRIORITY_LOW;
    1496             :   } else {
    1497           1 :     pri = PR_PRIORITY_NORMAL;
    1498             :   }
    1499             :   // If chaos mode is active, retain the randomly chosen priority
    1500           1 :   if (!ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
    1501           1 :     PR_SetThreadPriority(mThread, pri);
    1502             :   }
    1503             : 
    1504           1 :   return NS_OK;
    1505             : }
    1506             : 
    1507             : NS_IMETHODIMP
    1508           0 : nsThread::AdjustPriority(int32_t aDelta)
    1509             : {
    1510           0 :   return SetPriority(mPriority + aDelta);
    1511             : }
    1512             : 
    1513             : //-----------------------------------------------------------------------------
    1514             : // nsIThreadInternal
    1515             : 
    1516             : NS_IMETHODIMP
    1517           0 : nsThread::GetObserver(nsIThreadObserver** aObs)
    1518             : {
    1519           0 :   MutexAutoLock lock(mLock);
    1520           0 :   NS_IF_ADDREF(*aObs = mObserver);
    1521           0 :   return NS_OK;
    1522             : }
    1523             : 
    1524             : NS_IMETHODIMP
    1525           9 : nsThread::SetObserver(nsIThreadObserver* aObs)
    1526             : {
    1527           9 :   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
    1528           0 :     return NS_ERROR_NOT_SAME_THREAD;
    1529             :   }
    1530             : 
    1531          18 :   MutexAutoLock lock(mLock);
    1532           9 :   mObserver = aObs;
    1533           9 :   return NS_OK;
    1534             : }
    1535             : 
    1536             : uint32_t
    1537         307 : nsThread::RecursionDepth() const
    1538             : {
    1539         307 :   MOZ_ASSERT(PR_GetCurrentThread() == mThread);
    1540         307 :   return mNestedEventLoopDepth;
    1541             : }
    1542             : 
    1543             : NS_IMETHODIMP
    1544          12 : nsThread::AddObserver(nsIThreadObserver* aObserver)
    1545             : {
    1546          12 :   if (NS_WARN_IF(!aObserver)) {
    1547           0 :     return NS_ERROR_INVALID_ARG;
    1548             :   }
    1549          12 :   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
    1550           0 :     return NS_ERROR_NOT_SAME_THREAD;
    1551             :   }
    1552             : 
    1553          12 :   NS_WARNING_ASSERTION(!mEventObservers.Contains(aObserver),
    1554             :                        "Adding an observer twice!");
    1555             : 
    1556          12 :   if (!mEventObservers.AppendElement(WrapNotNull(aObserver))) {
    1557           0 :     NS_WARNING("Out of memory!");
    1558           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1559             :   }
    1560             : 
    1561          12 :   return NS_OK;
    1562             : }
    1563             : 
    1564             : NS_IMETHODIMP
    1565          11 : nsThread::RemoveObserver(nsIThreadObserver* aObserver)
    1566             : {
    1567          11 :   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
    1568           0 :     return NS_ERROR_NOT_SAME_THREAD;
    1569             :   }
    1570             : 
    1571          11 :   if (aObserver && !mEventObservers.RemoveElement(aObserver)) {
    1572           0 :     NS_WARNING("Removing an observer that was never added!");
    1573             :   }
    1574             : 
    1575          11 :   return NS_OK;
    1576             : }
    1577             : 
    1578             : NS_IMETHODIMP
    1579          17 : nsThread::PushEventQueue(nsIEventTarget** aResult)
    1580             : {
    1581          17 :   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
    1582           0 :     return NS_ERROR_NOT_SAME_THREAD;
    1583             :   }
    1584             : 
    1585             :   NotNull<nsChainedEventQueue*> queue =
    1586          17 :     WrapNotNull(new nsChainedEventQueue(mLock));
    1587          34 :   queue->mEventTarget = new nsNestedEventTarget(WrapNotNull(this), queue);
    1588             : 
    1589             :   {
    1590          34 :     MutexAutoLock lock(mLock);
    1591          17 :     queue->mNext = mEvents;
    1592          17 :     mEvents = queue;
    1593             :   }
    1594             : 
    1595          17 :   NS_ADDREF(*aResult = queue->mEventTarget);
    1596          17 :   return NS_OK;
    1597             : }
    1598             : 
    1599             : NS_IMETHODIMP
    1600          13 : nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget)
    1601             : {
    1602          13 :   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
    1603           0 :     return NS_ERROR_NOT_SAME_THREAD;
    1604             :   }
    1605             : 
    1606          13 :   if (NS_WARN_IF(!aInnermostTarget)) {
    1607           0 :     return NS_ERROR_NULL_POINTER;
    1608             :   }
    1609             : 
    1610             :   // Don't delete or release anything while holding the lock.
    1611          26 :   nsAutoPtr<nsChainedEventQueue> queue;
    1612          26 :   RefPtr<nsNestedEventTarget> target;
    1613             : 
    1614             :   {
    1615          26 :     MutexAutoLock lock(mLock);
    1616             : 
    1617             :     // Make sure we're popping the innermost event target.
    1618          13 :     if (NS_WARN_IF(mEvents->mEventTarget != aInnermostTarget)) {
    1619           0 :       return NS_ERROR_UNEXPECTED;
    1620             :     }
    1621             : 
    1622          13 :     MOZ_ASSERT(mEvents != &mEventsRoot);
    1623             : 
    1624          13 :     queue = mEvents;
    1625          13 :     mEvents = WrapNotNull(mEvents->mNext);
    1626             : 
    1627          26 :     nsCOMPtr<nsIRunnable> event;
    1628          13 :     while (queue->GetEvent(false, getter_AddRefs(event), nullptr, lock)) {
    1629           0 :       mEvents->PutEvent(event.forget(), lock);
    1630             :     }
    1631             : 
    1632             :     // Don't let the event target post any more events.
    1633          13 :     queue->mEventTarget.swap(target);
    1634          13 :     target->mQueue = nullptr;
    1635             :   }
    1636             : 
    1637          13 :   return NS_OK;
    1638             : }
    1639             : 
    1640             : void
    1641           4 : nsThread::SetScriptObserver(mozilla::CycleCollectedJSContext* aScriptObserver)
    1642             : {
    1643           4 :   if (!aScriptObserver) {
    1644           0 :     mScriptObserver = nullptr;
    1645           0 :     return;
    1646             :   }
    1647             : 
    1648           4 :   MOZ_ASSERT(!mScriptObserver);
    1649           4 :   mScriptObserver = aScriptObserver;
    1650             : }
    1651             : 
    1652             : void
    1653        1230 : nsThread::DoMainThreadSpecificProcessing(bool aReallyWait)
    1654             : {
    1655        1230 :   MOZ_ASSERT(mIsMainThread == MAIN_THREAD);
    1656             : 
    1657        1230 :   ipc::CancelCPOWs();
    1658             : 
    1659        1230 :   if (aReallyWait) {
    1660          89 :     HangMonitor::Suspend();
    1661             :   }
    1662             : 
    1663             :   // Fire a memory pressure notification, if one is pending.
    1664        1230 :   if (!ShuttingDown()) {
    1665        1230 :     MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
    1666        1230 :     if (mpPending != MemPressure_None) {
    1667           0 :       nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    1668             : 
    1669           0 :       if (os) {
    1670             :         // Use no-forward to prevent the notifications from being transferred to
    1671             :         // the children of this process.
    1672           0 :         os->NotifyObservers(nullptr, "memory-pressure",
    1673             :                             mpPending == MemPressure_New ? u"low-memory-no-forward" :
    1674           0 :                             u"low-memory-ongoing-no-forward");
    1675             :       } else {
    1676           0 :         NS_WARNING("Can't get observer service!");
    1677             :       }
    1678             :     }
    1679             :   }
    1680             : 
    1681             : #ifdef MOZ_CRASHREPORTER
    1682        1230 :   if (!ShuttingDown()) {
    1683        1230 :     SaveMemoryReportNearOOM(ShouldSaveMemoryReport::kMaybeReport);
    1684             :   }
    1685             : #endif
    1686        1230 : }
    1687             : 
    1688             : NS_IMETHODIMP
    1689           0 : nsThread::GetEventTarget(nsIEventTarget** aEventTarget)
    1690             : {
    1691           0 :   nsCOMPtr<nsIEventTarget> target = this;
    1692           0 :   target.forget(aEventTarget);
    1693           0 :   return NS_OK;
    1694             : }
    1695             : 
    1696             : nsIEventTarget*
    1697         704 : nsThread::EventTarget()
    1698             : {
    1699         704 :   return this;
    1700             : }
    1701             : 
    1702             : nsISerialEventTarget*
    1703          52 : nsThread::SerialEventTarget()
    1704             : {
    1705          52 :   return this;
    1706             : }
    1707             : 
    1708             : //-----------------------------------------------------------------------------
    1709             : 
    1710         209 : NS_IMPL_ISUPPORTS(nsThread::nsNestedEventTarget, nsIEventTarget)
    1711             : 
    1712             : NS_IMETHODIMP
    1713           0 : nsThread::nsNestedEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
    1714             : {
    1715           0 :   nsCOMPtr<nsIRunnable> event(aEvent);
    1716           0 :   return Dispatch(event.forget(), aFlags);
    1717             : }
    1718             : 
    1719             : NS_IMETHODIMP
    1720          36 : nsThread::nsNestedEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
    1721             : {
    1722          36 :   LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get().get(),
    1723             :        /*XXX aEvent*/ nullptr, aFlags, this));
    1724             : 
    1725          36 :   return mThread->DispatchInternal(Move(aEvent), aFlags, this);
    1726             : }
    1727             : 
    1728             : NS_IMETHODIMP
    1729           0 : nsThread::nsNestedEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
    1730             : {
    1731           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1732             : }
    1733             : 
    1734             : NS_IMETHODIMP
    1735           0 : nsThread::nsNestedEventTarget::IsOnCurrentThread(bool* aResult)
    1736             : {
    1737           0 :   return mThread->IsOnCurrentThread(aResult);
    1738             : }
    1739             : 
    1740             : NS_IMETHODIMP_(bool)
    1741           0 : nsThread::nsNestedEventTarget::IsOnCurrentThreadInfallible()
    1742             : {
    1743           0 :   return mThread->IsOnCurrentThread();
    1744             : }

Generated by: LCOV version 1.13