bug 1224022 record MSG memory sizes asynchronously r?padenot draft
authorKarl Tomlinson <karlt+@karlt.net>
Mon, 04 Jul 2016 10:40:48 +1200
changeset 383470 af4c15d949a73eb03eee125f0076b1a55121f474
parent 383469 c5a4b5a0f24fcb42794b18f520044fd9fca68c2c
child 524491 a0a774eec9d0bf3c95d02a3dbb61e00202187f1a
push id22037
push userktomlinson@mozilla.com
push dateMon, 04 Jul 2016 08:58:23 +0000
reviewerspadenot
bugs1224022
milestone50.0a1
bug 1224022 record MSG memory sizes asynchronously r?padenot This removes the one second timeout for MSG collection, extending the timeout period to the 50 second timeout of nsMemoryReporterManager. Also removed: * The condition variable logic that can stop waiting without checking the condition set when memory reports are complete. * Races with mAudioStreamSizes modification on two threads after wait timeout. Memory from streams in offline graphs that are not yet running is now also included. MozReview-Commit-ID: 3LeFVyu4J7
dom/media/MediaStreamGraph.cpp
dom/media/MediaStreamGraphImpl.h
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -1531,36 +1531,16 @@ MediaStreamGraphImpl::Process()
     mMixer.FinishMixing();
   }
 
   if (!allBlockedForever) {
     EnsureNextIteration();
   }
 }
 
-void
-MediaStreamGraphImpl::MaybeProduceMemoryReport()
-{
-  MonitorAutoLock lock(mMemoryReportMonitor);
-  if (mNeedsMemoryReport) {
-    mNeedsMemoryReport = false;
-
-    for (MediaStream* s : AllStreams()) {
-      AudioNodeStream* stream = s->AsAudioNodeStream();
-      if (stream) {
-        AudioNodeSizes usage;
-        stream->SizeOfAudioNodesIncludingThis(MallocSizeOf, usage);
-        mAudioStreamSizes.AppendElement(usage);
-      }
-    }
-
-    lock.Notify();
-  }
-}
-
 bool
 MediaStreamGraphImpl::UpdateMainThreadState()
 {
   MonitorAutoLock lock(mMonitor);
   bool finalUpdate = mForceShutDown ||
     (mProcessedTime >= mEndTime && AllFinishedStreamsNotified()) ||
     (IsEmpty() && mBackMessageQueue.IsEmpty());
   PrepareUpdatesToMainThreadState(finalUpdate);
@@ -1582,18 +1562,16 @@ MediaStreamGraphImpl::UpdateMainThreadSt
 }
 
 bool
 MediaStreamGraphImpl::OneIteration(GraphTime aStateEnd)
 {
   // Process graph message from the main thread for this iteration.
   RunMessagesInQueue();
 
-  MaybeProduceMemoryReport();
-
   GraphTime stateEnd = std::min(aStateEnd, mEndTime);
   UpdateGraph(stateEnd);
 
   mStateComputedTime = stateEnd;
 
   Process();
 
   GraphTime oldProcessedTime = mProcessedTime;
@@ -3302,20 +3280,17 @@ MediaStreamGraphImpl::MediaStreamGraphIm
   , mPostedRunInStableState(false)
   , mRealtime(aDriverRequested != OFFLINE_THREAD_DRIVER)
   , mNonRealtimeProcessing(false)
   , mStreamOrderDirty(false)
   , mLatencyLog(AsyncLatencyLogger::Get())
 #ifdef MOZ_WEBRTC
   , mFarendObserverRef(nullptr)
 #endif
-  , mMemoryReportMonitor("MSGIMemory")
   , mSelfRef(this)
-  , mAudioStreamSizes()
-  , mNeedsMemoryReport(false)
 #ifdef DEBUG
   , mCanRunMessagesSynchronously(false)
 #endif
   , mAudioChannel(aChannel)
 {
   if (mRealtime) {
     if (aDriverRequested == AUDIO_THREAD_DRIVER) {
       AudioCallbackDriver* driver = new AudioCallbackDriver(this);
@@ -3324,17 +3299,17 @@ MediaStreamGraphImpl::MediaStreamGraphIm
       mDriver = new SystemClockDriver(this);
     }
   } else {
     mDriver = new OfflineClockDriver(this, MEDIA_GRAPH_TARGET_PERIOD_MS);
   }
 
   mLastMainThreadUpdate = TimeStamp::Now();
 
-  RegisterWeakMemoryReporter(this);
+  RegisterWeakAsyncMemoryReporter(this);
 }
 
 void
 MediaStreamGraphImpl::Destroy()
 {
   // First unregister from memory reporting.
   UnregisterWeakMemoryReporter(this);
 
@@ -3431,69 +3406,139 @@ MediaStreamGraph::DestroyNonRealtimeInst
     // Start the graph, but don't produce anything
     graph->StartNonRealtimeProcessing(0);
   }
   graph->ForceShutDown(nullptr);
 }
 
 NS_IMPL_ISUPPORTS(MediaStreamGraphImpl, nsIMemoryReporter)
 
-struct ArrayClearer
-{
-  explicit ArrayClearer(nsTArray<AudioNodeSizes>& aArray) : mArray(aArray) {}
-  ~ArrayClearer() { mArray.Clear(); }
-  nsTArray<AudioNodeSizes>& mArray;
-};
-
 NS_IMETHODIMP
 MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
                                      nsISupports* aData, bool aAnonymize)
 {
   if (mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN) {
     // Shutting down, nothing to report.
+    FinishCollectReports(aHandleReport, aData, nsTArray<AudioNodeSizes>());
+    return NS_OK;
+  }
+
+  class Message final : public ControlMessage {
+  public:
+    Message(MediaStreamGraphImpl *aGraph,
+            nsIHandleReportCallback* aHandleReport,
+            nsISupports *aHandlerData)
+      : ControlMessage(nullptr)
+      , mGraph(aGraph)
+      , mHandleReport(aHandleReport)
+      , mHandlerData(aHandlerData) {}
+    void Run() override
+    {
+      mGraph->CollectSizesForMemoryReport(mHandleReport.forget(),
+                                          mHandlerData.forget());
+    }
+    void RunDuringShutdown() override
+    {
+      // Run this message during shutdown too, so that endReports is called.
+      Run();
+    }
+    MediaStreamGraphImpl *mGraph;
+    // nsMemoryReporterManager keeps the callback and data alive only if it
+    // does not time out.
+    nsCOMPtr<nsIHandleReportCallback> mHandleReport;
+    nsCOMPtr<nsISupports> mHandlerData;
+  };
+
+  // When a non-realtime graph has not started, there is no thread yet, so
+  // collect sizes on this thread.
+  if (!(mRealtime || mNonRealtimeProcessing)) {
+    CollectSizesForMemoryReport(do_AddRef(aHandleReport), do_AddRef(aData));
     return NS_OK;
   }
 
-  // Clears out the report array after we're done with it.
-  ArrayClearer reportCleanup(mAudioStreamSizes);
-
+  AppendMessage(MakeUnique<Message>(this, aHandleReport, aData));
+
+  return NS_OK;
+}
+
+void
+MediaStreamGraphImpl::CollectSizesForMemoryReport(
+  already_AddRefed<nsIHandleReportCallback> aHandleReport,
+  already_AddRefed<nsISupports> aHandlerData)
+{
+  class FinishCollectRunnable final : public Runnable
   {
-    MonitorAutoLock memoryReportLock(mMemoryReportMonitor);
-    mNeedsMemoryReport = true;
-
+  public:
+    explicit FinishCollectRunnable(
+               already_AddRefed<nsIHandleReportCallback> aHandleReport,
+               already_AddRefed<nsISupports> aHandlerData)
+      : mHandleReport(aHandleReport)
+      , mHandlerData(aHandlerData)
+    {}
+
+    NS_IMETHOD Run() override
     {
-      // Wake up the MSG thread if it's real time (Offline graphs can't be
-      // sleeping).
-      MonitorAutoLock monitorLock(mMonitor);
-      if (!CurrentDriver()->AsOfflineClockDriver()) {
-        CurrentDriver()->WakeUp();
-      }
+      MediaStreamGraphImpl::FinishCollectReports(mHandleReport, mHandlerData,
+                                                 Move(mAudioStreamSizes));
+      return NS_OK;
     }
 
-    // Wait for up to one second for the report to complete.
-    nsresult rv;
-    const PRIntervalTime kMaxWait = PR_SecondsToInterval(1);
-    while ((rv = memoryReportLock.Wait(kMaxWait)) != NS_OK) {
-      if (PR_GetError() != PR_PENDING_INTERRUPT_ERROR) {
-        return rv;
-      }
+    nsTArray<AudioNodeSizes> mAudioStreamSizes;
+
+  private:
+    ~FinishCollectRunnable() {}
+
+    // Avoiding nsCOMPtr because NSCAP_ASSERT_NO_QUERY_NEEDED in its
+    // constructor modifies the ref-count, which cannot be done off main
+    // thread.
+    RefPtr<nsIHandleReportCallback> mHandleReport;
+    RefPtr<nsISupports> mHandlerData;
+  };
+
+  RefPtr<FinishCollectRunnable> runnable =
+    new FinishCollectRunnable(Move(aHandleReport), Move(aHandlerData));
+
+  auto audioStreamSizes = &runnable->mAudioStreamSizes;
+
+  for (MediaStream* s : AllStreams()) {
+    AudioNodeStream* stream = s->AsAudioNodeStream();
+    if (stream) {
+      AudioNodeSizes* usage = audioStreamSizes->AppendElement();
+      stream->SizeOfAudioNodesIncludingThis(MallocSizeOf, *usage);
     }
   }
 
+  NS_DispatchToMainThread(runnable.forget());
+}
+
+void
+MediaStreamGraphImpl::
+FinishCollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+                     const nsTArray<AudioNodeSizes>& aAudioStreamSizes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIMemoryReporterManager> manager =
+    do_GetService("@mozilla.org/memory-reporter-manager;1");
+
+  if (!manager)
+    return;
+
 #define REPORT(_path, _amount, _desc)                                       \
   do {                                                                      \
     nsresult rv;                                                            \
     rv = aHandleReport->Callback(EmptyCString(), _path,                     \
                                  KIND_HEAP, UNITS_BYTES, _amount,           \
                                  NS_LITERAL_CSTRING(_desc), aData);         \
-    NS_ENSURE_SUCCESS(rv, rv);                                              \
+    if (NS_WARN_IF(NS_FAILED(rv)))                                          \
+      return;                                                               \
   } while (0)
 
-  for (size_t i = 0; i < mAudioStreamSizes.Length(); i++) {
-    const AudioNodeSizes& usage = mAudioStreamSizes[i];
+  for (size_t i = 0; i < aAudioStreamSizes.Length(); i++) {
+    const AudioNodeSizes& usage = aAudioStreamSizes[i];
     const char* const nodeType =
       usage.mNodeType ? usage.mNodeType : "<unknown>";
 
     nsPrintfCString enginePath("explicit/webaudio/audio-node/%s/engine-objects",
                                nodeType);
     REPORT(enginePath, usage.mEngine,
            "Memory used by AudioNode engine objects (Web Audio).");
 
@@ -3510,17 +3555,17 @@ MediaStreamGraphImpl::CollectReports(nsI
     REPORT(NS_LITERAL_CSTRING(
               "explicit/webaudio/audio-node/PannerNode/hrtf-databases"),
            hrtfLoaders,
            "Memory used by PannerNode databases (Web Audio).");
   }
 
 #undef REPORT
 
-  return NS_OK;
+  manager->EndReport();
 }
 
 SourceMediaStream*
 MediaStreamGraph::CreateSourceStream(DOMMediaStream* aWrapper)
 {
   SourceMediaStream* stream = new SourceMediaStream(aWrapper);
   AddStream(stream);
   return stream;
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -187,16 +187,25 @@ public:
    * This is called during application shutdown.
    */
   void ForceShutDown(ShutdownTicket* aShutdownTicket);
 
   /**
    * Called before the thread runs.
    */
   void Init();
+
+  /**
+   * Respond to CollectReports with sizes collected on the graph thread.
+   */
+  static void
+  FinishCollectReports(nsIHandleReportCallback* aHandleReport,
+                       nsISupports* aData,
+                       const nsTArray<AudioNodeSizes>& aAudioStreamSizes);
+
   // The following methods run on the graph thread (or possibly the main thread if
   // mLifecycleState > LIFECYCLE_RUNNING)
   void AssertOnGraphThreadOrNotRunning() const
   {
     // either we're on the right thread (and calling CurrentDriver() is safe),
     // or we're going to assert anyways, so don't cross-check CurrentDriver
 #ifdef DEBUG
     // if all the safety checks fail, assert we own the monitor
@@ -205,17 +214,19 @@ public:
             mLifecycleState > LIFECYCLE_RUNNING &&
             NS_IsMainThread())) {
         mMonitor.AssertCurrentThreadOwns();
       }
     }
 #endif
   }
 
-  void MaybeProduceMemoryReport();
+  void CollectSizesForMemoryReport(
+         already_AddRefed<nsIHandleReportCallback> aHandleReport,
+         already_AddRefed<nsISupports> aHandlerData);
 
   /**
    * Returns true if this MediaStreamGraph should keep running
    */
   bool UpdateMainThreadState();
 
   /**
    * Returns true if this MediaStreamGraph should keep running
@@ -815,45 +826,33 @@ public:
   dom::AudioChannel AudioChannel() const { return mAudioChannel; }
 
 private:
   virtual ~MediaStreamGraphImpl();
 
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   /**
-   * Used to signal that a memory report has been requested.
-   */
-  Monitor mMemoryReportMonitor;
-  /**
    * This class uses manual memory management, and all pointers to it are raw
    * pointers. However, in order for it to implement nsIMemoryReporter, it needs
    * to implement nsISupports and so be ref-counted. So it maintains a single
    * nsRefPtr to itself, giving it a ref-count of 1 during its entire lifetime,
    * and Destroy() nulls this self-reference in order to trigger self-deletion.
    */
   RefPtr<MediaStreamGraphImpl> mSelfRef;
-  /**
-   * Used to pass memory report information across threads.
-   */
-  nsTArray<AudioNodeSizes> mAudioStreamSizes;
 
   struct WindowAndStream
   {
     uint64_t mWindowId;
     RefPtr<ProcessedMediaStream> mCaptureStreamSink;
   };
   /**
    * Stream for window audio capture.
    */
   nsTArray<WindowAndStream> mWindowCaptureStreams;
-  /**
-   * Indicates that the MSG thread should gather data for a memory report.
-   */
-  bool mNeedsMemoryReport;
 
 #ifdef DEBUG
   /**
    * Used to assert when AppendMessage() runs ControlMessages synchronously.
    */
   bool mCanRunMessagesSynchronously;
 #endif