mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 897433 - Telemetry for SnowWhite and more async SnowWhite freeing (patch v4), r=mccr8
This commit is contained in:
parent
264abea880
commit
676d2b13e5
@ -135,6 +135,8 @@ static PRLogModuleInfo* gJSDiagnostics;
|
||||
// Large value used to specify that a script should run essentially forever
|
||||
#define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
|
||||
|
||||
#define NS_MAJOR_FORGET_SKIPPABLE_CALLS 2
|
||||
|
||||
// if you add statics here, add them to the list in nsJSRuntime::Startup
|
||||
|
||||
static nsITimer *sGCTimer;
|
||||
@ -2502,7 +2504,9 @@ FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless)
|
||||
{
|
||||
PRTime startTime = PR_Now();
|
||||
FinishAnyIncrementalGC();
|
||||
nsCycleCollector_forgetSkippable(aRemoveChildless);
|
||||
bool earlyForgetSkippable =
|
||||
sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS;
|
||||
nsCycleCollector_forgetSkippable(aRemoveChildless, earlyForgetSkippable);
|
||||
sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
|
||||
++sCleanupsSinceLastGC;
|
||||
PRTime delta = PR_Now() - startTime;
|
||||
@ -2552,8 +2556,9 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
|
||||
|
||||
// Run forgetSkippable synchronously to reduce the size of the CC graph. This
|
||||
// is particularly useful if we recently finished a GC.
|
||||
if (sCleanupsSinceLastGC < 2 && aExtraForgetSkippableCalls >= 0) {
|
||||
while (sCleanupsSinceLastGC < 2) {
|
||||
if (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS &&
|
||||
aExtraForgetSkippableCalls >= 0) {
|
||||
while (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS) {
|
||||
FireForgetSkippable(nsCycleCollector_suspectedCount(), false);
|
||||
ranSyncForgetSkippable = true;
|
||||
}
|
||||
@ -3029,9 +3034,6 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescrip
|
||||
|
||||
// The GC has more work to do, so schedule another GC slice.
|
||||
if (aProgress == JS::GC_SLICE_END) {
|
||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
nsCycleCollector_dispatchDeferredDeletion();
|
||||
}
|
||||
nsJSContext::KillInterSliceGCTimer();
|
||||
if (!sShuttingDown) {
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
|
||||
@ -3073,6 +3075,11 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescrip
|
||||
}
|
||||
}
|
||||
|
||||
if ((aProgress == JS::GC_SLICE_END || aProgress == JS::GC_CYCLE_END) &&
|
||||
ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
nsCycleCollector_dispatchDeferredDeletion();
|
||||
}
|
||||
|
||||
if (sPrevGCSliceCallback)
|
||||
(*sPrevGCSliceCallback)(aRt, aProgress, aDesc);
|
||||
}
|
||||
|
@ -86,6 +86,12 @@
|
||||
"kind": "flag",
|
||||
"description": "Set if the cycle collector ran out of memory at some point"
|
||||
},
|
||||
"CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING": {
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": 50,
|
||||
"description": "Time spent on one asynchronous SnowWhite freeing (ms)"
|
||||
},
|
||||
"FORGET_SKIPPABLE_MAX": {
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
|
@ -658,6 +658,8 @@ CanonicalizeParticipant(void **parti, nsCycleCollectionParticipant **cp)
|
||||
}
|
||||
}
|
||||
|
||||
class nsCycleCollector;
|
||||
|
||||
struct nsPurpleBuffer
|
||||
{
|
||||
private:
|
||||
@ -775,14 +777,16 @@ public:
|
||||
|
||||
void SelectPointers(GCGraphBuilder &builder);
|
||||
|
||||
// RemoveSkippable removes entries from the purple buffer if
|
||||
// nsPurpleBufferEntry::mRefCnt is 0 or if the object's
|
||||
// nsXPCOMCycleCollectionParticipant::CanSkip() returns true or
|
||||
// if nsPurpleBufferEntry::mRefCnt->IsPurple() is false.
|
||||
// If removeChildlessNodes is true, then any nodes in the purple buffer
|
||||
// that will have no children in the cycle collector graph will also be
|
||||
// removed. CanSkip() may be run on these children.
|
||||
void RemoveSkippable(bool removeChildlessNodes,
|
||||
// RemoveSkippable removes entries from the purple buffer synchronously
|
||||
// (1) if aAsyncSnowWhiteFreeing is false and nsPurpleBufferEntry::mRefCnt is 0 or
|
||||
// (2) if the object's nsXPCOMCycleCollectionParticipant::CanSkip() returns true or
|
||||
// (3) if nsPurpleBufferEntry::mRefCnt->IsPurple() is false.
|
||||
// (4) If removeChildlessNodes is true, then any nodes in the purple buffer
|
||||
// that will have no children in the cycle collector graph will also be
|
||||
// removed. CanSkip() may be run on these children.
|
||||
void RemoveSkippable(nsCycleCollector* aCollector,
|
||||
bool removeChildlessNodes,
|
||||
bool aAsyncSnowWhiteFreeing,
|
||||
CC_ForgetSkippableCallback aCb);
|
||||
|
||||
nsPurpleBufferEntry* NewEntry()
|
||||
@ -974,6 +978,8 @@ public:
|
||||
// Top level structure for the cycle collector.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AsyncFreeSnowWhite;
|
||||
|
||||
class nsCycleCollector
|
||||
{
|
||||
friend class GCGraphBuilder;
|
||||
@ -995,6 +1001,8 @@ public:
|
||||
nsCycleCollectorParams mParams;
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
|
||||
|
||||
nsTArray<PtrInfo*> *mWhiteNodes;
|
||||
uint32_t mWhiteNodeCount;
|
||||
|
||||
@ -1037,7 +1045,7 @@ public:
|
||||
void ScanRoots();
|
||||
void ScanWeakMaps();
|
||||
|
||||
void ForgetSkippable(bool removeChildlessNodes);
|
||||
void ForgetSkippable(bool aRemoveChildlessNodes, bool aAsyncSnowWhiteFreeing);
|
||||
|
||||
// returns whether anything was collected
|
||||
bool CollectWhite(nsICycleCollectorListener *aListener);
|
||||
@ -1072,11 +1080,17 @@ public:
|
||||
bool BeginCollection(ccType aCCType, nsICycleCollectorListener *aListener);
|
||||
bool FinishCollection(nsICycleCollectorListener *aListener);
|
||||
|
||||
void FreeSnowWhite(bool aUntilNoSWInPurpleBuffer);
|
||||
AsyncFreeSnowWhite* AsyncSnowWhiteFreer()
|
||||
{
|
||||
return mAsyncSnowWhiteFreer;
|
||||
}
|
||||
|
||||
bool FreeSnowWhite(bool aUntilNoSWInPurpleBuffer);
|
||||
|
||||
// If there is a cycle collector available in the current thread,
|
||||
// this calls FreeSnowWhite(false).
|
||||
static void TryToFreeSnowWhite();
|
||||
// this calls FreeSnowWhite(false). Returns true if some
|
||||
// snow-white objects were found.
|
||||
static bool TryToFreeSnowWhite();
|
||||
|
||||
uint32_t SuspectedCount();
|
||||
void Shutdown();
|
||||
@ -2163,6 +2177,44 @@ MayHaveChild(void *o, nsCycleCollectionParticipant* cp)
|
||||
return cf.MayHaveChild();
|
||||
}
|
||||
|
||||
class AsyncFreeSnowWhite : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
bool hadSnowWhiteObjects = nsCycleCollector::TryToFreeSnowWhite();
|
||||
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING,
|
||||
uint32_t((TimeStamp::Now() - start).ToMilliseconds()));
|
||||
if (hadSnowWhiteObjects && !mContinuation) {
|
||||
mContinuation = true;
|
||||
if (NS_FAILED(NS_DispatchToCurrentThread(this))) {
|
||||
mActive = false;
|
||||
}
|
||||
} else {
|
||||
mActive = false;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void Dispatch(nsCycleCollector* aCollector, bool aContinuation = false)
|
||||
{
|
||||
AsyncFreeSnowWhite* swf = aCollector->AsyncSnowWhiteFreer();
|
||||
if (swf->mContinuation) {
|
||||
swf->mContinuation = aContinuation;
|
||||
}
|
||||
if (!swf->mActive && NS_SUCCEEDED(NS_DispatchToCurrentThread(swf))) {
|
||||
swf->mActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
AsyncFreeSnowWhite() : mContinuation(false), mActive(false) {}
|
||||
|
||||
public:
|
||||
bool mContinuation;
|
||||
bool mActive;
|
||||
};
|
||||
|
||||
struct SnowWhiteObject
|
||||
{
|
||||
void* mPointer;
|
||||
@ -2223,10 +2275,15 @@ private:
|
||||
class RemoveSkippableVisitor : public SnowWhiteKiller
|
||||
{
|
||||
public:
|
||||
RemoveSkippableVisitor(uint32_t aMaxCount, bool aRemoveChildlessNodes,
|
||||
RemoveSkippableVisitor(nsCycleCollector* aCollector,
|
||||
uint32_t aMaxCount, bool aRemoveChildlessNodes,
|
||||
bool aAsyncSnowWhiteFreeing,
|
||||
CC_ForgetSkippableCallback aCb)
|
||||
: SnowWhiteKiller(aMaxCount),
|
||||
: SnowWhiteKiller(aAsyncSnowWhiteFreeing ? 0 : aMaxCount),
|
||||
mCollector(aCollector),
|
||||
mRemoveChildlessNodes(aRemoveChildlessNodes),
|
||||
mAsyncSnowWhiteFreeing(aAsyncSnowWhiteFreeing),
|
||||
mDispatchedDeferredDeletion(false),
|
||||
mCallback(aCb)
|
||||
{}
|
||||
|
||||
@ -2237,6 +2294,10 @@ public:
|
||||
if (mCallback) {
|
||||
mCallback();
|
||||
}
|
||||
if (HasSnowWhiteObjects()) {
|
||||
// Effectively a continuation.
|
||||
AsyncFreeSnowWhite::Dispatch(mCollector, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -2244,7 +2305,12 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(aEntry->mObject, "null mObject in purple buffer");
|
||||
if (!aEntry->mRefCnt->get()) {
|
||||
SnowWhiteKiller::Visit(aBuffer, aEntry);
|
||||
if (!mAsyncSnowWhiteFreeing) {
|
||||
SnowWhiteKiller::Visit(aBuffer, aEntry);
|
||||
} else if (!mDispatchedDeferredDeletion) {
|
||||
mDispatchedDeferredDeletion = true;
|
||||
nsCycleCollector_dispatchDeferredDeletion();
|
||||
}
|
||||
return;
|
||||
}
|
||||
void *o = aEntry->mObject;
|
||||
@ -2258,58 +2324,47 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
nsCycleCollector* mCollector;
|
||||
bool mRemoveChildlessNodes;
|
||||
bool mAsyncSnowWhiteFreeing;
|
||||
bool mDispatchedDeferredDeletion;
|
||||
CC_ForgetSkippableCallback mCallback;
|
||||
};
|
||||
|
||||
void
|
||||
nsPurpleBuffer::RemoveSkippable(bool removeChildlessNodes,
|
||||
nsPurpleBuffer::RemoveSkippable(nsCycleCollector* aCollector,
|
||||
bool aRemoveChildlessNodes,
|
||||
bool aAsyncSnowWhiteFreeing,
|
||||
CC_ForgetSkippableCallback aCb)
|
||||
{
|
||||
RemoveSkippableVisitor visitor(Count(), removeChildlessNodes, aCb);
|
||||
RemoveSkippableVisitor visitor(aCollector, Count(), aRemoveChildlessNodes,
|
||||
aAsyncSnowWhiteFreeing, aCb);
|
||||
VisitEntries(visitor);
|
||||
// If we're about to delete some objects when visitor goes out of scope,
|
||||
// try to delete some more soon.
|
||||
if (visitor.HasSnowWhiteObjects()) {
|
||||
nsCycleCollector_dispatchDeferredDeletion();
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncFreeSnowWhite : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsCycleCollector::TryToFreeSnowWhite();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void Dispatch()
|
||||
{
|
||||
nsRefPtr<AsyncFreeSnowWhite> ev = new AsyncFreeSnowWhite();
|
||||
NS_DispatchToCurrentThread(ev);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
bool
|
||||
nsCycleCollector::FreeSnowWhite(bool aUntilNoSWInPurpleBuffer)
|
||||
{
|
||||
bool hadSnowWhiteObjects = false;
|
||||
do {
|
||||
SnowWhiteKiller visitor(mPurpleBuf.Count());
|
||||
mPurpleBuf.VisitEntries(visitor);
|
||||
hadSnowWhiteObjects = hadSnowWhiteObjects ||
|
||||
visitor.HasSnowWhiteObjects();
|
||||
if (!visitor.HasSnowWhiteObjects()) {
|
||||
break;
|
||||
}
|
||||
} while (aUntilNoSWInPurpleBuffer);
|
||||
return hadSnowWhiteObjects;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
/* static */ bool
|
||||
nsCycleCollector::TryToFreeSnowWhite()
|
||||
{
|
||||
CollectorData* data = sCollectorData.get();
|
||||
if (data->mCollector) {
|
||||
data->mCollector->FreeSnowWhite(false);
|
||||
}
|
||||
return data->mCollector ?
|
||||
data->mCollector->FreeSnowWhite(false) :
|
||||
false;
|
||||
}
|
||||
|
||||
void
|
||||
@ -2319,12 +2374,14 @@ nsCycleCollector::SelectPurple(GCGraphBuilder &builder)
|
||||
}
|
||||
|
||||
void
|
||||
nsCycleCollector::ForgetSkippable(bool removeChildlessNodes)
|
||||
nsCycleCollector::ForgetSkippable(bool aRemoveChildlessNodes,
|
||||
bool aAsyncSnowWhiteFreeing)
|
||||
{
|
||||
if (mJSRuntime) {
|
||||
mJSRuntime->PrepareForForgetSkippable();
|
||||
}
|
||||
mPurpleBuf.RemoveSkippable(removeChildlessNodes, mForgetSkippableCB);
|
||||
mPurpleBuf.RemoveSkippable(this, aRemoveChildlessNodes,
|
||||
aAsyncSnowWhiteFreeing, mForgetSkippableCB);
|
||||
}
|
||||
|
||||
MOZ_NEVER_INLINE void
|
||||
@ -2651,6 +2708,7 @@ nsCycleCollector::nsCycleCollector(CCThreadingModel aModel) :
|
||||
mJSRuntime(nullptr),
|
||||
mRunner(nullptr),
|
||||
mThread(PR_GetCurrentThread()),
|
||||
mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()),
|
||||
mWhiteNodes(nullptr),
|
||||
mWhiteNodeCount(0),
|
||||
mVisitedRefCounted(0),
|
||||
@ -3272,7 +3330,8 @@ nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB)
|
||||
}
|
||||
|
||||
void
|
||||
nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes)
|
||||
nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes,
|
||||
bool aAsyncSnowWhiteFreeing)
|
||||
{
|
||||
CollectorData *data = sCollectorData.get();
|
||||
|
||||
@ -3282,14 +3341,18 @@ nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes)
|
||||
|
||||
PROFILER_LABEL("CC", "nsCycleCollector_forgetSkippable");
|
||||
TimeLog timeLog;
|
||||
data->mCollector->ForgetSkippable(aRemoveChildlessNodes);
|
||||
data->mCollector->ForgetSkippable(aRemoveChildlessNodes,
|
||||
aAsyncSnowWhiteFreeing);
|
||||
timeLog.Checkpoint("ForgetSkippable()");
|
||||
}
|
||||
|
||||
void
|
||||
nsCycleCollector_dispatchDeferredDeletion()
|
||||
{
|
||||
AsyncFreeSnowWhite::Dispatch();
|
||||
CollectorData* data = sCollectorData.get();
|
||||
if (data && data->mCollector) {
|
||||
AsyncFreeSnowWhite::Dispatch(data->mCollector);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -54,7 +54,8 @@ void nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB);
|
||||
typedef void (*CC_ForgetSkippableCallback)(void);
|
||||
void nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB);
|
||||
|
||||
void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false);
|
||||
void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false,
|
||||
bool aAsyncSnowWhiteFreeing = false);
|
||||
|
||||
void nsCycleCollector_dispatchDeferredDeletion();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user