Bug 1300118 P2 Make TaskQueue implement nsIEventTarget. r=bholley
authorBen Kelly <ben@wanderview.com>
Tue, 13 Sep 2016 20:12:15 -0700
changeset 313771 4844015b5fb76e47518cda47c7eb36188bd9df37
parent 313770 90a6f6dc4543bf8a57de97ff0b31f2197e3d4ce5
child 313772 e72b06de40c005266f3fed08331eaf7141051a60
push id30697
push usercbook@mozilla.com
push dateWed, 14 Sep 2016 10:04:12 +0000
treeherdermozilla-central@de96dcebba86 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1300118
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1300118 P2 Make TaskQueue implement nsIEventTarget. r=bholley
xpcom/threads/TaskQueue.cpp
xpcom/threads/TaskQueue.h
--- a/xpcom/threads/TaskQueue.cpp
+++ b/xpcom/threads/TaskQueue.cpp
@@ -1,20 +1,72 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/TaskQueue.h"
 
+#include "nsIEventTarget.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 
+class TaskQueue::EventTargetWrapper final : public nsIEventTarget
+{
+  RefPtr<TaskQueue> mTaskQueue;
+
+  ~EventTargetWrapper()
+  {
+  }
+
+public:
+  explicit EventTargetWrapper(TaskQueue* aTaskQueue)
+    : mTaskQueue(aTaskQueue)
+  {
+    MOZ_ASSERT(mTaskQueue);
+  }
+
+  NS_IMETHOD
+  DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) override
+  {
+    nsCOMPtr<nsIRunnable> ref = aEvent;
+    return Dispatch(ref.forget(), aFlags);
+  }
+
+  NS_IMETHOD
+  Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags) override
+  {
+    nsCOMPtr<nsIRunnable> runnable = aEvent;
+    MonitorAutoLock mon(mTaskQueue->mQueueMonitor);
+    return mTaskQueue->DispatchLocked(/* passed by ref */runnable,
+                                      AbortIfFlushing,
+                                      DontAssertDispatchSuccess,
+                                      NormalDispatch);
+  }
+
+  NS_IMETHOD
+  DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t aFlags) override
+  {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  NS_IMETHOD
+  IsOnCurrentThread(bool* aResult) override
+  {
+    *aResult = mTaskQueue->IsCurrentThreadIn();
+    return NS_OK;
+  }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(TaskQueue::EventTargetWrapper, nsIEventTarget)
+
 TaskQueue::TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
                      bool aRequireTailDispatch)
   : AbstractThread(aRequireTailDispatch)
   , mTarget(aTarget)
   , mQueueMonitor("TaskQueue::Queue")
   , mTailDispatcher(nullptr)
   , mIsRunning(false)
   , mIsShutdown(false)
@@ -138,16 +190,23 @@ TaskQueue::IsEmpty()
 bool
 TaskQueue::IsCurrentThreadIn()
 {
   bool in = NS_GetCurrentThread() == mRunningThread;
   MOZ_ASSERT(in == (GetCurrent() == this));
   return in;
 }
 
+already_AddRefed<nsIEventTarget>
+TaskQueue::WrapAsEventTarget()
+{
+  nsCOMPtr<nsIEventTarget> ref = new EventTargetWrapper(this);
+  return ref.forget();
+}
+
 nsresult
 TaskQueue::Runner::Run()
 {
   RefPtr<nsIRunnable> event;
   {
     MonitorAutoLock mon(mQueue->mQueueMonitor);
     MOZ_ASSERT(mQueue->mIsRunning);
     if (mQueue->mTasks.size() == 0) {
--- a/xpcom/threads/TaskQueue.h
+++ b/xpcom/threads/TaskQueue.h
@@ -24,17 +24,36 @@ namespace mozilla {
 
 typedef MozPromise<bool, bool, false> ShutdownPromise;
 
 // Abstracts executing runnables in order on an arbitrary event target. The
 // runnables dispatched to the TaskQueue will be executed in the order in which
 // they're received, and are guaranteed to not be executed concurrently.
 // They may be executed on different threads, and a memory barrier is used
 // to make this threadsafe for objects that aren't already threadsafe.
-class TaskQueue : public AbstractThread {
+//
+// Note, since a TaskQueue can also be converted to an nsIEventTarget using
+// WrapAsEventTarget() its possible to construct a hierarchy of TaskQueues.
+// Consider these three TaskQueues:
+//
+//  TQ1 dispatches to the main thread
+//  TQ2 dispatches to TQ1
+//  TQ3 dispatches to TQ1
+//
+// This ensures there is only ever a single runnable from the entire chain on
+// the main thread.  It also ensures that TQ2 and TQ3 only have a single runnable
+// in TQ1 at any time.
+//
+// This arrangement lets you prioritize work by dispatching runnables directly
+// to TQ1.  You can issue many runnables for important work.  Meanwhile the TQ2
+// and TQ3 work will always execute at most one runnable and then yield.
+class TaskQueue : public AbstractThread
+{
+  class EventTargetWrapper;
+
 public:
   explicit TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
                      bool aSupportsTailDispatch = false);
 
   TaskDispatcher& TailDispatcher() override;
 
   TaskQueue* AsTaskQueue() override { return this; }
 
@@ -71,16 +90,20 @@ public:
   void AwaitShutdownAndIdle();
 
   bool IsEmpty();
 
   // Returns true if the current thread is currently running a Runnable in
   // the task queue.
   bool IsCurrentThreadIn() override;
 
+  // Create a new nsIEventTarget wrapper object that dispatches to this
+  // TaskQueue.
+  already_AddRefed<nsIEventTarget> WrapAsEventTarget();
+
 protected:
   virtual ~TaskQueue();
 
 
   // Blocks until all task finish executing. Called internally by methods
   // that need to wait until the task queue is idle.
   // mQueueMonitor must be held.
   void AwaitIdleLocked();