LCOV - code coverage report
Current view: top level - xpcom/threads - TaskQueue.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 37 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 9 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             : #ifndef TaskQueue_h_
       8             : #define TaskQueue_h_
       9             : 
      10             : #include "mozilla/Monitor.h"
      11             : #include "mozilla/MozPromise.h"
      12             : #include "mozilla/RefPtr.h"
      13             : #include "mozilla/TaskDispatcher.h"
      14             : #include "mozilla/Unused.h"
      15             : 
      16             : #include <queue>
      17             : 
      18             : #include "nsThreadUtils.h"
      19             : 
      20             : class nsIEventTarget;
      21             : class nsIRunnable;
      22             : 
      23             : namespace mozilla {
      24             : 
      25             : typedef MozPromise<bool, bool, false> ShutdownPromise;
      26             : 
      27             : // Abstracts executing runnables in order on an arbitrary event target. The
      28             : // runnables dispatched to the TaskQueue will be executed in the order in which
      29             : // they're received, and are guaranteed to not be executed concurrently.
      30             : // They may be executed on different threads, and a memory barrier is used
      31             : // to make this threadsafe for objects that aren't already threadsafe.
      32             : //
      33             : // Note, since a TaskQueue can also be converted to an nsIEventTarget using
      34             : // WrapAsEventTarget() its possible to construct a hierarchy of TaskQueues.
      35             : // Consider these three TaskQueues:
      36             : //
      37             : //  TQ1 dispatches to the main thread
      38             : //  TQ2 dispatches to TQ1
      39             : //  TQ3 dispatches to TQ1
      40             : //
      41             : // This ensures there is only ever a single runnable from the entire chain on
      42             : // the main thread.  It also ensures that TQ2 and TQ3 only have a single runnable
      43             : // in TQ1 at any time.
      44             : //
      45             : // This arrangement lets you prioritize work by dispatching runnables directly
      46             : // to TQ1.  You can issue many runnables for important work.  Meanwhile the TQ2
      47             : // and TQ3 work will always execute at most one runnable and then yield.
      48             : class TaskQueue : public AbstractThread
      49             : {
      50             :   class EventTargetWrapper;
      51             : 
      52             : public:
      53             :   explicit TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
      54             :                      bool aSupportsTailDispatch = false);
      55             : 
      56             :   TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
      57             :             const char* aName,
      58             :             bool aSupportsTailDispatch = false);
      59             : 
      60             :   TaskDispatcher& TailDispatcher() override;
      61             : 
      62           0 :   TaskQueue* AsTaskQueue() override { return this; }
      63             : 
      64           0 :   void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
      65             :                 DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
      66             :                 DispatchReason aReason = NormalDispatch) override
      67             :   {
      68           0 :     nsCOMPtr<nsIRunnable> r = aRunnable;
      69             :     {
      70           0 :       MonitorAutoLock mon(mQueueMonitor);
      71           0 :       nsresult rv = DispatchLocked(/* passed by ref */r, aFailureHandling, aReason);
      72             : #if defined(DEBUG) || !defined(RELEASE_OR_BETA) || defined(EARLY_BETA_OR_EARLIER)
      73           0 :       if (NS_FAILED(rv) && aFailureHandling == AssertDispatchSuccess) {
      74           0 :         MOZ_CRASH_UNSAFE_PRINTF("%s: Dispatch failed. rv=%x", mName, uint32_t(rv));
      75             :       }
      76             : #endif
      77             :       Unused << rv;
      78             :     }
      79             :     // If the ownership of |r| is not transferred in DispatchLocked() due to
      80             :     // dispatch failure, it will be deleted here outside the lock. We do so
      81             :     // since the destructor of the runnable might access TaskQueue and result
      82             :     // in deadlocks.
      83           0 :   }
      84             : 
      85             :   // Prevent a GCC warning about the other overload of Dispatch being hidden.
      86             :   using AbstractThread::Dispatch;
      87             : 
      88             :   // Puts the queue in a shutdown state and returns immediately. The queue will
      89             :   // remain alive at least until all the events are drained, because the Runners
      90             :   // hold a strong reference to the task queue, and one of them is always held
      91             :   // by the target event queue when the task queue is non-empty.
      92             :   //
      93             :   // The returned promise is resolved when the queue goes empty.
      94             :   RefPtr<ShutdownPromise> BeginShutdown();
      95             : 
      96             :   // Blocks until all task finish executing.
      97             :   void AwaitIdle();
      98             : 
      99             :   // Blocks until the queue is flagged for shutdown and all tasks have finished
     100             :   // executing.
     101             :   void AwaitShutdownAndIdle();
     102             : 
     103             :   bool IsEmpty();
     104             :   uint32_t ImpreciseLengthForHeuristics();
     105             : 
     106             :   // Returns true if the current thread is currently running a Runnable in
     107             :   // the task queue.
     108             :   bool IsCurrentThreadIn() override;
     109             : 
     110             :   // Create a new nsIEventTarget wrapper object that dispatches to this
     111             :   // TaskQueue.
     112             :   already_AddRefed<nsISerialEventTarget> WrapAsEventTarget();
     113             : 
     114             : protected:
     115             :   virtual ~TaskQueue();
     116             : 
     117             : 
     118             :   // Blocks until all task finish executing. Called internally by methods
     119             :   // that need to wait until the task queue is idle.
     120             :   // mQueueMonitor must be held.
     121             :   void AwaitIdleLocked();
     122             : 
     123             :   nsresult DispatchLocked(nsCOMPtr<nsIRunnable>& aRunnable,
     124             :                           DispatchFailureHandling aFailureHandling,
     125             :                           DispatchReason aReason = NormalDispatch);
     126             : 
     127           0 :   void MaybeResolveShutdown()
     128             :   {
     129           0 :     mQueueMonitor.AssertCurrentThreadOwns();
     130           0 :     if (mIsShutdown && !mIsRunning) {
     131           0 :       mShutdownPromise.ResolveIfExists(true, __func__);
     132           0 :       mTarget = nullptr;
     133             :     }
     134           0 :   }
     135             : 
     136             :   nsCOMPtr<nsIEventTarget> mTarget;
     137             : 
     138             :   // Monitor that protects the queue and mIsRunning;
     139             :   Monitor mQueueMonitor;
     140             : 
     141             :   // Queue of tasks to run.
     142             :   std::queue<nsCOMPtr<nsIRunnable>> mTasks;
     143             : 
     144             :   // The thread currently running the task queue. We store a reference
     145             :   // to this so that IsCurrentThreadIn() can tell if the current thread
     146             :   // is the thread currently running in the task queue.
     147             :   //
     148             :   // This may be read on any thread, but may only be written on mRunningThread.
     149             :   // The thread can't die while we're running in it, and we only use it for
     150             :   // pointer-comparison with the current thread anyway - so we make it atomic
     151             :   // and don't refcount it.
     152             :   Atomic<PRThread*> mRunningThread;
     153             : 
     154             :   // RAII class that gets instantiated for each dispatched task.
     155             :   class AutoTaskGuard : public AutoTaskDispatcher
     156             :   {
     157             :   public:
     158           0 :     explicit AutoTaskGuard(TaskQueue* aQueue)
     159           0 :       : AutoTaskDispatcher(/* aIsTailDispatcher = */ true), mQueue(aQueue)
     160           0 :       , mLastCurrentThread(nullptr)
     161             :     {
     162             :       // NB: We don't hold the lock to aQueue here. Don't do anything that
     163             :       // might require it.
     164           0 :       MOZ_ASSERT(!mQueue->mTailDispatcher);
     165           0 :       mQueue->mTailDispatcher = this;
     166             : 
     167           0 :       mLastCurrentThread = sCurrentThreadTLS.get();
     168           0 :       sCurrentThreadTLS.set(aQueue);
     169             : 
     170           0 :       MOZ_ASSERT(mQueue->mRunningThread == nullptr);
     171           0 :       mQueue->mRunningThread = GetCurrentPhysicalThread();
     172           0 :     }
     173             : 
     174           0 :     ~AutoTaskGuard()
     175           0 :     {
     176           0 :       DrainDirectTasks();
     177             : 
     178           0 :       MOZ_ASSERT(mQueue->mRunningThread == GetCurrentPhysicalThread());
     179           0 :       mQueue->mRunningThread = nullptr;
     180             : 
     181           0 :       sCurrentThreadTLS.set(mLastCurrentThread);
     182           0 :       mQueue->mTailDispatcher = nullptr;
     183           0 :     }
     184             : 
     185             :   private:
     186             :   TaskQueue* mQueue;
     187             :   AbstractThread* mLastCurrentThread;
     188             :   };
     189             : 
     190             :   TaskDispatcher* mTailDispatcher;
     191             : 
     192             :   // True if we've dispatched an event to the target to execute events from
     193             :   // the queue.
     194             :   bool mIsRunning;
     195             : 
     196             :   // True if we've started our shutdown process.
     197             :   bool mIsShutdown;
     198             :   MozPromiseHolder<ShutdownPromise> mShutdownPromise;
     199             : 
     200             :   // The name of this TaskQueue. Useful when debugging dispatch failures.
     201             :   const char* const mName;
     202             : 
     203           0 :   class Runner : public Runnable {
     204             :   public:
     205           0 :     explicit Runner(TaskQueue* aQueue)
     206           0 :       : Runnable("TaskQueue::Runner")
     207           0 :       , mQueue(aQueue)
     208             :     {
     209           0 :     }
     210             :     NS_IMETHOD Run() override;
     211             :   private:
     212             :     RefPtr<TaskQueue> mQueue;
     213             :   };
     214             : };
     215             : 
     216             : } // namespace mozilla
     217             : 
     218             : #endif // TaskQueue_h_

Generated by: LCOV version 1.13