Bug 1518038 - Add nsIThread.hasPendingHighPriorityEvents, r=froydnj
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 10 Jan 2019 19:07:34 +0200
changeset 510380 c9fcbe28afdadd45d371ba150ff9c1502a19c6e3
parent 510379 be769aa73f883b218cf655519a08aa874500ee7e
child 510381 ed68c008e5d8db0940a1fbc31bfc24efccba6c26
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1518038
milestone66.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 1518038 - Add nsIThread.hasPendingHighPriorityEvents, r=froydnj
xpcom/threads/AbstractEventQueue.h
xpcom/threads/EventQueue.h
xpcom/threads/LabeledEventQueue.h
xpcom/threads/LazyIdleThread.cpp
xpcom/threads/PrioritizedEventQueue.cpp
xpcom/threads/PrioritizedEventQueue.h
xpcom/threads/Scheduler.cpp
xpcom/threads/SynchronizedEventQueue.h
xpcom/threads/ThreadEventQueue.cpp
xpcom/threads/ThreadEventQueue.h
xpcom/threads/nsIThread.idl
xpcom/threads/nsThread.cpp
xpcom/threads/nsThreadManager.cpp
xpcom/threads/nsThreadManager.h
--- a/xpcom/threads/AbstractEventQueue.h
+++ b/xpcom/threads/AbstractEventQueue.h
@@ -59,16 +59,19 @@ class AbstractEventQueue {
   // Returns true if the queue is empty. Implies !HasReadyEvent().
   virtual bool IsEmpty(const MutexAutoLock& aProofOfLock) = 0;
 
   // Returns true if the queue is non-empty and if the event in front is ready
   // to run. Implies !IsEmpty(). This should return true iff GetEvent returns a
   // non-null value.
   virtual bool HasReadyEvent(const MutexAutoLock& aProofOfLock) = 0;
 
+  virtual bool HasPendingHighPriorityEvents(
+      const MutexAutoLock& aProofOfLock) = 0;
+
   // Returns the number of events in the queue.
   virtual size_t Count(const MutexAutoLock& aProofOfLock) const = 0;
 
   virtual void EnableInputEventPrioritization(
       const MutexAutoLock& aProofOfLock) = 0;
   virtual void FlushInputEventPrioritization(
       const MutexAutoLock& aProofOfLock) = 0;
   virtual void SuspendInputEventPrioritization(
--- a/xpcom/threads/EventQueue.h
+++ b/xpcom/threads/EventQueue.h
@@ -24,16 +24,20 @@ class EventQueue final : public Abstract
 
   void PutEvent(already_AddRefed<nsIRunnable>&& aEvent, EventPriority aPriority,
                 const MutexAutoLock& aProofOfLock) final;
   already_AddRefed<nsIRunnable> GetEvent(
       EventPriority* aPriority, const MutexAutoLock& aProofOfLock) final;
 
   bool IsEmpty(const MutexAutoLock& aProofOfLock) final;
   bool HasReadyEvent(const MutexAutoLock& aProofOfLock) final;
+  bool HasPendingHighPriorityEvents(const MutexAutoLock& aProofOfLock) final {
+    // EventQueue doesn't support any prioritization.
+    return false;
+  }
 
   size_t Count(const MutexAutoLock& aProofOfLock) const final;
   already_AddRefed<nsIRunnable> PeekEvent(const MutexAutoLock& aProofOfLock);
 
   void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {
   }
   void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
   void SuspendInputEventPrioritization(
--- a/xpcom/threads/LabeledEventQueue.h
+++ b/xpcom/threads/LabeledEventQueue.h
@@ -36,16 +36,20 @@ class LabeledEventQueue final : public A
   void PutEvent(already_AddRefed<nsIRunnable>&& aEvent, EventPriority aPriority,
                 const MutexAutoLock& aProofOfLock) final;
   already_AddRefed<nsIRunnable> GetEvent(
       EventPriority* aPriority, const MutexAutoLock& aProofOfLock) final;
 
   bool IsEmpty(const MutexAutoLock& aProofOfLock) final;
   size_t Count(const MutexAutoLock& aProofOfLock) const final;
   bool HasReadyEvent(const MutexAutoLock& aProofOfLock) final;
+  bool HasPendingHighPriorityEvents(const MutexAutoLock& aProofOfLock) final {
+    // LabeledEventQueue itself isn't prioritized.
+    return false;
+  }
 
   void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {
   }
   void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
   void SuspendInputEventPrioritization(
       const MutexAutoLock& aProofOfLock) final {}
   void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {
   }
--- a/xpcom/threads/LazyIdleThread.cpp
+++ b/xpcom/threads/LazyIdleThread.cpp
@@ -473,16 +473,24 @@ NS_IMETHODIMP
 LazyIdleThread::HasPendingEvents(bool* aHasPendingEvents) {
   // This is only supposed to be called from the thread itself so it's not
   // implemented here.
   MOZ_ASSERT_UNREACHABLE("Shouldn't ever call this!");
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
+LazyIdleThread::HasPendingHighPriorityEvents(bool* aHasPendingEvents) {
+  // This is only supposed to be called from the thread itself so it's not
+  // implemented here.
+  MOZ_ASSERT_UNREACHABLE("Shouldn't ever call this!");
+  return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
 LazyIdleThread::IdleDispatch(already_AddRefed<nsIRunnable> aEvent) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 LazyIdleThread::ProcessNextEvent(bool aMayWait, bool* aEventWasProcessed) {
   // This is only supposed to be called from the thread itself so it's not
   // implemented here.
--- a/xpcom/threads/PrioritizedEventQueue.cpp
+++ b/xpcom/threads/PrioritizedEventQueue.cpp
@@ -280,16 +280,22 @@ bool PrioritizedEventQueue<InnerQueueT>:
     mHasPendingEventsPromisedIdleEvent = true;
     return true;
   }
 
   return false;
 }
 
 template <class InnerQueueT>
+bool PrioritizedEventQueue<InnerQueueT>::HasPendingHighPriorityEvents(
+    const MutexAutoLock& aProofOfLock) {
+  return !mHighQueue->IsEmpty(aProofOfLock);
+}
+
+template <class InnerQueueT>
 size_t PrioritizedEventQueue<InnerQueueT>::Count(
     const MutexAutoLock& aProofOfLock) const {
   MOZ_CRASH("unimplemented");
 }
 
 template <class InnerQueueT>
 void PrioritizedEventQueue<InnerQueueT>::EnableInputEventPrioritization(
     const MutexAutoLock& aProofOfLock) {
--- a/xpcom/threads/PrioritizedEventQueue.h
+++ b/xpcom/threads/PrioritizedEventQueue.h
@@ -50,16 +50,17 @@ class PrioritizedEventQueue final : publ
   void PutEvent(already_AddRefed<nsIRunnable>&& aEvent, EventPriority aPriority,
                 const MutexAutoLock& aProofOfLock) final;
   already_AddRefed<nsIRunnable> GetEvent(
       EventPriority* aPriority, const MutexAutoLock& aProofOfLock) final;
 
   bool IsEmpty(const MutexAutoLock& aProofOfLock) final;
   size_t Count(const MutexAutoLock& aProofOfLock) const final;
   bool HasReadyEvent(const MutexAutoLock& aProofOfLock) final;
+  bool HasPendingHighPriorityEvents(const MutexAutoLock& aProofOfLock) final;
 
   // When checking the idle deadline, we need to drop whatever mutex protects
   // this queue. This method allows that mutex to be stored so that we can drop
   // it and reacquire it when checking the idle deadline. The mutex must live at
   // least as long as the queue.
   void SetMutexRef(Mutex& aMutex) { mMutex = &aMutex; }
 
 #ifndef RELEASE_OR_BETA
--- a/xpcom/threads/Scheduler.cpp
+++ b/xpcom/threads/Scheduler.cpp
@@ -46,16 +46,19 @@ class SchedulerEventQueue final : public
 
   void Disconnect(const MutexAutoLock& aProofOfLock) final {}
 
   already_AddRefed<nsIRunnable> GetEvent(bool aMayWait,
                                          EventPriority* aPriority) final;
   bool HasPendingEvent() final;
   bool HasPendingEvent(const MutexAutoLock& aProofOfLock);
 
+  bool HasPendingHighPriorityEvents() final;
+  bool HasPendingHighPriorityEvents(const MutexAutoLock& aProofOfLock);
+
   bool ShutdownIfNoPendingEvents() final;
 
   already_AddRefed<nsIThreadObserver> GetObserver() final;
   already_AddRefed<nsIThreadObserver> GetObserverOnThread() final;
   void SetObserver(nsIThreadObserver* aObserver) final;
 
   void EnableInputEventPrioritization() final;
   void FlushInputEventPrioritization() final;
@@ -299,16 +302,26 @@ bool SchedulerEventQueue::HasPendingEven
   MutexAutoLock lock(mLock);
   return HasPendingEvent(lock);
 }
 
 bool SchedulerEventQueue::HasPendingEvent(const MutexAutoLock& aProofOfLock) {
   return mQueue->HasReadyEvent(aProofOfLock);
 }
 
+bool SchedulerEventQueue::HasPendingHighPriorityEvents() {
+  MutexAutoLock lock(mLock);
+  return HasPendingHighPriorityEvents(lock);
+}
+
+bool SchedulerEventQueue::HasPendingHighPriorityEvents(
+    const MutexAutoLock& aProofOfLock) {
+  return mQueue->HasPendingHighPriorityEvents(aProofOfLock);
+}
+
 bool SchedulerEventQueue::ShutdownIfNoPendingEvents() {
   MutexAutoLock lock(mLock);
 
   MOZ_ASSERT(!mScheduler);
 
   if (mQueue->IsEmpty(lock)) {
     mEventsAreDoomed = true;
     return true;
--- a/xpcom/threads/SynchronizedEventQueue.h
+++ b/xpcom/threads/SynchronizedEventQueue.h
@@ -53,16 +53,18 @@ class ThreadTargetSink {
 };
 
 class SynchronizedEventQueue : public ThreadTargetSink {
  public:
   virtual already_AddRefed<nsIRunnable> GetEvent(bool aMayWait,
                                                  EventPriority* aPriority) = 0;
   virtual bool HasPendingEvent() = 0;
 
+  virtual bool HasPendingHighPriorityEvents() = 0;
+
   // This method atomically checks if there are pending events and, if there are
   // none, forbids future events from being posted. It returns true if there
   // were no pending events.
   virtual bool ShutdownIfNoPendingEvents() = 0;
 
   // These methods provide access to an nsIThreadObserver, whose methods are
   // called when posting and processing events. SetObserver should only be
   // called on the thread that processes events. GetObserver can be called from
--- a/xpcom/threads/ThreadEventQueue.cpp
+++ b/xpcom/threads/ThreadEventQueue.cpp
@@ -159,16 +159,29 @@ bool ThreadEventQueue<InnerQueueT>::HasP
   if (mNestedQueues.IsEmpty()) {
     return mBaseQueue->HasReadyEvent(lock);
   } else {
     return mNestedQueues.LastElement().mQueue->HasReadyEvent(lock);
   }
 }
 
 template <class InnerQueueT>
+bool ThreadEventQueue<InnerQueueT>::HasPendingHighPriorityEvents() {
+  MutexAutoLock lock(mLock);
+
+  // We always get events from the topmost queue when there are nested queues.
+  if (mNestedQueues.IsEmpty()) {
+    return mBaseQueue->HasPendingHighPriorityEvents(lock);
+  } else {
+    return mNestedQueues.LastElement().mQueue->HasPendingHighPriorityEvents(
+        lock);
+  }
+}
+
+template <class InnerQueueT>
 bool ThreadEventQueue<InnerQueueT>::ShutdownIfNoPendingEvents() {
   MutexAutoLock lock(mLock);
   if (mNestedQueues.IsEmpty() && mBaseQueue->IsEmpty(lock)) {
     mEventsAreDoomed = true;
     return true;
   }
   return false;
 }
--- a/xpcom/threads/ThreadEventQueue.h
+++ b/xpcom/threads/ThreadEventQueue.h
@@ -39,16 +39,17 @@ class ThreadEventQueue final : public Sy
   explicit ThreadEventQueue(UniquePtr<InnerQueueT> aQueue);
 
   bool PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
                 EventPriority aPriority) final;
 
   already_AddRefed<nsIRunnable> GetEvent(bool aMayWait,
                                          EventPriority* aPriority) final;
   bool HasPendingEvent() final;
+  bool HasPendingHighPriorityEvents() final;
 
   bool ShutdownIfNoPendingEvents() final;
 
   void Disconnect(const MutexAutoLock& aProofOfLock) final {}
 
   void EnableInputEventPrioritization() final;
   void FlushInputEventPrioritization() final;
   void SuspendInputEventPrioritization() final;
--- a/xpcom/threads/nsIThread.idl
+++ b/xpcom/threads/nsIThread.idl
@@ -79,16 +79,21 @@ interface nsIThread : nsISerialEventTarg
    *
    * @throws NS_ERROR_UNEXPECTED
    *   Indicates that this method was erroneously called when this thread was
    *   not the current thread.
    */
   boolean hasPendingEvents();
 
   /**
+   * Similar to above, but checks only possible high priority queue.
+   */
+  boolean hasPendingHighPriorityEvents();
+
+  /**
    * Process the next event.  If there are no pending events, then this method
    * may wait -- depending on the value of the mayWait parameter -- until an
    * event is dispatched to this thread.  This method is re-entrant but may
    * only be called if this thread is the current thread.
    *
    * @param mayWait
    *   A boolean parameter that if "true" indicates that the method may block
    *   the calling thread to wait for a pending event.
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -886,16 +886,26 @@ nsThread::HasPendingEvents(bool* aResult
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   *aResult = mEvents->HasPendingEvent();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsThread::HasPendingHighPriorityEvents(bool* aResult) {
+  if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
+    return NS_ERROR_NOT_SAME_THREAD;
+  }
+
+  *aResult = mEvents->HasPendingHighPriorityEvents();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsThread::IdleDispatch(already_AddRefed<nsIRunnable> aEvent) {
   nsCOMPtr<nsIRunnable> event = aEvent;
 
   if (NS_WARN_IF(!event)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (!mEvents->PutEvent(event.forget(), EventPriority::Idle)) {
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -580,16 +580,26 @@ void nsThreadManager::SuspendInputEventP
   mMainThread->SuspendInputEventPrioritization();
 }
 
 void nsThreadManager::ResumeInputEventPrioritization() {
   MOZ_ASSERT(NS_IsMainThread());
   mMainThread->ResumeInputEventPrioritization();
 }
 
+// static
+bool nsThreadManager::MainThreadHasPendingHighPriorityEvents() {
+  MOZ_ASSERT(NS_IsMainThread());
+  bool retVal = false;
+  if (get().mMainThread) {
+    get().mMainThread->HasPendingHighPriorityEvents(&retVal);
+  }
+  return retVal;
+}
+
 NS_IMETHODIMP
 nsThreadManager::IdleDispatchToMainThread(nsIRunnable* aEvent,
                                           uint32_t aTimeout) {
   // Note: C++ callers should instead use NS_IdleDispatchToThread or
   // NS_IdleDispatchToCurrentThread.
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIRunnable> event(aEvent);
--- a/xpcom/threads/nsThreadManager.h
+++ b/xpcom/threads/nsThreadManager.h
@@ -61,16 +61,18 @@ class nsThreadManager : public nsIThread
   // class with older compilers (e.g., egcs-2.91.66).
   ~nsThreadManager() {}
 
   void EnableMainThreadEventPrioritization();
   void FlushInputEventPrioritization();
   void SuspendInputEventPrioritization();
   void ResumeInputEventPrioritization();
 
+  static bool MainThreadHasPendingHighPriorityEvents();
+
  private:
   nsThreadManager()
       : mCurThreadIndex(0), mMainPRThread(nullptr), mInitialized(false) {}
 
   nsresult SpinEventLoopUntilInternal(nsINestedEventLoopCondition* aCondition,
                                       bool aCheckingShutdown);
 
   static void ReleaseThread(void* aData);