Bug 926678 - Ensure GC gets triggered when gcMallocBytes drops below zero r=billm

This commit is contained in:
Jon Coppeard 2013-10-16 09:45:27 +01:00
parent 3203317e2b
commit 8afde533eb
5 changed files with 41 additions and 26 deletions

View File

@ -127,7 +127,14 @@ Zone::setGCMaxMallocBytes(size_t value)
void void
Zone::onTooMuchMalloc() Zone::onTooMuchMalloc()
{ {
TriggerZoneGC(this, gcreason::TOO_MUCH_MALLOC); if (TriggerZoneGC(this, gcreason::TOO_MUCH_MALLOC)) {
/*
* Set gcMallocBytes to stop updateMallocCounter() calling this method
* again before the counter is reset by GC.
*/
gcMallocBytes = PTRDIFF_MAX;
}
} }
void void

View File

@ -272,10 +272,8 @@ struct Zone : public JS::shadow::Zone,
* Note: this code may be run from worker threads. We * Note: this code may be run from worker threads. We
* tolerate any thread races when updating gcMallocBytes. * tolerate any thread races when updating gcMallocBytes.
*/ */
ptrdiff_t oldCount = gcMallocBytes; gcMallocBytes -= ptrdiff_t(nbytes);
ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes); if (JS_UNLIKELY(isTooMuchMalloc()))
gcMallocBytes = newCount;
if (JS_UNLIKELY(newCount <= 0 && oldCount > 0))
onTooMuchMalloc(); onTooMuchMalloc();
} }

View File

@ -1930,29 +1930,31 @@ TriggerOperationCallback(JSRuntime *rt, JS::gcreason::Reason reason)
rt->triggerOperationCallback(JSRuntime::TriggerCallbackMainThread); rt->triggerOperationCallback(JSRuntime::TriggerCallbackMainThread);
} }
void bool
js::TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason) js::TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason)
{ {
/* Wait till end of parallel section to trigger GC. */ /* Wait till end of parallel section to trigger GC. */
if (InParallelSection()) { if (InParallelSection()) {
ForkJoinSlice::Current()->requestGC(reason); ForkJoinSlice::Current()->requestGC(reason);
return; return true;
} }
/* Don't trigger GCs when allocating under the operation callback lock. */ /* Don't trigger GCs when allocating under the operation callback lock. */
if (rt->currentThreadOwnsOperationCallbackLock()) if (rt->currentThreadOwnsOperationCallbackLock())
return; return false;
JS_ASSERT(CurrentThreadCanAccessRuntime(rt)); JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
if (rt->isHeapBusy()) /* GC is already running. */
return; if (rt->isHeapCollecting())
return true;
JS::PrepareForFullGC(rt); JS::PrepareForFullGC(rt);
TriggerOperationCallback(rt, reason); TriggerOperationCallback(rt, reason);
return true;
} }
void bool
js::TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason) js::TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
{ {
/* /*
@ -1961,35 +1963,37 @@ js::TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
*/ */
if (InParallelSection()) { if (InParallelSection()) {
ForkJoinSlice::Current()->requestZoneGC(zone, reason); ForkJoinSlice::Current()->requestZoneGC(zone, reason);
return; return true;
} }
/* Zones in use by a thread with an exclusive context can't be collected. */ /* Zones in use by a thread with an exclusive context can't be collected. */
if (zone->usedByExclusiveThread) if (zone->usedByExclusiveThread)
return; return false;
JSRuntime *rt = zone->runtimeFromMainThread(); JSRuntime *rt = zone->runtimeFromMainThread();
/* Don't trigger GCs when allocating under the operation callback lock. */ /* Don't trigger GCs when allocating under the operation callback lock. */
if (rt->currentThreadOwnsOperationCallbackLock()) if (rt->currentThreadOwnsOperationCallbackLock())
return; return false;
if (rt->isHeapBusy()) /* GC is already running. */
return; if (rt->isHeapCollecting())
return true;
if (rt->gcZeal() == ZealAllocValue) { if (rt->gcZeal() == ZealAllocValue) {
TriggerGC(rt, reason); TriggerGC(rt, reason);
return; return true;
} }
if (rt->isAtomsZone(zone)) { if (rt->isAtomsZone(zone)) {
/* We can't do a zone GC of the atoms compartment. */ /* We can't do a zone GC of the atoms compartment. */
TriggerGC(rt, reason); TriggerGC(rt, reason);
return; return true;
} }
PrepareZoneForGC(zone); PrepareZoneForGC(zone);
TriggerOperationCallback(rt, reason); TriggerOperationCallback(rt, reason);
return true;
} }
void void

View File

@ -687,11 +687,11 @@ extern void
TraceRuntime(JSTracer *trc); TraceRuntime(JSTracer *trc);
/* Must be called with GC lock taken. */ /* Must be called with GC lock taken. */
extern void extern bool
TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason); TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason);
/* Must be called with GC lock taken. */ /* Must be called with GC lock taken. */
extern void extern bool
TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason); TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason);
extern void extern void

View File

@ -724,10 +724,8 @@ void
JSRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes) JSRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes)
{ {
/* We tolerate any thread races when updating gcMallocBytes. */ /* We tolerate any thread races when updating gcMallocBytes. */
ptrdiff_t oldCount = gcMallocBytes; gcMallocBytes -= ptrdiff_t(nbytes);
ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes); if (JS_UNLIKELY(gcMallocBytes <= 0))
gcMallocBytes = newCount;
if (JS_UNLIKELY(newCount <= 0 && oldCount > 0))
onTooMuchMalloc(); onTooMuchMalloc();
else if (zone) else if (zone)
zone->updateMallocCounter(nbytes); zone->updateMallocCounter(nbytes);
@ -736,8 +734,16 @@ JSRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes)
JS_FRIEND_API(void) JS_FRIEND_API(void)
JSRuntime::onTooMuchMalloc() JSRuntime::onTooMuchMalloc()
{ {
if (CurrentThreadCanAccessRuntime(this)) if (!CurrentThreadCanAccessRuntime(this))
TriggerGC(this, JS::gcreason::TOO_MUCH_MALLOC); return;
if (TriggerGC(this, JS::gcreason::TOO_MUCH_MALLOC)) {
/*
* Set gcMallocBytes to stop updateMallocCounter() calling this method
* again before the counter is reset by GC.
*/
gcMallocBytes = PTRDIFF_MAX;
}
} }
JS_FRIEND_API(void *) JS_FRIEND_API(void *)