Bug 661903 - Move script filename table to compartment (r=igor)

This commit is contained in:
Bill McCloskey 2011-07-14 16:02:12 -07:00
parent 32014abfe0
commit fd629a392b
7 changed files with 71 additions and 194 deletions

View File

@ -660,7 +660,7 @@ check-malloc-function-usage: $(filter-out %jsalloc.h %jscntxt.h %jsutil.h, $(ALL
# We desire these numbers to go down, not up. See "User guide to memory
# management within SpiderMonkey" in jsutil.h.
$(srcdir)/config/check_source_count.py OffTheBooks:: 54 \
$(srcdir)/config/check_source_count.py OffTheBooks:: 52 \
"in Makefile.in" "{cx,rt}->{new_,new_array,malloc_,calloc_,realloc_}" $^
# This should go to zero, if possible.
$(srcdir)/config/check_source_count.py UnwantedForeground:: 33 \

View File

@ -695,8 +695,6 @@ JSRuntime::init(uint32 maxbytes)
return false;
if (!InitRuntimeNumberState(this))
return false;
if (!InitRuntimeScriptState(this))
return false;
return true;
}
@ -724,7 +722,6 @@ JSRuntime::~JSRuntime()
FinishJIT();
#endif
FreeRuntimeScriptState(this);
FinishRuntimeNumberState(this);
js_FinishThreads(this);
js_FinishAtomState(this);

View File

@ -131,6 +131,9 @@ JSCompartment::init()
if (!crossCompartmentWrappers.init())
return false;
if (!scriptFilenameTable.init())
return false;
regExpAllocator = rt->new_<WTF::BumpPointerAllocator>();
if (!regExpAllocator)
return false;

View File

@ -363,6 +363,25 @@ class DtoaCache {
};
struct ScriptFilenameEntry
{
bool marked;
char filename[1];
};
struct ScriptFilenameHasher
{
typedef const char *Lookup;
static HashNumber hash(const char *l) { return JS_HashString(l); }
static bool match(const ScriptFilenameEntry *e, const char *l) {
return strcmp(e->filename, l) == 0;
}
};
typedef HashSet<ScriptFilenameEntry *,
ScriptFilenameHasher,
SystemAllocPolicy> ScriptFilenameTable;
} /* namespace js */
struct JS_FRIEND_API(JSCompartment) {
@ -473,6 +492,8 @@ struct JS_FRIEND_API(JSCompartment) {
typedef js::Maybe<js::ToSourceCache> LazyToSourceCache;
LazyToSourceCache toSourceCache;
js::ScriptFilenameTable scriptFilenameTable;
JSCompartment(JSRuntime *rt);
~JSCompartment();

View File

@ -1820,6 +1820,17 @@ MarkContext(JSTracer *trc, JSContext *acx)
MarkValue(trc, acx->iterValue, "iterValue");
}
#define PER_COMPARTMENT_OP(rt, op) \
if ((rt)->gcCurrentCompartment) { \
JSCompartment *c = (rt)->gcCurrentCompartment; \
op; \
} else { \
for (JSCompartment **i = rt->compartments.begin(); i != rt->compartments.end(); ++i) { \
JSCompartment *c = *i; \
op; \
} \
}
JS_REQUIRES_STACK void
MarkRuntime(JSTracer *trc)
{
@ -1842,9 +1853,7 @@ MarkRuntime(JSTracer *trc)
MarkContext(trc, acx);
#ifdef JS_TRACER
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
if ((*c)->hasTraceMonitor())
(*c)->traceMonitor()->mark(trc);
PER_COMPARTMENT_OP(rt, if (c->hasTraceMonitor()) c->traceMonitor()->mark(trc));
#endif
for (ThreadDataIter i(rt); !i.empty(); i.popFront())
@ -2250,12 +2259,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
rt->protoHazardShape = 0;
}
if (rt->gcCurrentCompartment) {
rt->gcCurrentCompartment->purge(cx);
} else {
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
(*c)->purge(cx);
}
PER_COMPARTMENT_OP(rt, c->purge(cx));
js_PurgeThreads(cx);
{
@ -2284,8 +2288,6 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
if (comp) {
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
(*c)->markCrossCompartmentWrappers(&gcmarker);
} else {
js_MarkScriptFilenames(rt);
}
MarkRuntime(&gcmarker);
@ -2385,16 +2387,16 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
PropertyTree::dumpShapes(cx);
#endif
if (!comp) {
SweepCompartments(cx, gckind);
/*
* Sweep script filenames after sweeping functions in the generic loop
* above. In this way when a scripted function's finalizer destroys the
* script and calls rt->destroyScriptHook, the hook can still access the
* script's filename. See bug 323267.
*/
js_SweepScriptFilenames(rt);
PER_COMPARTMENT_OP(rt, js_SweepScriptFilenames(c));
if (!comp) {
SweepCompartments(cx, gckind);
/* non-compartmental sweep pieces */
Probes::GCEndSweepPhase(NULL);

View File

@ -783,112 +783,29 @@ Class js_ScriptClass = {
/*
* Shared script filename management.
*/
static int
js_compare_strings(const void *k1, const void *k2)
{
return strcmp((const char *) k1, (const char *) k2) == 0;
}
/* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
typedef struct ScriptFilenameEntry {
JSHashEntry *next; /* hash chain linkage */
JSHashNumber keyHash; /* key hash function result */
const void *key; /* ptr to filename, below */
JSPackedBool mark; /* GC mark flag */
char filename[3]; /* two or more bytes, NUL-terminated */
} ScriptFilenameEntry;
static void *
js_alloc_table_space(void *priv, size_t size)
{
return OffTheBooks::malloc_(size);
}
static void
js_free_table_space(void *priv, void *item, size_t size)
{
UnwantedForeground::free_(item);
}
static JSHashEntry *
js_alloc_sftbl_entry(void *priv, const void *key)
{
size_t nbytes = offsetof(ScriptFilenameEntry, filename) +
strlen((const char *) key) + 1;
return (JSHashEntry *) OffTheBooks::malloc_(JS_MAX(nbytes, sizeof(JSHashEntry)));
}
static void
js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag)
{
if (flag != HT_FREE_ENTRY)
return;
UnwantedForeground::free_(he);
}
static JSHashAllocOps sftbl_alloc_ops = {
js_alloc_table_space, js_free_table_space,
js_alloc_sftbl_entry, js_free_sftbl_entry
};
namespace js {
bool
InitRuntimeScriptState(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
JS_ASSERT(!rt->scriptFilenameTableLock);
rt->scriptFilenameTableLock = JS_NEW_LOCK();
if (!rt->scriptFilenameTableLock)
return false;
#endif
JS_ASSERT(!rt->scriptFilenameTable);
rt->scriptFilenameTable =
JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL,
&sftbl_alloc_ops, NULL);
if (!rt->scriptFilenameTable)
return false;
return true;
}
void
FreeRuntimeScriptState(JSRuntime *rt)
{
if (rt->scriptFilenameTable)
JS_HashTableDestroy(rt->scriptFilenameTable);
#ifdef JS_THREADSAFE
if (rt->scriptFilenameTableLock)
JS_DESTROY_LOCK(rt->scriptFilenameTableLock);
#endif
}
} /* namespace js */
static const char *
SaveScriptFilename(JSContext *cx, const char *filename)
{
JSRuntime *rt = cx->runtime;
JS_AUTO_LOCK_GUARD(g, rt->scriptFilenameTableLock);
JSCompartment *comp = cx->compartment;
JSHashTable *table = rt->scriptFilenameTable;
JSHashNumber hash = JS_HashString(filename);
JSHashEntry **hep = JS_HashTableRawLookup(table, hash, filename);
ScriptFilenameTable::AddPtr p = comp->scriptFilenameTable.lookupForAdd(filename);
if (!p) {
size_t size = offsetof(ScriptFilenameEntry, filename) + strlen(filename) + 1;
ScriptFilenameEntry *entry = (ScriptFilenameEntry *) cx->malloc_(size);
if (!entry)
return NULL;
entry->marked = false;
strcpy(entry->filename, filename);
ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) *hep;
if (!sfe) {
sfe = (ScriptFilenameEntry *)
JS_HashTableRawAdd(table, hep, hash, filename, NULL);
if (!sfe) {
if (!comp->scriptFilenameTable.add(p, entry)) {
Foreground::free_(entry);
JS_ReportOutOfMemory(cx);
return NULL;
}
sfe->key = strcpy(sfe->filename, filename);
sfe->mark = JS_FALSE;
}
return sfe->filename;
return (*p)->filename;
}
/*
@ -897,70 +814,26 @@ SaveScriptFilename(JSContext *cx, const char *filename)
#define FILENAME_TO_SFE(fn) \
((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
/*
* The sfe->key member, redundant given sfe->filename but required by the old
* jshash.c code, here gives us a useful sanity check. This assertion will
* very likely botch if someone tries to mark a string that wasn't allocated
* as an sfe->filename.
*/
#define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename)
void
js_MarkScriptFilename(const char *filename)
{
ScriptFilenameEntry *sfe;
sfe = FILENAME_TO_SFE(filename);
ASSERT_VALID_SFE(sfe);
sfe->mark = JS_TRUE;
}
static intN
js_script_filename_marker(JSHashEntry *he, intN i, void *arg)
{
ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
sfe->mark = JS_TRUE;
return HT_ENUMERATE_NEXT;
ScriptFilenameEntry *sfe = FILENAME_TO_SFE(filename);
sfe->marked = true;
}
void
js_MarkScriptFilenames(JSRuntime *rt)
js_SweepScriptFilenames(JSCompartment *comp)
{
if (!rt->scriptFilenameTable)
return;
if (rt->gcKeepAtoms) {
JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
js_script_filename_marker,
rt);
ScriptFilenameTable &table = comp->scriptFilenameTable;
for (ScriptFilenameTable::Enum e(table); !e.empty(); e.popFront()) {
ScriptFilenameEntry *entry = e.front();
if (entry->marked) {
entry->marked = false;
} else if (!comp->rt->gcKeepAtoms) {
Foreground::free_(entry);
e.removeFront();
}
}
static intN
js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg)
{
ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
if (!sfe->mark)
return HT_ENUMERATE_REMOVE;
sfe->mark = JS_FALSE;
return HT_ENUMERATE_NEXT;
}
void
js_SweepScriptFilenames(JSRuntime *rt)
{
if (!rt->scriptFilenameTable)
return;
/*
* JS_HashTableEnumerateEntries shrinks the table if many entries are
* removed preventing wasting memory on a too sparse table.
*/
JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
js_script_filename_sweeper,
rt);
}
/*

View File

@ -695,30 +695,11 @@ extern JS_FRIEND_DATA(js::Class) js_ScriptClass;
extern JSObject *
js_InitScriptClass(JSContext *cx, JSObject *obj);
namespace js {
extern bool
InitRuntimeScriptState(JSRuntime *rt);
/*
* On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any
* script filename table entries that have not been GC'd.
*
* This allows script filename prefixes to outlive any context in rt.
*/
extern void
FreeRuntimeScriptState(JSRuntime *rt);
} /* namespace js */
extern void
js_MarkScriptFilename(const char *filename);
extern void
js_MarkScriptFilenames(JSRuntime *rt);
extern void
js_SweepScriptFilenames(JSRuntime *rt);
js_SweepScriptFilenames(JSCompartment *comp);
/*
* New-script-hook calling is factored from js_NewScriptFromCG so that it