Bug 1194555 - Part 6: Run reporters asynchronously. r=njn,jld

This commit is contained in:
Eric Rahm 2015-10-06 11:23:36 -07:00
parent f2b2a50da6
commit 98fe7788c7
6 changed files with 166 additions and 23 deletions

View File

@ -895,6 +895,32 @@ NS_IMPL_ISUPPORTS(
, nsIMemoryReporterCallback
)
class MemoryReportFinishedCallback final : public nsIFinishReportingCallback
{
public:
NS_DECL_ISUPPORTS
explicit MemoryReportFinishedCallback(MemoryReportRequestChild* aActor)
: mActor(aActor)
{
}
NS_IMETHOD Callback(nsISupports* aUnused) override
{
bool sent = PMemoryReportRequestChild::Send__delete__(mActor);
return sent ? NS_OK : NS_ERROR_FAILURE;
}
private:
~MemoryReportFinishedCallback() {}
nsRefPtr<MemoryReportRequestChild> mActor;
};
NS_IMPL_ISUPPORTS(
MemoryReportFinishedCallback
, nsIFinishReportingCallback
)
bool
ContentChild::RecvPMemoryReportRequestConstructor(
PMemoryReportRequestChild* aChild,
@ -931,11 +957,12 @@ NS_IMETHODIMP MemoryReportRequestChild::Run()
// MemoryReport.
nsRefPtr<MemoryReportCallback> cb =
new MemoryReportCallback(this, process);
mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
FileDescriptorToFILE(mDMDFile, "wb"));
nsRefPtr<MemoryReportFinishedCallback> finished =
new MemoryReportFinishedCallback(this);
bool sent = Send__delete__(this);
return sent ? NS_OK : NS_ERROR_FAILURE;
return mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
FileDescriptorToFILE(mDMDFile, "wb"),
finished, nullptr);
}
bool

View File

@ -552,7 +552,14 @@ End of 5th\n\
SimpleTest.waitForClipboard(
function(aActual) {
mostRecentActual = aActual;
return aActual.trim() === aExpected.trim();
let rslt = aActual.trim() === aExpected.trim();
if (!rslt) {
// Try copying again.
synthesizeKey("A", {accelKey: true});
synthesizeKey("C", {accelKey: true});
}
return rslt;
},
function() {
synthesizeKey("A", {accelKey: true});

View File

@ -106,7 +106,14 @@
SimpleTest.waitForClipboard(
function(aActual) {
mostRecentActual = aActual;
return aActual.trim() === aExpected.trim();
let rslt = aActual.trim() === aExpected.trim();
if (!rslt) {
// Try copying again.
synthesizeKey("A", {accelKey: true});
synthesizeKey("C", {accelKey: true});
}
return rslt;
},
function() {
synthesizeKey("A", {accelKey: true});

View File

@ -205,7 +205,7 @@ interface nsIFinishReportingCallback : nsISupports
void callback(in nsISupports data);
};
[scriptable, builtinclass, uuid(babbadb0-b22d-4b05-9ca9-01e617828ec1)]
[scriptable, builtinclass, uuid(61de6dc7-ed11-4104-a577-79941f22f434)]
interface nsIMemoryReporterManager : nsISupports
{
/*
@ -299,7 +299,14 @@ interface nsIMemoryReporterManager : nsISupports
getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData,
in boolean anonymize,
in FILE DMDFile);
in FILE DMDFile,
in nsIFinishReportingCallback finishReporting,
in nsISupports finishReportingData);
/*
* Called by an asynchronous memory reporter upon completion.
*/
[noscript] void endReport();
/*
* The memory reporter manager, for the most part, treats reporters

View File

@ -1357,6 +1357,7 @@ nsMemoryReporterManager::nsMemoryReporterManager()
, mSavedWeakReporters(nullptr)
, mNextGeneration(1)
, mPendingProcessesState(nullptr)
, mPendingReportersState(nullptr)
{
}
@ -1466,8 +1467,11 @@ nsMemoryReporterManager::StartGettingReports()
}
}
#endif
// This is async.
GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
s->mAnonymize, parentDMDFile);
s->mAnonymize, parentDMDFile,
s->mFinishReporting, s->mFinishReportingData);
nsTArray<ContentParent*> childWeakRefs;
ContentParent::GetAll(childWeakRefs);
@ -1499,16 +1503,44 @@ nsMemoryReporterManager::StartGettingReports()
s->mTimer.swap(timer);
}
// The parent's report is done; make note of that, and start
// launching child process reports (if any).
EndProcessReport(s->mGeneration, true);
return NS_OK;
}
void
nsMemoryReporterManager::DispatchReporter(
nsIMemoryReporter* aReporter, bool aIsAsync,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
bool aAnonymize)
{
MOZ_ASSERT(mPendingReportersState);
// Grab refs to everything used in the lambda function.
nsRefPtr<nsMemoryReporterManager> self = this;
nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
nsCOMPtr<nsISupports> handleReportData = aHandleReportData;
nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
[self, reporter, aIsAsync, handleReport, handleReportData, aAnonymize] () {
reporter->CollectReports(handleReport,
handleReportData,
aAnonymize);
if (!aIsAsync) {
self->EndReport();
}
});
NS_DispatchToMainThread(event);
mPendingReportersState->mReportsPending++;
}
NS_IMETHODIMP
nsMemoryReporterManager::GetReportsForThisProcessExtended(
nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData,
bool aAnonymize, FILE* aDMDFile)
bool aAnonymize, FILE* aDMDFile,
nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData)
{
// Memory reporters are not necessarily threadsafe, so this function must
// be called from the main thread.
@ -1516,6 +1548,11 @@ nsMemoryReporterManager::GetReportsForThisProcessExtended(
MOZ_CRASH();
}
if (NS_WARN_IF(mPendingReportersState)) {
// Report is already in progress.
return NS_ERROR_IN_PROGRESS;
}
#ifdef MOZ_DMD
if (aDMDFile) {
// Clear DMD's reportedness state before running the memory
@ -1526,26 +1563,47 @@ nsMemoryReporterManager::GetReportsForThisProcessExtended(
MOZ_ASSERT(!aDMDFile);
#endif
nsCOMArray<nsIMemoryReporter> allReporters;
mPendingReportersState = new PendingReportersState(
aFinishReporting, aFinishReportingData, aDMDFile);
{
mozilla::MutexAutoLock autoLock(mMutex);
for (auto iter = mStrongReporters->Iter(); !iter.Done(); iter.Next()) {
allReporters.AppendElement(iter.Key());
DispatchReporter(iter.Key(), iter.Data(),
aHandleReport, aHandleReportData, aAnonymize);
}
for (auto iter = mWeakReporters->Iter(); !iter.Done(); iter.Next()) {
allReporters.AppendElement(iter.Key());
nsCOMPtr<nsIMemoryReporter> reporter = iter.Key();
DispatchReporter(reporter, iter.Data(),
aHandleReport, aHandleReportData, aAnonymize);
}
}
for (uint32_t i = 0; i < allReporters.Length(); i++) {
allReporters[i]->CollectReports(aHandleReport, aHandleReportData,
aAnonymize);
}
return NS_OK;
}
NS_IMETHODIMP
nsMemoryReporterManager::EndReport()
{
if (--mPendingReportersState->mReportsPending == 0) {
#ifdef MOZ_DMD
if (aDMDFile) {
return nsMemoryInfoDumper::DumpDMDToFile(aDMDFile);
}
if (mPendingReportersState->mDMDFile) {
nsMemoryInfoDumper::DumpDMDToFile(mPendingReportersState->mDMDFile);
}
#endif
if (mPendingProcessesState) {
// This is the parent process.
EndProcessReport(mPendingProcessesState->mGeneration, true);
} else {
mPendingReportersState->mFinishReporting->Callback(
mPendingReportersState->mFinishReportingData);
}
delete mPendingReportersState;
mPendingReportersState = nullptr;
}
return NS_OK;
}

View File

@ -188,6 +188,11 @@ private:
nsresult StartGettingReports();
nsresult FinishReporting();
void DispatchReporter(nsIMemoryReporter* aReporter, bool aIsAsync,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
bool aAnonymize);
static void TimeoutCallback(nsITimer* aTimer, void* aData);
// Note: this timeout needs to be long enough to allow for the
// possibility of DMD reports and/or running on a low-end phone.
@ -205,6 +210,9 @@ private:
uint32_t mNextGeneration;
// Used to keep track of state of which processes are currently running and
// waiting to run memory reports. Holds references to parameters needed when
// requesting a memory report and finishing reporting.
struct PendingProcessesState
{
uint32_t mGeneration;
@ -230,11 +238,40 @@ private:
const nsAString& aDMDDumpIdent);
};
// Used to keep track of the state of the asynchronously run memory
// reporters. The callback and file handle used when all memory reporters
// have finished are also stored here.
struct PendingReportersState
{
// Number of memory reporters currently running.
uint32_t mReportsPending;
// Callback for when all memory reporters have completed.
nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
nsCOMPtr<nsISupports> mFinishReportingData;
// File handle to write a DMD report to if requested.
FILE* mDMDFile;
PendingReportersState(nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData,
FILE* aDMDFile)
: mReportsPending(0)
, mFinishReporting(aFinishReporting)
, mFinishReportingData(aFinishReportingData)
, mDMDFile(aDMDFile)
{
}
};
// When this is non-null, a request is in flight. Note: We use manual
// new/delete for this because its lifetime doesn't match block scope or
// anything like that.
PendingProcessesState* mPendingProcessesState;
// This is reinitialized each time a call to GetReports is initiated.
PendingReportersState* mPendingReportersState;
PendingProcessesState* GetStateForGeneration(uint32_t aGeneration);
static bool StartChildReport(mozilla::dom::ContentParent* aChild,
const PendingProcessesState* aState);