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
* all the allocations and released the empty GC chunks.
*/
{
ShrinkGCBuffers(this);
#ifdef JS_THREADSAFE
{
AutoLockGC lock(this);
gcHelperThread.waitBackgroundSweepOrAllocEnd();
#endif
gcChunkPool.expireAndFree(this, true);
}
#endif
if (!p)
p = OffTheBooks::malloc_(nbytes);
else if (p == reinterpret_cast<void *>(1))

View File

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

View File

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

View File

@ -2423,28 +2423,53 @@ GCHelperThread::threadLoop()
}
bool
GCHelperThread::prepareForBackgroundSweep(JSContext *cx)
GCHelperThread::prepareForBackgroundSweep()
{
JS_ASSERT(cx->runtime == rt);
JS_ASSERT(state == IDLE);
size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * rt->compartments.length();
if (!finalizeVector.reserve(maxArenaLists))
return false;
context = cx;
return true;
return finalizeVector.reserve(maxArenaLists);
}
/* Must be called with the GC lock taken. */
inline void
GCHelperThread::startBackgroundSweep(bool shouldShrink)
void
GCHelperThread::startBackgroundSweep(JSContext *cx, bool shouldShrink)
{
/* The caller takes the GC lock. */
JS_ASSERT(state == IDLE);
JS_ASSERT(cx);
JS_ASSERT(!finalizationContext);
finalizationContext = cx;
shrinkFlag = shouldShrink;
state = SWEEPING;
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. */
void
GCHelperThread::waitBackgroundSweepEnd()
@ -2496,9 +2521,8 @@ GCHelperThread::replenishAndFreeLater(void *ptr)
void
GCHelperThread::doSweep()
{
JS_ASSERT(context);
{
if (JSContext *cx = finalizationContext) {
finalizationContext = NULL;
AutoUnlockGC unlock(rt);
/*
@ -2506,11 +2530,9 @@ GCHelperThread::doSweep()
* finalizeObjects.
*/
for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i)
ArenaLists::backgroundFinalize(context, *i);
ArenaLists::backgroundFinalize(cx, *i);
finalizeVector.resize(0);
context = NULL;
if (freeCursor) {
void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
freeElementsAndArray(array, freeCursor);
@ -2525,7 +2547,18 @@ GCHelperThread::doSweep()
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 */
@ -3008,7 +3041,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
JS_ASSERT(!cx->gcBackgroundFree);
rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
if (rt->gcHelperThread.prepareForBackgroundSweep(cx))
if (rt->gcHelperThread.prepareForBackgroundSweep())
cx->gcBackgroundFree = &rt->gcHelperThread;
}
#endif
@ -3022,7 +3055,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
if (cx->gcBackgroundFree) {
JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
cx->gcBackgroundFree = NULL;
rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK);
rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK);
}
#endif
@ -3100,6 +3133,18 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcstats::Re
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 {
JSRuntime *rt;

View File

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