bug 557538 - custom GC chunk allocation. r=gal,jorendorff

This commit is contained in:
Igor Bukanov 2010-06-07 11:17:15 +02:00
parent 3744861727
commit 24dee0ab53
9 changed files with 171 additions and 7 deletions

View File

@ -197,6 +197,7 @@ INSTALLED_HEADERS = \
jsemit.h \
jsfun.h \
jsgc.h \
jsgcchunk.h \
jshash.h \
jsinterp.h \
jsinttypes.h \

View File

@ -45,6 +45,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
PROGRAM = jsapi-tests$(BIN_SUFFIX)
CPPSRCS = \
tests.cpp \
selfTest.cpp \
@ -53,6 +54,7 @@ CPPSRCS = \
testDefineGetterSetterNonEnumerable.cpp \
testDefineProperty.cpp \
testExtendedEq.cpp \
testGCChunkAlloc.cpp \
testIntString.cpp \
testIsAboutToBeFinalized.cpp \
testLookup.cpp \

View File

@ -0,0 +1,111 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
* Contributor: Igor Bukanov
*/
#include "tests.h"
#include "jsgcchunk.h"
#include "jscntxt.h"
/* We allow to allocate only single chunk. */
class CustomGCChunkAllocator: public js::GCChunkAllocator {
public:
CustomGCChunkAllocator() : pool(NULL) {}
void *pool;
private:
virtual void *doAlloc() {
if (!pool)
return NULL;
void *chunk = pool;
pool = NULL;
return chunk;
}
virtual void doFree(void *chunk) {
JS_ASSERT(!pool);
pool = chunk;
}
};
static CustomGCChunkAllocator customGCChunkAllocator;
static unsigned errorCount = 0;
static void
ErrorCounter(JSContext *cx, const char *message, JSErrorReport *report)
{
++errorCount;
}
BEGIN_TEST(testGCChunkAlloc)
{
JS_SetErrorReporter(cx, ErrorCounter);
jsvalRoot root(cx);
/*
* We loop until out-of-memory happens during the chunk allocation. But
* we have to disable the jit since it cannot tolerate OOM during the
* chunk allocation.
*/
JS_ToggleOptions(cx, JSOPTION_JIT);
static const char source[] =
"var max = 0; (function() {"
" var array = [];"
" for (; ; ++max)"
" array.push(max + 0.1);"
"})();";
JSBool ok = JS_EvaluateScript(cx, global, source, strlen(source), "", 1,
root.addr());
/* Check that we get OOM. */
CHECK(!ok);
CHECK(!JS_IsExceptionPending(cx));
CHECK(errorCount == 1);
CHECK(!customGCChunkAllocator.pool);
JS_GC(cx);
JS_ToggleOptions(cx, JSOPTION_JIT);
EVAL("(function() {"
" var array = [];"
" for (var i = max >> 1; i != 0;) {"
" --i;"
" array.push(i + 0.1);"
" }"
"})();", root.addr());
CHECK(errorCount == 1);
return true;
}
virtual JSRuntime * createRuntime() {
/*
* To test failure of chunk allocation allow to use GC twice the memory
* the single chunk contains.
*/
JSRuntime *rt = JS_NewRuntime(2 * js::GC_CHUNK_SIZE);
if (!rt)
return NULL;
customGCChunkAllocator.pool = js::AllocGCChunk();
JS_ASSERT(customGCChunkAllocator.pool);
rt->setCustomGCChunkAllocator(&customGCChunkAllocator);
return rt;
}
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;
}
END_TEST(testGCChunkAlloc)

View File

@ -141,7 +141,7 @@ public:
cx = NULL;
}
if (rt) {
JS_DestroyRuntime(rt);
destroyRuntime();
rt = NULL;
}
}
@ -214,6 +214,12 @@ protected:
return JS_NewRuntime(8L * 1024 * 1024);
}
virtual void destroyRuntime() {
JS_ASSERT(!cx);
JS_ASSERT(rt);
JS_DestroyRuntime(rt);
}
static void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
fprintf(stderr, "%s:%u:%s\n",
report->filename ? report->filename : "<no filename>",

View File

@ -539,6 +539,7 @@ static JSBool js_NewRuntimeWasCalled = JS_FALSE;
#endif
JSRuntime::JSRuntime()
: gcChunkAllocator(&defaultGCChunkAllocator)
{
/* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
JS_INIT_CLIST(&contextList);

View File

@ -59,6 +59,7 @@
#include "jsdhash.h"
#include "jsdtoa.h"
#include "jsgc.h"
#include "jsgcchunk.h"
#include "jshashtable.h"
#include "jsinterp.h"
#include "jsobj.h"
@ -1272,6 +1273,14 @@ struct JSRuntime {
JSBackgroundThread gcHelperThread;
#endif
js::GCChunkAllocator *gcChunkAllocator;
void setCustomGCChunkAllocator(js::GCChunkAllocator *allocator) {
JS_ASSERT(allocator);
JS_ASSERT(state == JSRTS_DOWN);
gcChunkAllocator = allocator;
}
/*
* The trace operation and its data argument to trace embedding-specific
* GC roots.

View File

@ -610,7 +610,7 @@ static jsrefcount destroyChunkCount = 0;
inline void *
GetGCChunk(JSRuntime *rt)
{
void *p = AllocGCChunk();
void *p = rt->gcChunkAllocator->alloc();
#ifdef MOZ_GCTIMER
if (p)
JS_ATOMIC_INCREMENT(&newChunkCount);
@ -630,7 +630,7 @@ ReleaseGCChunk(JSRuntime *rt, jsuword chunk)
#endif
JS_ASSERT(rt->gcStats.nchunks != 0);
METER(rt->gcStats.nchunks--);
FreeGCChunk(p);
rt->gcChunkAllocator->free(p);
}
static JSGCArena *

View File

@ -242,6 +242,8 @@ UnmapPages(void *addr, size_t size)
namespace js {
GCChunkAllocator defaultGCChunkAllocator;
inline void *
FindChunkStart(void *p)
{
@ -250,7 +252,7 @@ FindChunkStart(void *p)
return reinterpret_cast<void *>(addr);
}
void *
JS_FRIEND_API(void *)
AllocGCChunk()
{
void *p;
@ -297,7 +299,7 @@ AllocGCChunk()
return p;
}
void
JS_FRIEND_API(void)
FreeGCChunk(void *p)
{
JS_ASSERT(p);

View File

@ -55,12 +55,44 @@ const size_t GC_CHUNK_SHIFT = 20;
const size_t GC_CHUNK_SIZE = size_t(1) << GC_CHUNK_SHIFT;
const size_t GC_CHUNK_MASK = GC_CHUNK_SIZE - 1;
void *
JS_FRIEND_API(void *)
AllocGCChunk();
void
JS_FRIEND_API(void)
FreeGCChunk(void *p);
class GCChunkAllocator {
public:
GCChunkAllocator() {}
void *alloc() {
void *chunk = doAlloc();
JS_ASSERT(!(reinterpret_cast<jsuword>(chunk) & GC_CHUNK_MASK));
return chunk;
}
void free(void *chunk) {
JS_ASSERT(chunk);
JS_ASSERT(!(reinterpret_cast<jsuword>(chunk) & GC_CHUNK_MASK));
doFree(chunk);
}
private:
virtual void *doAlloc() {
return AllocGCChunk();
}
virtual void doFree(void *chunk) {
FreeGCChunk(chunk);
}
/* No copy or assignment semantics. */
GCChunkAllocator(const GCChunkAllocator &);
void operator=(const GCChunkAllocator &);
};
extern GCChunkAllocator defaultGCChunkAllocator;
}
#endif /* jsgchunk_h__ */