Bug 130118 P2 Make TaskQueue implement nsIEventTarget. r=bholley
authorBen Kelly <ben@wanderview.com>
Mon, 12 Sep 2016 11:21:01 -0700
changeset 313529 4b66e6bb1db83dca773b01c9bcb35c2f93ef712b
parent 313528 61813035f9301376b8e4cce9fbb3524b6102392c
child 313530 0393ff1b2645b623ec3cd750d18d7b7ded75f0b1
push id81651
push userbkelly@mozilla.com
push dateMon, 12 Sep 2016 18:21:09 +0000
treeherdermozilla-inbound@980659720b86 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs130118
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 130118 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();