Bug 1361164 - Add nsISerialEventTarget (r=froydnj)
authorBill McCloskey <billm@mozilla.com>
Mon, 22 May 2017 14:25:43 -0700
changeset 363614 5dae479fd477bd4ed51db736e259fc7a4542221a
parent 363613 a26040f4d439626f98509caa71009675721d12d7
child 363615 a2e617b58ebaca758fbbca97bd385fafc9bec707
push id32021
push usercbook@mozilla.com
push dateTue, 13 Jun 2017 10:10:08 +0000
treeherdermozilla-central@a8f8e440d627 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1361164
milestone56.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 1361164 - Add nsISerialEventTarget (r=froydnj) MozReview-Commit-ID: 8y1GdEGCPSB
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
xpcom/tests/gtest/TestEventTargetQI.cpp
xpcom/tests/gtest/TestFile.cpp
xpcom/tests/gtest/moz.build
xpcom/threads/LazyIdleThread.cpp
xpcom/threads/SchedulerGroup.cpp
xpcom/threads/TaskQueue.cpp
xpcom/threads/TaskQueue.h
xpcom/threads/ThrottledEventQueue.cpp
xpcom/threads/moz.build
xpcom/threads/nsISerialEventTarget.idl
xpcom/threads/nsIThread.idl
xpcom/threads/nsThread.cpp
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2065,17 +2065,17 @@ WorkerLoadInfo::ProxyReleaseMainThreadOb
 
   RefPtr<MainThreadReleaseRunnable> runnable =
     new MainThreadReleaseRunnable(doomed, aLoadGroupToCancel);
   return NS_SUCCEEDED(aWorkerPrivate->DispatchToMainThread(runnable.forget()));
 }
 
 template <class Derived>
 class WorkerPrivateParent<Derived>::EventTarget final
-  : public nsIEventTarget
+  : public nsISerialEventTarget
 {
   // This mutex protects mWorkerPrivate and must be acquired *before* the
   // WorkerPrivate's mutex whenever they must both be held.
   mozilla::Mutex mMutex;
   WorkerPrivate* mWorkerPrivate;
   nsIEventTarget* mWeakNestedEventTarget;
   nsCOMPtr<nsIEventTarget> mNestedEventTarget;
 
@@ -3003,22 +3003,22 @@ WorkerPrivateParent<Derived>::MaybeWrapA
   }
 
   workerRunnable =
     new ExternalRunnableWrapper(ParentAsWorkerPrivate(), runnable);
   return workerRunnable.forget();
 }
 
 template <class Derived>
-already_AddRefed<nsIEventTarget>
+already_AddRefed<nsISerialEventTarget>
 WorkerPrivateParent<Derived>::GetEventTarget()
 {
   WorkerPrivate* self = ParentAsWorkerPrivate();
 
-  nsCOMPtr<nsIEventTarget> target;
+  nsCOMPtr<nsISerialEventTarget> target;
 
   {
     MutexAutoLock lock(mMutex);
 
     if (!mEventTarget &&
         ParentStatus() <= Running &&
         self->mStatus <= Running) {
       mEventTarget = new EventTarget(self);
@@ -7017,16 +7017,17 @@ NS_IMPL_ISUPPORTS_INHERITED0(ExternalRun
 template <class Derived>
 NS_IMPL_ADDREF(WorkerPrivateParent<Derived>::EventTarget)
 
 template <class Derived>
 NS_IMPL_RELEASE(WorkerPrivateParent<Derived>::EventTarget)
 
 template <class Derived>
 NS_INTERFACE_MAP_BEGIN(WorkerPrivateParent<Derived>::EventTarget)
+  NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 #ifdef DEBUG
   // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
   // result.
   if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
     *aInstancePtr = this;
     return NS_OK;
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -43,16 +43,17 @@
 
 class nsIChannel;
 class nsIConsoleReportCollector;
 class nsIDocument;
 class nsIEventTarget;
 class nsIPrincipal;
 class nsIScriptContext;
 class nsIScriptTimeoutHandler;
+class nsISerialEventTarget;
 class nsISerializable;
 class nsIThread;
 class nsIThreadInternal;
 class nsITimer;
 class nsIURI;
 template<class T> class nsMainThreadPtrHandle;
 
 namespace JS {
@@ -330,17 +331,17 @@ public:
   DispatchControlRunnable(already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable);
 
   nsresult
   DispatchDebuggerRunnable(already_AddRefed<WorkerRunnable> aDebuggerRunnable);
 
   already_AddRefed<WorkerRunnable>
   MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable);
 
-  already_AddRefed<nsIEventTarget>
+  already_AddRefed<nsISerialEventTarget>
   GetEventTarget();
 
   // May be called on any thread...
   bool
   Start();
 
   // Called on the parent thread.
   bool
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/gtest/TestEventTargetQI.cpp
@@ -0,0 +1,95 @@
+/* -*- 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/LazyIdleThread.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/SystemGroup.h"
+#include "mozilla/ThrottledEventQueue.h"
+#include "nsCOMPtr.h"
+#include "nsIThreadPool.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMCIDInternal.h"
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+TEST(TestEventTargetQI, ThreadPool)
+{
+  nsCOMPtr<nsIThreadPool> thing = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
+  EXPECT_TRUE(thing);
+
+  nsCOMPtr<nsISerialEventTarget> serial = do_QueryInterface(thing);
+  EXPECT_FALSE(serial);
+
+  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(thing);
+  EXPECT_TRUE(target);
+
+  thing->Shutdown();
+}
+
+TEST(TestEventTargetQI, SharedThreadPool)
+{
+  nsCOMPtr<nsIThreadPool> thing = SharedThreadPool::Get(NS_LITERAL_CSTRING("TestPool"), 1);
+  EXPECT_TRUE(thing);
+
+  nsCOMPtr<nsISerialEventTarget> serial = do_QueryInterface(thing);
+  EXPECT_FALSE(serial);
+
+  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(thing);
+  EXPECT_TRUE(target);
+}
+
+TEST(TestEventTargetQI, Thread)
+{
+  nsCOMPtr<nsIThread> thing = do_GetCurrentThread();
+  EXPECT_TRUE(thing);
+
+  nsCOMPtr<nsISerialEventTarget> serial = do_QueryInterface(thing);
+  EXPECT_TRUE(serial);
+
+  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(thing);
+  EXPECT_TRUE(target);
+}
+
+TEST(TestEventTargetQI, ThrottledEventQueue)
+{
+  nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+  RefPtr<ThrottledEventQueue> thing = ThrottledEventQueue::Create(thread);
+  EXPECT_TRUE(thing);
+
+  nsCOMPtr<nsISerialEventTarget> serial = do_QueryInterface(thing);
+  EXPECT_FALSE(serial);
+
+  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(thing);
+  EXPECT_TRUE(target);
+}
+
+TEST(TestEventTargetQI, LazyIdleThread)
+{
+  nsCOMPtr<nsIThread> thing = new LazyIdleThread(0, NS_LITERAL_CSTRING("TestThread"));
+  EXPECT_TRUE(thing);
+
+  nsCOMPtr<nsISerialEventTarget> serial = do_QueryInterface(thing);
+  EXPECT_TRUE(serial);
+
+  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(thing);
+  EXPECT_TRUE(target);
+
+  thing->Shutdown();
+}
+
+TEST(TestEventTargetQI, SchedulerGroup)
+{
+  nsCOMPtr<nsIEventTarget> thing = SystemGroup::EventTargetFor(TaskCategory::Other);
+  EXPECT_TRUE(thing);
+
+  nsCOMPtr<nsISerialEventTarget> serial = do_QueryInterface(thing);
+  EXPECT_TRUE(serial);
+
+  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(thing);
+  EXPECT_TRUE(target);
+}
--- a/xpcom/tests/gtest/TestFile.cpp
+++ b/xpcom/tests/gtest/TestFile.cpp
@@ -2,16 +2,17 @@
 /* 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 "prio.h"
 #include "prsystem.h"
 
 #include "nsIFile.h"
+#include "nsString.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 
 #include "gtest/gtest.h"
 
 static bool VerifyResult(nsresult aRV, const char* aMsg)
 {
   bool failed = NS_FAILED(aRV);
--- a/xpcom/tests/gtest/moz.build
+++ b/xpcom/tests/gtest/moz.build
@@ -12,16 +12,17 @@ UNIFIED_SOURCES += [
     'TestAutoRef.cpp',
     'TestBase64.cpp',
     'TestCallTemplates.cpp',
     'TestCloneInputStream.cpp',
     'TestCOMPtrEq.cpp',
     'TestCRT.cpp',
     'TestEncoding.cpp',
     'TestEscapeURL.cpp',
+    'TestEventTargetQI.cpp',
     'TestExpirationTracker.cpp',
     'TestFile.cpp',
     'TestGCPostBarriers.cpp',
     'TestID.cpp',
     'TestMultiplexInputStream.cpp',
     'TestNsDeque.cpp',
     'TestNSPRLogModulesParser.cpp',
     'TestObserverArray.cpp',
--- a/xpcom/threads/LazyIdleThread.cpp
+++ b/xpcom/threads/LazyIdleThread.cpp
@@ -375,16 +375,17 @@ LazyIdleThread::Release()
     }
   }
 
   return count;
 }
 
 NS_IMPL_QUERY_INTERFACE(LazyIdleThread, nsIThread,
                         nsIEventTarget,
+                        nsISerialEventTarget,
                         nsITimerCallback,
                         nsIThreadObserver,
                         nsIObserver)
 
 NS_IMETHODIMP
 LazyIdleThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
 {
   nsCOMPtr<nsIRunnable> event(aEvent);
--- a/xpcom/threads/SchedulerGroup.cpp
+++ b/xpcom/threads/SchedulerGroup.cpp
@@ -22,17 +22,17 @@ using namespace mozilla;
 /* SchedulerEventTarget */
 
 namespace {
 
 #define NS_DISPATCHEREVENTTARGET_IID \
 { 0xbf4e36c8, 0x7d04, 0x4ef4, \
   { 0xbb, 0xd8, 0x11, 0x09, 0x0a, 0xdb, 0x4d, 0xf7 } }
 
-class SchedulerEventTarget final : public nsIEventTarget
+class SchedulerEventTarget final : public nsISerialEventTarget
 {
   RefPtr<SchedulerGroup> mDispatcher;
   TaskCategory mCategory;
 
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DISPATCHEREVENTTARGET_IID)
 
   SchedulerEventTarget(SchedulerGroup* aDispatcher, TaskCategory aCategory)
@@ -126,17 +126,20 @@ AutoCollectVsyncTelemetry::CollectTeleme
 
   Telemetry::Accumulate(Telemetry::CONTENT_JS_KNOWN_TICK_DELAY_MS, duration);
 
   return;
 }
 
 } // namespace
 
-NS_IMPL_ISUPPORTS(SchedulerEventTarget, SchedulerEventTarget, nsIEventTarget)
+NS_IMPL_ISUPPORTS(SchedulerEventTarget,
+                  SchedulerEventTarget,
+                  nsIEventTarget,
+                  nsISerialEventTarget)
 
 NS_IMETHODIMP
 SchedulerEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
 {
   return Dispatch(do_AddRef(aRunnable), aFlags);
 }
 
 NS_IMETHODIMP
--- a/xpcom/threads/TaskQueue.cpp
+++ b/xpcom/threads/TaskQueue.cpp
@@ -1,22 +1,22 @@
 /* -*- 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 "nsISerialEventTarget.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 
-class TaskQueue::EventTargetWrapper final : public nsIEventTarget
+class TaskQueue::EventTargetWrapper final : public nsISerialEventTarget
 {
   RefPtr<TaskQueue> mTaskQueue;
 
   ~EventTargetWrapper()
   {
   }
 
 public:
@@ -60,17 +60,19 @@ public:
   IsOnCurrentThreadInfallible() override
   {
     return mTaskQueue->mTarget->IsOnCurrentThread();
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
 };
 
-NS_IMPL_ISUPPORTS(TaskQueue::EventTargetWrapper, nsIEventTarget)
+NS_IMPL_ISUPPORTS(TaskQueue::EventTargetWrapper,
+                  nsIEventTarget,
+                  nsISerialEventTarget)
 
 TaskQueue::TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
                      const char* aName,
                      bool aRequireTailDispatch)
   : AbstractThread(aRequireTailDispatch)
   , mTarget(aTarget)
   , mQueueMonitor("TaskQueue::Queue")
   , mTailDispatcher(nullptr)
@@ -206,20 +208,20 @@ TaskQueue::ImpreciseLengthForHeuristics(
 
 bool
 TaskQueue::IsCurrentThreadIn()
 {
   bool in = NS_GetCurrentThread() == mRunningThread;
   return in;
 }
 
-already_AddRefed<nsIEventTarget>
+already_AddRefed<nsISerialEventTarget>
 TaskQueue::WrapAsEventTarget()
 {
-  nsCOMPtr<nsIEventTarget> ref = new EventTargetWrapper(this);
+  nsCOMPtr<nsISerialEventTarget> ref = new EventTargetWrapper(this);
   return ref.forget();
 }
 
 nsresult
 TaskQueue::Runner::Run()
 {
   RefPtr<nsIRunnable> event;
   {
--- a/xpcom/threads/TaskQueue.h
+++ b/xpcom/threads/TaskQueue.h
@@ -101,17 +101,17 @@ public:
   uint32_t ImpreciseLengthForHeuristics();
 
   // 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();
+  already_AddRefed<nsISerialEventTarget> 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.
--- a/xpcom/threads/ThrottledEventQueue.cpp
+++ b/xpcom/threads/ThrottledEventQueue.cpp
@@ -369,17 +369,19 @@ public:
     return mBaseTarget->IsOnCurrentThread();
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS(ThrottledEventQueue::Inner, nsIObserver);
 
-NS_IMPL_ISUPPORTS(ThrottledEventQueue, ThrottledEventQueue, nsIEventTarget);
+NS_IMPL_ISUPPORTS(ThrottledEventQueue,
+                  ThrottledEventQueue,
+                  nsIEventTarget);
 
 ThrottledEventQueue::ThrottledEventQueue(already_AddRefed<Inner> aInner)
   : mInner(aInner)
 {
   MOZ_ASSERT(mInner);
 }
 
 ThrottledEventQueue::~ThrottledEventQueue()
--- a/xpcom/threads/moz.build
+++ b/xpcom/threads/moz.build
@@ -6,16 +6,17 @@
 
 XPIDL_SOURCES += [
     'nsIEnvironment.idl',
     'nsIEventTarget.idl',
     'nsIIdlePeriod.idl',
     'nsINamed.idl',
     'nsIProcess.idl',
     'nsIRunnable.idl',
+    'nsISerialEventTarget.idl',
     'nsISupportsPriority.idl',
     'nsIThread.idl',
     'nsIThreadInternal.idl',
     'nsIThreadManager.idl',
     'nsIThreadPool.idl',
     'nsITimer.idl',
 ]
 
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/nsISerialEventTarget.idl
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "nsIEventTarget.idl"
+
+/**
+ * A serial event target is an event dispatching interface like
+ * nsIEventTarget. Runnables dispatched to an nsISerialEventTarget are required
+ * to execute serially. That is, two different runnables dispatched to the
+ * target should never be allowed to execute simultaneously. So, for example, a
+ * thread pool should not implement nsISerialEventTarget. However, one can
+ * "convert" a thread pool into an nsISerialEventTarget by putting a
+ * TaskQueue in front of it.
+ */
+[builtinclass, scriptable, uuid(9f982380-24b4-49f3-88f6-45e2952036c7)]
+interface nsISerialEventTarget : nsIEventTarget
+{
+};
--- a/xpcom/threads/nsIThread.idl
+++ b/xpcom/threads/nsIThread.idl
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "nsIEventTarget.idl"
+#include "nsISerialEventTarget.idl"
 #include "nsIIdlePeriod.idl"
 
 %{C++
 #include "mozilla/AlreadyAddRefed.h"
 %}
 
 [ptr] native PRThread(PRThread);
 
@@ -20,17 +20,17 @@ native alreadyAddRefed_nsIIdlePeriod(alr
  * thread.
  *
  * Threads have a built-in event queue, and a thread is an event target that
  * can receive nsIRunnable objects (events) to be processed on the thread.
  *
  * See nsIThreadManager for the API used to create and locate threads.
  */
 [builtinclass, scriptable, uuid(5801d193-29d1-4964-a6b7-70eb697ddf2b)]
-interface nsIThread : nsIEventTarget
+interface nsIThread : nsISerialEventTarget
 {
   /**
    * @returns
    *   The NSPR thread object corresponding to this nsIThread.
    */
   [noscript] readonly attribute PRThread PRThread;
 
   /**
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -176,16 +176,17 @@ nsThreadClassInfo::GetClassIDNoAlloc(nsC
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ADDREF(nsThread)
 NS_IMPL_RELEASE(nsThread)
 NS_INTERFACE_MAP_BEGIN(nsThread)
   NS_INTERFACE_MAP_ENTRY(nsIThread)
   NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
+  NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
     static nsThreadClassInfo sThreadClassInfo;
     foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
   } else
 NS_INTERFACE_MAP_END
 NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,