--- 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