LCOV - code coverage report
Current view: top level - xpcom/threads - ThreadEventQueue.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 28 105 26.7 %
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 "mozilla/ThreadEventQueue.h"
       8             : #include "mozilla/EventQueue.h"
       9             : #include "LabeledEventQueue.h"
      10             : 
      11             : #include "LeakRefPtr.h"
      12             : #include "nsComponentManagerUtils.h"
      13             : #include "nsIThreadInternal.h"
      14             : #include "nsThreadUtils.h"
      15             : #include "PrioritizedEventQueue.h"
      16             : #include "ThreadEventTarget.h"
      17             : 
      18             : using namespace mozilla;
      19             : 
      20             : template<class InnerQueueT>
      21           0 : class ThreadEventQueue<InnerQueueT>::NestedSink : public ThreadTargetSink
      22             : {
      23             : public:
      24           0 :   NestedSink(EventQueue* aQueue, ThreadEventQueue* aOwner)
      25             :     : mQueue(aQueue)
      26           0 :     , mOwner(aOwner)
      27             :   {
      28           0 :   }
      29             : 
      30           0 :   bool PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
      31             :                 EventPriority aPriority) final
      32             :   {
      33           0 :     return mOwner->PutEventInternal(std::move(aEvent), aPriority, this);
      34             :   }
      35             : 
      36           0 :   void Disconnect(const MutexAutoLock& aProofOfLock) final
      37             :   {
      38           0 :     mQueue = nullptr;
      39           0 :   }
      40             : 
      41             : private:
      42             :   friend class ThreadEventQueue;
      43             : 
      44             :   // This is a non-owning reference. It must live at least until Disconnect is
      45             :   // called to clear it out.
      46             :   EventQueue* mQueue;
      47             :   RefPtr<ThreadEventQueue> mOwner;
      48             : };
      49             : 
      50             : template<class InnerQueueT>
      51           0 : ThreadEventQueue<InnerQueueT>::ThreadEventQueue(UniquePtr<InnerQueueT> aQueue)
      52           0 :   : mBaseQueue(std::move(aQueue))
      53             :   , mLock("ThreadEventQueue")
      54           0 :   , mEventsAvailable(mLock, "EventsAvail")
      55             : {
      56             :   static_assert(IsBaseOf<AbstractEventQueue, InnerQueueT>::value,
      57             :                 "InnerQueueT must be an AbstractEventQueue subclass");
      58           0 : }
      59             : 
      60             : template<class InnerQueueT>
      61           0 : ThreadEventQueue<InnerQueueT>::~ThreadEventQueue()
      62             : {
      63           0 :   MOZ_ASSERT(mNestedQueues.IsEmpty());
      64           0 : }
      65             : 
      66             : template<class InnerQueueT>
      67             : bool
      68           0 : ThreadEventQueue<InnerQueueT>::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
      69             :                                         EventPriority aPriority)
      70             : {
      71           0 :   return PutEventInternal(std::move(aEvent), aPriority, nullptr);
      72             : }
      73             : 
      74             : template<class InnerQueueT>
      75             : bool
      76           0 : ThreadEventQueue<InnerQueueT>::PutEventInternal(already_AddRefed<nsIRunnable>&& aEvent,
      77             :                                                 EventPriority aPriority,
      78             :                                                 NestedSink* aSink)
      79             : {
      80             :   // We want to leak the reference when we fail to dispatch it, so that
      81             :   // we won't release the event in a wrong thread.
      82           0 :   LeakRefPtr<nsIRunnable> event(std::move(aEvent));
      83           0 :   nsCOMPtr<nsIThreadObserver> obs;
      84             : 
      85             :   {
      86             :     // Check if the runnable wants to override the passed-in priority.
      87             :     // Do this outside the lock, so runnables implemented in JS can QI
      88             :     // (and possibly GC) outside of the lock.
      89             :     if (InnerQueueT::SupportsPrioritization) {
      90           0 :       auto* e = event.get();    // can't do_QueryInterface on LeakRefPtr.
      91           0 :       if (nsCOMPtr<nsIRunnablePriority> runnablePrio = do_QueryInterface(e)) {
      92           0 :         uint32_t prio = nsIRunnablePriority::PRIORITY_NORMAL;
      93           0 :         runnablePrio->GetPriority(&prio);
      94           0 :         if (prio == nsIRunnablePriority::PRIORITY_HIGH) {
      95             :           aPriority = EventPriority::High;
      96           0 :         } else if (prio == nsIRunnablePriority::PRIORITY_INPUT) {
      97           0 :           aPriority = EventPriority::Input;
      98             :         }
      99             :       }
     100             :     }
     101             : 
     102           0 :     MutexAutoLock lock(mLock);
     103             : 
     104           0 :     if (mEventsAreDoomed) {
     105           0 :       return false;
     106             :     }
     107             : 
     108           0 :     if (aSink) {
     109           0 :       if (!aSink->mQueue) {
     110             :         return false;
     111             :       }
     112             : 
     113           0 :       aSink->mQueue->PutEvent(event.take(), aPriority, lock);
     114             :     } else {
     115           0 :       mBaseQueue->PutEvent(event.take(), aPriority, lock);
     116             :     }
     117             : 
     118           0 :     mEventsAvailable.Notify();
     119             : 
     120             :     // Make sure to grab the observer before dropping the lock, otherwise the
     121             :     // event that we just placed into the queue could run and eventually delete
     122             :     // this nsThread before the calling thread is scheduled again. We would then
     123             :     // crash while trying to access a dead nsThread.
     124           0 :     obs = mObserver;
     125             :   }
     126             : 
     127           0 :   if (obs) {
     128           0 :     obs->OnDispatchedEvent();
     129             :   }
     130             : 
     131             :   return true;
     132             : }
     133             : 
     134             : template<class InnerQueueT>
     135             : already_AddRefed<nsIRunnable>
     136           0 : ThreadEventQueue<InnerQueueT>::GetEvent(bool aMayWait,
     137             :                                         EventPriority* aPriority)
     138             : {
     139           0 :   MutexAutoLock lock(mLock);
     140             : 
     141           0 :   nsCOMPtr<nsIRunnable> event;
     142         223 :   for (;;) {
     143           0 :     if (mNestedQueues.IsEmpty()) {
     144           0 :       event = mBaseQueue->GetEvent(aPriority, lock);
     145             :     } else {
     146             :       // We always get events from the topmost queue when there are nested
     147             :       // queues.
     148           0 :       event = mNestedQueues.LastElement().mQueue->GetEvent(aPriority, lock);
     149             :     }
     150             : 
     151           0 :     if (event || !aMayWait) {
     152             :       break;
     153             :     }
     154             : 
     155           0 :     AUTO_PROFILER_LABEL("ThreadEventQueue::GetEvent::Wait", IDLE);
     156         240 :     mEventsAvailable.Wait();
     157             :   }
     158             : 
     159        5252 :   return event.forget();
     160             : }
     161             : 
     162             : template<class InnerQueueT>
     163             : bool
     164        3045 : ThreadEventQueue<InnerQueueT>::HasPendingEvent()
     165             : {
     166        6090 :   MutexAutoLock lock(mLock);
     167             : 
     168             :   // We always get events from the topmost queue when there are nested queues.
     169           0 :   if (mNestedQueues.IsEmpty()) {
     170        2435 :     return mBaseQueue->HasReadyEvent(lock);
     171             :   } else {
     172         610 :     return mNestedQueues.LastElement().mQueue->HasReadyEvent(lock);
     173             :   }
     174             : }
     175             : 
     176             : template<class InnerQueueT>
     177             : bool
     178          37 : ThreadEventQueue<InnerQueueT>::ShutdownIfNoPendingEvents()
     179             : {
     180           0 :   MutexAutoLock lock(mLock);
     181           0 :   if (mNestedQueues.IsEmpty() && mBaseQueue->IsEmpty(lock)) {
     182           0 :     mEventsAreDoomed = true;
     183          37 :     return true;
     184             :   }
     185             :   return false;
     186             : }
     187             : 
     188             : template<class InnerQueueT>
     189             : void
     190           0 : ThreadEventQueue<InnerQueueT>::EnableInputEventPrioritization()
     191             : {
     192           0 :   MutexAutoLock lock(mLock);
     193           0 :   mBaseQueue->EnableInputEventPrioritization(lock);
     194           0 : }
     195             : 
     196             : template<class InnerQueueT>
     197             : void
     198           0 : ThreadEventQueue<InnerQueueT>::FlushInputEventPrioritization()
     199             : {
     200           0 :   MutexAutoLock lock(mLock);
     201           0 :   mBaseQueue->FlushInputEventPrioritization(lock);
     202           0 : }
     203             : 
     204             : template<class InnerQueueT>
     205             : void
     206           0 : ThreadEventQueue<InnerQueueT>::SuspendInputEventPrioritization()
     207             : {
     208           0 :   MutexAutoLock lock(mLock);
     209           0 :   mBaseQueue->SuspendInputEventPrioritization(lock);
     210           0 : }
     211             : 
     212             : template<class InnerQueueT>
     213             : void
     214           0 : ThreadEventQueue<InnerQueueT>::ResumeInputEventPrioritization()
     215             : {
     216           0 :   MutexAutoLock lock(mLock);
     217           0 :   mBaseQueue->ResumeInputEventPrioritization(lock);
     218           0 : }
     219             : 
     220             : template<class InnerQueueT>
     221             : already_AddRefed<nsISerialEventTarget>
     222          91 : ThreadEventQueue<InnerQueueT>::PushEventQueue()
     223             : {
     224           0 :   auto queue = MakeUnique<EventQueue>();
     225           0 :   RefPtr<NestedSink> sink = new NestedSink(queue.get(), this);
     226         364 :   RefPtr<ThreadEventTarget> eventTarget = new ThreadEventTarget(sink, NS_IsMainThread());
     227             : 
     228         182 :   MutexAutoLock lock(mLock);
     229             : 
     230           0 :   mNestedQueues.AppendElement(NestedQueueItem(std::move(queue), eventTarget));
     231         273 :   return eventTarget.forget();
     232             : }
     233             : 
     234             : template<class InnerQueueT>
     235             : void
     236          87 : ThreadEventQueue<InnerQueueT>::PopEventQueue(nsIEventTarget* aTarget)
     237             : {
     238         174 :   MutexAutoLock lock(mLock);
     239             : 
     240         174 :   MOZ_ASSERT(!mNestedQueues.IsEmpty());
     241             : 
     242          87 :   NestedQueueItem& item = mNestedQueues.LastElement();
     243             : 
     244         174 :   MOZ_ASSERT(aTarget == item.mEventTarget);
     245             : 
     246             :   // Disconnect the event target that will be popped.
     247          87 :   item.mEventTarget->Disconnect(lock);
     248             : 
     249             :   AbstractEventQueue* prevQueue =
     250           0 :     mNestedQueues.Length() == 1
     251           0 :     ? static_cast<AbstractEventQueue*>(mBaseQueue.get())
     252         257 :     : static_cast<AbstractEventQueue*>(mNestedQueues[mNestedQueues.Length() - 2].mQueue.get());
     253             : 
     254             :   // Move events from the old queue to the new one.
     255          87 :   nsCOMPtr<nsIRunnable> event;
     256             :   EventPriority prio;
     257           1 :   while ((event = item.mQueue->GetEvent(&prio, lock))) {
     258           0 :     prevQueue->PutEvent(event.forget(), prio, lock);
     259             :   }
     260             : 
     261           0 :   mNestedQueues.RemoveLastElement();
     262          87 : }
     263             : 
     264             : template<class InnerQueueT>
     265             : already_AddRefed<nsIThreadObserver>
     266          37 : ThreadEventQueue<InnerQueueT>::GetObserver()
     267             : {
     268           0 :   MutexAutoLock lock(mLock);
     269          74 :   return do_AddRef(mObserver);
     270             : }
     271             : 
     272             : template<class InnerQueueT>
     273             : already_AddRefed<nsIThreadObserver>
     274        2577 : ThreadEventQueue<InnerQueueT>::GetObserverOnThread()
     275             : {
     276        2577 :   return do_AddRef(mObserver);
     277             : }
     278             : 
     279             : template<class InnerQueueT>
     280             : void
     281          41 : ThreadEventQueue<InnerQueueT>::SetObserver(nsIThreadObserver* aObserver)
     282             : {
     283           0 :   MutexAutoLock lock(mLock);
     284           0 :   mObserver = aObserver;
     285             : }
     286             : 
     287             : namespace mozilla {
     288             : template class ThreadEventQueue<EventQueue>;
     289             : template class ThreadEventQueue<PrioritizedEventQueue<EventQueue>>;
     290             : template class ThreadEventQueue<PrioritizedEventQueue<LabeledEventQueue>>;
     291             : }

Generated by: LCOV version 1.13-14-ga5dd952