mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
[Bug 378918] Scallable free lists for GC, r=brendan
This commit is contained in:
commit
94e1a9ac1d
@ -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--;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
128
js/src/jsgc.cpp
128
js/src/jsgc.cpp
@ -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) {
|
||||
|
@ -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];
|
||||
|
Loading…
Reference in New Issue
Block a user