Move GSN cache for best sharing and correct clearing (360612, r=igor).

This commit is contained in:
brendan%mozilla.org 2006-11-24 03:03:18 +00:00
Родитель 625ff4a4a2
Коммит 987c97e7e8
3 изменённых файлов: 78 добавлений и 47 удалений

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

@ -59,6 +59,34 @@
JS_BEGIN_EXTERN_C
/*
* js_GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
* given pc in a script.
*/
typedef struct JSGSNCache {
JSScript *script;
JSDHashTable table;
#ifdef JS_GSNMETER
uint32 hits;
uint32 misses;
uint32 fills;
uint32 clears;
# define GSN_CACHE_METER(cx,cnt) (++JS_GSN_CACHE(cx).cnt)
#else
# define GSN_CACHE_METER(cx,cnt) /* nothing */
#endif
} JSGSNCache;
#define JS_CLEAR_GSN_CACHE(cx) \
JS_BEGIN_MACRO \
JS_GSN_CACHE(cx).script = NULL; \
if (JS_GSN_CACHE(cx).table.ops) { \
JS_DHashTableFinish(&JS_GSN_CACHE(cx).table); \
JS_GSN_CACHE(cx).table.ops = NULL; \
} \
GSN_CACHE_METER(cx, clears); \
JS_END_MACRO
#ifdef JS_THREADSAFE
/*
@ -85,8 +113,19 @@ struct JSThread {
/* Flag indicating that the current thread is executing close hooks. */
JSBool gcRunningCloseHooks;
#endif
/*
* Store the GSN cache in struct JSThread, not struct JSContext, both to
* save space and to simplify cleanup in js_GC. Any embedding (Firefox
* or another Gecko application) that uses many contexts per thread is
* unlikely to interleave js_GetSrcNote-intensive loops in the decompiler
* among two or more contexts running script in one thread.
*/
JSGSNCache gsnCache;
};
#define JS_GSN_CACHE(cx) ((cx)->thread->gsnCache)
extern void JS_DLL_CALLBACK
js_ThreadDestructorCB(void *ptr);
@ -350,6 +389,18 @@ struct JSRuntime {
*/
JSNativeIteratorState *nativeIteratorStates;
#ifndef JS_THREADSAFE
/*
* For thread-unsafe embeddings, the GSN cache lives in the runtime and
* not each context, since we expect it to be filled once when decompiling
* a longer script, then hit repeatedly as js_GetSrcNote is called during
* the decompiler activation that filled it.
*/
JSGSNCache gsnCache;
#define JS_GSN_CACHE(cx) ((cx)->runtime->gsnCache)
#endif
#ifdef DEBUG
/* Function invocation metering. */
jsrefcount inlineCalls;
@ -715,37 +766,11 @@ struct JSContext {
/* Top of the GC mark stack. */
void *gcCurrentMarkNode;
#endif
/*
* js_GetSrcNote cache to avoid O(n^2) growth in finding a source note for
* a given pc in a script.
*/
struct JSGSNCache {
JSScript *script;
JSDHashTable table;
#ifdef JS_GSNMETER
uint32 hits;
uint32 misses;
uint32 fills;
uint32 clears;
# define GSN_CACHE_METER(cx,cnt) (++(cx)->gsnCache.cnt)
#else
# define GSN_CACHE_METER(cx,cnt) /* nothing */
#endif
} gsnCache;
};
#define JS_CLEAR_GSN_CACHE(cx) \
JS_BEGIN_MACRO \
(cx)->gsnCache.script = NULL; \
if ((cx)->gsnCache.table.ops) { \
JS_DHashTableFinish(&(cx)->gsnCache.table); \
(cx)->gsnCache.table.ops = NULL; \
} \
GSN_CACHE_METER(cx, clears); \
JS_END_MACRO
#define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0)
#ifdef JS_THREADSAFE
# define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0)
#endif
#ifdef __cplusplus
/* FIXME(bug 332648): Move this into a public header. */

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

@ -2824,8 +2824,11 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
* freelist more than once. To avoid redundant clearing we unroll the
* current thread's step.
*
* Also, in case a JSScript wrapped in an object was finalized, we clear
* the acx->gsnCache.script pointer and finish the cache's hashtable.
* Also, in case a JSScript wrapped within an object was finalized, we
* null acx->thread->gsnCache.script and finish the cache's hashtable.
* Note that js_DestroyScript, called from script_finalize, will have
* already cleared cx->thread->gsnCache above during finalization, so we
* don't have to here.
*/
memset(cx->thread->gcFreeLists, 0, sizeof cx->thread->gcFreeLists);
iter = NULL;
@ -2835,6 +2838,9 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
memset(acx->thread->gcFreeLists, 0, sizeof acx->thread->gcFreeLists);
JS_CLEAR_GSN_CACHE(acx);
}
#else
/* The thread-unsafe case just has to clear the runtime's GSN cache. */
JS_CLEAR_GSN_CACHE(cx);
#endif
restart:
@ -2971,10 +2977,7 @@ restart:
* Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set
* so that any attempt to allocate a GC-thing from a finalizer will fail,
* rather than nest badly and leave the unmarked newborn to be swept.
*/
js_SweepAtomState(&rt->atomState);
/*
*
* Finalize smaller objects before larger, to guarantee finalization of
* GC-allocated obj->slots after obj. See FreeSlots in jsobj.c.
*/
@ -3021,14 +3024,16 @@ restart:
/*
* Sweep the runtime's property tree after finalizing objects, in case any
* had watchpoints referencing tree nodes.
* had watchpoints referencing tree nodes. Then sweep atoms, which may be
* referenced from dead property ids.
*/
js_SweepScopeProperties(rt);
js_SweepAtomState(&rt->atomState);
/*
* Sweep script filenames after sweeping functions in the generic loop
* above. In this way when scripted function's finalizer destroys script
* triggering a call to rt->destroyScriptHook, the hook can still access
* 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);

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

@ -1399,7 +1399,7 @@ js_DestroyScript(JSContext *cx, JSScript *script)
js_FreeAtomMap(cx, &script->atomMap);
if (script->principals)
JSPRINCIPALS_DROP(cx, script->principals);
if (cx->gsnCache.script == script)
if (JS_GSN_CACHE(cx).script == script)
JS_CLEAR_GSN_CACHE(cx);
JS_free(cx, script);
}
@ -1442,10 +1442,11 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
if ((uint32)target >= script->length)
return NULL;
if (cx->gsnCache.script == script) {
if (JS_GSN_CACHE(cx).script == script) {
GSN_CACHE_METER(cx, hits);
entry = (GSNCacheEntry *)
JS_DHashTableOperate(&cx->gsnCache.table, pc, JS_DHASH_LOOKUP);
JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
JS_DHASH_LOOKUP);
return entry->sn;
}
@ -1463,7 +1464,7 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
}
}
if (cx->gsnCache.script != script &&
if (JS_GSN_CACHE(cx).script != script &&
script->length >= GSN_CACHE_THRESHOLD) {
JS_CLEAR_GSN_CACHE(cx);
nsrcnotes = 0;
@ -1472,9 +1473,9 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
if (SN_IS_GETTABLE(sn))
++nsrcnotes;
}
if (!JS_DHashTableInit(&cx->gsnCache.table, JS_DHashGetStubOps(), NULL,
sizeof(GSNCacheEntry), nsrcnotes)) {
cx->gsnCache.table.ops = NULL;
if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(),
NULL, sizeof(GSNCacheEntry), nsrcnotes)) {
JS_GSN_CACHE(cx).table.ops = NULL;
} else {
pc = script->code;
for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
@ -1482,13 +1483,13 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
pc += SN_DELTA(sn);
if (SN_IS_GETTABLE(sn)) {
entry = (GSNCacheEntry *)
JS_DHashTableOperate(&cx->gsnCache.table, pc,
JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
JS_DHASH_ADD);
entry->pc = pc;
entry->sn = sn;
}
}
cx->gsnCache.script = script;
JS_GSN_CACHE(cx).script = script;
GSN_CACHE_METER(cx, fills);
}
}