bug 713916 - JS API to shrink GC buffers. r=wmccloskey

This commit is contained in:
Igor Bukanov 2011-12-28 21:08:44 +01:00
parent d75b0d6c5e
commit ac8ad79e00
5 changed files with 87 additions and 25 deletions

View File

@ -1592,13 +1592,13 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
* Retry when we are done with the background sweeping and have stopped * Retry when we are done with the background sweeping and have stopped
* all the allocations and released the empty GC chunks. * all the allocations and released the empty GC chunks.
*/ */
{ ShrinkGCBuffers(this);
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
{
AutoLockGC lock(this); AutoLockGC lock(this);
gcHelperThread.waitBackgroundSweepOrAllocEnd(); gcHelperThread.waitBackgroundSweepOrAllocEnd();
#endif
gcChunkPool.expireAndFree(this, true);
} }
#endif
if (!p) if (!p)
p = OffTheBooks::malloc_(nbytes); p = OffTheBooks::malloc_(nbytes);
else if (p == reinterpret_cast<void *>(1)) else if (p == reinterpret_cast<void *>(1))

View File

@ -125,12 +125,18 @@ JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObj
return obj; return obj;
} }
JS_PUBLIC_API(void) JS_FRIEND_API(void)
JS_ShrinkingGC(JSContext *cx) JS_ShrinkingGC(JSContext *cx)
{ {
js_GC(cx, NULL, GC_SHRINK, gcstats::PUBLIC_API); js_GC(cx, NULL, GC_SHRINK, gcstats::PUBLIC_API);
} }
JS_FRIEND_API(void)
JS_ShrinkGCBuffers(JSRuntime *rt)
{
ShrinkGCBuffers(rt);
}
JS_FRIEND_API(JSPrincipals *) JS_FRIEND_API(JSPrincipals *)
JS_GetCompartmentPrincipals(JSCompartment *compartment) JS_GetCompartmentPrincipals(JSCompartment *compartment)
{ {

View File

@ -73,6 +73,9 @@ JS_ObjectCountDynamicSlots(JSObject *obj);
extern JS_FRIEND_API(void) extern JS_FRIEND_API(void)
JS_ShrinkingGC(JSContext *cx); JS_ShrinkingGC(JSContext *cx);
extern JS_FRIEND_API(void)
JS_ShrinkGCBuffers(JSRuntime *rt);
extern JS_FRIEND_API(size_t) extern JS_FRIEND_API(size_t)
JS_GetE4XObjectsCreated(JSContext *cx); JS_GetE4XObjectsCreated(JSContext *cx);

View File

@ -2423,28 +2423,53 @@ GCHelperThread::threadLoop()
} }
bool bool
GCHelperThread::prepareForBackgroundSweep(JSContext *cx) GCHelperThread::prepareForBackgroundSweep()
{ {
JS_ASSERT(cx->runtime == rt);
JS_ASSERT(state == IDLE); JS_ASSERT(state == IDLE);
size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * rt->compartments.length(); size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * rt->compartments.length();
if (!finalizeVector.reserve(maxArenaLists)) return finalizeVector.reserve(maxArenaLists);
return false;
context = cx;
return true;
} }
/* Must be called with the GC lock taken. */ /* Must be called with the GC lock taken. */
inline void void
GCHelperThread::startBackgroundSweep(bool shouldShrink) GCHelperThread::startBackgroundSweep(JSContext *cx, bool shouldShrink)
{ {
/* The caller takes the GC lock. */ /* The caller takes the GC lock. */
JS_ASSERT(state == IDLE); JS_ASSERT(state == IDLE);
JS_ASSERT(cx);
JS_ASSERT(!finalizationContext);
finalizationContext = cx;
shrinkFlag = shouldShrink; shrinkFlag = shouldShrink;
state = SWEEPING; state = SWEEPING;
PR_NotifyCondVar(wakeup); PR_NotifyCondVar(wakeup);
} }
/* Must be called with the GC lock taken. */
void
GCHelperThread::startBackgroundShrink()
{
switch (state) {
case IDLE:
JS_ASSERT(!finalizationContext);
shrinkFlag = true;
state = SWEEPING;
PR_NotifyCondVar(wakeup);
break;
case SWEEPING:
shrinkFlag = true;
break;
case ALLOCATING:
case CANCEL_ALLOCATION:
/*
* If we have started background allocation there is nothing to
* shrink.
*/
break;
case SHUTDOWN:
JS_NOT_REACHED("No shrink on shutdown");
}
}
/* Must be called with the GC lock taken. */ /* Must be called with the GC lock taken. */
void void
GCHelperThread::waitBackgroundSweepEnd() GCHelperThread::waitBackgroundSweepEnd()
@ -2496,9 +2521,8 @@ GCHelperThread::replenishAndFreeLater(void *ptr)
void void
GCHelperThread::doSweep() GCHelperThread::doSweep()
{ {
JS_ASSERT(context); if (JSContext *cx = finalizationContext) {
finalizationContext = NULL;
{
AutoUnlockGC unlock(rt); AutoUnlockGC unlock(rt);
/* /*
@ -2506,11 +2530,9 @@ GCHelperThread::doSweep()
* finalizeObjects. * finalizeObjects.
*/ */
for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i) for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i)
ArenaLists::backgroundFinalize(context, *i); ArenaLists::backgroundFinalize(cx, *i);
finalizeVector.resize(0); finalizeVector.resize(0);
context = NULL;
if (freeCursor) { if (freeCursor) {
void **array = freeCursorEnd - FREE_ARRAY_LENGTH; void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
freeElementsAndArray(array, freeCursor); freeElementsAndArray(array, freeCursor);
@ -2525,7 +2547,18 @@ GCHelperThread::doSweep()
freeVector.resize(0); freeVector.resize(0);
} }
ExpireChunksAndArenas(rt, shouldShrink()); bool shrinking = shrinkFlag;
ExpireChunksAndArenas(rt, shrinking);
/*
* The main thread may have called ShrinkGCBuffers while
* ExpireChunksAndArenas(rt, false) was running, so we recheck the flag
* afterwards.
*/
if (!shrinking && shrinkFlag) {
shrinkFlag = false;
ExpireChunksAndArenas(rt, true);
}
} }
#endif /* JS_THREADSAFE */ #endif /* JS_THREADSAFE */
@ -3008,7 +3041,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
JS_ASSERT(!cx->gcBackgroundFree); JS_ASSERT(!cx->gcBackgroundFree);
rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
if (rt->gcHelperThread.prepareForBackgroundSweep(cx)) if (rt->gcHelperThread.prepareForBackgroundSweep())
cx->gcBackgroundFree = &rt->gcHelperThread; cx->gcBackgroundFree = &rt->gcHelperThread;
} }
#endif #endif
@ -3022,7 +3055,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
if (cx->gcBackgroundFree) { if (cx->gcBackgroundFree) {
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
cx->gcBackgroundFree = NULL; cx->gcBackgroundFree = NULL;
rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK); rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK);
} }
#endif #endif
@ -3100,6 +3133,18 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcstats::Re
namespace js { namespace js {
void
ShrinkGCBuffers(JSRuntime *rt)
{
AutoLockGC lock(rt);
JS_ASSERT(!rt->gcRunning);
#ifndef JS_THREADSAFE
ExpireChunksAndArenas(rt, true);
#else
rt->gcHelperThread.startBackgroundShrink();
#endif
}
class AutoCopyFreeListToArenas { class AutoCopyFreeListToArenas {
JSRuntime *rt; JSRuntime *rt;
@ -3312,7 +3357,7 @@ RunDebugGC(JSContext *cx)
rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL;
if (rt->gcTriggerCompartment == rt->atomsCompartment) if (rt->gcTriggerCompartment == rt->atomsCompartment)
rt->gcTriggerCompartment = NULL; rt->gcTriggerCompartment = NULL;
RunLastDitchGC(cx); RunLastDitchGC(cx);
#endif #endif
} }

View File

@ -1388,6 +1388,9 @@ TriggerCompartmentGC(JSCompartment *comp, js::gcstats::Reason reason);
extern void extern void
MaybeGC(JSContext *cx); MaybeGC(JSContext *cx);
extern void
ShrinkGCBuffers(JSRuntime *rt);
} /* namespace js */ } /* namespace js */
/* /*
@ -1459,7 +1462,7 @@ class GCHelperThread {
PRCondVar *done; PRCondVar *done;
volatile State state; volatile State state;
JSContext *context; JSContext *finalizationContext;
bool shrinkFlag; bool shrinkFlag;
Vector<void **, 16, js::SystemAllocPolicy> freeVector; Vector<void **, 16, js::SystemAllocPolicy> freeVector;
@ -1495,6 +1498,8 @@ class GCHelperThread {
wakeup(NULL), wakeup(NULL),
done(NULL), done(NULL),
state(IDLE), state(IDLE),
finalizationContext(NULL),
shrinkFlag(false),
freeCursor(NULL), freeCursor(NULL),
freeCursorEnd(NULL), freeCursorEnd(NULL),
backgroundAllocation(true) backgroundAllocation(true)
@ -1504,7 +1509,10 @@ class GCHelperThread {
void finish(); void finish();
/* Must be called with the GC lock taken. */ /* Must be called with the GC lock taken. */
inline void startBackgroundSweep(bool shouldShrink); void startBackgroundSweep(JSContext *cx, bool shouldShrink);
/* Must be called with the GC lock taken. */
void startBackgroundShrink();
/* Must be called with the GC lock taken. */ /* Must be called with the GC lock taken. */
void waitBackgroundSweepEnd(); void waitBackgroundSweepEnd();
@ -1549,7 +1557,7 @@ class GCHelperThread {
} }
/* Must be called with the GC lock taken. */ /* Must be called with the GC lock taken. */
bool prepareForBackgroundSweep(JSContext *cx); bool prepareForBackgroundSweep();
}; };
#endif /* JS_THREADSAFE */ #endif /* JS_THREADSAFE */