bug 506243 - eliminate GC thread-local lists pools. r=brendan

This commit is contained in:
Igor Bukanov 2009-07-26 18:24:39 +02:00
parent 769c24ef85
commit 83b4dee9ba
5 changed files with 18 additions and 142 deletions

View File

@ -927,16 +927,12 @@ JS_PUBLIC_API(void)
JS_BeginRequest(JSContext *cx)
{
#ifdef JS_THREADSAFE
JSRuntime *rt;
JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
if (!cx->requestDepth) {
JS_ASSERT(cx->gcLocalFreeLists == &js_GCEmptyFreeListSet);
/* Wait until the GC is finished. */
rt = cx->runtime;
JSRuntime *rt = cx->runtime;
JS_LOCK_GC(rt);
/* Wait until the GC is finished. */
if (rt->gcThread != cx->thread) {
while (rt->gcLevel > 0)
JS_AWAIT_GC_DONE(rt);
@ -974,7 +970,6 @@ JS_EndRequest(JSContext *cx)
cx->outstandingRequests--;
js_ShareWaitingTitles(cx);
js_RevokeGCLocalFreeLists(cx);
/* Give the GC a chance to run if this was the last request running. */
JS_ASSERT(rt->requestCount > 0);

View File

@ -263,6 +263,7 @@ thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
return JS_DHASH_REMOVE;
}
PurgeThreadData(cx, &thread->data);
memset(thread->gcFreeLists, 0, sizeof(thread->gcFreeLists));
return JS_DHASH_NEXT;
}
@ -379,9 +380,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
cx->stackLimit = (jsuword) -1;
#endif
cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
#ifdef JS_THREADSAFE
cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet;
#endif
JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
JS_ASSERT(cx->version == JSVERSION_DEFAULT);
VOUCH_DOES_NOT_REQUIRE_STACK();
@ -633,7 +631,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
*/
if (cx->requestDepth == 0)
js_WaitForGC(rt);
js_RevokeGCLocalFreeLists(cx);
#endif
JS_REMOVE_LINK(&cx->link);
last = (rt->contextList.next == &rt->contextList);

View File

@ -273,6 +273,8 @@ struct JSThread {
/* Indicates that the thread is waiting in ClaimTitle from jslock.cpp. */
JSTitle *titleToShare;
JSGCThing *gcFreeLists[GC_NUM_FREELISTS];
/* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
JSThreadData data;
};
@ -365,7 +367,6 @@ struct JSRuntime {
JSGCChunkInfo *gcChunkList;
JSGCArenaList gcArenaList[GC_NUM_FREELISTS];
JSGCDoubleArenaList gcDoubleArenaList;
JSGCFreeListSet *gcFreeListsPool;
JSDHashTable gcRootsHash;
JSDHashTable *gcLocksHash;
jsrefcount gcKeepAtoms;
@ -1009,10 +1010,6 @@ struct JSContext {
/* Stack of thread-stack-allocated temporary GC roots. */
JSTempValueRooter *tempValueRooters;
#ifdef JS_THREADSAFE
JSGCFreeListSet *gcLocalFreeLists;
#endif
/* List of pre-allocated doubles. */
JSGCDoubleCell *doubleFreeList;

View File

@ -1473,11 +1473,6 @@ static void
CheckLeakedRoots(JSRuntime *rt);
#endif
#ifdef JS_THREADSAFE
static void
TrimGCFreeListsPool(JSRuntime *rt, uintN keepCount);
#endif
void
js_FinishGC(JSRuntime *rt)
{
@ -1489,10 +1484,6 @@ js_FinishGC(JSRuntime *rt)
#endif
FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo);
#ifdef JS_THREADSAFE
TrimGCFreeListsPool(rt, 0);
JS_ASSERT(!rt->gcFreeListsPool);
#endif
FinishGCArenaLists(rt);
if (rt->gcRootsHash.ops) {
@ -1732,74 +1723,6 @@ static struct GCHist {
unsigned gchpos = 0;
#endif
#ifdef JS_THREADSAFE
const JSGCFreeListSet js_GCEmptyFreeListSet = {
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, NULL
};
static void
TrimGCFreeListsPool(JSRuntime *rt, uintN keepCount)
{
JSGCFreeListSet **cursor, *freeLists, *link;
cursor = &rt->gcFreeListsPool;
while (keepCount != 0) {
--keepCount;
freeLists = *cursor;
if (!freeLists)
return;
memset(freeLists->array, 0, sizeof freeLists->array);
cursor = &freeLists->link;
}
freeLists = *cursor;
if (freeLists) {
*cursor = NULL;
do {
link = freeLists->link;
free(freeLists);
} while ((freeLists = link) != NULL);
}
}
void
js_RevokeGCLocalFreeLists(JSContext *cx)
{
JS_ASSERT(!cx->gcLocalFreeLists->link);
if (cx->gcLocalFreeLists != &js_GCEmptyFreeListSet) {
cx->gcLocalFreeLists->link = cx->runtime->gcFreeListsPool;
cx->runtime->gcFreeListsPool = cx->gcLocalFreeLists;
cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet;
}
}
static JSGCFreeListSet *
EnsureLocalFreeList(JSContext *cx)
{
JSGCFreeListSet *freeLists;
freeLists = cx->gcLocalFreeLists;
if (freeLists != &js_GCEmptyFreeListSet) {
JS_ASSERT(freeLists);
return freeLists;
}
freeLists = cx->runtime->gcFreeListsPool;
if (freeLists) {
cx->runtime->gcFreeListsPool = freeLists->link;
freeLists->link = NULL;
} else {
/* JS_malloc is not used as the caller reports out-of-memory itself. */
freeLists = (JSGCFreeListSet *) calloc(1, sizeof *freeLists);
if (!freeLists)
return NULL;
}
cx->gcLocalFreeLists = freeLists;
return freeLists;
}
#endif
void
JSRuntime::setGCTriggerFactor(uint32 factor)
{
@ -1853,7 +1776,6 @@ NewGCThing(JSContext *cx, uintN flags)
#ifdef JS_THREADSAFE
JSBool gcLocked;
uintN localMallocBytes;
JSGCFreeListSet *freeLists;
JSGCThing **lastptr;
JSGCThing *tmpthing;
uint8 *tmpflagp;
@ -1873,12 +1795,13 @@ NewGCThing(JSContext *cx, uintN flags)
#ifdef JS_THREADSAFE
gcLocked = JS_FALSE;
JS_ASSERT(cx->thread);
freeLists = cx->gcLocalFreeLists;
thing = freeLists->array[flindex];
JSGCThing *&freeList = cx->thread->gcFreeLists[flindex];
thing = freeList;
localMallocBytes = JS_THREAD_DATA(cx)->gcMallocBytes;
if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) {
flagp = thing->flagp;
freeLists->array[flindex] = thing->next;
freeList = thing->next;
METER(astats->localalloc++);
goto success;
}
@ -1937,19 +1860,13 @@ NewGCThing(JSContext *cx, uintN flags)
#ifdef JS_THREADSAFE
/*
* Refill the local free list by taking several things from the
* global free list unless we are still at rt->gcMaxMallocBytes
* barrier or the free list is already populated. The former
* happens when GC is canceled due to gcCallback(cx, JSGC_BEGIN)
* returning false. The latter is caused via allocating new
* things in gcCallback(cx, JSGC_END).
* global free list unless the free list is already populated or
* we are still at rt->gcMaxMallocBytes barrier. The former is
* caused via allocating new things in gcCallback(cx, JSGC_END).
* The latter happens when GC is canceled due to
* gcCallback(cx, JSGC_BEGIN) returning false.
*/
if (rt->gcMallocBytes >= rt->gcMaxMallocBytes)
break;
freeLists = EnsureLocalFreeList(cx);
if (!freeLists)
goto fail;
if (freeLists->array[flindex])
if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes)
break;
tmpthing = arenaList->freeList;
@ -1961,7 +1878,7 @@ NewGCThing(JSContext *cx, uintN flags)
tmpthing = tmpthing->next;
} while (--maxFreeThings != 0);
freeLists->array[flindex] = arenaList->freeList;
freeList = arenaList->freeList;
arenaList->freeList = tmpthing->next;
tmpthing->next = NULL;
}
@ -2019,15 +1936,9 @@ testReservedObjects:
* arena. Prefer to order free things by ascending address in the
* (unscientific) hope of better cache locality.
*/
if (rt->gcMallocBytes >= rt->gcMaxMallocBytes)
if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes)
break;
freeLists = EnsureLocalFreeList(cx);
if (!freeLists)
goto fail;
if (freeLists->array[flindex])
break;
lastptr = &freeLists->array[flindex];
lastptr = &freeList;
maxFreeThings = thingsLimit - arenaList->lastCount;
if (maxFreeThings > MAX_THREAD_LOCAL_THINGS)
maxFreeThings = MAX_THREAD_LOCAL_THINGS;
@ -3046,10 +2957,6 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
} \
JS_END_MACRO
#ifdef JS_THREADSAFE
js_RevokeGCLocalFreeLists(acx);
#endif
/*
* Release the stackPool's arenas if the stackPool has existed for
* longer than the limit specified by gcEmptyArenaPoolLifespan.
@ -3779,14 +3686,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
nlivearenas, nkilledarenas, nthings));
}
#ifdef JS_THREADSAFE
/*
* Release all but two free list sets to avoid allocating a new set in
* js_NewGCThing.
*/
TrimGCFreeListsPool(rt, 2);
#endif
ap = &rt->gcDoubleArenaList.first;
METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0));
while ((a = *ap) != NULL) {

View File

@ -309,18 +309,6 @@ typedef struct JSGCDoubleArenaList {
things */
} JSGCDoubleArenaList;
typedef struct JSGCFreeListSet JSGCFreeListSet;
struct JSGCFreeListSet {
JSGCThing *array[GC_NUM_FREELISTS];
JSGCFreeListSet *link;
};
extern const JSGCFreeListSet js_GCEmptyFreeListSet;
extern void
js_RevokeGCLocalFreeLists(JSContext *cx);
extern void
js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data);