From 5c0d522df5e61ac129e5078b50321d47132eff6f Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 30 Dec 2011 11:09:16 +1300 Subject: [PATCH] Backed out changeset c245807aad3a for bug 714066 --- js/src/jscntxt.cpp | 30 ++++++++++++++++++-- js/src/jscntxt.h | 6 ++++ js/src/jsgc.cpp | 71 +++++++++++++++++++++++++--------------------- js/src/jsgc.h | 3 -- 4 files changed, 73 insertions(+), 37 deletions(-) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 1b901cb3d00d..cc10655ab5ca 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -104,6 +104,7 @@ ThreadData::ThreadData(JSRuntime *rt) #ifdef JS_THREADSAFE requestDepth(0), #endif + waiveGCQuota(false), tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), execAlloc(NULL), bumpAlloc(NULL), @@ -1269,9 +1270,34 @@ js_InvokeOperationCallback(JSContext *cx) #endif JS_UNLOCK_GC(rt); - if (rt->gcIsNeeded) + if (rt->gcIsNeeded) { js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason); + /* + * On trace we can exceed the GC quota, see comments in NewGCArena. So + * we check the quota and report OOM here when we are off trace. + */ + if (checkOutOfMemory(rt)) { +#ifdef JS_THREADSAFE + /* + * We have to wait until the background thread is done in order + * to get a correct answer. + */ + { + AutoLockGC lock(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); + } + if (checkOutOfMemory(rt)) { + js_ReportOutOfMemory(cx); + return false; + } +#else + js_ReportOutOfMemory(cx); + return false; +#endif + } + } + #ifdef JS_THREADSAFE /* * We automatically yield the current context every time the operation @@ -1572,7 +1598,7 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) AutoLockGC lock(this); gcHelperThread.waitBackgroundSweepOrAllocEnd(); #endif - gcChunkPool.expireAndFree(this, true); + gcChunkPool.expire(this, true); } if (!p) p = OffTheBooks::malloc_(nbytes); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 59d60ce45068..7a878701a9e3 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -140,6 +140,12 @@ struct ThreadData { /* Keeper of the contiguous stack used by all contexts in this thread. */ StackSpace stackSpace; + /* + * Flag indicating that we are waiving any soft limits on the GC heap + * because we want allocations to be infallible (except when we hit OOM). + */ + bool waiveGCQuota; + /* Temporary arena pool used while compiling and decompiling. */ static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; LifoAlloc tempLifoAlloc; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index acdadd9dd796..84a9f10ce6da 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -517,22 +517,6 @@ ChunkPool::expire(JSRuntime *rt, bool releaseAll) return freeList; } -static void -FreeChunkList(Chunk *chunkListHead) -{ - while (Chunk *chunk = chunkListHead) { - JS_ASSERT(!chunk->info.numArenasFreeCommitted); - chunkListHead = chunk->info.next; - FreeChunk(chunk); - } -} - -void -ChunkPool::expireAndFree(JSRuntime *rt, bool releaseAll) -{ - FreeChunkList(expire(rt, releaseAll)); -} - JS_FRIEND_API(int64_t) ChunkPool::countCleanDecommittedArenas(JSRuntime *rt) { @@ -569,6 +553,16 @@ Chunk::release(JSRuntime *rt, Chunk *chunk) FreeChunk(chunk); } +static void +FreeChunkList(Chunk *chunkListHead) +{ + while (Chunk *chunk = chunkListHead) { + JS_ASSERT(!chunk->info.numArenasFreeCommitted); + chunkListHead = chunk->info.next; + FreeChunk(chunk); + } +} + inline void Chunk::prepareToBeFreed(JSRuntime *rt) { @@ -1188,7 +1182,7 @@ js_FinishGC(JSRuntime *rt) * Finish the pool after the background thread stops in case it was doing * the background sweeping. */ - rt->gcChunkPool.expireAndFree(rt, true); + FreeChunkList(rt->gcChunkPool.expire(rt, true)); #ifdef DEBUG if (!rt->gcRootsHash.empty()) @@ -1647,6 +1641,12 @@ RunLastDitchGC(JSContext *cx) #endif } +inline bool +IsGCAllowed(JSContext *cx) +{ + return !JS_THREAD_DATA(cx)->waiveGCQuota; +} + /* static */ void * ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) { @@ -1658,7 +1658,7 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) bool runGC = !!rt->gcIsNeeded; for (;;) { - if (JS_UNLIKELY(runGC)) { + if (JS_UNLIKELY(runGC) && IsGCAllowed(cx)) { RunLastDitchGC(cx); /* Report OOM of the GC failed to free enough memory. */ @@ -1679,11 +1679,14 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) return thing; /* - * We failed to allocate. Run the GC if we haven't done it already. - * Otherwise report OOM. + * We failed to allocate. Run the GC if we can unless we have done it + * already. Otherwise report OOM but first schedule a new GC soon. */ - if (runGC) + if (runGC || !IsGCAllowed(cx)) { + AutoLockGC lock(rt); + TriggerGC(rt, gcstats::REFILL); break; + } runGC = true; } @@ -2990,10 +2993,12 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) js_PurgeThreads_PostGlobalSweep(cx); #ifdef JS_THREADSAFE - if (cx->gcBackgroundFree) { + if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); cx->gcBackgroundFree = NULL; rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK); + } else { + JS_ASSERT(!cx->gcBackgroundFree); } #endif @@ -3274,17 +3279,19 @@ void RunDebugGC(JSContext *cx) { #ifdef JS_GC_ZEAL - JSRuntime *rt = cx->runtime; + if (IsGCAllowed(cx)) { + JSRuntime *rt = cx->runtime; - /* - * If rt->gcDebugCompartmentGC is true, only GC the current - * compartment. But don't GC the atoms compartment. - */ - rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; - if (rt->gcTriggerCompartment == rt->atomsCompartment) - rt->gcTriggerCompartment = NULL; - - RunLastDitchGC(cx); + /* + * If rt->gcDebugCompartmentGC is true, only GC the current + * compartment. But don't GC the atoms compartment. + */ + rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; + if (rt->gcTriggerCompartment == rt->atomsCompartment) + rt->gcTriggerCompartment = NULL; + + RunLastDitchGC(cx); + } #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 72fdc4db7664..d4bfa3a09aac 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -808,9 +808,6 @@ class ChunkPool { */ Chunk *expire(JSRuntime *rt, bool releaseAll); - /* Must be called with the GC lock taken. */ - void expireAndFree(JSRuntime *rt, bool releaseAll); - /* Must be called either during the GC or with the GC lock taken. */ JS_FRIEND_API(int64_t) countCleanDecommittedArenas(JSRuntime *rt); };