Bug 666058 - Don't share chunks for system compartments. r=gal,igor.

This commit is contained in:
Gregor Wagner 2011-07-05 14:14:33 +10:00
Родитель 159859c23d
Коммит 2930b1da1e
6 изменённых файлов: 96 добавлений и 25 удалений

Просмотреть файл

@ -308,6 +308,9 @@ nsSystemPrincipal::nsSystemPrincipal()
{
}
// Don't rename the system principal!
// The JS engine (NewCompartment) relies on this name.
// XXX: bug 669123 will fix this hack.
#define SYSTEM_PRINCIPAL_SPEC "[System Principal]"
nsresult
@ -320,7 +323,7 @@ nsSystemPrincipal::Init()
NS_WARNING("Out of memory initializing system principal");
return NS_ERROR_OUT_OF_MEMORY;
}
return mJSPrincipals.Init(this, str);
}

Просмотреть файл

@ -10,26 +10,38 @@
#include "jsgcchunk.h"
#include "jscntxt.h"
/* We allow to allocate only single chunk. */
/* We allow to allocate 2 (system/user) chunks. */
/* XXX: using pool[0] and pool[1] is a hack; bug 669123 will fix this. */
class CustomGCChunkAllocator: public js::GCChunkAllocator {
public:
CustomGCChunkAllocator() : pool(NULL) {}
void *pool;
CustomGCChunkAllocator() { pool[0] = NULL; pool[1] = NULL; }
void *pool[2];
private:
virtual void *doAlloc() {
if (!pool)
if (!pool[0] && !pool[1])
return NULL;
void *chunk = pool;
pool = NULL;
void *chunk = NULL;
if (pool[0]) {
chunk = pool[0];
pool[0] = NULL;
} else {
chunk = pool[1];
pool[1] = NULL;
}
return chunk;
}
virtual void doFree(void *chunk) {
JS_ASSERT(!pool);
pool = chunk;
JS_ASSERT(!pool[0] || !pool[1]);
if (!pool[0]) {
pool[0] = chunk;
} else {
pool[1] = chunk;
}
}
};
@ -69,7 +81,8 @@ BEGIN_TEST(testGCChunkAlloc)
CHECK(!ok);
CHECK(!JS_IsExceptionPending(cx));
CHECK(errorCount == 1);
CHECK(!customGCChunkAllocator.pool);
CHECK(!customGCChunkAllocator.pool[0]);
CHECK(!customGCChunkAllocator.pool[1]);
JS_GC(cx);
JS_ToggleOptions(cx, JSOPTION_JIT);
EVAL("(function() {"
@ -92,8 +105,10 @@ virtual JSRuntime * createRuntime() {
if (!rt)
return NULL;
customGCChunkAllocator.pool = js::AllocGCChunk();
JS_ASSERT(customGCChunkAllocator.pool);
customGCChunkAllocator.pool[0] = js::AllocGCChunk();
customGCChunkAllocator.pool[1] = js::AllocGCChunk();
JS_ASSERT(customGCChunkAllocator.pool[0]);
JS_ASSERT(customGCChunkAllocator.pool[1]);
rt->setCustomGCChunkAllocator(&customGCChunkAllocator);
return rt;
@ -103,9 +118,12 @@ virtual void destroyRuntime() {
JS_DestroyRuntime(rt);
/* We should get the initial chunk back at this point. */
JS_ASSERT(customGCChunkAllocator.pool);
js::FreeGCChunk(customGCChunkAllocator.pool);
customGCChunkAllocator.pool = NULL;
JS_ASSERT(customGCChunkAllocator.pool[0]);
JS_ASSERT(customGCChunkAllocator.pool[1]);
js::FreeGCChunk(customGCChunkAllocator.pool[0]);
js::FreeGCChunk(customGCChunkAllocator.pool[1]);
customGCChunkAllocator.pool[0] = NULL;
customGCChunkAllocator.pool[1] = NULL;
}
END_TEST(testGCChunkAlloc)

Просмотреть файл

@ -666,6 +666,7 @@ JSRuntime::init(uint32 maxbytes)
return false;
}
atomsCompartment->systemGCChunks = true;
atomsCompartment->setGCLastBytes(8192, GC_NORMAL);
if (!js_InitAtomState(this))

Просмотреть файл

@ -376,6 +376,7 @@ struct JSRuntime {
/* Garbage collector state, used by jsgc.c. */
js::GCChunkSet gcChunkSet;
js::GCChunkSet gcSystemChunkSet;
js::RootedValueMap gcRootsHash;
js::GCLocks gcLocksHash;

Просмотреть файл

@ -378,6 +378,7 @@ struct JS_FRIEND_API(JSCompartment) {
size_t gcLastBytes;
bool hold;
bool systemGCChunks;
#ifdef JS_TRACER
private:

Просмотреть файл

@ -462,12 +462,15 @@ PickChunk(JSContext *cx)
if (chunk && chunk->hasAvailableArenas())
return chunk;
JSRuntime *rt = cx->runtime;
bool systemGCChunks = cx->compartment->systemGCChunks;
/*
* The chunk used for the last allocation is full, search all chunks for
* free arenas.
*/
JSRuntime *rt = cx->runtime;
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) {
GCChunkSet::Range r(systemGCChunks ? rt->gcSystemChunkSet.all() : rt->gcChunkSet.all());
for (; !r.empty(); r.popFront()) {
chunk = r.front();
if (chunk->hasAvailableArenas()) {
cx->compartment->chunk = chunk;
@ -476,18 +479,39 @@ PickChunk(JSContext *cx)
}
chunk = AllocateGCChunk(rt);
if (!chunk)
return NULL;
if (!chunk) {
/* Our last chance is to find an empty chunk in the other chunk set. */
GCChunkSet::Enum e(systemGCChunks ? rt->gcChunkSet : rt->gcSystemChunkSet);
for (; !e.empty(); e.popFront()) {
if (e.front()->info.numFree == ArenasPerChunk) {
chunk = e.front();
e.removeFront();
break;
}
}
if (!chunk)
return NULL;
}
/*
* FIXME bug 583732 - chunk is newly allocated and cannot be present in
* the table so using ordinary lookupForAdd is suboptimal here.
*/
GCChunkSet::AddPtr p = rt->gcChunkSet.lookupForAdd(chunk);
GCChunkSet::AddPtr p = systemGCChunks ?
rt->gcSystemChunkSet.lookupForAdd(chunk) :
rt->gcChunkSet.lookupForAdd(chunk);
JS_ASSERT(!p);
if (!rt->gcChunkSet.add(p, chunk)) {
ReleaseGCChunk(rt, chunk);
return NULL;
if (systemGCChunks) {
if (!rt->gcSystemChunkSet.add(p, chunk)) {
ReleaseGCChunk(rt, chunk);
return NULL;
}
} else {
if (!rt->gcChunkSet.add(p, chunk)) {
ReleaseGCChunk(rt, chunk);
return NULL;
}
}
chunk->init(rt);
@ -517,6 +541,18 @@ ExpireGCChunks(JSRuntime *rt, JSGCInvocationKind gckind)
rt->gcChunksWaitingToExpire++;
}
}
for (GCChunkSet::Enum e(rt->gcSystemChunkSet); !e.empty(); e.popFront()) {
Chunk *chunk = e.front();
JS_ASSERT(chunk->info.runtime == rt);
if (chunk->unused()) {
if (gckind == GC_SHRINK || chunk->info.age++ > MaxAge) {
e.removeFront();
ReleaseGCChunk(rt, chunk);
continue;
}
rt->gcChunksWaitingToExpire++;
}
}
}
JS_FRIEND_API(bool)
@ -561,6 +597,9 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
if (!rt->gcChunkSet.init(16))
return false;
if (!rt->gcSystemChunkSet.init(16))
return false;
if (!rt->gcRootsHash.init(256))
return false;
@ -700,7 +739,8 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w)
Chunk *chunk = Chunk::fromAddress(addr);
if (!trc->context->runtime->gcChunkSet.has(chunk))
if (!trc->context->runtime->gcChunkSet.has(chunk) &&
!trc->context->runtime->gcSystemChunkSet.has(chunk))
return CGCT_NOTCHUNK;
/*
@ -909,7 +949,10 @@ js_FinishGC(JSRuntime *rt)
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
ReleaseGCChunk(rt, r.front());
for (GCChunkSet::Range r(rt->gcSystemChunkSet.all()); !r.empty(); r.popFront())
ReleaseGCChunk(rt, r.front());
rt->gcChunkSet.clear();
rt->gcSystemChunkSet.clear();
#ifdef JS_THREADSAFE
rt->gcHelperThread.finish(rt);
@ -2230,7 +2273,10 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
rt->gcMarkingTracer = &gcmarker;
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
r.front()->bitmap.clear();
r.front()->bitmap.clear();
for (GCChunkSet::Range r(rt->gcSystemChunkSet.all()); !r.empty(); r.popFront())
r.front()->bitmap.clear();
if (comp) {
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
@ -2790,6 +2836,7 @@ NewCompartment(JSContext *cx, JSPrincipals *principals)
JSRuntime *rt = cx->runtime;
JSCompartment *compartment = cx->new_<JSCompartment>(rt);
if (compartment && compartment->init()) {
compartment->systemGCChunks = principals && !strcmp(principals->codebase, "[System Principal]");
if (principals) {
compartment->principals = principals;
JSPRINCIPALS_HOLD(cx, principals);