mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 673795 - part2, using lists of avaiulable chunks for faster chunk selection. r=wmccloskey
--HG-- extra : rebase_source : ae4f5a82bc4042e341fdb5c08e3f0fe4b4ae8935
This commit is contained in:
parent
d6d80f5a73
commit
907a4b7654
@ -2720,7 +2720,7 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key)
|
|||||||
case JSGC_UNUSED_CHUNKS:
|
case JSGC_UNUSED_CHUNKS:
|
||||||
return uint32(rt->gcEmptyChunkCount);
|
return uint32(rt->gcEmptyChunkCount);
|
||||||
case JSGC_TOTAL_CHUNKS:
|
case JSGC_TOTAL_CHUNKS:
|
||||||
return uint32(rt->gcUserChunkSet.count() + rt->gcSystemChunkSet.count() + rt->gcEmptyChunkCount);
|
return uint32(rt->gcChunkSet.count() + rt->gcEmptyChunkCount);
|
||||||
default:
|
default:
|
||||||
JS_ASSERT(key == JSGC_NUMBER);
|
JS_ASSERT(key == JSGC_NUMBER);
|
||||||
return rt->gcNumber;
|
return rt->gcNumber;
|
||||||
|
@ -390,8 +390,29 @@ struct JSRuntime
|
|||||||
uint32 protoHazardShape;
|
uint32 protoHazardShape;
|
||||||
|
|
||||||
/* Garbage collector state, used by jsgc.c. */
|
/* Garbage collector state, used by jsgc.c. */
|
||||||
js::GCChunkSet gcUserChunkSet;
|
|
||||||
js::GCChunkSet gcSystemChunkSet;
|
/*
|
||||||
|
* Set of all GC chunks with at least one allocated thing. The
|
||||||
|
* conservative GC uses it to quickly check if a possible GC thing points
|
||||||
|
* into an allocated chunk.
|
||||||
|
*/
|
||||||
|
js::GCChunkSet gcChunkSet;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Doubly-linked lists of chunks from user and system compartments. The GC
|
||||||
|
* allocates its arenas from the corresponding list and when all arenas
|
||||||
|
* in the list head are taken, then the chunk is removed from the list.
|
||||||
|
* During the GC when all arenas in a chunk become free, that chunk is
|
||||||
|
* removed from the list and scheduled for release.
|
||||||
|
*/
|
||||||
|
js::gc::Chunk *gcSystemAvailableChunkListHead;
|
||||||
|
js::gc::Chunk *gcUserAvailableChunkListHead;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Singly-linked list of empty chunks and its length. We use the list not
|
||||||
|
* to release empty chunks immediately so they can be used for future
|
||||||
|
* allocations. This avoids very high overhead of chunk release/allocation.
|
||||||
|
*/
|
||||||
js::gc::Chunk *gcEmptyChunkListHead;
|
js::gc::Chunk *gcEmptyChunkListHead;
|
||||||
size_t gcEmptyChunkCount;
|
size_t gcEmptyChunkCount;
|
||||||
|
|
||||||
|
@ -127,7 +127,6 @@ JSCompartment::~JSCompartment()
|
|||||||
bool
|
bool
|
||||||
JSCompartment::init()
|
JSCompartment::init()
|
||||||
{
|
{
|
||||||
chunk = NULL;
|
|
||||||
for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
|
for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
|
||||||
arenas[i].init();
|
arenas[i].init();
|
||||||
freeLists.init();
|
freeLists.init();
|
||||||
@ -467,8 +466,6 @@ JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
|
|||||||
void
|
void
|
||||||
JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
|
JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
|
||||||
{
|
{
|
||||||
chunk = NULL;
|
|
||||||
|
|
||||||
/* Remove dead wrappers from the table. */
|
/* Remove dead wrappers from the table. */
|
||||||
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||||
JS_ASSERT_IF(IsAboutToBeFinalized(cx, e.front().key.toGCThing()) &&
|
JS_ASSERT_IF(IsAboutToBeFinalized(cx, e.front().key.toGCThing()) &&
|
||||||
|
@ -390,7 +390,6 @@ typedef HashSet<ScriptFilenameEntry *,
|
|||||||
struct JS_FRIEND_API(JSCompartment) {
|
struct JS_FRIEND_API(JSCompartment) {
|
||||||
JSRuntime *rt;
|
JSRuntime *rt;
|
||||||
JSPrincipals *principals;
|
JSPrincipals *principals;
|
||||||
js::gc::Chunk *chunk;
|
|
||||||
|
|
||||||
js::gc::ArenaList arenas[js::gc::FINALIZE_LIMIT];
|
js::gc::ArenaList arenas[js::gc::FINALIZE_LIMIT];
|
||||||
js::gc::FreeLists freeLists;
|
js::gc::FreeLists freeLists;
|
||||||
|
179
js/src/jsgc.cpp
179
js/src/jsgc.cpp
@ -337,27 +337,49 @@ Chunk::init(JSRuntime *rt)
|
|||||||
|
|
||||||
for (size_t i = 0; i != JS_ARRAY_LENGTH(markingDelay); ++i)
|
for (size_t i = 0; i != JS_ARRAY_LENGTH(markingDelay); ++i)
|
||||||
markingDelay[i].init();
|
markingDelay[i].init();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The rest of info fields is initailzied in PickChunk. We do not clear
|
||||||
|
* the mark bitmap as that is done at the start of the next GC.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
inline Chunk **
|
||||||
Chunk::unused()
|
GetAvailableChunkList(JSCompartment *comp)
|
||||||
{
|
{
|
||||||
return info.numFree == ArenasPerChunk;
|
JSRuntime *rt = comp->rt;
|
||||||
|
return comp->isSystemCompartment
|
||||||
|
? &rt->gcSystemAvailableChunkListHead
|
||||||
|
: &rt->gcUserAvailableChunkListHead;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
inline void
|
||||||
Chunk::hasAvailableArenas()
|
Chunk::addToAvailableList(JSCompartment *comp)
|
||||||
{
|
{
|
||||||
return info.numFree > 0;
|
Chunk **listHeadp = GetAvailableChunkList(comp);
|
||||||
|
JS_ASSERT(!info.prevp);
|
||||||
|
JS_ASSERT(!info.next);
|
||||||
|
info.prevp = listHeadp;
|
||||||
|
Chunk *head = *listHeadp;
|
||||||
|
if (head) {
|
||||||
|
JS_ASSERT(head->info.prevp == listHeadp);
|
||||||
|
head->info.prevp = &info.next;
|
||||||
|
}
|
||||||
|
info.next = head;
|
||||||
|
*listHeadp = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
inline void
|
||||||
Chunk::withinArenasRange(Cell *cell)
|
Chunk::removeFromAvailableList()
|
||||||
{
|
{
|
||||||
uintptr_t addr = uintptr_t(cell);
|
JS_ASSERT(info.prevp);
|
||||||
if (addr >= uintptr_t(&arenas[0]) && addr < uintptr_t(&arenas[ArenasPerChunk]))
|
*info.prevp = info.next;
|
||||||
return true;
|
if (info.next) {
|
||||||
return false;
|
JS_ASSERT(info.next->info.prevp == &info.next);
|
||||||
|
info.next->info.prevp = info.prevp;
|
||||||
|
}
|
||||||
|
info.prevp = NULL;
|
||||||
|
info.next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t thingSize>
|
template <size_t thingSize>
|
||||||
@ -371,6 +393,9 @@ Chunk::allocateArena(JSContext *cx, unsigned thingKind)
|
|||||||
aheader->init(comp, thingKind, thingSize);
|
aheader->init(comp, thingKind, thingSize);
|
||||||
--info.numFree;
|
--info.numFree;
|
||||||
|
|
||||||
|
if (!hasAvailableArenas())
|
||||||
|
removeFromAvailableList();
|
||||||
|
|
||||||
JSRuntime *rt = info.runtime;
|
JSRuntime *rt = info.runtime;
|
||||||
Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes + ArenaSize);
|
Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes + ArenaSize);
|
||||||
JS_ATOMIC_ADD(&rt->gcBytes, ArenaSize);
|
JS_ATOMIC_ADD(&rt->gcBytes, ArenaSize);
|
||||||
@ -409,9 +434,15 @@ Chunk::releaseArena(ArenaHeader *aheader)
|
|||||||
aheader->next = info.emptyArenaListHead;
|
aheader->next = info.emptyArenaListHead;
|
||||||
info.emptyArenaListHead = aheader;
|
info.emptyArenaListHead = aheader;
|
||||||
++info.numFree;
|
++info.numFree;
|
||||||
if (unused()) {
|
if (info.numFree == 1) {
|
||||||
rt->gcUserChunkSet.remove(this);
|
JS_ASSERT(!info.prevp);
|
||||||
rt->gcSystemChunkSet.remove(this);
|
JS_ASSERT(!info.next);
|
||||||
|
addToAvailableList(aheader->compartment);
|
||||||
|
} else if (!unused()) {
|
||||||
|
JS_ASSERT(info.prevp);
|
||||||
|
} else {
|
||||||
|
rt->gcChunkSet.remove(this);
|
||||||
|
removeFromAvailableList();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We keep empty chunks until we are done with finalization to allow
|
* We keep empty chunks until we are done with finalization to allow
|
||||||
@ -420,7 +451,7 @@ Chunk::releaseArena(ArenaHeader *aheader)
|
|||||||
* GC_SHRINK.
|
* GC_SHRINK.
|
||||||
*/
|
*/
|
||||||
info.age = 0;
|
info.age = 0;
|
||||||
info.link = rt->gcEmptyChunkListHead;
|
info.next = rt->gcEmptyChunkListHead;
|
||||||
rt->gcEmptyChunkListHead = this;
|
rt->gcEmptyChunkListHead = this;
|
||||||
rt->gcEmptyChunkCount++;
|
rt->gcEmptyChunkCount++;
|
||||||
}
|
}
|
||||||
@ -450,34 +481,23 @@ ReleaseGCChunk(JSRuntime *rt, Chunk *p)
|
|||||||
inline Chunk *
|
inline Chunk *
|
||||||
PickChunk(JSContext *cx)
|
PickChunk(JSContext *cx)
|
||||||
{
|
{
|
||||||
Chunk *chunk = cx->compartment->chunk;
|
JSCompartment *comp = cx->compartment;
|
||||||
if (chunk && chunk->hasAvailableArenas())
|
JSRuntime *rt = comp->rt;
|
||||||
|
Chunk **listHeadp = GetAvailableChunkList(comp);
|
||||||
|
Chunk *chunk = *listHeadp;
|
||||||
|
if (chunk)
|
||||||
return chunk;
|
return chunk;
|
||||||
|
|
||||||
JSRuntime *rt = cx->runtime;
|
|
||||||
bool isSystemCompartment = cx->compartment->isSystemCompartment;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The chunk used for the last allocation is full, search all chunks for
|
* We do not have available chunks, either get one from the empty set or
|
||||||
* free arenas.
|
* allocate one.
|
||||||
*/
|
*/
|
||||||
GCChunkSet *chunkSet = isSystemCompartment ? &rt->gcSystemChunkSet : &rt->gcUserChunkSet;
|
|
||||||
for (GCChunkSet::Range r(chunkSet->all()); !r.empty(); r.popFront()) {
|
|
||||||
chunk = r.front();
|
|
||||||
if (chunk->hasAvailableArenas()) {
|
|
||||||
cx->compartment->chunk = chunk;
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use an empty chunk when available or allocate a new one. */
|
|
||||||
chunk = rt->gcEmptyChunkListHead;
|
chunk = rt->gcEmptyChunkListHead;
|
||||||
if (chunk) {
|
if (chunk) {
|
||||||
JS_ASSERT(chunk->unused());
|
JS_ASSERT(chunk->unused());
|
||||||
JS_ASSERT(!rt->gcUserChunkSet.has(chunk));
|
JS_ASSERT(!rt->gcChunkSet.has(chunk));
|
||||||
JS_ASSERT(!rt->gcSystemChunkSet.has(chunk));
|
|
||||||
JS_ASSERT(rt->gcEmptyChunkCount >= 1);
|
JS_ASSERT(rt->gcEmptyChunkCount >= 1);
|
||||||
rt->gcEmptyChunkListHead = chunk->info.link;
|
rt->gcEmptyChunkListHead = chunk->info.next;
|
||||||
rt->gcEmptyChunkCount--;
|
rt->gcEmptyChunkCount--;
|
||||||
} else {
|
} else {
|
||||||
chunk = AllocateGCChunk(rt);
|
chunk = AllocateGCChunk(rt);
|
||||||
@ -492,27 +512,18 @@ PickChunk(JSContext *cx)
|
|||||||
* FIXME bug 583732 - chunk is newly allocated and cannot be present in
|
* FIXME bug 583732 - chunk is newly allocated and cannot be present in
|
||||||
* the table so using ordinary lookupForAdd is suboptimal here.
|
* the table so using ordinary lookupForAdd is suboptimal here.
|
||||||
*/
|
*/
|
||||||
GCChunkSet::AddPtr p = chunkSet->lookupForAdd(chunk);
|
GCChunkSet::AddPtr p = rt->gcChunkSet.lookupForAdd(chunk);
|
||||||
JS_ASSERT(!p);
|
JS_ASSERT(!p);
|
||||||
if (!chunkSet->add(p, chunk)) {
|
if (!rt->gcChunkSet.add(p, chunk)) {
|
||||||
ReleaseGCChunk(rt, chunk);
|
ReleaseGCChunk(rt, chunk);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cx->compartment->chunk = chunk;
|
chunk->info.prevp = NULL;
|
||||||
return chunk;
|
chunk->info.next = NULL;
|
||||||
}
|
chunk->addToAvailableList(comp);
|
||||||
|
|
||||||
static void
|
return chunk;
|
||||||
ReleaseEmptyGCChunks(JSRuntime *rt)
|
|
||||||
{
|
|
||||||
for (Chunk *chunk = rt->gcEmptyChunkListHead; chunk; ) {
|
|
||||||
Chunk *next = chunk->info.link;
|
|
||||||
ReleaseGCChunk(rt, chunk);
|
|
||||||
chunk = next;
|
|
||||||
}
|
|
||||||
rt->gcEmptyChunkListHead = NULL;
|
|
||||||
rt->gcEmptyChunkCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -520,23 +531,21 @@ ExpireGCChunks(JSRuntime *rt, JSGCInvocationKind gckind)
|
|||||||
{
|
{
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
|
|
||||||
if (gckind == GC_SHRINK) {
|
/* Return old empty chunks to the system. */
|
||||||
ReleaseEmptyGCChunks(rt);
|
for (Chunk **chunkp = &rt->gcEmptyChunkListHead; *chunkp; ) {
|
||||||
} else {
|
JS_ASSERT(rt->gcEmptyChunkCount);
|
||||||
/* Return old empty chunks to the system. */
|
Chunk *chunk = *chunkp;
|
||||||
for (Chunk **chunkp = &rt->gcEmptyChunkListHead; *chunkp; ) {
|
JS_ASSERT(chunk->unused());
|
||||||
JS_ASSERT(rt->gcEmptyChunkCount);
|
JS_ASSERT(!rt->gcChunkSet.has(chunk));
|
||||||
Chunk *chunk = *chunkp;
|
JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE);
|
||||||
JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE);
|
if (gckind == GC_SHRINK || chunk->info.age == MAX_EMPTY_CHUNK_AGE) {
|
||||||
if (chunk->info.age == MAX_EMPTY_CHUNK_AGE) {
|
*chunkp = chunk->info.next;
|
||||||
*chunkp = chunk->info.link;
|
--rt->gcEmptyChunkCount;
|
||||||
--rt->gcEmptyChunkCount;
|
ReleaseGCChunk(rt, chunk);
|
||||||
ReleaseGCChunk(rt, chunk);
|
} else {
|
||||||
} else {
|
/* Keep the chunk but increase its age. */
|
||||||
/* Keep the chunk but increase its age. */
|
++chunk->info.age;
|
||||||
++chunk->info.age;
|
chunkp = &chunk->info.next;
|
||||||
chunkp = &chunk->info.link;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -576,14 +585,7 @@ static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 120 * 1000 * 1000;
|
|||||||
JSBool
|
JSBool
|
||||||
js_InitGC(JSRuntime *rt, uint32 maxbytes)
|
js_InitGC(JSRuntime *rt, uint32 maxbytes)
|
||||||
{
|
{
|
||||||
/*
|
if (!rt->gcChunkSet.init(INITIAL_CHUNK_CAPACITY))
|
||||||
* Make room for at least 16 chunks so the table would not grow before
|
|
||||||
* the browser starts up.
|
|
||||||
*/
|
|
||||||
if (!rt->gcUserChunkSet.init(INITIAL_CHUNK_CAPACITY))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!rt->gcSystemChunkSet.init(INITIAL_CHUNK_CAPACITY))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!rt->gcRootsHash.init(256))
|
if (!rt->gcRootsHash.init(256))
|
||||||
@ -725,8 +727,7 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w)
|
|||||||
|
|
||||||
Chunk *chunk = Chunk::fromAddress(addr);
|
Chunk *chunk = Chunk::fromAddress(addr);
|
||||||
|
|
||||||
if (!trc->context->runtime->gcUserChunkSet.has(chunk) &&
|
if (!trc->context->runtime->gcChunkSet.has(chunk))
|
||||||
!trc->context->runtime->gcSystemChunkSet.has(chunk))
|
|
||||||
return CGCT_NOTCHUNK;
|
return CGCT_NOTCHUNK;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -933,13 +934,18 @@ js_FinishGC(JSRuntime *rt)
|
|||||||
rt->compartments.clear();
|
rt->compartments.clear();
|
||||||
rt->atomsCompartment = NULL;
|
rt->atomsCompartment = NULL;
|
||||||
|
|
||||||
for (GCChunkSet::Range r(rt->gcUserChunkSet.all()); !r.empty(); r.popFront())
|
rt->gcSystemAvailableChunkListHead = NULL;
|
||||||
|
rt->gcUserAvailableChunkListHead = NULL;
|
||||||
|
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
||||||
ReleaseGCChunk(rt, r.front());
|
ReleaseGCChunk(rt, r.front());
|
||||||
for (GCChunkSet::Range r(rt->gcSystemChunkSet.all()); !r.empty(); r.popFront())
|
rt->gcChunkSet.clear();
|
||||||
ReleaseGCChunk(rt, r.front());
|
for (Chunk *chunk = rt->gcEmptyChunkListHead; chunk; ) {
|
||||||
rt->gcUserChunkSet.clear();
|
Chunk *next = chunk->info.next;
|
||||||
rt->gcSystemChunkSet.clear();
|
ReleaseGCChunk(rt, chunk);
|
||||||
ReleaseEmptyGCChunks(rt);
|
chunk = next;
|
||||||
|
}
|
||||||
|
rt->gcEmptyChunkListHead = NULL;
|
||||||
|
rt->gcEmptyChunkCount = 0;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
rt->gcHelperThread.finish(rt);
|
rt->gcHelperThread.finish(rt);
|
||||||
@ -2265,10 +2271,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
|
|||||||
JS_ASSERT(gcmarker.getMarkColor() == BLACK);
|
JS_ASSERT(gcmarker.getMarkColor() == BLACK);
|
||||||
rt->gcMarkingTracer = &gcmarker;
|
rt->gcMarkingTracer = &gcmarker;
|
||||||
|
|
||||||
for (GCChunkSet::Range r(rt->gcUserChunkSet.all()); !r.empty(); r.popFront())
|
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
||||||
r.front()->bitmap.clear();
|
|
||||||
|
|
||||||
for (GCChunkSet::Range r(rt->gcSystemChunkSet.all()); !r.empty(); r.popFront())
|
|
||||||
r.front()->bitmap.clear();
|
r.front()->bitmap.clear();
|
||||||
|
|
||||||
if (comp) {
|
if (comp) {
|
||||||
|
@ -514,8 +514,9 @@ struct MarkingDelay {
|
|||||||
|
|
||||||
/* The chunk header (located at the end of the chunk to preserve arena alignment). */
|
/* The chunk header (located at the end of the chunk to preserve arena alignment). */
|
||||||
struct ChunkInfo {
|
struct ChunkInfo {
|
||||||
Chunk *link;
|
|
||||||
JSRuntime *runtime;
|
JSRuntime *runtime;
|
||||||
|
Chunk *next;
|
||||||
|
Chunk **prevp;
|
||||||
ArenaHeader *emptyArenaListHead;
|
ArenaHeader *emptyArenaListHead;
|
||||||
size_t age;
|
size_t age;
|
||||||
size_t numFree;
|
size_t numFree;
|
||||||
@ -620,9 +621,17 @@ struct Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init(JSRuntime *rt);
|
void init(JSRuntime *rt);
|
||||||
bool unused();
|
|
||||||
bool hasAvailableArenas();
|
bool unused() const {
|
||||||
bool withinArenasRange(Cell *cell);
|
return info.numFree == ArenasPerChunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasAvailableArenas() const {
|
||||||
|
return info.numFree > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void addToAvailableList(JSCompartment *compartment);
|
||||||
|
inline void removeFromAvailableList();
|
||||||
|
|
||||||
template <size_t thingSize>
|
template <size_t thingSize>
|
||||||
ArenaHeader *allocateArena(JSContext *cx, unsigned thingKind);
|
ArenaHeader *allocateArena(JSContext *cx, unsigned thingKind);
|
||||||
|
Loading…
Reference in New Issue
Block a user