Bug 1195767 - part 2 - create an nsEventQueueBase templated over the monitor type; r=gerald
authorNathan Froyd <froydnj@mozilla.com>
Fri, 28 Aug 2015 13:26:17 -0400
changeset 293855 8b01d290c47ce933f1b31457c13db2642af22c9a
parent 293854 94563bd2e26bb1138fbe30ba64d9f30ee14f6f46
child 293856 0bd307e9015466b55c69d42c8c12aaa6aa590af2
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1195767
milestone43.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 1195767 - part 2 - create an nsEventQueueBase templated over the monitor type; r=gerald Clients of nsEventQueue don't always need fully reentrant monitors. Let's account for that by having a base class templated on the monitor type. This change also opens up the possibility of having the monitor for the event queue not owned by the event queue itself, but by the client class, which makes a lot more sense than the current design.
xpcom/threads/nsEventQueue.cpp
xpcom/threads/nsEventQueue.h
--- a/xpcom/threads/nsEventQueue.cpp
+++ b/xpcom/threads/nsEventQueue.cpp
@@ -22,91 +22,100 @@ GetLog()
   }
   return sLog;
 }
 #ifdef LOG
 #undef LOG
 #endif
 #define LOG(args) MOZ_LOG(GetLog(), mozilla::LogLevel::Debug, args)
 
-nsEventQueue::nsEventQueue()
-  : mReentrantMonitor("nsEventQueue.mReentrantMonitor")
-  , mHead(nullptr)
+template<typename MonitorType>
+nsEventQueueBase<MonitorType>::nsEventQueueBase()
+  : mHead(nullptr)
   , mTail(nullptr)
   , mOffsetHead(0)
   , mOffsetTail(0)
 {
 }
 
-nsEventQueue::~nsEventQueue()
+template nsEventQueueBase<Monitor>::nsEventQueueBase();
+template nsEventQueueBase<ReentrantMonitor>::nsEventQueueBase();
+
+nsEventQueue::nsEventQueue()
+  : mMonitor("[nsEventQueue.mMonitor]")
+{
+}
+
+template<typename MonitorType>
+nsEventQueueBase<MonitorType>::~nsEventQueueBase()
 {
   // It'd be nice to be able to assert that no one else is holding the monitor,
   // but NSPR doesn't really expose APIs for it.
   NS_ASSERTION(IsEmpty(),
                "Non-empty event queue being destroyed; events being leaked.");
 
   if (mHead) {
     FreePage(mHead);
   }
 }
 
-bool
-nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aResult)
-{
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+template nsEventQueueBase<Monitor>::~nsEventQueueBase();
+template nsEventQueueBase<ReentrantMonitor>::~nsEventQueueBase();
 
-    while (IsEmpty()) {
-      if (!aMayWait) {
-        if (aResult) {
-          *aResult = nullptr;
-        }
-        return false;
+template<typename MonitorType>
+bool
+nsEventQueueBase<MonitorType>::GetEvent(bool aMayWait, nsIRunnable** aResult,
+                                        MonitorAutoEnterType& aProofOfLock)
+{
+  while (IsEmpty()) {
+    if (!aMayWait) {
+      if (aResult) {
+        *aResult = nullptr;
       }
-      LOG(("EVENTQ(%p): wait begin\n", this));
-      mon.Wait();
-      LOG(("EVENTQ(%p): wait end\n", this));
+      return false;
     }
-
-    if (aResult) {
-      *aResult = mHead->mEvents[mOffsetHead++];
+    LOG(("EVENTQ(%p): wait begin\n", this));
+    aProofOfLock.Wait();
+    LOG(("EVENTQ(%p): wait end\n", this));
+  }
 
-      // Check if mHead points to empty Page
-      if (mOffsetHead == EVENTS_PER_PAGE) {
-        Page* dead = mHead;
-        mHead = mHead->mNext;
-        FreePage(dead);
-        mOffsetHead = 0;
-      }
+  if (aResult) {
+    *aResult = mHead->mEvents[mOffsetHead++];
+
+    // Check if mHead points to empty Page
+    if (mOffsetHead == EVENTS_PER_PAGE) {
+      Page* dead = mHead;
+      mHead = mHead->mNext;
+      FreePage(dead);
+      mOffsetHead = 0;
     }
   }
 
   return true;
 }
 
-void
-nsEventQueue::PutEvent(nsIRunnable* aRunnable)
+template bool nsEventQueueBase<Monitor>::GetEvent(bool aMayWait, nsIRunnable** aResult,
+                                                  MonitorAutoLock& aProofOfLock);
+template bool nsEventQueueBase<ReentrantMonitor>::GetEvent(bool aMayWait, nsIRunnable** aResult,
+                                                           ReentrantMonitorAutoEnter& aProofOfLock);
+
+bool
+nsEventQueue::GetEvent(bool aMayWait, nsIRunnable** aEvent)
 {
-  nsCOMPtr<nsIRunnable> event(aRunnable);
-  PutEvent(event.forget());
+  MonitorAutoEnterType mon(mMonitor);
+
+  return Base::GetEvent(aMayWait, aEvent, mon);
 }
 
+template<typename MonitorType>
 void
-nsEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable)
+nsEventQueueBase<MonitorType>::PutEvent(
+    already_AddRefed<nsIRunnable>&& aRunnable,
+    MonitorAutoEnterType& aProofOfLock)
 {
-  if (ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
-    // With probability 0.5, yield so other threads have a chance to
-    // dispatch events to this queue first.
-    if (ChaosMode::randomUint32LessThan(2)) {
-      PR_Sleep(PR_INTERVAL_NO_WAIT);
-    }
-  }
-
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
   if (!mHead) {
     mHead = NewPage();
     MOZ_ASSERT(mHead);
 
     mTail = mHead;
     mOffsetHead = 0;
     mOffsetTail = 0;
   } else if (mOffsetTail == EVENTS_PER_PAGE) {
@@ -118,24 +127,51 @@ nsEventQueue::PutEvent(already_AddRefed<
     mOffsetTail = 0;
   }
 
   nsIRunnable*& queueLocation = mTail->mEvents[mOffsetTail];
   MOZ_ASSERT(!queueLocation);
   queueLocation = aRunnable.take();
   ++mOffsetTail;
   LOG(("EVENTQ(%p): notify\n", this));
-  mon.NotifyAll();
+  aProofOfLock.NotifyAll();
+}
+
+template void nsEventQueueBase<Monitor>::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable,
+                                                  MonitorAutoLock& aProofOfLock);
+template void nsEventQueueBase<ReentrantMonitor>::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable,
+                                                           ReentrantMonitorAutoEnter& aProofOfLock);
+
+void
+nsEventQueue::PutEvent(nsIRunnable* aRunnable)
+{
+  nsCOMPtr<nsIRunnable> event(aRunnable);
+  PutEvent(event.forget());
 }
 
-size_t
-nsEventQueue::Count()
+void
+nsEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aRunnable)
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  if (ChaosMode::isActive(ChaosFeature::ThreadScheduling)) {
+    // With probability 0.5, yield so other threads have a chance to
+    // dispatch events to this queue first.
+    if (ChaosMode::randomUint32LessThan(2)) {
+      PR_Sleep(PR_INTERVAL_NO_WAIT);
+    }
+  }
 
+  MonitorAutoEnterType mon(mMonitor);
+
+  Base::PutEvent(Move(aRunnable), mon);
+}
+
+template<typename MonitorType>
+size_t
+nsEventQueueBase<MonitorType>::Count(MonitorAutoEnterType& aProofOfLock)
+{
   // It is obvious count is 0 when the queue is empty.
   if (!mHead) {
     return 0;
   }
 
   /* How we count the number of events in the queue:
    * 1. Let pageCount(x, y) denote the number of pages excluding the tail page
    *    where x is the index of head page and y is the index of the tail page.
@@ -156,8 +192,19 @@ nsEventQueue::Count()
     count += EVENTS_PER_PAGE;
   }
 
   count += mOffsetTail;
   MOZ_ASSERT(count >= 0);
 
   return count;
 }
+
+template size_t nsEventQueueBase<Monitor>::Count(MonitorAutoLock& aProofOfLock);
+template size_t nsEventQueueBase<ReentrantMonitor>::Count(ReentrantMonitorAutoEnter& aProofOfLock);
+
+size_t
+nsEventQueue::Count()
+{
+  MonitorAutoEnterType mon(mMonitor);
+
+  return Base::Count(mon);
+}
--- a/xpcom/threads/nsEventQueue.h
+++ b/xpcom/threads/nsEventQueue.h
@@ -3,66 +3,73 @@
 /* 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/. */
 
 #ifndef nsEventQueue_h__
 #define nsEventQueue_h__
 
 #include <stdlib.h>
+#include "mozilla/Monitor.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsIRunnable.h"
 #include "nsCOMPtr.h"
 #include "mozilla/AlreadyAddRefed.h"
 
-// A threadsafe FIFO event queue...
-class nsEventQueue
+template<typename MonitorType>
+struct MonitorAutoEnterChooser;
+
+template<>
+struct MonitorAutoEnterChooser<mozilla::Monitor>
+{
+  typedef mozilla::MonitorAutoLock Type;
+};
+
+template<>
+struct MonitorAutoEnterChooser<mozilla::ReentrantMonitor>
 {
-  typedef mozilla::ReentrantMonitor ReentrantMonitor;
+  typedef mozilla::ReentrantMonitorAutoEnter Type;
+};
 
+// A threadsafe FIFO event queue...
+template<typename MonitorType>
+class nsEventQueueBase
+{
 public:
-  nsEventQueue();
-  ~nsEventQueue();
+  typedef MonitorType Monitor;
+  typedef typename MonitorAutoEnterChooser<Monitor>::Type MonitorAutoEnterType;
+
+  nsEventQueueBase();
+  ~nsEventQueueBase();
 
   // This method adds a new event to the pending event queue.  The queue holds
   // a strong reference to the event after this method returns.  This method
   // cannot fail.
-  void PutEvent(nsIRunnable* aEvent);
-  void PutEvent(already_AddRefed<nsIRunnable>&& aEvent);
+  void PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
+                MonitorAutoEnterType& aProofOfLock);
 
   // This method gets an event from the event queue.  If mayWait is true, then
   // the method will block the calling thread until an event is available.  If
   // the event is null, then the method returns immediately indicating whether
   // or not an event is pending.  When the resulting event is non-null, the
   // caller is responsible for releasing the event object.  This method does
   // not alter the reference count of the resulting event.
-  bool GetEvent(bool aMayWait, nsIRunnable** aEvent);
-
-  // This method returns true if there is a pending event.
-  bool HasPendingEvent()
-  {
-    return GetEvent(false, nullptr);
-  }
+  bool GetEvent(bool aMayWait, nsIRunnable** aEvent,
+                MonitorAutoEnterType& aProofOfLock);
 
   // This method returns the next pending event or null.
-  bool GetPendingEvent(nsIRunnable** runnable)
+  bool GetPendingEvent(nsIRunnable** aRunnable,
+                       MonitorAutoEnterType& aProofOfLock)
   {
-    return GetEvent(false, runnable);
+    return GetEvent(false, aRunnable, aProofOfLock);
   }
 
-  // Expose the event queue's monitor for "power users"
-  ReentrantMonitor& GetReentrantMonitor()
-  {
-    return mReentrantMonitor;
-  }
-
-  size_t Count();
+  size_t Count(MonitorAutoEnterType& aProofOfLock);
 
 private:
-
   bool IsEmpty()
   {
     return !mHead || (mHead == mTail && mOffsetHead == mOffsetTail);
   }
 
   enum
   {
     EVENTS_PER_PAGE = 255
@@ -84,18 +91,64 @@ private:
     return static_cast<Page*>(moz_xcalloc(1, sizeof(Page)));
   }
 
   static void FreePage(Page* aPage)
   {
     free(aPage);
   }
 
-  ReentrantMonitor mReentrantMonitor;
-
   Page* mHead;
   Page* mTail;
 
   uint16_t mOffsetHead;  // offset into mHead where next item is removed
   uint16_t mOffsetTail;  // offset into mTail where next item is added
 };
 
+class nsEventQueue : protected nsEventQueueBase<mozilla::ReentrantMonitor>
+{
+private:
+  typedef nsEventQueueBase<mozilla::ReentrantMonitor> Base;
+  // Can't use typedefs or type alias templates here to name the base type.
+  friend class nsEventQueueBase<mozilla::ReentrantMonitor>;
+
+  typedef Base::Monitor MonitorType;
+  typedef Base::MonitorAutoEnterType MonitorAutoEnterType;
+  MonitorType mMonitor;
+
+public:
+  nsEventQueue();
+
+  // This method adds a new event to the pending event queue.  The queue holds
+  // a strong reference to the event after this method returns.  This method
+  // cannot fail.
+  void PutEvent(nsIRunnable* aEvent);
+  void PutEvent(already_AddRefed<nsIRunnable>&& aEvent);
+
+  // This method gets an event from the event queue.  If mayWait is true, then
+  // the method will block the calling thread until an event is available.  If
+  // the event is null, then the method returns immediately indicating whether
+  // or not an event is pending.  When the resulting event is non-null, the
+  // caller is responsible for releasing the event object.  This method does
+  // not alter the reference count of the resulting event.
+  bool GetEvent(bool aMayWait, nsIRunnable** aEvent);
+
+  // This method returns true if there is a pending event.
+  bool HasPendingEvent()
+  {
+    return GetEvent(false, nullptr);
+  }
+
+  // This method returns the next pending event or null.
+  bool GetPendingEvent(nsIRunnable** aRunnable)
+  {
+    return GetEvent(false, aRunnable);
+  }
+
+  size_t Count();
+
+  MonitorType& GetReentrantMonitor()
+  {
+    return mMonitor;
+  }
+};
+
 #endif  // nsEventQueue_h__