Bug 1314833 - Part 1.1: Refator XPCOMThreadWrapper as EventTargetWrapper to allow EventTargets to be AbstractThreads. r=jwwang
authorBevis Tseng <btseng@mozilla.com>
Tue, 29 Nov 2016 13:01:18 +0800
changeset 377899 8ab1b5ce5bbc6c1fea58d011e94fae39a2cd6cb5
parent 377898 7468856333b8faad262457d9fe09cdf0a434ac38
child 377900 1cf74b47953add5094f559f37766b5e99dfb32a7
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang
bugs1314833
milestone53.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 1314833 - Part 1.1: Refator XPCOMThreadWrapper as EventTargetWrapper to allow EventTargets to be AbstractThreads. r=jwwang MozReview-Commit-ID: FYjbKxcIc5o
xpcom/threads/AbstractThread.cpp
xpcom/threads/AbstractThread.h
--- a/xpcom/threads/AbstractThread.cpp
+++ b/xpcom/threads/AbstractThread.cpp
@@ -23,92 +23,177 @@
 namespace mozilla {
 
 LazyLogModule gMozPromiseLog("MozPromise");
 LazyLogModule gStateWatchingLog("StateWatching");
 
 StaticRefPtr<AbstractThread> sMainThread;
 MOZ_THREAD_LOCAL(AbstractThread*) AbstractThread::sCurrentThreadTLS;
 
-class XPCOMThreadWrapper : public AbstractThread
+class EventTargetWrapper : public AbstractThread
 {
 public:
-  explicit XPCOMThreadWrapper(nsIThread* aTarget, bool aRequireTailDispatch)
+  explicit EventTargetWrapper(nsIEventTarget* aTarget, bool aRequireTailDispatch)
     : AbstractThread(aRequireTailDispatch)
     , mTarget(aTarget)
   {
     // Our current mechanism of implementing tail dispatch is appshell-specific.
     // This is because a very similar mechanism already exists on the main
     // thread, and we want to avoid making event dispatch on the main thread
     // more complicated than it already is.
     //
     // If you need to use tail dispatch on other XPCOM threads, you'll need to
     // implement an nsIThreadObserver to fire the tail dispatcher at the
     // appropriate times.
+    nsCOMPtr<nsIThread> thread(do_QueryInterface(aTarget));
+    bool isOnCurrentThread = false;
+    aTarget->IsOnCurrentThread(&isOnCurrentThread);
+
     MOZ_ASSERT_IF(aRequireTailDispatch,
-                  NS_IsMainThread() && NS_GetCurrentThread() == aTarget);
+      (thread && NS_IsMainThread() && NS_GetCurrentThread() == thread) ||
+      (!thread && NS_IsMainThread() && isOnCurrentThread));
+
+    // XXX Bug 1323742:
+    // We hold mRunningThread for IsCurrentThreadIn() for now.
+    // This shall be replaced by this == GetCurrent() in the future in
+    // AbstractThread perspective instead of PR_Thread perspective.
+    mRunningThread = thread ? thread.get() : NS_GetCurrentThread();
+    MOZ_ASSERT(mRunningThread);
   }
 
   virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
                         DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
                         DispatchReason aReason = NormalDispatch) override
   {
-    nsCOMPtr<nsIRunnable> r = aRunnable;
     AbstractThread* currentThread;
     if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
-      currentThread->TailDispatcher().AddTask(this, r.forget(), aFailureHandling);
+      currentThread->TailDispatcher().AddTask(this, Move(aRunnable), aFailureHandling);
       return;
     }
 
-    nsresult rv = mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
+    RefPtr<nsIRunnable> runner(new Runner(this, Move(aRunnable), false /* already drained by TaskGroupRunnable  */));
+    nsresult rv = mTarget->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
     MOZ_DIAGNOSTIC_ASSERT(aFailureHandling == DontAssertDispatchSuccess || NS_SUCCEEDED(rv));
     Unused << rv;
   }
 
   virtual bool IsCurrentThreadIn() override
   {
     // Compare NSPR threads so that this works after shutdown when
     // NS_GetCurrentThread starts returning null.
     PRThread* thread = nullptr;
-    mTarget->GetPRThread(&thread);
+    mRunningThread->GetPRThread(&thread);
     bool in = PR_GetCurrentThread() == thread;
     return in;
   }
 
   void FireTailDispatcher()
   {
     MOZ_DIAGNOSTIC_ASSERT(mTailDispatcher.isSome());
     mTailDispatcher.ref().DrainDirectTasks();
     mTailDispatcher.reset();
   }
 
   virtual TaskDispatcher& TailDispatcher() override
   {
-    MOZ_ASSERT(this == sMainThread); // See the comment in the constructor.
+    // See the comment in the constructor.
+    MOZ_ASSERT(mRunningThread ==
+      static_cast<EventTargetWrapper*>(sMainThread.get())->mRunningThread);
     MOZ_ASSERT(IsCurrentThreadIn());
     if (!mTailDispatcher.isSome()) {
       mTailDispatcher.emplace(/* aIsTailDispatcher = */ true);
 
-      nsCOMPtr<nsIRunnable> event = NewRunnableMethod(this, &XPCOMThreadWrapper::FireTailDispatcher);
+      nsCOMPtr<nsIRunnable> event = NewRunnableMethod(this, &EventTargetWrapper::FireTailDispatcher);
       nsContentUtils::RunInStableState(event.forget());
     }
 
     return mTailDispatcher.ref();
   }
 
   virtual bool MightHaveTailTasks() override
   {
     return mTailDispatcher.isSome();
   }
 
-  virtual nsIThread* AsXPCOMThread() override { return mTarget; }
+  virtual nsIEventTarget* AsEventTarget() override { return mTarget; }
 
 private:
-  RefPtr<nsIThread> mTarget;
+  nsCOMPtr<nsIThread> mRunningThread;
+  RefPtr<nsIEventTarget> mTarget;
   Maybe<AutoTaskDispatcher> mTailDispatcher;
+
+  virtual already_AddRefed<nsIRunnable>
+  CreateDirectTaskDrainer(already_AddRefed<nsIRunnable> aRunnable) override
+  {
+    RefPtr<Runner> runner =
+      new Runner(this, Move(aRunnable), /* aDrainDirectTasks */ true);
+    return runner.forget();
+  }
+
+  class Runner : public Runnable {
+  public:
+    explicit Runner(EventTargetWrapper* aThread,
+                    already_AddRefed<nsIRunnable> aRunnable,
+                    bool aDrainDirectTasks)
+      : mThread(aThread)
+      , mRunnable(aRunnable)
+      , mDrainDirectTasks(aDrainDirectTasks)
+    {
+    }
+
+    NS_IMETHOD Run() override
+    {
+      class MOZ_STACK_CLASS AutoTaskGuard final {
+      public:
+        explicit AutoTaskGuard(EventTargetWrapper* aThread)
+          : mLastCurrentThread(nullptr)
+        {
+          MOZ_ASSERT(aThread);
+          mLastCurrentThread = sCurrentThreadTLS.get();
+          sCurrentThreadTLS.set(aThread);
+        }
+
+        ~AutoTaskGuard()
+        {
+          sCurrentThreadTLS.set(mLastCurrentThread);
+        }
+      private:
+        AbstractThread* mLastCurrentThread;
+      } taskGuard(mThread);
+
+      MOZ_ASSERT(mThread == AbstractThread::GetCurrent());
+      MOZ_ASSERT(mThread->IsCurrentThreadIn());
+      nsresult rv = mRunnable->Run();
+
+      if (mDrainDirectTasks) {
+        mThread->TailDispatcher().DrainDirectTasks();
+      }
+
+      return rv;
+    }
+
+    NS_IMETHOD GetName(nsACString& aName) override
+    {
+      aName.AssignLiteral("AbstractThread::Runner");
+      if (nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable)) {
+        nsAutoCString name;
+        named->GetName(name);
+        if (!name.IsEmpty()) {
+          aName.AppendLiteral(" for ");
+          aName.Append(name);
+        }
+      }
+      return NS_OK;
+    }
+
+  private:
+    RefPtr<EventTargetWrapper> mThread;
+    RefPtr<nsIRunnable> mRunnable;
+    bool mDrainDirectTasks;
+  };
 };
 
 void
 AbstractThread::TailDispatchTasksFor(AbstractThread* aThread)
 {
   if (MightHaveTailTasks()) {
     TailDispatcher().DispatchTasksFor(aThread);
   }
@@ -149,17 +234,17 @@ AbstractThread::MainThread()
 void
 AbstractThread::InitStatics()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!sMainThread);
   nsCOMPtr<nsIThread> mainThread;
   NS_GetMainThread(getter_AddRefs(mainThread));
   MOZ_DIAGNOSTIC_ASSERT(mainThread);
-  sMainThread = new XPCOMThreadWrapper(mainThread.get(), /* aRequireTailDispatch = */ true);
+  sMainThread = new EventTargetWrapper(mainThread.get(), /* aRequireTailDispatch = */ true);
   ClearOnShutdown(&sMainThread);
 
   if (!sCurrentThreadTLS.init()) {
     MOZ_CRASH();
   }
   sCurrentThreadTLS.set(sMainThread);
 }
 
@@ -174,19 +259,35 @@ AbstractThread::DispatchDirectTask(alrea
 {
   GetCurrent()->TailDispatcher().AddDirectTask(Move(aRunnable));
 }
 
 /* static */
 already_AddRefed<AbstractThread>
 AbstractThread::CreateXPCOMThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch)
 {
-  RefPtr<XPCOMThreadWrapper> wrapper = new XPCOMThreadWrapper(aThread, aRequireTailDispatch);
+  RefPtr<EventTargetWrapper> wrapper = new EventTargetWrapper(aThread, aRequireTailDispatch);
   // Set the thread-local sCurrentThreadTLS to point to the wrapper on the
   // target thread. This ensures that sCurrentThreadTLS is as expected by
   // AbstractThread::GetCurrent() on the target thread.
   nsCOMPtr<nsIRunnable> r =
     NS_NewRunnableFunction([wrapper]() { sCurrentThreadTLS.set(wrapper); });
   aThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
   return wrapper.forget();
 }
 
+/* static  */
+already_AddRefed<AbstractThread>
+AbstractThread::CreateEventTargetWrapper(nsIEventTarget* aEventTarget,
+                                         bool aRequireTailDispatch)
+{
+  MOZ_ASSERT(aEventTarget);
+  nsCOMPtr<nsIThread> thread(do_QueryInterface(aEventTarget));
+  Unused << thread; // simpler than DebugOnly<nsCOMPtr<nsIThread>>
+  MOZ_ASSERT(!thread, "nsIThread should be wrapped by CreateXPCOMThreadWrapper!");
+
+  RefPtr<EventTargetWrapper> wrapper =
+    new EventTargetWrapper(aEventTarget, aRequireTailDispatch);
+
+  return wrapper.forget();
+}
+
 } // namespace mozilla
--- a/xpcom/threads/AbstractThread.h
+++ b/xpcom/threads/AbstractThread.h
@@ -23,33 +23,39 @@ class TaskDispatcher;
 /*
  * We often want to run tasks on a target that guarantees that events will never
  * run in parallel. There are various target types that achieve this - namely
  * nsIThread and TaskQueue. Note that nsIThreadPool (which implements
  * nsIEventTarget) does not have this property, so we do not want to use
  * nsIEventTarget for this purpose. This class encapsulates the specifics of
  * the structures we might use here and provides a consistent interface.
  *
- * At present, the supported AbstractThread implementations are TaskQueue
- * and AbstractThread::MainThread. If you add support for another thread that is
- * not the MainThread, you'll need to figure out how to make it unique such that
- * comparing AbstractThread pointers is equivalent to comparing nsIThread pointers.
+ * At present, the supported AbstractThread implementations are TaskQueue,
+ * AbstractThread::MainThread() and DocGroup::AbstractThreadFor().
+ * If you add support for another thread that is not the MainThread, you'll need
+ * to figure out how to make it unique such that comparing AbstractThread
+ * pointers is equivalent to comparing nsIThread pointers.
  */
 class AbstractThread
 {
 public:
   // Returns the AbstractThread that the caller is currently running in, or null
   // if the caller is not running in an AbstractThread.
   static AbstractThread* GetCurrent() { return sCurrentThreadTLS.get(); }
 
   AbstractThread(bool aSupportsTailDispatch) : mSupportsTailDispatch(aSupportsTailDispatch) {}
 
+  // Returns an AbstractThread wrapper of a nsIThread.
   static already_AddRefed<AbstractThread>
   CreateXPCOMThreadWrapper(nsIThread* aThread, bool aRequireTailDispatch);
 
+  // Returns an AbstractThread wrapper of a non-nsIThread EventTarget on the main thread.
+  static already_AddRefed<AbstractThread>
+  CreateEventTargetWrapper(nsIEventTarget* aEventTarget, bool aRequireTailDispatch);
+
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractThread);
 
   enum DispatchFailureHandling { AssertDispatchSuccess, DontAssertDispatchSuccess };
   enum DispatchReason { NormalDispatch, TailDispatch };
   virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
                         DispatchFailureHandling aHandling = AssertDispatchSuccess,
                         DispatchReason aReason = NormalDispatch) = 0;
 
@@ -80,28 +86,38 @@ public:
   bool SupportsTailDispatch() const { return mSupportsTailDispatch; }
 
   // Returns true if this thread requires all dispatches originating from
   // aThread go through the tail dispatcher.
   bool RequiresTailDispatch(AbstractThread* aThread) const;
   bool RequiresTailDispatchFromCurrentThread() const;
 
   virtual TaskQueue* AsTaskQueue() { MOZ_CRASH("Not a task queue!"); }
-  virtual nsIThread* AsXPCOMThread() { MOZ_CRASH("Not an XPCOM thread!"); }
+  virtual nsIEventTarget* AsEventTarget() { MOZ_CRASH("Not an event target!"); }
 
-  // Convenience method for getting an AbstractThread for the main thread.
+  // Returns the non-DocGroup version of AbstractThread on the main thread.
+  // A DocGroup-versioned one is available in DispatcherTrait::AbstractThreadFor().
+  // Note: DispatcherTrait::AbstractThreadFor() SHALL be used when possible.
   static AbstractThread* MainThread();
 
   // Must be called exactly once during startup.
   static void InitStatics();
 
   void DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable);
 
   static void DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable);
 
+  // Create a runnable that will run |aRunnable| and drain the direct tasks
+  // generated by it.
+  virtual already_AddRefed<nsIRunnable>
+  CreateDirectTaskDrainer(already_AddRefed<nsIRunnable> aRunnable)
+  {
+    MOZ_CRASH("Not support!");
+  }
+
 protected:
   virtual ~AbstractThread() {}
   static MOZ_THREAD_LOCAL(AbstractThread*) sCurrentThreadTLS;
 
   // True if we want to require that every task dispatched from tasks running in
   // this queue go through our queue's tail dispatcher.
   const bool mSupportsTailDispatch;
 };