LCOV - code coverage report
Current view: top level - xpcom/threads - Scheduler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 32 327 9.8 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
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 "Scheduler.h"
       8             : 
       9             : #include "jsfriendapi.h"
      10             : #include "LabeledEventQueue.h"
      11             : #include "LeakRefPtr.h"
      12             : #include "MainThreadQueue.h"
      13             : #include "mozilla/CooperativeThreadPool.h"
      14             : #include "mozilla/dom/ScriptSettings.h"
      15             : #include "mozilla/ipc/BackgroundChild.h"
      16             : #include "mozilla/SchedulerGroup.h"
      17             : #include "nsCycleCollector.h"
      18             : #include "nsIThread.h"
      19             : #include "nsPrintfCString.h"
      20             : #include "nsThread.h"
      21             : #include "nsThreadManager.h"
      22             : #include "PrioritizedEventQueue.h"
      23             : #include "xpcpublic.h"
      24             : #include "xpccomponents.h"
      25             : 
      26             : // Windows silliness. winbase.h defines an empty no-argument Yield macro.
      27             : #undef Yield
      28             : 
      29             : using namespace mozilla;
      30             : 
      31             : // Using the anonymous namespace here causes GCC to generate:
      32             : // error: 'mozilla::SchedulerImpl' has a field 'mozilla::SchedulerImpl::mQueue' whose type uses the anonymous namespace
      33             : namespace mozilla {
      34             : namespace detail {
      35             : 
      36           0 : class SchedulerEventQueue final : public SynchronizedEventQueue
      37             : {
      38             : public:
      39           0 :   explicit SchedulerEventQueue(UniquePtr<AbstractEventQueue> aQueue)
      40           0 :     : mLock("Scheduler")
      41             :     , mNonCooperativeCondVar(mLock, "SchedulerNonCoop")
      42           0 :     , mQueue(std::move(aQueue))
      43           0 :     , mScheduler(nullptr)
      44           0 :   {}
      45             : 
      46             :   bool PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
      47             :                 EventPriority aPriority) final;
      48             : 
      49           0 :   void Disconnect(const MutexAutoLock& aProofOfLock) final {}
      50             : 
      51             :   already_AddRefed<nsIRunnable> GetEvent(bool aMayWait,
      52             :                                          EventPriority* aPriority) final;
      53             :   bool HasPendingEvent() final;
      54             :   bool HasPendingEvent(const MutexAutoLock& aProofOfLock);
      55             : 
      56             :   bool ShutdownIfNoPendingEvents() final;
      57             : 
      58             :   already_AddRefed<nsIThreadObserver> GetObserver() final;
      59             :   already_AddRefed<nsIThreadObserver> GetObserverOnThread() final;
      60             :   void SetObserver(nsIThreadObserver* aObserver) final;
      61             : 
      62             :   void EnableInputEventPrioritization() final;
      63             :   void FlushInputEventPrioritization() final;
      64             :   void SuspendInputEventPrioritization() final;
      65             :   void ResumeInputEventPrioritization() final;
      66             : 
      67             :   bool UseCooperativeScheduling() const;
      68             :   void SetScheduler(SchedulerImpl* aScheduler);
      69             : 
      70           0 :   Mutex& MutexRef() { return mLock; }
      71             : 
      72             : private:
      73             :   Mutex mLock;
      74             :   CondVar mNonCooperativeCondVar;
      75             : 
      76             :   // Using the actual type here would avoid a virtual dispatch. However, that
      77             :   // would prevent us from switching between EventQueue and LabeledEventQueue at
      78             :   // runtime.
      79             :   UniquePtr<AbstractEventQueue> mQueue;
      80             : 
      81             :   bool mEventsAreDoomed = false;
      82             :   SchedulerImpl* mScheduler;
      83             :   nsCOMPtr<nsIThreadObserver> mObserver;
      84             : };
      85             : 
      86             : } // namespace detail
      87             : } // namespace mozilla
      88             : 
      89             : using mozilla::detail::SchedulerEventQueue;
      90             : 
      91           0 : class mozilla::SchedulerImpl
      92             : {
      93             : public:
      94             :   explicit SchedulerImpl(SchedulerEventQueue* aQueue);
      95             : 
      96             :   void Start();
      97             :   void Stop(already_AddRefed<nsIRunnable> aStoppedCallback);
      98             :   void Shutdown();
      99             : 
     100             :   void Dispatch(already_AddRefed<nsIRunnable> aEvent);
     101             : 
     102             :   void Yield();
     103             : 
     104             :   static void EnterNestedEventLoop(Scheduler::EventLoopActivation& aOuterActivation);
     105             :   static void ExitNestedEventLoop(Scheduler::EventLoopActivation& aOuterActivation);
     106             : 
     107             :   static void StartEvent(Scheduler::EventLoopActivation& aActivation);
     108             :   static void FinishEvent(Scheduler::EventLoopActivation& aActivation);
     109             : 
     110             :   void SetJSContext(size_t aIndex, JSContext* aCx)
     111             :   {
     112           0 :     mContexts[aIndex] = aCx;
     113             :   }
     114             : 
     115             :   static void YieldCallback(JSContext* aCx);
     116             :   static bool InterruptCallback(JSContext* aCx);
     117             : 
     118           0 :   CooperativeThreadPool* GetThreadPool() { return mThreadPool.get(); }
     119             : 
     120           0 :   static bool UnlabeledEventRunning() { return sUnlabeledEventRunning; }
     121           0 :   static bool AnyEventRunning() { return sNumThreadsRunning > 0; }
     122             : 
     123             :   void BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback);
     124             :   void UnblockThreadedExecution();
     125             : 
     126           0 :   CooperativeThreadPool::Resource* GetQueueResource() { return &mQueueResource; }
     127             :   bool UseCooperativeScheduling() const { return mQueue->UseCooperativeScheduling(); }
     128             : 
     129             :   // Preferences.
     130             :   static bool sPrefChaoticScheduling;
     131             :   static bool sPrefPreemption;
     132             :   static size_t sPrefThreadCount;
     133             :   static bool sPrefUseMultipleQueues;
     134             : 
     135             : private:
     136             :   void Interrupt(JSContext* aCx);
     137             :   void YieldFromJS(JSContext* aCx);
     138             : 
     139             :   static void SwitcherThread(void* aData);
     140             :   void Switcher();
     141             : 
     142             :   size_t mNumThreads;
     143             : 
     144             :   // Protects mQueue as well as mThreadPool. The lock comes from the SchedulerEventQueue.
     145             :   Mutex& mLock;
     146             :   CondVar mShutdownCondVar;
     147             : 
     148             :   bool mShuttingDown;
     149             : 
     150             :   // Runnable to call when the scheduler has finished shutting down.
     151             :   nsTArray<nsCOMPtr<nsIRunnable>> mShutdownCallbacks;
     152             : 
     153             :   UniquePtr<CooperativeThreadPool> mThreadPool;
     154             : 
     155             :   RefPtr<SchedulerEventQueue> mQueue;
     156             : 
     157             :   class QueueResource : public CooperativeThreadPool::Resource
     158             :   {
     159             :   public:
     160             :     explicit QueueResource(SchedulerImpl* aScheduler)
     161           0 :       : mScheduler(aScheduler)
     162             :     {}
     163             : 
     164             :     bool IsAvailable(const MutexAutoLock& aProofOfLock) override;
     165             : 
     166             :   private:
     167             :     SchedulerImpl* mScheduler;
     168             :   };
     169             :   QueueResource mQueueResource;
     170             : 
     171             :   class SystemZoneResource : public CooperativeThreadPool::Resource
     172             :   {
     173             :   public:
     174             :     explicit SystemZoneResource(SchedulerImpl* aScheduler)
     175           0 :       : mScheduler(aScheduler) {}
     176             : 
     177             :     bool IsAvailable(const MutexAutoLock& aProofOfLock) override;
     178             : 
     179             :   private:
     180             :     SchedulerImpl* mScheduler;
     181             :   };
     182             :   SystemZoneResource mSystemZoneResource;
     183             : 
     184           0 :   class ThreadController : public CooperativeThreadPool::Controller
     185             :   {
     186             :   public:
     187           0 :     ThreadController(SchedulerImpl* aScheduler, SchedulerEventQueue* aQueue)
     188           0 :       : mScheduler(aScheduler)
     189           0 :       , mMainVirtual(GetCurrentVirtualThread())
     190           0 :       , mMainLoop(MessageLoop::current())
     191           0 :       , mMainQueue(aQueue)
     192           0 :     {}
     193             : 
     194             :     void OnStartThread(size_t aIndex, const nsACString& aName, void* aStackTop) override;
     195             :     void OnStopThread(size_t aIndex) override;
     196             : 
     197             :     void OnSuspendThread(size_t aIndex) override;
     198             :     void OnResumeThread(size_t aIndex) override;
     199             : 
     200             :   private:
     201             :     SchedulerImpl* mScheduler;
     202             :     PRThread* mMainVirtual;
     203             :     MessageLoop* mMainLoop;
     204             :     MessageLoop* mOldMainLoop;
     205             :     RefPtr<SynchronizedEventQueue> mMainQueue;
     206             :   };
     207             :   ThreadController mController;
     208             : 
     209             :   static size_t sNumThreadsRunning;
     210             :   static bool sUnlabeledEventRunning;
     211             : 
     212             :   // Number of times that BlockThreadedExecution has been called without
     213             :   // corresponding calls to UnblockThreadedExecution. If this is non-zero,
     214             :   // scheduling is disabled.
     215             :   size_t mNumSchedulerBlocks = 0;
     216             : 
     217             :   JSContext* mContexts[CooperativeThreadPool::kMaxThreads];
     218             : };
     219             : 
     220             : bool SchedulerImpl::sPrefChaoticScheduling = false;
     221             : bool SchedulerImpl::sPrefPreemption = false;
     222             : bool SchedulerImpl::sPrefUseMultipleQueues = false;
     223             : size_t SchedulerImpl::sPrefThreadCount = 2;
     224             : 
     225             : size_t SchedulerImpl::sNumThreadsRunning;
     226             : bool SchedulerImpl::sUnlabeledEventRunning;
     227             : 
     228             : bool
     229           0 : SchedulerEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
     230             :                               EventPriority aPriority)
     231             : {
     232             :   // We want to leak the reference when we fail to dispatch it, so that
     233             :   // we won't release the event in a wrong thread.
     234           0 :   LeakRefPtr<nsIRunnable> event(std::move(aEvent));
     235           0 :   nsCOMPtr<nsIThreadObserver> obs;
     236             : 
     237             :   {
     238           0 :     MutexAutoLock lock(mLock);
     239             : 
     240           0 :     if (mEventsAreDoomed) {
     241           0 :       return false;
     242             :     }
     243             : 
     244           0 :     mQueue->PutEvent(event.take(), aPriority, lock);
     245             : 
     246           0 :     if (mScheduler) {
     247           0 :       CooperativeThreadPool* pool = mScheduler->GetThreadPool();
     248           0 :       MOZ_ASSERT(pool);
     249           0 :       pool->RecheckBlockers(lock);
     250             :     } else {
     251           0 :       mNonCooperativeCondVar.Notify();
     252             :     }
     253             : 
     254             :     // Make sure to grab the observer before dropping the lock, otherwise the
     255             :     // event that we just placed into the queue could run and eventually delete
     256             :     // this nsThread before the calling thread is scheduled again. We would then
     257             :     // crash while trying to access a dead nsThread.
     258           0 :     obs = mObserver;
     259             :   }
     260             : 
     261           0 :   if (obs) {
     262           0 :     obs->OnDispatchedEvent();
     263             :   }
     264             : 
     265             :   return true;
     266             : }
     267             : 
     268             : already_AddRefed<nsIRunnable>
     269           0 : SchedulerEventQueue::GetEvent(bool aMayWait,
     270             :                               EventPriority* aPriority)
     271             : {
     272           0 :   MutexAutoLock lock(mLock);
     273             : 
     274           0 :   if (SchedulerImpl::sPrefChaoticScheduling) {
     275           0 :     CooperativeThreadPool::Yield(nullptr, lock);
     276             :   }
     277             : 
     278           0 :   nsCOMPtr<nsIRunnable> event;
     279             :   for (;;) {
     280           0 :     event = mQueue->GetEvent(aPriority, lock);
     281             : 
     282           0 :     if (event || !aMayWait) {
     283             :       break;
     284             :     }
     285             : 
     286           0 :     if (mScheduler) {
     287           0 :       CooperativeThreadPool::Yield(mScheduler->GetQueueResource(), lock);
     288             :     } else {
     289           0 :       AUTO_PROFILER_LABEL("SchedulerEventQueue::GetEvent::Wait", IDLE);
     290           0 :       mNonCooperativeCondVar.Wait();
     291             :     }
     292             :   }
     293             : 
     294           0 :   return event.forget();
     295             : }
     296             : 
     297             : bool
     298           0 : SchedulerEventQueue::HasPendingEvent()
     299             : {
     300           0 :   MutexAutoLock lock(mLock);
     301           0 :   return HasPendingEvent(lock);
     302             : }
     303             : 
     304             : bool
     305           0 : SchedulerEventQueue::HasPendingEvent(const MutexAutoLock& aProofOfLock)
     306             : {
     307           0 :   return mQueue->HasReadyEvent(aProofOfLock);
     308             : }
     309             : 
     310             : bool
     311           0 : SchedulerEventQueue::ShutdownIfNoPendingEvents()
     312             : {
     313           0 :   MutexAutoLock lock(mLock);
     314             : 
     315           0 :   MOZ_ASSERT(!mScheduler);
     316             : 
     317           0 :   if (mQueue->IsEmpty(lock)) {
     318           0 :     mEventsAreDoomed = true;
     319           0 :     return true;
     320             :   }
     321             :   return false;
     322             : }
     323             : 
     324             : bool
     325           0 : SchedulerEventQueue::UseCooperativeScheduling() const
     326             : {
     327           0 :   MOZ_ASSERT(NS_IsMainThread());
     328           0 :   return !!mScheduler;
     329             : }
     330             : 
     331             : void
     332           0 : SchedulerEventQueue::SetScheduler(SchedulerImpl* aScheduler)
     333             : {
     334           0 :   MutexAutoLock lock(mLock);
     335           0 :   mScheduler = aScheduler;
     336           0 : }
     337             : 
     338             : already_AddRefed<nsIThreadObserver>
     339           0 : SchedulerEventQueue::GetObserver()
     340             : {
     341           0 :   MutexAutoLock lock(mLock);
     342           0 :   return do_AddRef(mObserver);
     343             : }
     344             : 
     345             : already_AddRefed<nsIThreadObserver>
     346           0 : SchedulerEventQueue::GetObserverOnThread()
     347             : {
     348           0 :   MOZ_ASSERT(NS_IsMainThread());
     349           0 :   return do_AddRef(mObserver);
     350             : }
     351             : 
     352             : void
     353           0 : SchedulerEventQueue::SetObserver(nsIThreadObserver* aObserver)
     354             : {
     355           0 :   MutexAutoLock lock(mLock);
     356           0 :   mObserver = aObserver;
     357           0 : }
     358             : 
     359             : void
     360           0 : SchedulerEventQueue::EnableInputEventPrioritization()
     361             : {
     362           0 :   MutexAutoLock lock(mLock);
     363           0 :   mQueue->EnableInputEventPrioritization(lock);
     364           0 : }
     365             : 
     366             : void
     367           0 : SchedulerEventQueue::FlushInputEventPrioritization()
     368             : {
     369           0 :   MutexAutoLock lock(mLock);
     370           0 :   mQueue->FlushInputEventPrioritization(lock);
     371           0 : }
     372             : 
     373             : void
     374           0 : SchedulerEventQueue::SuspendInputEventPrioritization()
     375             : {
     376           0 :   MutexAutoLock lock(mLock);
     377           0 :   mQueue->SuspendInputEventPrioritization(lock);
     378           0 : }
     379             : 
     380             : void
     381           0 : SchedulerEventQueue::ResumeInputEventPrioritization()
     382             : {
     383           0 :   MutexAutoLock lock(mLock);
     384           0 :   mQueue->ResumeInputEventPrioritization(lock);
     385           0 : }
     386             : 
     387           1 : UniquePtr<SchedulerImpl> Scheduler::sScheduler;
     388             : 
     389           0 : SchedulerImpl::SchedulerImpl(SchedulerEventQueue* aQueue)
     390             :   : mNumThreads(sPrefThreadCount)
     391           0 :   , mLock(aQueue->MutexRef())
     392             :   , mShutdownCondVar(aQueue->MutexRef(), "SchedulerImpl")
     393             :   , mShuttingDown(false)
     394             :   , mQueue(aQueue)
     395             :   , mQueueResource(this)
     396             :   , mSystemZoneResource(this)
     397             :   , mController(this, aQueue)
     398           0 :   , mContexts()
     399             : {
     400           0 : }
     401             : 
     402             : void
     403           0 : SchedulerImpl::Interrupt(JSContext* aCx)
     404             : {
     405           0 :   MutexAutoLock lock(mLock);
     406           0 :   CooperativeThreadPool::Yield(nullptr, lock);
     407           0 : }
     408             : 
     409             : /* static */ bool
     410           0 : SchedulerImpl::InterruptCallback(JSContext* aCx)
     411             : {
     412           0 :   Scheduler::sScheduler->Interrupt(aCx);
     413           0 :   return true;
     414             : }
     415             : 
     416             : void
     417           0 : SchedulerImpl::YieldFromJS(JSContext* aCx)
     418             : {
     419           0 :   MutexAutoLock lock(mLock);
     420           0 :   CooperativeThreadPool::Yield(&mSystemZoneResource, lock);
     421           0 : }
     422             : 
     423             : /* static */ void
     424           0 : SchedulerImpl::YieldCallback(JSContext* aCx)
     425             : {
     426           0 :   Scheduler::sScheduler->YieldFromJS(aCx);
     427           0 : }
     428             : 
     429             : void
     430           0 : SchedulerImpl::Switcher()
     431             : {
     432             :   // This thread switcher is extremely basic and only meant for testing. The
     433             :   // goal is to switch as much as possible without regard for performance.
     434             : 
     435           0 :   MutexAutoLock lock(mLock);
     436           0 :   while (!mShuttingDown) {
     437           0 :     CooperativeThreadPool::SelectedThread threadIndex = mThreadPool->CurrentThreadIndex(lock);
     438           0 :     if (threadIndex.is<size_t>()) {
     439           0 :       JSContext* cx = mContexts[threadIndex.as<size_t>()];
     440           0 :       if (cx) {
     441           0 :         JS_RequestInterruptCallbackCanWait(cx);
     442             :       }
     443             :     }
     444             : 
     445           0 :     mShutdownCondVar.Wait(TimeDuration::FromMicroseconds(50));
     446             :   }
     447           0 : }
     448             : 
     449             : /* static */ void
     450           0 : SchedulerImpl::SwitcherThread(void* aData)
     451             : {
     452           0 :   static_cast<SchedulerImpl*>(aData)->Switcher();
     453           0 : }
     454             : 
     455             : void
     456           0 : SchedulerImpl::Start()
     457             : {
     458           0 :   MOZ_ASSERT(mNumSchedulerBlocks == 0);
     459             : 
     460           0 :   NS_DispatchToMainThread(NS_NewRunnableFunction("Scheduler::Start", [this]() -> void {
     461             :     // Let's pretend the runnable here isn't actually running.
     462           0 :     MOZ_ASSERT(sUnlabeledEventRunning);
     463           0 :     sUnlabeledEventRunning = false;
     464           0 :     MOZ_ASSERT(sNumThreadsRunning == 1);
     465           0 :     sNumThreadsRunning = 0;
     466             : 
     467           0 :     mQueue->SetScheduler(this);
     468             : 
     469           0 :     xpc::YieldCooperativeContext();
     470             : 
     471           0 :     mThreadPool = MakeUnique<CooperativeThreadPool>(mNumThreads, mLock,
     472           0 :                                                     mController);
     473             : 
     474           0 :     PRThread* switcher = nullptr;
     475           0 :     if (sPrefPreemption) {
     476             :       switcher = PR_CreateThread(PR_USER_THREAD,
     477             :                                  SwitcherThread,
     478             :                                  this,
     479             :                                  PR_PRIORITY_HIGH,
     480             :                                  PR_GLOBAL_THREAD,
     481             :                                  PR_JOINABLE_THREAD,
     482           0 :                                  0);
     483             :     }
     484             : 
     485             :     {
     486           0 :       MutexAutoLock mutex(mLock);
     487           0 :       while (!mShuttingDown) {
     488           0 :         mShutdownCondVar.Wait();
     489             :       }
     490             :     }
     491             : 
     492           0 :     if (switcher) {
     493           0 :       PR_JoinThread(switcher);
     494             :     }
     495             : 
     496           0 :     mThreadPool->Shutdown();
     497           0 :     mThreadPool = nullptr;
     498             : 
     499           0 :     mQueue->SetScheduler(nullptr);
     500             : 
     501           0 :     xpc::ResumeCooperativeContext();
     502             : 
     503             :     // Put things back to the way they were before we started scheduling.
     504           0 :     MOZ_ASSERT(!sUnlabeledEventRunning);
     505           0 :     sUnlabeledEventRunning = true;
     506           0 :     MOZ_ASSERT(sNumThreadsRunning == 0);
     507           0 :     sNumThreadsRunning = 1;
     508             : 
     509           0 :     mShuttingDown = false;
     510           0 :     nsTArray<nsCOMPtr<nsIRunnable>> callbacks = std::move(mShutdownCallbacks);
     511           0 :     for (nsIRunnable* runnable : callbacks) {
     512           0 :       runnable->Run();
     513             :     }
     514           0 :   }));
     515           0 : }
     516             : 
     517             : void
     518           0 : SchedulerImpl::Stop(already_AddRefed<nsIRunnable> aStoppedCallback)
     519             : {
     520           0 :   MOZ_ASSERT(mNumSchedulerBlocks > 0);
     521             : 
     522             :   // Note that this may be called when mShuttingDown is already true. We still
     523             :   // want to invoke the callback in that case.
     524             : 
     525           0 :   MutexAutoLock lock(mLock);
     526           0 :   mShuttingDown = true;
     527           0 :   mShutdownCallbacks.AppendElement(aStoppedCallback);
     528           0 :   mShutdownCondVar.Notify();
     529           0 : }
     530             : 
     531             : void
     532           0 : SchedulerImpl::Shutdown()
     533             : {
     534           0 :   MOZ_ASSERT(mNumSchedulerBlocks == 0);
     535             : 
     536           0 :   MutexAutoLock lock(mLock);
     537           0 :   mShuttingDown = true;
     538             : 
     539             :   // Delete the SchedulerImpl once shutdown is complete.
     540           0 :   mShutdownCallbacks.AppendElement(NS_NewRunnableFunction("SchedulerImpl::Shutdown",
     541           0 :                                                           [] { Scheduler::sScheduler = nullptr; }));
     542             : 
     543           0 :   mShutdownCondVar.Notify();
     544           0 : }
     545             : 
     546             : bool
     547           0 : SchedulerImpl::QueueResource::IsAvailable(const MutexAutoLock& aProofOfLock)
     548             : {
     549           0 :   mScheduler->mLock.AssertCurrentThreadOwns();
     550             : 
     551           0 :   RefPtr<SchedulerEventQueue> queue = mScheduler->mQueue;
     552           0 :   return queue->HasPendingEvent(aProofOfLock);
     553             : }
     554             : 
     555             : bool
     556           0 : SchedulerImpl::SystemZoneResource::IsAvailable(const MutexAutoLock& aProofOfLock)
     557             : {
     558           0 :   mScheduler->mLock.AssertCurrentThreadOwns();
     559             : 
     560             :   // It doesn't matter which context we pick; we really just some main-thread
     561             :   // JSContext.
     562           0 :   JSContext* cx = mScheduler->mContexts[0];
     563           0 :   return js::SystemZoneAvailable(cx);
     564             : }
     565             : 
     566             : MOZ_THREAD_LOCAL(Scheduler::EventLoopActivation*) Scheduler::EventLoopActivation::sTopActivation;
     567             : 
     568             : /* static */ void
     569           1 : Scheduler::EventLoopActivation::Init()
     570             : {
     571           0 :   sTopActivation.infallibleInit();
     572           1 : }
     573             : 
     574           0 : Scheduler::EventLoopActivation::EventLoopActivation()
     575        1626 :   : mPrev(sTopActivation.get())
     576             :   , mProcessingEvent(false)
     577        3252 :   , mIsLabeled(false)
     578             : {
     579        1626 :   sTopActivation.set(this);
     580             : 
     581           0 :   if (mPrev && mPrev->mProcessingEvent) {
     582          63 :     SchedulerImpl::EnterNestedEventLoop(*mPrev);
     583             :   }
     584        1626 : }
     585             : 
     586        3250 : Scheduler::EventLoopActivation::~EventLoopActivation()
     587             : {
     588           0 :   if (mProcessingEvent) {
     589        1624 :     SchedulerImpl::FinishEvent(*this);
     590             :   }
     591             : 
     592           0 :   MOZ_ASSERT(sTopActivation.get() == this);
     593        3250 :   sTopActivation.set(mPrev);
     594             : 
     595           0 :   if (mPrev && mPrev->mProcessingEvent) {
     596          63 :     SchedulerImpl::ExitNestedEventLoop(*mPrev);
     597             :   }
     598        1625 : }
     599             : 
     600             : /* static */ void
     601        1688 : SchedulerImpl::StartEvent(Scheduler::EventLoopActivation& aActivation)
     602             : {
     603           0 :   MOZ_ASSERT(!sUnlabeledEventRunning);
     604           0 :   if (aActivation.IsLabeled()) {
     605           0 :     SchedulerGroup::SetValidatingAccess(SchedulerGroup::StartValidation);
     606           0 :     aActivation.EventGroupsAffected().SetIsRunning(true);
     607             :   } else {
     608        1688 :     sUnlabeledEventRunning = true;
     609             :   }
     610           0 :   sNumThreadsRunning++;
     611        1688 : }
     612             : 
     613             : /* static */ void
     614        1687 : SchedulerImpl::FinishEvent(Scheduler::EventLoopActivation& aActivation)
     615             : {
     616           0 :   if (aActivation.IsLabeled()) {
     617           0 :     aActivation.EventGroupsAffected().SetIsRunning(false);
     618           0 :     SchedulerGroup::SetValidatingAccess(SchedulerGroup::EndValidation);
     619             :   } else {
     620           0 :     MOZ_ASSERT(sUnlabeledEventRunning);
     621        1687 :     sUnlabeledEventRunning = false;
     622             :   }
     623             : 
     624           0 :   MOZ_ASSERT(sNumThreadsRunning > 0);
     625           0 :   sNumThreadsRunning--;
     626        1687 : }
     627             : 
     628             : // When we enter a nested event loop, we act as if the outer event loop's event
     629             : // finished. When we exit the nested event loop, we "resume" the outer event
     630             : // loop's event.
     631             : /* static */ void
     632           0 : SchedulerImpl::EnterNestedEventLoop(Scheduler::EventLoopActivation& aOuterActivation)
     633             : {
     634           1 :   FinishEvent(aOuterActivation);
     635           0 : }
     636             : 
     637             : /* static */ void
     638           0 : SchedulerImpl::ExitNestedEventLoop(Scheduler::EventLoopActivation& aOuterActivation)
     639             : {
     640           1 :   StartEvent(aOuterActivation);
     641           0 : }
     642             : 
     643             : void
     644        1626 : Scheduler::EventLoopActivation::SetEvent(nsIRunnable* aEvent,
     645             :                                          EventPriority aPriority)
     646             : {
     647           0 :   if (nsCOMPtr<nsILabelableRunnable> labelable = do_QueryInterface(aEvent)) {
     648           0 :     if (labelable->GetAffectedSchedulerGroups(mEventGroups)) {
     649           0 :       mIsLabeled = true;
     650             :     }
     651             :   }
     652             : 
     653           0 :   mPriority = aPriority;
     654        1626 :   mProcessingEvent = aEvent != nullptr;
     655             : 
     656           0 :   if (aEvent) {
     657        1625 :     SchedulerImpl::StartEvent(*this);
     658             :   }
     659        1626 : }
     660             : 
     661             : void
     662           0 : SchedulerImpl::ThreadController::OnStartThread(size_t aIndex, const nsACString& aName, void* aStackTop)
     663             : {
     664             :   using mozilla::ipc::BackgroundChild;
     665             : 
     666             :   // Causes GetCurrentVirtualThread() to return mMainVirtual and NS_IsMainThread()
     667             :   // to return true.
     668           0 :   NS_SetMainThread(mMainVirtual);
     669             : 
     670             :   // This will initialize the thread's mVirtualThread to mMainVirtual since
     671             :   // GetCurrentVirtualThread() now returns mMainVirtual.
     672           0 :   nsThreadManager::get().CreateCurrentThread(mMainQueue, nsThread::MAIN_THREAD);
     673             : 
     674           0 :   PROFILER_REGISTER_THREAD(aName.BeginReading());
     675             : 
     676           0 :   mOldMainLoop = MessageLoop::current();
     677             : 
     678           0 :   MessageLoop::set_current(mMainLoop);
     679             : 
     680           0 :   xpc::CreateCooperativeContext();
     681             : 
     682           0 :   JSContext* cx = dom::danger::GetJSContext();
     683           0 :   mScheduler->SetJSContext(aIndex, cx);
     684           0 :   if (sPrefPreemption) {
     685           0 :     JS_AddInterruptCallback(cx, SchedulerImpl::InterruptCallback);
     686             :   }
     687           0 :   Servo_InitializeCooperativeThread();
     688           0 : }
     689             : 
     690             : void
     691           0 : SchedulerImpl::ThreadController::OnStopThread(size_t aIndex)
     692             : {
     693           0 :   xpc::DestroyCooperativeContext();
     694             : 
     695           0 :   NS_UnsetMainThread();
     696           0 :   MessageLoop::set_current(mOldMainLoop);
     697             : 
     698           0 :   RefPtr<nsThread> self = static_cast<nsThread*>(NS_GetCurrentThread());
     699           0 :   nsThreadManager::get().UnregisterCurrentThread(*self);
     700             : 
     701           0 :   PROFILER_UNREGISTER_THREAD();
     702           0 : }
     703             : 
     704             : void
     705           0 : SchedulerImpl::ThreadController::OnSuspendThread(size_t aIndex)
     706             : {
     707           0 :   xpc::YieldCooperativeContext();
     708           0 : }
     709             : 
     710             : void
     711           0 : SchedulerImpl::ThreadController::OnResumeThread(size_t aIndex)
     712             : {
     713           0 :   xpc::ResumeCooperativeContext();
     714           0 : }
     715             : 
     716             : void
     717           0 : SchedulerImpl::Yield()
     718             : {
     719           0 :   MutexAutoLock lock(mLock);
     720           0 :   CooperativeThreadPool::Yield(nullptr, lock);
     721           0 : }
     722             : 
     723             : void
     724           0 : SchedulerImpl::BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback)
     725             : {
     726           0 :   if (mNumSchedulerBlocks++ == 0 || mShuttingDown) {
     727           0 :     Stop(NewRunnableMethod("BlockThreadedExecution", aCallback,
     728           0 :                            &nsIBlockThreadedExecutionCallback::Callback));
     729             :   } else {
     730             :     // The scheduler is already blocked.
     731           0 :     nsCOMPtr<nsIBlockThreadedExecutionCallback> kungFuDeathGrip(aCallback);
     732           0 :     aCallback->Callback();
     733             :   }
     734           0 : }
     735             : 
     736             : void
     737           0 : SchedulerImpl::UnblockThreadedExecution()
     738             : {
     739           0 :   if (--mNumSchedulerBlocks == 0) {
     740           0 :     Start();
     741             :   }
     742           0 : }
     743             : 
     744             : /* static */ already_AddRefed<nsThread>
     745           0 : Scheduler::Init(nsIIdlePeriod* aIdlePeriod)
     746             : {
     747           0 :   MOZ_ASSERT(!sScheduler);
     748             : 
     749           0 :   RefPtr<SchedulerEventQueue> queue;
     750           0 :   RefPtr<nsThread> mainThread;
     751           0 :   if (Scheduler::UseMultipleQueues()) {
     752           0 :     mainThread = CreateMainThread<SchedulerEventQueue, LabeledEventQueue>(aIdlePeriod, getter_AddRefs(queue));
     753             :   } else {
     754           0 :     mainThread = CreateMainThread<SchedulerEventQueue, EventQueue>(aIdlePeriod, getter_AddRefs(queue));
     755             :   }
     756             : 
     757           0 :   sScheduler = MakeUnique<SchedulerImpl>(queue);
     758           0 :   return mainThread.forget();
     759             : }
     760             : 
     761             : /* static */ void
     762           0 : Scheduler::Start()
     763             : {
     764           0 :   sScheduler->Start();
     765           0 : }
     766             : 
     767             : /* static */ void
     768           0 : Scheduler::Shutdown()
     769             : {
     770           0 :   if (sScheduler) {
     771           0 :     sScheduler->Shutdown();
     772             :   }
     773           0 : }
     774             : 
     775             : /* static */ nsCString
     776           2 : Scheduler::GetPrefs()
     777             : {
     778           2 :   MOZ_ASSERT(XRE_IsParentProcess());
     779             :   nsPrintfCString result("%d%d%d%d,%d",
     780             :                          false, // XXX The scheduler is always disabled.
     781           2 :                          Preferences::GetBool("dom.ipc.scheduler.chaoticScheduling",
     782             :                                               SchedulerImpl::sPrefChaoticScheduling),
     783           2 :                          Preferences::GetBool("dom.ipc.scheduler.preemption",
     784             :                                               SchedulerImpl::sPrefPreemption),
     785           2 :                          Preferences::GetBool("dom.ipc.scheduler.useMultipleQueues",
     786             :                                               SchedulerImpl::sPrefUseMultipleQueues),
     787             :                          Preferences::GetInt("dom.ipc.scheduler.threadCount",
     788          10 :                                              SchedulerImpl::sPrefThreadCount));
     789             : 
     790           4 :   return result;
     791             : }
     792             : 
     793             : /* static */ void
     794           0 : Scheduler::SetPrefs(const char* aPrefs)
     795             : {
     796           0 :   MOZ_ASSERT(XRE_IsContentProcess());
     797             : 
     798             :   // If the prefs weren't sent to this process, use the default values.
     799           0 :   if (!aPrefs) {
     800             :     return;
     801             :   }
     802             : 
     803             :   // If the pref string appears truncated, use the default values.
     804           0 :   if (strlen(aPrefs) < 6) {
     805             :     return;
     806             :   }
     807             : 
     808           0 :   SchedulerImpl::sPrefChaoticScheduling = aPrefs[1] == '1';
     809           0 :   SchedulerImpl::sPrefPreemption = aPrefs[2] == '1';
     810           0 :   SchedulerImpl::sPrefUseMultipleQueues = aPrefs[3] == '1';
     811           0 :   MOZ_ASSERT(aPrefs[4] == ',');
     812           0 :   SchedulerImpl::sPrefThreadCount = atoi(aPrefs + 5);
     813             : }
     814             : 
     815             : /* static */ bool
     816           0 : Scheduler::IsSchedulerEnabled()
     817             : {
     818             :   // XXX We never enable the scheduler because it will crash immediately.
     819           0 :   return false;
     820             : }
     821             : 
     822             : /* static */ bool
     823           0 : Scheduler::UseMultipleQueues()
     824             : {
     825           0 :   return SchedulerImpl::sPrefUseMultipleQueues;
     826             : }
     827             : 
     828             : /* static */ bool
     829           0 : Scheduler::IsCooperativeThread()
     830             : {
     831           0 :   return CooperativeThreadPool::IsCooperativeThread();
     832             : }
     833             : 
     834             : /* static */ void
     835           0 : Scheduler::Yield()
     836             : {
     837           0 :   sScheduler->Yield();
     838           0 : }
     839             : 
     840             : /* static */ bool
     841           0 : Scheduler::UnlabeledEventRunning()
     842             : {
     843           0 :   return SchedulerImpl::UnlabeledEventRunning();
     844             : }
     845             : 
     846             : /* static */ bool
     847           0 : Scheduler::AnyEventRunning()
     848             : {
     849           0 :   return SchedulerImpl::AnyEventRunning();
     850             : }
     851             : 
     852             : /* static */ void
     853           0 : Scheduler::BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback)
     854             : {
     855           0 :   if (!sScheduler) {
     856           0 :     nsCOMPtr<nsIBlockThreadedExecutionCallback> kungFuDeathGrip(aCallback);
     857           0 :     aCallback->Callback();
     858             :     return;
     859             :   }
     860             : 
     861           0 :   sScheduler->BlockThreadedExecution(aCallback);
     862             : }
     863             : 
     864             : /* static */ void
     865           0 : Scheduler::UnblockThreadedExecution()
     866             : {
     867           0 :   if (!sScheduler) {
     868             :     return;
     869             :   }
     870             : 
     871           0 :   sScheduler->UnblockThreadedExecution();
     872             : }

Generated by: LCOV version 1.13-14-ga5dd952