Bug 1296484 - Automatically run a CC if COMPARTMENT_REVIVED GC ends mostly gray; r=jonco,r=mccr8

--HG--
extra : rebase_source : 26aa07342c0b286d772422401bd7d5dd4efbb2fa
This commit is contained in:
Terrence Cole 2016-08-18 13:30:32 -07:00
parent cc5c2c3ffc
commit 1432ea6638
5 changed files with 83 additions and 0 deletions

View File

@ -386,6 +386,16 @@ using GCNurseryCollectionCallback = void(*)(JSContext* cx, GCNurseryProgress pro
extern JS_PUBLIC_API(GCNurseryCollectionCallback)
SetGCNurseryCollectionCallback(JSContext* cx, GCNurseryCollectionCallback callback);
typedef void
(* DoCycleCollectionCallback)(JSContext* cx);
/**
* The purge gray callback is called after any COMPARTMENT_REVIVED GC in which
* the majority of compartments have been marked gray.
*/
extern JS_PUBLIC_API(DoCycleCollectionCallback)
SetDoCycleCollectionCallback(JSContext* cx, DoCycleCollectionCallback callback);
/**
* Incremental GC defaults to enabled, but may be disabled for testing or in
* embeddings that have not yet implemented barriers on their native classes.

View File

@ -778,6 +778,8 @@ class GCRuntime
JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
JS::GCNurseryCollectionCallback setNurseryCollectionCallback(
JS::GCNurseryCollectionCallback callback);
JS::DoCycleCollectionCallback setDoCycleCollectionCallback(JS::DoCycleCollectionCallback callback);
void callDoCycleCollectionCallback(JSContext* cx);
void setFullCompartmentChecks(bool enable);
@ -947,6 +949,7 @@ class GCRuntime
void traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
AutoLockForExclusiveAccess& lock);
void bufferGrayRoots();
void maybeDoCycleCollection();
void markCompartments();
IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase);
template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
@ -1305,6 +1308,7 @@ class GCRuntime
bool fullCompartmentChecks;
Callback<JSGCCallback> gcCallback;
Callback<JS::DoCycleCollectionCallback> gcDoCycleCollectionCallback;
Callback<JSObjectsTenuredCallback> tenuredCallback;
CallbackVector<JSFinalizeCallback> finalizeCallbacks;
CallbackVector<JSWeakPointerZoneGroupCallback> updateWeakPointerZoneGroupCallbacks;

View File

@ -1481,6 +1481,21 @@ GCRuntime::setNurseryCollectionCallback(JS::GCNurseryCollectionCallback callback
return stats.setNurseryCollectionCallback(callback);
}
JS::DoCycleCollectionCallback
GCRuntime::setDoCycleCollectionCallback(JS::DoCycleCollectionCallback callback)
{
auto prior = gcDoCycleCollectionCallback;
gcDoCycleCollectionCallback = Callback<JS::DoCycleCollectionCallback>(callback, nullptr);
return prior.op;
}
void
GCRuntime::callDoCycleCollectionCallback(JSContext* cx)
{
if (gcDoCycleCollectionCallback.op)
gcDoCycleCollectionCallback.op(cx);
}
bool
GCRuntime::addRoot(Value* vp, const char* name)
{
@ -6171,6 +6186,31 @@ GCRuntime::scanZonesBeforeGC()
return zoneStats;
}
// The GC can only clean up scheduledForDestruction compartments that were
// marked live by a barrier (e.g. by RemapWrappers from a navigation event).
// It is also common to have compartments held live because they are part of a
// cycle in gecko, e.g. involving the HTMLDocument wrapper. In this case, we
// need to run the CycleCollector in order to remove these edges before the
// compartment can be freed.
void
GCRuntime::maybeDoCycleCollection()
{
const static double ExcessiveGrayCompartments = 0.8;
const static size_t LimitGrayCompartments = 200;
size_t compartmentsTotal = 0;
size_t compartmentsGray = 0;
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
++compartmentsTotal;
GlobalObject* global = c->unsafeUnbarrieredMaybeGlobal();
if (global && global->asTenured().isMarked(GRAY))
++compartmentsGray;
}
double grayFraction = double(compartmentsGray) / double(compartmentsTotal);
if (grayFraction > ExcessiveGrayCompartments || compartmentsGray > LimitGrayCompartments)
callDoCycleCollectionCallback(rt->contextFromMainThread());
}
void
GCRuntime::checkCanCallAPI()
{
@ -6251,6 +6291,9 @@ GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::R
repeat = (poked && cleanUpEverything) || wasReset || repeatForDeadZone;
} while (repeat);
if (reason == JS::gcreason::COMPARTMENT_REVIVED)
maybeDoCycleCollection();
#ifdef JS_GC_ZEAL
if (shouldCompact() && rt->hasZealMode(ZealMode::CheckHeapOnMovingGC)) {
gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
@ -7169,6 +7212,12 @@ JS::SetGCSliceCallback(JSContext* cx, GCSliceCallback callback)
return cx->gc.setSliceCallback(callback);
}
JS_PUBLIC_API(JS::DoCycleCollectionCallback)
JS::SetDoCycleCollectionCallback(JSContext* cx, JS::DoCycleCollectionCallback callback)
{
return cx->gc.setDoCycleCollectionCallback(callback);
}
JS_PUBLIC_API(JS::GCNurseryCollectionCallback)
JS::SetGCNurseryCollectionCallback(JSContext* cx, GCNurseryCollectionCallback callback)
{

View File

@ -23,6 +23,7 @@
#include "nsIObserverService.h"
#include "nsIDebug2.h"
#include "nsIDocShell.h"
#include "nsIRunnable.h"
#include "amIAddonManager.h"
#include "nsPIDOMWindow.h"
#include "nsPrintfCString.h"
@ -720,6 +721,21 @@ XPCJSRuntime::GCSliceCallback(JSContext* cx,
(*self->mPrevGCSliceCallback)(cx, progress, desc);
}
/* static */ void
XPCJSRuntime::DoCycleCollectionCallback(JSContext* cx)
{
// The GC has detected that a CC at this point would collect a tremendous
// amount of garbage that is being revivified unnecessarily.
NS_DispatchToMainThread(NS_NewRunnableFunction([](){nsJSContext::CycleCollectNow(nullptr);}));
XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
if (!self)
return;
if (self->mPrevDoCycleCollectionCallback)
(*self->mPrevDoCycleCollectionCallback)(cx);
}
void
XPCJSRuntime::CustomGCCallback(JSGCStatus status)
{
@ -3481,6 +3497,8 @@ XPCJSRuntime::Initialize()
JS_SetSizeOfIncludingThisCompartmentCallback(cx, CompartmentSizeOfIncludingThisCallback);
JS_SetCompartmentNameCallback(cx, CompartmentNameCallback);
mPrevGCSliceCallback = JS::SetGCSliceCallback(cx, GCSliceCallback);
mPrevDoCycleCollectionCallback = JS::SetDoCycleCollectionCallback(cx,
DoCycleCollectionCallback);
JS_AddFinalizeCallback(cx, FinalizeCallback, nullptr);
JS_AddWeakPointerZoneGroupCallback(cx, WeakPointerZoneGroupCallback, this);
JS_AddWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback, this);

View File

@ -551,6 +551,7 @@ public:
static void GCSliceCallback(JSContext* cx,
JS::GCProgress progress,
const JS::GCDescription& desc);
static void DoCycleCollectionCallback(JSContext* cx);
static void FinalizeCallback(JSFreeOp* fop,
JSFinalizeStatus status,
bool isZoneGC,
@ -628,6 +629,7 @@ private:
nsTArray<xpcGCCallback> extraGCCallbacks;
RefPtr<WatchdogManager> mWatchdogManager;
JS::GCSliceCallback mPrevGCSliceCallback;
JS::DoCycleCollectionCallback mPrevDoCycleCollectionCallback;
JS::PersistentRootedObject mUnprivilegedJunkScope;
JS::PersistentRootedObject mPrivilegedJunkScope;
JS::PersistentRootedObject mCompilationScope;