[Bug 378918] Scallable free lists for GC, r=brendan

This commit is contained in:
Igor Bukanov 2008-06-24 15:17:52 +02:00
commit 94e1a9ac1d
5 changed files with 139 additions and 28 deletions

View File

@ -879,6 +879,8 @@ JS_BeginRequest(JSContext *cx)
JS_ASSERT(cx->thread->id == js_CurrentThreadId());
if (!cx->requestDepth) {
JS_ASSERT(cx->gcLocalFreeLists == &js_GCEmptyFreeListSet);
/* Wait until the GC is finished. */
rt = cx->runtime;
JS_LOCK_GC(rt);
@ -947,6 +949,8 @@ JS_EndRequest(JSContext *cx)
if (shared)
JS_NOTIFY_ALL_CONDVAR(rt->titleSharingDone);
js_RevokeGCLocalFreeLists(cx);
/* Give the GC a chance to run if this was the last request running. */
JS_ASSERT(rt->requestCount > 0);
rt->requestCount--;

View File

@ -168,11 +168,10 @@ js_SetContextThread(JSContext *cx)
}
/*
* Clear gcFreeLists and caches on each transition from 0 to 1 context
* active on the current thread. See bug 351602 and bug 425828.
* Clear caches on each transition from 0 to 1 context active on the
* current thread. See bug 425828.
*/
if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
memset(thread->gcFreeLists, 0, sizeof(thread->gcFreeLists));
memset(&thread->gsnCache, 0, sizeof(thread->gsnCache));
memset(&thread->propertyCache, 0, sizeof(thread->propertyCache));
}
@ -195,12 +194,6 @@ js_ClearContextThread(JSContext *cx)
*/
JS_ASSERT(cx->thread == js_GetCurrentThread(cx->runtime) || !cx->thread);
JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
#ifdef DEBUG
if (JS_CLIST_IS_EMPTY(&cx->thread->contextList)) {
memset(cx->thread->gcFreeLists, JS_FREE_PATTERN,
sizeof(cx->thread->gcFreeLists));
}
#endif
cx->thread = NULL;
}
@ -243,6 +236,7 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
#endif
cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
#ifdef JS_THREADSAFE
cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet;
JS_INIT_CLIST(&cx->threadLinks);
js_SetContextThread(cx);
#endif
@ -363,6 +357,9 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
last = (rt->contextList.next == &rt->contextList);
if (last)
rt->state = JSRTS_LANDING;
#ifdef JS_THREADSAFE
js_RevokeGCLocalFreeLists(cx);
#endif
JS_UNLOCK_GC(rt);
if (last) {

View File

@ -110,9 +110,6 @@ struct JSThread {
*/
uint32 gcMallocBytes;
/* Thread-local gc free lists array. */
JSGCThing *gcFreeLists[GC_NUM_FREELISTS];
/*
* Store the GSN cache in struct JSThread, not struct JSContext, both to
* save space and to simplify cleanup in js_GC. Any embedding (Firefox
@ -188,6 +185,7 @@ struct JSRuntime {
JSGCChunkInfo *gcChunkList;
JSGCArenaList gcArenaList[GC_NUM_FREELISTS];
JSGCDoubleArenaList gcDoubleArenaList;
JSGCFreeListSet *gcFreeListsPool;
JSDHashTable gcRootsHash;
JSDHashTable *gcLocksHash;
jsrefcount gcKeepAtoms;
@ -788,6 +786,10 @@ 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

@ -1405,6 +1405,11 @@ static void
CheckLeakedRoots(JSRuntime *rt);
#endif
#ifdef JS_THREADSAFE
static void
TrimGCFreeListsPool(JSRuntime *rt, uintN keepCount);
#endif
void
js_FinishGC(JSRuntime *rt)
{
@ -1416,6 +1421,10 @@ 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) {
@ -1673,6 +1682,74 @@ 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 *
js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
{
@ -1691,7 +1768,8 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
#ifdef JS_THREADSAFE
JSBool gcLocked;
uintN localMallocBytes;
JSGCThing **flbase, **lastptr;
JSGCFreeListSet *freeLists;
JSGCThing **lastptr;
JSGCThing *tmpthing;
uint8 *tmpflagp;
uintN maxFreeThings; /* max to take from the global free list */
@ -1709,13 +1787,12 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
#ifdef JS_THREADSAFE
gcLocked = JS_FALSE;
JS_ASSERT(cx->thread);
flbase = cx->thread->gcFreeLists;
JS_ASSERT(flbase);
thing = flbase[flindex];
freeLists = cx->gcLocalFreeLists;
thing = freeLists->array[flindex];
localMallocBytes = cx->thread->gcMallocBytes;
if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) {
flagp = thing->flagp;
flbase[flindex] = thing->next;
freeLists->array[flindex] = thing->next;
METER(astats->localalloc++);
goto success;
}
@ -1775,8 +1852,15 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
* or no gcPoke. The latter is caused via allocating new things
* in gcCallback(cx, JSGC_END).
*/
if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex])
if (rt->gcMallocBytes >= rt->gcMaxMallocBytes)
break;
freeLists = EnsureLocalFreeList(cx);
if (!freeLists)
goto fail;
if (freeLists->array[flindex])
break;
tmpthing = arenaList->freeList;
if (tmpthing) {
maxFreeThings = MAX_THREAD_LOCAL_THINGS;
@ -1786,7 +1870,7 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
tmpthing = tmpthing->next;
} while (--maxFreeThings != 0);
flbase[flindex] = arenaList->freeList;
freeLists->array[flindex] = arenaList->freeList;
arenaList->freeList = tmpthing->next;
tmpthing->next = NULL;
}
@ -1829,9 +1913,15 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
* arena. Prefer to order free things by ascending address in the
* (unscientific) hope of better cache locality.
*/
if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex])
if (rt->gcMallocBytes >= rt->gcMaxMallocBytes)
break;
lastptr = &flbase[flindex];
freeLists = EnsureLocalFreeList(cx);
if (!freeLists)
goto fail;
if (freeLists->array[flindex])
break;
lastptr = &freeLists->array[flindex];
maxFreeThings = thingsLimit - arenaList->lastCount;
if (maxFreeThings > MAX_THREAD_LOCAL_THINGS)
maxFreeThings = MAX_THREAD_LOCAL_THINGS;
@ -2770,6 +2860,10 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
JS_FinishArenaPool(&acx->stackPool);
}
#ifdef JS_THREADSAFE
js_RevokeGCLocalFreeLists(acx);
#endif
/*
* Clear the double free list to release all the pre-allocated doubles.
*/
@ -3179,22 +3273,16 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
#ifdef JS_THREADSAFE
/*
* Set all thread local freelists to NULL. We may visit a thread's
* freelist more than once. To avoid redundant clearing we unroll the
* current thread's step.
*
* Also, in case a JSScript wrapped within an object was finalized, we
* In case a JSScript wrapped within an object was finalized, we
* null acx->thread->gsnCache.script and finish the cache's hashtable.
* Note that js_DestroyScript, called from script_finalize, will have
* already cleared cx->thread->gsnCache above during finalization, so we
* don't have to here.
*/
memset(cx->thread->gcFreeLists, 0, sizeof cx->thread->gcFreeLists);
iter = NULL;
while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
if (!acx->thread || acx->thread == cx->thread)
continue;
memset(acx->thread->gcFreeLists, 0, sizeof acx->thread->gcFreeLists);
GSN_CACHE_CLEAR(&acx->thread->gsnCache);
js_DisablePropertyCache(acx);
js_FlushPropertyCache(acx);
@ -3383,6 +3471,14 @@ 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

@ -328,6 +328,18 @@ 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);
struct JSWeakRoots {
/* Most recently created things by type, members of the GC's root set. */
void *newborn[GCX_NTYPES];