Bug 1477512: Part 1 - Add memory reporter functions to thread event queues. r?erahm draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 21 Jul 2018 14:16:50 -0700
changeset 821176 dcd928484f504d5b783c8b9e5c512ba446391106
parent 821175 4ec9098805e2e855b2ecfe6c25f38be947ea7edd
child 821177 a987e7de4f8d3f9deb68bd0619dbb4639f57f8f3
push id117030
push usermaglione.k@gmail.com
push dateSat, 21 Jul 2018 23:02:46 +0000
reviewerserahm
bugs1477512
milestone63.0a1
Bug 1477512: Part 1 - Add memory reporter functions to thread event queues. r?erahm MozReview-Commit-ID: J4EdwUWfyPK
xpcom/threads/AbstractEventQueue.h
xpcom/threads/EventQueue.h
xpcom/threads/LabeledEventQueue.h
xpcom/threads/PrioritizedEventQueue.h
xpcom/threads/Queue.h
xpcom/threads/Scheduler.cpp
xpcom/threads/SynchronizedEventQueue.h
xpcom/threads/ThreadEventQueue.cpp
xpcom/threads/ThreadEventQueue.h
xpcom/threads/ThreadEventTarget.h
--- a/xpcom/threads/AbstractEventQueue.h
+++ b/xpcom/threads/AbstractEventQueue.h
@@ -3,16 +3,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/. */
 
 #ifndef mozilla_AbstractEventQueue_h
 #define mozilla_AbstractEventQueue_h
 
 #include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 
 class nsIRunnable;
 
 namespace mozilla {
 
 enum class EventPriority
 {
@@ -68,14 +69,21 @@ public:
   // 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(const MutexAutoLock& aProofOfLock) = 0;
   virtual void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) = 0;
 
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+
   virtual ~AbstractEventQueue() {}
 };
 
 } // namespace mozilla
 
 #endif // mozilla_AbstractEventQueue_h
--- a/xpcom/threads/EventQueue.h
+++ b/xpcom/threads/EventQueue.h
@@ -35,15 +35,20 @@ public:
   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(const MutexAutoLock& aProofOfLock) final {}
   void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
 
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override
+  {
+    return mQueue.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   mozilla::Queue<nsCOMPtr<nsIRunnable>> mQueue;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_EventQueue_h
--- a/xpcom/threads/LabeledEventQueue.h
+++ b/xpcom/threads/LabeledEventQueue.h
@@ -44,16 +44,22 @@ public:
   size_t Count(const MutexAutoLock& aProofOfLock) const final;
   bool HasReadyEvent(const MutexAutoLock& aProofOfLock) final;
 
   void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
   void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
   void SuspendInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
   void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) final {}
 
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override
+  {
+    return (mEpochs.ShallowSizeOfExcludingThis(aMallocSizeOf) +
+            mUnlabeled.ShallowSizeOfExcludingThis(aMallocSizeOf));
+  }
+
 private:
 
   // The basic problem here is to keep track of the ordering relationships
   // between events. As long as there are only labeled events, there can be one
   // queue per SchedulerGroup. However, if an unlabeled event is pushed, we must
   // remember that it should run after all the labeled events currently in the
   // queue. To do this, the queues are arranged in "epochs". Each time the tail
   // of the queue transitions from labeled to unlabeled (or from unlabeled to
--- a/xpcom/threads/PrioritizedEventQueue.h
+++ b/xpcom/threads/PrioritizedEventQueue.h
@@ -71,16 +71,36 @@ public:
   void SetNextIdleDeadlineRef(TimeStamp& aDeadline) { mNextIdleDeadline = &aDeadline; }
 #endif
 
   void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
   void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
   void SuspendInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
   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 += mNormalQueue->SizeOfIncludingThis(aMallocSizeOf);
+    n += mIdleQueue->SizeOfIncludingThis(aMallocSizeOf);
+
+    if (mMutex) {
+      n += aMallocSizeOf(mMutex);
+    }
+
+    if (mIdlePeriod) {
+      n += aMallocSizeOf(mIdlePeriod);
+    }
+
+    return n;
+  }
+
 private:
   EventPriority 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;
--- a/xpcom/threads/Queue.h
+++ b/xpcom/threads/Queue.h
@@ -2,16 +2,18 @@
 /* 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/. */
 
 #ifndef mozilla_Queue_h
 #define mozilla_Queue_h
 
+#include "mozilla/MemoryReporting.h"
+
 namespace mozilla {
 
 // A queue implements a singly linked list of pages, each of which contains some
 // number of elements. Since the queue needs to store a "next" pointer, the
 // actual number of elements per page won't be quite as many as were requested.
 //
 // This class should only be used if it's valid to construct T elements from all
 // zeroes. The class also fails to call the destructor on items. However, it
@@ -150,16 +152,30 @@ public:
     }
 
     count += mOffsetTail;
     MOZ_ASSERT(count >= 0);
 
     return count;
   }
 
+  size_t ShallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t n = 0;
+    for (Page* page = mHead; page != mTail; page = page->mNext) {
+      n += aMallocSizeOf(page);
+    }
+    return n;
+  }
+
+  size_t ShallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   static_assert((RequestedItemsPerPage & (RequestedItemsPerPage - 1)) == 0,
                 "RequestedItemsPerPage should be a power of two to avoid heap slop.");
 
   // Since a Page must also contain a "next" pointer, we use one of the items to
   // store this pointer. If sizeof(T) > sizeof(Page*), then some space will be
   // wasted. So be it.
   static const size_t ItemsPerPage = RequestedItemsPerPage - 1;
--- a/xpcom/threads/Scheduler.cpp
+++ b/xpcom/threads/Scheduler.cpp
@@ -64,16 +64,22 @@ public:
   void SuspendInputEventPrioritization() final;
   void ResumeInputEventPrioritization() final;
 
   bool UseCooperativeScheduling() const;
   void SetScheduler(SchedulerImpl* aScheduler);
 
   Mutex& MutexRef() { return mLock; }
 
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override
+  {
+    return (SynchronizedEventQueue::SizeOfExcludingThis(aMallocSizeOf) +
+            mQueue->SizeOfIncludingThis(aMallocSizeOf));
+  }
+
 private:
   Mutex mLock;
   CondVar mNonCooperativeCondVar;
 
   // Using the actual type here would avoid a virtual dispatch. However, that
   // would prevent us from switching between EventQueue and LabeledEventQueue at
   // runtime.
   UniquePtr<AbstractEventQueue> mQueue;
--- a/xpcom/threads/SynchronizedEventQueue.h
+++ b/xpcom/threads/SynchronizedEventQueue.h
@@ -4,16 +4,17 @@
  * 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 mozilla_SynchronizedEventQueue_h
 #define mozilla_SynchronizedEventQueue_h
 
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/AbstractEventQueue.h"
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "nsTObserverArray.h"
 
 class nsIThreadObserver;
 
 namespace mozilla {
 
 // A SynchronizedEventQueue is an abstract class for event queues that can be
@@ -36,16 +37,23 @@ public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadTargetSink)
 
   virtual bool PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
                         EventPriority aPriority) = 0;
 
   // After this method is called, no more events can be posted.
   virtual void Disconnect(const MutexAutoLock& aProofOfLock) = 0;
 
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+
 protected:
   virtual ~ThreadTargetSink() {}
 };
 
 class SynchronizedEventQueue : public ThreadTargetSink
 {
 public:
   virtual already_AddRefed<nsIRunnable> GetEvent(bool aMayWait,
@@ -70,16 +78,21 @@ public:
   void RemoveObserver(nsIThreadObserver* aObserver);
   const nsTObserverArray<nsCOMPtr<nsIThreadObserver>>& EventObservers();
 
   virtual void EnableInputEventPrioritization() = 0;
   virtual void FlushInputEventPrioritization() = 0;
   virtual void SuspendInputEventPrioritization() = 0;
   virtual void ResumeInputEventPrioritization() = 0;
 
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override
+  {
+    return mEventObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   virtual ~SynchronizedEventQueue() {}
 
 private:
   nsTObserverArray<nsCOMPtr<nsIThreadObserver>> mEventObservers;
 };
 
 } // namespace mozilla
--- a/xpcom/threads/ThreadEventQueue.cpp
+++ b/xpcom/threads/ThreadEventQueue.cpp
@@ -33,16 +33,24 @@ public:
     return mOwner->PutEventInternal(std::move(aEvent), aPriority, this);
   }
 
   void Disconnect(const MutexAutoLock& aProofOfLock) final
   {
     mQueue = nullptr;
   }
 
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+  {
+    if (mQueue) {
+      return mQueue->SizeOfIncludingThis(aMallocSizeOf);
+    }
+    return 0;
+  }
+
 private:
   friend class ThreadEventQueue;
 
   // This is a non-owning reference. It must live at least until Disconnect is
   // called to clear it out.
   EventQueue* mQueue;
   RefPtr<ThreadEventQueue> mOwner;
 };
--- a/xpcom/threads/ThreadEventQueue.h
+++ b/xpcom/threads/ThreadEventQueue.h
@@ -78,16 +78,30 @@ public:
   void PopEventQueue(nsIEventTarget* aTarget);
 
   already_AddRefed<nsIThreadObserver> GetObserver() final;
   already_AddRefed<nsIThreadObserver> GetObserverOnThread() final;
   void SetObserver(nsIThreadObserver* aObserver) final;
 
   Mutex& MutexRef() { return mLock; }
 
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override
+  {
+    size_t n = 0;
+
+    n += mBaseQueue->SizeOfIncludingThis(aMallocSizeOf);
+
+    n += mNestedQueues.ShallowSizeOfExcludingThis(aMallocSizeOf);
+    for (auto& queue : mNestedQueues) {
+      n += queue.mEventTarget->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return SynchronizedEventQueue::SizeOfExcludingThis(aMallocSizeOf) + n;
+  }
+
 private:
   class NestedSink;
 
   virtual ~ThreadEventQueue();
 
   bool PutEventInternal(already_AddRefed<nsIRunnable>&& aEvent,
                         EventPriority aPriority,
                         NestedSink* aQueue);
--- a/xpcom/threads/ThreadEventTarget.h
+++ b/xpcom/threads/ThreadEventTarget.h
@@ -2,16 +2,17 @@
 /* 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/. */
 
 #ifndef mozilla_ThreadEventTarget_h
 #define mozilla_ThreadEventTarget_h
 
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/SynchronizedEventQueue.h" // for ThreadTargetSink
 #include "nsISerialEventTarget.h"
 
 namespace mozilla {
 
 // ThreadEventTarget handles the details of posting an event to a thread. It can
 // be used with any ThreadTargetSink implementation.
@@ -25,16 +26,25 @@ public:
   NS_DECL_NSIEVENTTARGET_FULL
 
   // Disconnects the target so that it can no longer post events.
   void Disconnect(const MutexAutoLock& aProofOfLock) { mSink->Disconnect(aProofOfLock); }
 
   // Sets the thread for which IsOnCurrentThread returns true to the current thread.
   void SetCurrentThread();
 
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+  {
+    size_t n = 0;
+    if (mSink) {
+      n += mSink->SizeOfIncludingThis(aMallocSizeOf);
+    }
+    return aMallocSizeOf(this) + n;
+  }
+
 private:
   ~ThreadEventTarget() {}
 
   RefPtr<ThreadTargetSink> mSink;
   bool mIsMainThread;
 };
 
 } // namespace mozilla