LCOV - code coverage report
Current view: top level - xpcom/threads - TaskQueue.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 125 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 25 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/TaskQueue.h"
       8             : 
       9             : #include "nsISerialEventTarget.h"
      10             : #include "nsThreadUtils.h"
      11             : 
      12             : namespace mozilla {
      13             : 
      14             : class TaskQueue::EventTargetWrapper final : public nsISerialEventTarget
      15             : {
      16             :   RefPtr<TaskQueue> mTaskQueue;
      17             : 
      18           0 :   ~EventTargetWrapper()
      19           0 :   {
      20           0 :   }
      21             : 
      22             : public:
      23           0 :   explicit EventTargetWrapper(TaskQueue* aTaskQueue)
      24           0 :     : mTaskQueue(aTaskQueue)
      25             :   {
      26           0 :     MOZ_ASSERT(mTaskQueue);
      27           0 :   }
      28             : 
      29             :   NS_IMETHOD
      30           0 :   DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) override
      31             :   {
      32           0 :     nsCOMPtr<nsIRunnable> ref = aEvent;
      33           0 :     return Dispatch(ref.forget(), aFlags);
      34             :   }
      35             : 
      36             :   NS_IMETHOD
      37           0 :   Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags) override
      38             :   {
      39           0 :     nsCOMPtr<nsIRunnable> runnable = aEvent;
      40           0 :     MonitorAutoLock mon(mTaskQueue->mQueueMonitor);
      41           0 :     return mTaskQueue->DispatchLocked(/* passed by ref */runnable,
      42             :                                       DontAssertDispatchSuccess,
      43           0 :                                       NormalDispatch);
      44             :   }
      45             : 
      46             :   NS_IMETHOD
      47           0 :   DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t aFlags) override
      48             :   {
      49           0 :     return NS_ERROR_NOT_IMPLEMENTED;
      50             :   }
      51             : 
      52             :   NS_IMETHOD
      53           0 :   IsOnCurrentThread(bool* aResult) override
      54             :   {
      55           0 :     *aResult = mTaskQueue->IsCurrentThreadIn();
      56           0 :     return NS_OK;
      57             :   }
      58             : 
      59             :   NS_IMETHOD_(bool)
      60           0 :   IsOnCurrentThreadInfallible() override
      61             :   {
      62           0 :     return mTaskQueue->mTarget->IsOnCurrentThread();
      63             :   }
      64             : 
      65             :   NS_DECL_THREADSAFE_ISUPPORTS
      66             : };
      67             : 
      68           0 : NS_IMPL_ISUPPORTS(TaskQueue::EventTargetWrapper,
      69             :                   nsIEventTarget,
      70             :                   nsISerialEventTarget)
      71             : 
      72           0 : TaskQueue::TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
      73             :                      const char* aName,
      74           0 :                      bool aRequireTailDispatch)
      75             :   : AbstractThread(aRequireTailDispatch)
      76             :   , mTarget(aTarget)
      77             :   , mQueueMonitor("TaskQueue::Queue")
      78             :   , mTailDispatcher(nullptr)
      79             :   , mIsRunning(false)
      80             :   , mIsShutdown(false)
      81           0 :   , mName(aName)
      82             : {
      83           0 : }
      84             : 
      85           0 : TaskQueue::TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
      86           0 :                      bool aSupportsTailDispatch)
      87           0 :   : TaskQueue(Move(aTarget), "Unnamed", aSupportsTailDispatch)
      88             : {
      89           0 : }
      90             : 
      91           0 : TaskQueue::~TaskQueue()
      92             : {
      93           0 :   MonitorAutoLock mon(mQueueMonitor);
      94           0 :   MOZ_ASSERT(mIsShutdown);
      95           0 : }
      96             : 
      97             : TaskDispatcher&
      98           0 : TaskQueue::TailDispatcher()
      99             : {
     100           0 :   MOZ_ASSERT(IsCurrentThreadIn());
     101           0 :   MOZ_ASSERT(mTailDispatcher);
     102           0 :   return *mTailDispatcher;
     103             : }
     104             : 
     105             : // Note aRunnable is passed by ref to support conditional ownership transfer.
     106             : // See Dispatch() in TaskQueue.h for more details.
     107             : nsresult
     108           0 : TaskQueue::DispatchLocked(nsCOMPtr<nsIRunnable>& aRunnable,
     109             :                           DispatchFailureHandling aFailureHandling,
     110             :                           DispatchReason aReason)
     111             : {
     112           0 :   mQueueMonitor.AssertCurrentThreadOwns();
     113           0 :   if (mIsShutdown) {
     114           0 :     return NS_ERROR_FAILURE;
     115             :   }
     116             : 
     117             :   AbstractThread* currentThread;
     118           0 :   if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
     119           0 :     currentThread->TailDispatcher().AddTask(this, aRunnable.forget(), aFailureHandling);
     120           0 :     return NS_OK;
     121             :   }
     122             : 
     123           0 :   mTasks.push(aRunnable.forget());
     124           0 :   if (mIsRunning) {
     125           0 :     return NS_OK;
     126             :   }
     127           0 :   RefPtr<nsIRunnable> runner(new Runner(this));
     128           0 :   nsresult rv = mTarget->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
     129           0 :   if (NS_FAILED(rv)) {
     130           0 :     NS_WARNING("Failed to dispatch runnable to run TaskQueue");
     131           0 :     return rv;
     132             :   }
     133           0 :   mIsRunning = true;
     134             : 
     135           0 :   return NS_OK;
     136             : }
     137             : 
     138             : void
     139           0 : TaskQueue::AwaitIdle()
     140             : {
     141           0 :   MonitorAutoLock mon(mQueueMonitor);
     142           0 :   AwaitIdleLocked();
     143           0 : }
     144             : 
     145             : void
     146           0 : TaskQueue::AwaitIdleLocked()
     147             : {
     148             :   // Make sure there are no tasks for this queue waiting in the caller's tail
     149             :   // dispatcher.
     150           0 :   MOZ_ASSERT_IF(AbstractThread::GetCurrent(),
     151             :                 !AbstractThread::GetCurrent()->HasTailTasksFor(this));
     152             : 
     153           0 :   mQueueMonitor.AssertCurrentThreadOwns();
     154           0 :   MOZ_ASSERT(mIsRunning || mTasks.empty());
     155           0 :   while (mIsRunning) {
     156           0 :     mQueueMonitor.Wait();
     157             :   }
     158           0 : }
     159             : 
     160             : void
     161           0 : TaskQueue::AwaitShutdownAndIdle()
     162             : {
     163           0 :   MOZ_ASSERT(!IsCurrentThreadIn());
     164             :   // Make sure there are no tasks for this queue waiting in the caller's tail
     165             :   // dispatcher.
     166           0 :   MOZ_ASSERT_IF(AbstractThread::GetCurrent(),
     167             :                 !AbstractThread::GetCurrent()->HasTailTasksFor(this));
     168             : 
     169           0 :   MonitorAutoLock mon(mQueueMonitor);
     170           0 :   while (!mIsShutdown) {
     171           0 :     mQueueMonitor.Wait();
     172             :   }
     173           0 :   AwaitIdleLocked();
     174           0 : }
     175             : 
     176             : RefPtr<ShutdownPromise>
     177           0 : TaskQueue::BeginShutdown()
     178             : {
     179             :   // Dispatch any tasks for this queue waiting in the caller's tail dispatcher,
     180             :   // since this is the last opportunity to do so.
     181           0 :   if (AbstractThread* currentThread = AbstractThread::GetCurrent()) {
     182           0 :     currentThread->TailDispatchTasksFor(this);
     183             :   }
     184             : 
     185           0 :   MonitorAutoLock mon(mQueueMonitor);
     186           0 :   mIsShutdown = true;
     187           0 :   RefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
     188           0 :   MaybeResolveShutdown();
     189           0 :   mon.NotifyAll();
     190           0 :   return p;
     191             : }
     192             : 
     193             : bool
     194           0 : TaskQueue::IsEmpty()
     195             : {
     196           0 :   MonitorAutoLock mon(mQueueMonitor);
     197           0 :   return mTasks.empty();
     198             : }
     199             : 
     200             : uint32_t
     201           0 : TaskQueue::ImpreciseLengthForHeuristics()
     202             : {
     203           0 :   MonitorAutoLock mon(mQueueMonitor);
     204           0 :   return mTasks.size();
     205             : }
     206             : 
     207             : bool
     208           0 : TaskQueue::IsCurrentThreadIn()
     209             : {
     210           0 :   bool in = mRunningThread == GetCurrentPhysicalThread();
     211           0 :   return in;
     212             : }
     213             : 
     214             : already_AddRefed<nsISerialEventTarget>
     215           0 : TaskQueue::WrapAsEventTarget()
     216             : {
     217           0 :   nsCOMPtr<nsISerialEventTarget> ref = new EventTargetWrapper(this);
     218           0 :   return ref.forget();
     219             : }
     220             : 
     221             : nsresult
     222           0 : TaskQueue::Runner::Run()
     223             : {
     224           0 :   RefPtr<nsIRunnable> event;
     225             :   {
     226           0 :     MonitorAutoLock mon(mQueue->mQueueMonitor);
     227           0 :     MOZ_ASSERT(mQueue->mIsRunning);
     228           0 :     if (mQueue->mTasks.size() == 0) {
     229           0 :       mQueue->mIsRunning = false;
     230           0 :       mQueue->MaybeResolveShutdown();
     231           0 :       mon.NotifyAll();
     232           0 :       return NS_OK;
     233             :     }
     234           0 :     event = mQueue->mTasks.front().forget();
     235           0 :     mQueue->mTasks.pop();
     236             :   }
     237           0 :   MOZ_ASSERT(event);
     238             : 
     239             :   // Note that dropping the queue monitor before running the task, and
     240             :   // taking the monitor again after the task has run ensures we have memory
     241             :   // fences enforced. This means that if the object we're calling wasn't
     242             :   // designed to be threadsafe, it will be, provided we're only calling it
     243             :   // in this task queue.
     244             :   {
     245           0 :     AutoTaskGuard g(mQueue);
     246           0 :     event->Run();
     247             :   }
     248             : 
     249             :   // Drop the reference to event. The event will hold a reference to the
     250             :   // object it's calling, and we don't want to keep it alive, it may be
     251             :   // making assumptions what holds references to it. This is especially
     252             :   // the case if the object is waiting for us to shutdown, so that it
     253             :   // can shutdown (like in the MediaDecoderStateMachine's SHUTDOWN case).
     254           0 :   event = nullptr;
     255             : 
     256             :   {
     257           0 :     MonitorAutoLock mon(mQueue->mQueueMonitor);
     258           0 :     if (mQueue->mTasks.size() == 0) {
     259             :       // No more events to run. Exit the task runner.
     260           0 :       mQueue->mIsRunning = false;
     261           0 :       mQueue->MaybeResolveShutdown();
     262           0 :       mon.NotifyAll();
     263           0 :       return NS_OK;
     264             :     }
     265             :   }
     266             : 
     267             :   // There's at least one more event that we can run. Dispatch this Runner
     268             :   // to the target again to ensure it runs again. Note that we don't just
     269             :   // run in a loop here so that we don't hog the target. This means we may
     270             :   // run on another thread next time, but we rely on the memory fences from
     271             :   // mQueueMonitor for thread safety of non-threadsafe tasks.
     272           0 :   nsresult rv = mQueue->mTarget->Dispatch(this, NS_DISPATCH_AT_END);
     273           0 :   if (NS_FAILED(rv)) {
     274             :     // Failed to dispatch, shutdown!
     275           0 :     MonitorAutoLock mon(mQueue->mQueueMonitor);
     276           0 :     mQueue->mIsRunning = false;
     277           0 :     mQueue->mIsShutdown = true;
     278           0 :     mQueue->MaybeResolveShutdown();
     279           0 :     mon.NotifyAll();
     280             :   }
     281             : 
     282           0 :   return NS_OK;
     283             : }
     284             : 
     285             : } // namespace mozilla

Generated by: LCOV version 1.13