Bug 958353 - Add finishCC() and ccSlice() methods for testing incremental cycle collection. r=smaug

This commit is contained in:
Andrew McCreight 2014-05-06 17:25:26 -07:00
parent ab3d274c1e
commit b89c24ee20
6 changed files with 105 additions and 13 deletions

View File

@ -18,13 +18,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=
<script type="application/javascript">
<![CDATA[
/** Test for Bug **/
let Ci = Components.interfaces;
var obs = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
.getService(Ci.nsIObserverService);
var didCall = false;
var observer = {
QueryInterface: function QueryInterface(aIID) {
if (aIID.equals(Components.interfaces.nsIObserver) ||
aIID.equals(Components.interfaces.nsISupports))
if (aIID.equals(Ci.nsIObserver) ||
aIID.equals(Ci.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
@ -35,9 +36,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=
}
};
// Make sure that we call the observer even if we're in the middle
// of an ICC when we add the observer. See bug 981033.
SpecialPowers.finishCC();
SpecialPowers.ccSlice(1);
obs.addObserver(observer, "cycle-collector-begin", false);
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils).cycleCollect();
SpecialPowers.DOMWindowUtils.cycleCollect();
ok(didCall, "Observer should have been called!");
]]>

View File

@ -121,7 +121,7 @@ interface ScheduledGCCallback : nsISupports
/**
* interface of Components.utils
*/
[scriptable, uuid(45b80e00-fb0d-439e-b7bf-54f24af0c4a6)]
[scriptable, uuid(c9b6f5a0-cfe8-11e3-9c1a-0800200c9a66)]
interface nsIXPCComponents_Utils : nsISupports
{
@ -260,6 +260,22 @@ interface nsIXPCComponents_Utils : nsISupports
*/
void forceCC();
/*
* To be called from JS only.
*
* If any incremental CC is in progress, finish it. For testing.
*/
void finishCC();
/*
* To be called from JS only.
*
* Do some cycle collector work, with the given work budget.
* The cost of calling Traverse() on a single object is set as 1.
* For testing.
*/
void ccSlice(in long long budget);
/*
* To be called from JS only.
*

View File

@ -2786,6 +2786,22 @@ nsXPCComponents_Utils::ForceCC()
return NS_OK;
}
/* void finishCC(); */
NS_IMETHODIMP
nsXPCComponents_Utils::FinishCC()
{
nsCycleCollector_finishAnyCurrentCollection();
return NS_OK;
}
/* void ccSlice(long long budget); */
NS_IMETHODIMP
nsXPCComponents_Utils::CcSlice(int64_t budget)
{
nsCycleCollector_collectSliceWork(budget);
return NS_OK;
}
/* void forceShrinkingGC (); */
NS_IMETHODIMP
nsXPCComponents_Utils::ForceShrinkingGC()

View File

@ -1317,6 +1317,14 @@ SpecialPowersAPI.prototype = {
Cu.forceCC();
},
finishCC: function() {
Cu.finishCC();
},
ccSlice: function(budget) {
Cu.ccSlice(budget);
},
// Due to various dependencies between JS objects and C++ objects, an ordinary
// forceGC doesn't necessarily clear all unused objects, thus the GC and CC
// needs to run several times and when no other JS is running.

View File

@ -1217,6 +1217,7 @@ public:
void RemoveObjectFromGraph(void *aPtr);
void PrepareForGarbageCollection();
void FinishAnyCurrentCollection();
bool Collect(ccType aCCType,
SliceBudget &aBudget,
@ -3363,8 +3364,18 @@ nsCycleCollector::PrepareForGarbageCollection()
return;
}
FinishAnyCurrentCollection();
}
void
nsCycleCollector::FinishAnyCurrentCollection()
{
if (mIncrementalPhase == IdlePhase) {
return;
}
SliceBudget unlimitedBudget;
PrintPhase("PrepareForGarbageCollection");
PrintPhase("FinishAnyCurrentCollection");
// Use SliceCC because we only want to finish the CC in progress.
Collect(SliceCC, unlimitedBudget, nullptr);
MOZ_ASSERT(mIncrementalPhase == IdlePhase);
@ -3871,10 +3882,25 @@ nsCycleCollector_collectSlice(int64_t aSliceTime)
PROFILER_LABEL("CC", "nsCycleCollector_collectSlice");
SliceBudget budget;
if (aSliceTime > 0) {
if (aSliceTime >= 0) {
budget = SliceBudget::TimeBudget(aSliceTime);
} else if (aSliceTime == 0) {
budget = SliceBudget::WorkBudget(1);
}
data->mCollector->Collect(SliceCC, budget, nullptr);
}
void
nsCycleCollector_collectSliceWork(int64_t aSliceWork)
{
CollectorData *data = sCollectorData.get();
// We should have started the cycle collector by now.
MOZ_ASSERT(data);
MOZ_ASSERT(data->mCollector);
PROFILER_LABEL("CC", "nsCycleCollector_collectSliceWork");
SliceBudget budget;
if (aSliceWork >= 0) {
budget = SliceBudget::WorkBudget(aSliceWork);
}
data->mCollector->Collect(SliceCC, budget, nullptr);
}
@ -3893,6 +3919,20 @@ nsCycleCollector_prepareForGarbageCollection()
data->mCollector->PrepareForGarbageCollection();
}
void
nsCycleCollector_finishAnyCurrentCollection()
{
CollectorData *data = sCollectorData.get();
MOZ_ASSERT(data);
if (!data->mCollector) {
return;
}
data->mCollector->FinishAnyCurrentCollection();
}
void
nsCycleCollector_shutdown()
{

View File

@ -37,16 +37,22 @@ void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false,
void nsCycleCollector_prepareForGarbageCollection();
// If an incremental cycle collection is in progress, finish it.
void nsCycleCollector_finishAnyCurrentCollection();
void nsCycleCollector_dispatchDeferredDeletion(bool aContinuation = false);
bool nsCycleCollector_doDeferredDeletion();
void nsCycleCollector_collect(nsICycleCollectorListener *aManualListener);
// If aSliceTime is negative, the CC will run to completion. If aSliceTime
// is 0, only a minimum quantum of work will be done. Otherwise, aSliceTime
// will be used as the time budget for the slice, in ms.
// If aSliceTime is negative, the CC will run to completion. Otherwise,
// aSliceTime will be used as the time budget for the slice, in ms.
void nsCycleCollector_collectSlice(int64_t aSliceTime);
// If aSliceTime is negative, the CC will run to completion. Otherwise,
// aSliceTime will be used as the work budget for the slice.
void nsCycleCollector_collectSliceWork(int64_t aSliceWork);
uint32_t nsCycleCollector_suspectedCount();
void nsCycleCollector_shutdown();