Bug 1524006 - Add a medium-high priority queue between high and normal, r=froydnj
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 28 Feb 2019 22:38:53 +0200
changeset 461874 535b72af7ed8161582fb3a84f88689639c177435
parent 461873 0507c090702b82431521119b5d6f9873d8e0486f
child 461875 9ce3a685bbcd0901128dab0c7261ae00dd209381
push id35631
push userrgurzau@mozilla.com
push dateFri, 01 Mar 2019 13:06:03 +0000
treeherdermozilla-central@d4e19870e27f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1524006
milestone67.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 1524006 - Add a medium-high priority queue between high and normal, r=froydnj
modules/libpref/init/StaticPrefList.h
xpcom/threads/AbstractEventQueue.h
xpcom/threads/MainThreadQueue.h
xpcom/threads/PrioritizedEventQueue.cpp
xpcom/threads/PrioritizedEventQueue.h
xpcom/threads/ThreadEventQueue.cpp
xpcom/threads/nsIRunnable.idl
xpcom/threads/nsThreadUtils.cpp
xpcom/threads/nsThreadUtils.h
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -2199,13 +2199,19 @@ VARCACHE_PREF(
 
 // How many reports should be stored in the report queue before being delivered.
 VARCACHE_PREF(
   "dom.reporting.delivering.maxReports",
    dom_reporting_delivering_maxReports,
   uint32_t, 100
 )
 
+VARCACHE_PREF(
+  "medium_high_event_queue.enabled",
+   medium_high_event_queue_enabled,
+  RelaxedAtomicBool, true
+)
+
 //---------------------------------------------------------------------------
 // End of prefs
 //---------------------------------------------------------------------------
 
 // clang-format on
--- a/xpcom/threads/AbstractEventQueue.h
+++ b/xpcom/threads/AbstractEventQueue.h
@@ -13,16 +13,17 @@
 
 class nsIRunnable;
 
 namespace mozilla {
 
 enum class EventQueuePriority {
   High,
   Input,
+  MediumHigh,
   Normal,
   DeferredTimers,
   Idle,
 
   Count
 };
 
 // AbstractEventQueue is an abstract base class for all our unsynchronized event
--- a/xpcom/threads/MainThreadQueue.h
+++ b/xpcom/threads/MainThreadQueue.h
@@ -20,16 +20,17 @@ template <typename SynchronizedQueueT, t
 inline already_AddRefed<nsThread> CreateMainThread(
     nsIIdlePeriod* aIdlePeriod,
     SynchronizedQueueT** aSynchronizedQueue = nullptr) {
   using MainThreadQueueT = PrioritizedEventQueue<InnerQueueT>;
 
   auto queue = MakeUnique<MainThreadQueueT>(
       MakeUnique<InnerQueueT>(EventQueuePriority::High),
       MakeUnique<InnerQueueT>(EventQueuePriority::Input),
+      MakeUnique<InnerQueueT>(EventQueuePriority::MediumHigh),
       MakeUnique<InnerQueueT>(EventQueuePriority::Normal),
       MakeUnique<InnerQueueT>(EventQueuePriority::DeferredTimers),
       MakeUnique<InnerQueueT>(EventQueuePriority::Idle),
       do_AddRef(aIdlePeriod));
 
   MainThreadQueueT* prioritized = queue.get();
 
   RefPtr<SynchronizedQueueT> synchronizedQueue =
--- a/xpcom/threads/PrioritizedEventQueue.cpp
+++ b/xpcom/threads/PrioritizedEventQueue.cpp
@@ -2,31 +2,34 @@
 /* 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 "PrioritizedEventQueue.h"
 #include "mozilla/EventQueue.h"
 #include "mozilla/ScopeExit.h"
+#include "mozilla/StaticPrefs.h"
 #include "nsThreadManager.h"
 #include "nsXPCOMPrivate.h"  // for gXPCOMThreadsShutDown
 #include "InputEventStatistics.h"
 
 using namespace mozilla;
 
 template <class InnerQueueT>
 PrioritizedEventQueue<InnerQueueT>::PrioritizedEventQueue(
     UniquePtr<InnerQueueT> aHighQueue, UniquePtr<InnerQueueT> aInputQueue,
+    UniquePtr<InnerQueueT> aMediumHighQueue,
     UniquePtr<InnerQueueT> aNormalQueue,
     UniquePtr<InnerQueueT> aDeferredTimersQueue,
     UniquePtr<InnerQueueT> aIdleQueue,
     already_AddRefed<nsIIdlePeriod> aIdlePeriod)
     : mHighQueue(std::move(aHighQueue)),
       mInputQueue(std::move(aInputQueue)),
+      mMediumHighQueue(std::move(aMediumHighQueue)),
       mNormalQueue(std::move(aNormalQueue)),
       mDeferredTimersQueue(std::move(aDeferredTimersQueue)),
       mIdleQueue(std::move(aIdleQueue)),
       mIdlePeriod(aIdlePeriod) {
   static_assert(IsBaseOf<AbstractEventQueue, InnerQueueT>::value,
                 "InnerQueueT must be an AbstractEventQueue subclass");
 }
 
@@ -36,25 +39,31 @@ void PrioritizedEventQueue<InnerQueueT>:
     const MutexAutoLock& aProofOfLock) {
   // Double check the priority with a QI.
   RefPtr<nsIRunnable> event(aEvent);
   EventQueuePriority priority = aPriority;
 
   if (priority == EventQueuePriority::Input &&
       mInputQueueState == STATE_DISABLED) {
     priority = EventQueuePriority::Normal;
+  } else if (priority == EventQueuePriority::MediumHigh &&
+             !StaticPrefs::medium_high_event_queue_enabled()) {
+    priority = EventQueuePriority::Normal;
   }
 
   switch (priority) {
     case EventQueuePriority::High:
       mHighQueue->PutEvent(event.forget(), priority, aProofOfLock);
       break;
     case EventQueuePriority::Input:
       mInputQueue->PutEvent(event.forget(), priority, aProofOfLock);
       break;
+    case EventQueuePriority::MediumHigh:
+      mMediumHighQueue->PutEvent(event.forget(), priority, aProofOfLock);
+      break;
     case EventQueuePriority::Normal:
       mNormalQueue->PutEvent(event.forget(), priority, aProofOfLock);
       break;
     case EventQueuePriority::DeferredTimers:
       mDeferredTimersQueue->PutEvent(event.forget(), priority, aProofOfLock);
       break;
     case EventQueuePriority::Idle:
       mIdleQueue->PutEvent(event.forget(), priority, aProofOfLock);
@@ -123,17 +132,18 @@ EventQueuePriority PrioritizedEventQueue
   }
 
   // We check the different queues in the following order. The conditions we use
   // are meant to avoid starvation and to ensure that we don't process an event
   // at the wrong time.
   //
   // HIGH: if mProcessHighPriorityQueue
   // INPUT: if inputCount > 0 && TimeStamp::Now() > mInputHandlingStartTime
-  // NORMAL: if normalPending
+  // MEDIUMHIGH: if medium high pending
+  // NORMAL: if normal pending
   //
   // If we still don't have an event, then we take events from the queues
   // in the following order:
   //
   // HIGH
   // INPUT
   // DEFERREDTIMERS: if GetIdleDeadline()
   // IDLE: if GetIdleDeadline()
@@ -147,16 +157,21 @@ EventQueuePriority PrioritizedEventQueue
 
   if (mProcessHighPriorityQueue) {
     queue = EventQueuePriority::High;
   } else if (inputCount > 0 && (mInputQueueState == STATE_FLUSHING ||
                                 (mInputQueueState == STATE_ENABLED &&
                                  !mInputHandlingStartTime.IsNull() &&
                                  TimeStamp::Now() > mInputHandlingStartTime))) {
     queue = EventQueuePriority::Input;
+  } else if (!mMediumHighQueue->IsEmpty(aProofOfLock)) {
+    MOZ_ASSERT(
+        mInputQueueState != STATE_FLUSHING,
+        "Shouldn't consume medium high event when flushing input events");
+    queue = EventQueuePriority::MediumHigh;
   } else if (!mNormalQueue->IsEmpty(aProofOfLock)) {
     MOZ_ASSERT(mInputQueueState != STATE_FLUSHING,
                "Shouldn't consume normal event when flushing input events");
     queue = EventQueuePriority::Normal;
   } else if (highPending) {
     queue = EventQueuePriority::High;
   } else if (inputCount > 0 && mInputQueueState != STATE_SUSPEND) {
     MOZ_ASSERT(
@@ -210,16 +225,22 @@ already_AddRefed<nsIRunnable> Prioritize
 
   if (queue == EventQueuePriority::Input) {
     nsCOMPtr<nsIRunnable> event =
         mInputQueue->GetEvent(aPriority, aProofOfLock);
     MOZ_ASSERT(event);
     return event.forget();
   }
 
+  if (queue == EventQueuePriority::MediumHigh) {
+    nsCOMPtr<nsIRunnable> event =
+        mMediumHighQueue->GetEvent(aPriority, aProofOfLock);
+    return event.forget();
+  }
+
   if (queue == EventQueuePriority::Normal) {
     nsCOMPtr<nsIRunnable> event =
         mNormalQueue->GetEvent(aPriority, aProofOfLock);
     return event.forget();
   }
 
   // If we get here, then all queues except deferredtimers and idle are empty.
   MOZ_ASSERT(queue == EventQueuePriority::Idle ||
@@ -259,32 +280,35 @@ already_AddRefed<nsIRunnable> Prioritize
 
 template <class InnerQueueT>
 bool PrioritizedEventQueue<InnerQueueT>::IsEmpty(
     const MutexAutoLock& aProofOfLock) {
   // Just check IsEmpty() on the sub-queues. Don't bother checking the idle
   // deadline since that only determines whether an idle event is ready or not.
   return mHighQueue->IsEmpty(aProofOfLock) &&
          mInputQueue->IsEmpty(aProofOfLock) &&
+         mMediumHighQueue->IsEmpty(aProofOfLock) &&
          mNormalQueue->IsEmpty(aProofOfLock) &&
          mDeferredTimersQueue->IsEmpty(aProofOfLock) &&
          mIdleQueue->IsEmpty(aProofOfLock);
 }
 
 template <class InnerQueueT>
 bool PrioritizedEventQueue<InnerQueueT>::HasReadyEvent(
     const MutexAutoLock& aProofOfLock) {
   mHasPendingEventsPromisedIdleEvent = false;
 
   EventQueuePriority queue = SelectQueue(false, aProofOfLock);
 
   if (queue == EventQueuePriority::High) {
     return mHighQueue->HasReadyEvent(aProofOfLock);
   } else if (queue == EventQueuePriority::Input) {
     return mInputQueue->HasReadyEvent(aProofOfLock);
+  } else if (queue == EventQueuePriority::MediumHigh) {
+    return mMediumHighQueue->HasReadyEvent(aProofOfLock);
   } else if (queue == EventQueuePriority::Normal) {
     return mNormalQueue->HasReadyEvent(aProofOfLock);
   }
 
   MOZ_ASSERT(queue == EventQueuePriority::Idle ||
              queue == EventQueuePriority::DeferredTimers);
 
   // If we get here, then both the high and normal queues are empty.
--- a/xpcom/threads/PrioritizedEventQueue.h
+++ b/xpcom/threads/PrioritizedEventQueue.h
@@ -37,16 +37,17 @@ namespace mozilla {
 //   is almost over.
 template <class InnerQueueT>
 class PrioritizedEventQueue final : public AbstractEventQueue {
  public:
   static const bool SupportsPrioritization = true;
 
   PrioritizedEventQueue(UniquePtr<InnerQueueT> aHighQueue,
                         UniquePtr<InnerQueueT> aInputQueue,
+                        UniquePtr<InnerQueueT> aMediumHighQueue,
                         UniquePtr<InnerQueueT> aNormalQueue,
                         UniquePtr<InnerQueueT> aDeferredTimersQueue,
                         UniquePtr<InnerQueueT> aIdleQueue,
                         already_AddRefed<nsIIdlePeriod> aIdlePeriod);
 
   void PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
                 EventQueuePriority aPriority,
                 const MutexAutoLock& aProofOfLock) final;
@@ -79,16 +80,17 @@ class PrioritizedEventQueue final : publ
   void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
 
   size_t SizeOfExcludingThis(
       mozilla::MallocSizeOf aMallocSizeOf) const override {
     size_t n = 0;
 
     n += mHighQueue->SizeOfIncludingThis(aMallocSizeOf);
     n += mInputQueue->SizeOfIncludingThis(aMallocSizeOf);
+    n += mMediumHighQueue->SizeOfIncludingThis(aMallocSizeOf);
     n += mNormalQueue->SizeOfIncludingThis(aMallocSizeOf);
     n += mDeferredTimersQueue->SizeOfIncludingThis(aMallocSizeOf);
     n += mIdleQueue->SizeOfIncludingThis(aMallocSizeOf);
 
     if (mIdlePeriod) {
       n += aMallocSizeOf(mIdlePeriod);
     }
 
@@ -99,16 +101,17 @@ class PrioritizedEventQueue final : publ
   EventQueuePriority SelectQueue(bool aUpdateState,
                                  const MutexAutoLock& aProofOfLock);
 
   // Returns a null TimeStamp if we're not in the idle period.
   mozilla::TimeStamp GetIdleDeadline();
 
   UniquePtr<InnerQueueT> mHighQueue;
   UniquePtr<InnerQueueT> mInputQueue;
+  UniquePtr<InnerQueueT> mMediumHighQueue;
   UniquePtr<InnerQueueT> mNormalQueue;
   UniquePtr<InnerQueueT> mDeferredTimersQueue;
   UniquePtr<InnerQueueT> mIdleQueue;
 
   // We need to drop the queue mutex when checking the idle deadline, so we keep
   // a pointer to it here.
   Mutex* mMutex = nullptr;
 
--- a/xpcom/threads/ThreadEventQueue.cpp
+++ b/xpcom/threads/ThreadEventQueue.cpp
@@ -82,16 +82,18 @@ bool ThreadEventQueue<InnerQueueT>::PutE
       auto* e = event.get();  // can't do_QueryInterface on LeakRefPtr.
       if (nsCOMPtr<nsIRunnablePriority> runnablePrio = do_QueryInterface(e)) {
         uint32_t prio = nsIRunnablePriority::PRIORITY_NORMAL;
         runnablePrio->GetPriority(&prio);
         if (prio == nsIRunnablePriority::PRIORITY_HIGH) {
           aPriority = EventQueuePriority::High;
         } else if (prio == nsIRunnablePriority::PRIORITY_INPUT) {
           aPriority = EventQueuePriority::Input;
+        } else if (prio == nsIRunnablePriority::PRIORITY_MEDIUMHIGH) {
+          aPriority = EventQueuePriority::MediumHigh;
         }
       }
     }
 
     MutexAutoLock lock(mLock);
 
     if (mEventsAreDoomed) {
       return false;
--- a/xpcom/threads/nsIRunnable.idl
+++ b/xpcom/threads/nsIRunnable.idl
@@ -19,10 +19,11 @@ interface nsIRunnable : nsISupports
 };
 
 [scriptable, uuid(e75aa42a-80a9-11e6-afb5-e89d87348e2c)]
 interface nsIRunnablePriority : nsISupports
 {
     const unsigned short PRIORITY_NORMAL = 0;
     const unsigned short PRIORITY_INPUT = 1;
     const unsigned short PRIORITY_HIGH = 2;
+    const unsigned short PRIORITY_MEDIUMHIGH = 3;
     readonly attribute unsigned long priority;
 };
--- a/xpcom/threads/nsThreadUtils.cpp
+++ b/xpcom/threads/nsThreadUtils.cpp
@@ -112,16 +112,23 @@ PrioritizableRunnable::Run() {
 }
 
 NS_IMETHODIMP
 PrioritizableRunnable::GetPriority(uint32_t* aPriority) {
   *aPriority = mPriority;
   return NS_OK;
 }
 
+already_AddRefed<nsIRunnable> mozilla::CreateMediumHighRunnable(
+    already_AddRefed<nsIRunnable>&& aRunnable) {
+  nsCOMPtr<nsIRunnable> runnable = new PrioritizableRunnable(
+      std::move(aRunnable), nsIRunnablePriority::PRIORITY_MEDIUMHIGH);
+  return runnable.forget();
+}
+
 #endif  // XPCOM_GLUE_AVOID_NSPR
 
 //-----------------------------------------------------------------------------
 
 nsresult NS_NewNamedThread(const nsACString& aName, nsIThread** aResult,
                            nsIRunnable* aEvent, uint32_t aStackSize) {
   nsCOMPtr<nsIThread> thread;
 #ifdef MOZILLA_INTERNAL_API
--- a/xpcom/threads/nsThreadUtils.h
+++ b/xpcom/threads/nsThreadUtils.h
@@ -536,16 +536,19 @@ class PrioritizableRunnable : public Run
   NS_DECL_NSIRUNNABLEPRIORITY
 
  protected:
   virtual ~PrioritizableRunnable(){};
   nsCOMPtr<nsIRunnable> mRunnable;
   uint32_t mPriority;
 };
 
+extern already_AddRefed<nsIRunnable> CreateMediumHighRunnable(
+    already_AddRefed<nsIRunnable>&& aRunnable);
+
 namespace detail {
 
 // An event that can be used to call a C++11 functions or function objects,
 // including lambdas. The function must have no required arguments, and must
 // return void.
 template <typename StoredFunction>
 class RunnableFunction : public Runnable {
  public: