mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 557538 - custom GC chunk allocation. r=gal,jorendorff
This commit is contained in:
parent
3744861727
commit
24dee0ab53
@ -197,6 +197,7 @@ INSTALLED_HEADERS = \
|
||||
jsemit.h \
|
||||
jsfun.h \
|
||||
jsgc.h \
|
||||
jsgcchunk.h \
|
||||
jshash.h \
|
||||
jsinterp.h \
|
||||
jsinttypes.h \
|
||||
|
@ -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 \
|
||||
|
111
js/src/jsapi-tests/testGCChunkAlloc.cpp
Normal file
111
js/src/jsapi-tests/testGCChunkAlloc.cpp
Normal 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)
|
@ -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>",
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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 *
|
||||
|
@ -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);
|
||||
|
@ -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__ */
|
||||
|
Loading…
Reference in New Issue
Block a user