Bug 553671 part 1 - Extract GC() from js_GC(). r=jwalden.

--HG--
extra : rebase_source : 4fc17e71f7f7f3bd1d7392af585e61d265851918
This commit is contained in:
Jason Orendorff 2010-04-08 07:54:17 -05:00
parent ecdd402081
commit 926b177eaa

View File

@ -2842,8 +2842,240 @@ void dumpGCTimer(GCTimer *gcT, uint64 firstEnter, bool lastGC)
if (lastGC)
fclose(gcFile);
}
# define GCTIMER_PARAM , GCTimer &gcTimer
# define GCTIMER_ARG , gcTimer
# define TIMESTAMP(x) (x = rdtsc())
#else
# define GCTIMER_PARAM
# define GCTIMER_ARG
# define TIMESTAMP(x) ((void) 0)
#endif
/*
* Perform mark-and-sweep GC.
*
* In a JS_THREADSAFE build, the calling thread must be rt->gcThread and each
* other thread must be either outside all requests or blocked waiting for GC
* to finish. Note that the caller does not hold rt->gcLock.
*/
static void
GC(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
{
JSRuntime *rt = cx->runtime;
JSTracer trc;
JSGCArena *emptyArenas, *a, **ap;
#ifdef JS_GCMETER
uint32 nlivearenas, nkilledarenas, nthings;
#endif
rt->gcNumber++;
JS_ASSERT(!rt->gcUnmarkedArenaStackTop);
JS_ASSERT(rt->gcMarkLaterCount == 0);
/*
* Mark phase.
*/
JS_TRACER_INIT(&trc, cx, NULL);
rt->gcMarkingTracer = &trc;
JS_ASSERT(IS_GC_MARKING_TRACER(&trc));
#ifdef DEBUG
for (a = rt->gcDoubleArenaList.head; a; a = a->info.prev)
JS_ASSERT(!a->info.hasMarkedDoubles);
#endif
{
/*
* Query rt->gcKeepAtoms only when we know that all other threads are
* suspended, see bug 541790.
*/
bool keepAtoms = (gckind & GC_KEEP_ATOMS) || rt->gcKeepAtoms != 0;
js_TraceRuntime(&trc, keepAtoms);
js_MarkScriptFilenames(rt, keepAtoms);
}
/*
* Mark children of things that caused too deep recursion during the above
* tracing.
*/
MarkDelayedChildren(&trc);
JS_ASSERT(!cx->insideGCMarkCallback);
if (rt->gcCallback) {
cx->insideGCMarkCallback = JS_TRUE;
(void) rt->gcCallback(cx, JSGC_MARK_END);
JS_ASSERT(cx->insideGCMarkCallback);
cx->insideGCMarkCallback = JS_FALSE;
}
JS_ASSERT(rt->gcMarkLaterCount == 0);
rt->gcMarkingTracer = NULL;
#ifdef JS_THREADSAFE
cx->createDeallocatorTask();
#endif
/*
* Sweep phase.
*
* 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.
*
* We first sweep atom state so we can use js_IsAboutToBeFinalized on
* JSString or jsdouble held in a hashtable to check if the hashtable
* entry can be freed. Note that even after the entry is freed, JSObject
* finalizers can continue to access the corresponding jsdouble* and
* JSString* assuming that they are unique. This works since the
* atomization API must not be called during GC.
*/
TIMESTAMP(gcTimer.startSweep);
js_SweepAtomState(cx);
/* Finalize iterator states before the objects they iterate over. */
CloseNativeIterators(cx);
/* Finalize watch points associated with unreachable objects. */
js_SweepWatchPoints(cx);
#ifdef DEBUG
/* Save the pre-sweep count of scope-mapped properties. */
rt->liveScopePropsPreSweep = rt->liveScopeProps;
#endif
/*
* We finalize JSObject instances before JSString, double and other GC
* things to ensure that object's finalizer can access them even if they
* will be freed.
*
* To minimize the number of checks per each to be freed object and
* function we use separated list finalizers when a debug hook is
* installed.
*/
emptyArenas = NULL;
if (!cx->debugHooks->objectHook) {
FinalizeArenaList<JSObject, FinalizeObject>
(cx, FINALIZE_OBJECT, &emptyArenas);
FinalizeArenaList<JSFunction, FinalizeFunction>
(cx, FINALIZE_FUNCTION, &emptyArenas);
} else {
FinalizeArenaList<JSObject, FinalizeHookedObject>
(cx, FINALIZE_OBJECT, &emptyArenas);
FinalizeArenaList<JSFunction, FinalizeHookedFunction>
(cx, FINALIZE_FUNCTION, &emptyArenas);
}
#if JS_HAS_XML_SUPPORT
FinalizeArenaList<JSXML, FinalizeXML>(cx, FINALIZE_XML, &emptyArenas);
#endif
TIMESTAMP(gcTimer.sweepObjectEnd);
/*
* We sweep the deflated cache before we finalize the strings so the
* cache can safely use js_IsAboutToBeFinalized..
*/
rt->deflatedStringCache->sweep(cx);
FinalizeArenaList<JSString, FinalizeString>
(cx, FINALIZE_STRING, &emptyArenas);
for (unsigned i = FINALIZE_EXTERNAL_STRING0;
i <= FINALIZE_EXTERNAL_STRING_LAST;
++i) {
FinalizeArenaList<JSString, FinalizeExternalString>
(cx, i, &emptyArenas);
}
TIMESTAMP(gcTimer.sweepStringEnd);
ap = &rt->gcDoubleArenaList.head;
METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0));
while ((a = *ap) != NULL) {
if (!a->info.hasMarkedDoubles) {
/* No marked double values in the arena. */
*ap = a->info.prev;
a->info.prev = emptyArenas;
emptyArenas = a;
METER(nkilledarenas++);
} else {
#ifdef JS_GCMETER
for (jsuword offset = 0;
offset != DOUBLES_PER_ARENA * sizeof(jsdouble);
offset += sizeof(jsdouble)) {
if (IsMarkedGCThing(a, offset))
METER(nthings++);
}
METER(nlivearenas++);
#endif
a->info.hasMarkedDoubles = false;
ap = &a->info.prev;
}
}
METER(UpdateArenaStats(&rt->gcStats.doubleArenaStats,
nlivearenas, nkilledarenas, nthings));
rt->gcDoubleArenaList.cursor = rt->gcDoubleArenaList.head;
TIMESTAMP(gcTimer.sweepDoubleEnd);
/*
* Sweep the runtime's property tree after finalizing objects, in case any
* had watchpoints referencing tree nodes.
*/
js::SweepScopeProperties(cx);
/*
* 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);
/*
* Destroy arenas after we finished the sweeping so finalizers can safely
* use js_IsAboutToBeFinalized().
*/
DestroyGCArenas(rt, emptyArenas);
TIMESTAMP(gcTimer.sweepDestroyEnd);
#ifdef JS_THREADSAFE
cx->submitDeallocatorTask();
#endif
if (rt->gcCallback)
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
#ifdef DEBUG_srcnotesize
{ extern void DumpSrcNoteSizeHist();
DumpSrcNoteSizeHist();
printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes);
}
#endif
#ifdef JS_SCOPE_DEPTH_METER
{ static FILE *fp;
if (!fp)
fp = fopen("/tmp/scopedepth.stats", "w");
if (fp) {
JS_DumpBasicStats(&rt->protoLookupDepthStats, "proto-lookup depth", fp);
JS_DumpBasicStats(&rt->scopeSearchDepthStats, "scope-search depth", fp);
JS_DumpBasicStats(&rt->hostenvScopeDepthStats, "hostenv scope depth", fp);
JS_DumpBasicStats(&rt->lexicalScopeDepthStats, "lexical scope depth", fp);
putc('\n', fp);
fflush(fp);
}
}
#endif /* JS_SCOPE_DEPTH_METER */
#ifdef JS_DUMP_LOOP_STATS
{ static FILE *lsfp;
if (!lsfp)
lsfp = fopen("/tmp/loopstats", "w");
if (lsfp) {
JS_DumpBasicStats(&rt->loopStats, "loops", lsfp);
fflush(lsfp);
}
}
#endif /* JS_DUMP_LOOP_STATS */
}
/*
* The gckind flag bit GC_LOCK_HELD indicates a call from js_NewGCThing with
* rt->gcLock already held, so the lock should be kept on return.
@ -2853,14 +3085,9 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
{
JSRuntime *rt;
JSGCCallback callback;
JSTracer trc;
JSGCArena *emptyArenas, *a, **ap;
#ifdef JS_THREADSAFE
size_t requestDebit;
#endif
#ifdef JS_GCMETER
uint32 nlivearenas, nkilledarenas, nthings;
#endif
JS_ASSERT_IF(gckind == GC_LAST_DITCH, !JS_ON_TRACE(cx));
rt = cx->runtime;
@ -2889,9 +3116,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
static uint64 firstEnter = rdtsc();
GCTimer gcTimer;
memset(&gcTimer, 0, sizeof(GCTimer));
# define TIMESTAMP(x) (x = rdtsc())
#else
# define TIMESTAMP(x) ((void) 0)
#endif
TIMESTAMP(gcTimer.enter);
@ -3153,214 +3377,10 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
TIMESTAMP(gcTimer.startMark);
restart:
rt->gcNumber++;
JS_ASSERT(!rt->gcUnmarkedArenaStackTop);
JS_ASSERT(rt->gcMarkLaterCount == 0);
/*
* Mark phase.
*/
JS_TRACER_INIT(&trc, cx, NULL);
rt->gcMarkingTracer = &trc;
JS_ASSERT(IS_GC_MARKING_TRACER(&trc));
#ifdef DEBUG
for (a = rt->gcDoubleArenaList.head; a; a = a->info.prev)
JS_ASSERT(!a->info.hasMarkedDoubles);
#endif
{
/*
* Query rt->gcKeepAtoms only when we know that all other threads are
* suspended, see bug 541790.
*/
bool keepAtoms = (gckind & GC_KEEP_ATOMS) || rt->gcKeepAtoms != 0;
js_TraceRuntime(&trc, keepAtoms);
js_MarkScriptFilenames(rt, keepAtoms);
}
/*
* Mark children of things that caused too deep recursion during the above
* tracing.
*/
MarkDelayedChildren(&trc);
JS_ASSERT(!cx->insideGCMarkCallback);
if (rt->gcCallback) {
cx->insideGCMarkCallback = JS_TRUE;
(void) rt->gcCallback(cx, JSGC_MARK_END);
JS_ASSERT(cx->insideGCMarkCallback);
cx->insideGCMarkCallback = JS_FALSE;
}
JS_ASSERT(rt->gcMarkLaterCount == 0);
rt->gcMarkingTracer = NULL;
#ifdef JS_THREADSAFE
cx->createDeallocatorTask();
#endif
/*
* Sweep phase.
*
* 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.
*
* We first sweep atom state so we can use js_IsAboutToBeFinalized on
* JSString or jsdouble held in a hashtable to check if the hashtable
* entry can be freed. Note that even after the entry is freed, JSObject
* finalizers can continue to access the corresponding jsdouble* and
* JSString* assuming that they are unique. This works since the
* atomization API must not be called during GC.
*/
TIMESTAMP(gcTimer.startSweep);
js_SweepAtomState(cx);
/* Finalize iterator states before the objects they iterate over. */
CloseNativeIterators(cx);
/* Finalize watch points associated with unreachable objects. */
js_SweepWatchPoints(cx);
#ifdef DEBUG
/* Save the pre-sweep count of scope-mapped properties. */
rt->liveScopePropsPreSweep = rt->liveScopeProps;
#endif
/*
* We finalize JSObject instances before JSString, double and other GC
* things to ensure that object's finalizer can access them even if they
* will be freed.
*
* To minimize the number of checks per each to be freed object and
* function we use separated list finalizers when a debug hook is
* installed.
*/
emptyArenas = NULL;
if (!cx->debugHooks->objectHook) {
FinalizeArenaList<JSObject, FinalizeObject>
(cx, FINALIZE_OBJECT, &emptyArenas);
FinalizeArenaList<JSFunction, FinalizeFunction>
(cx, FINALIZE_FUNCTION, &emptyArenas);
} else {
FinalizeArenaList<JSObject, FinalizeHookedObject>
(cx, FINALIZE_OBJECT, &emptyArenas);
FinalizeArenaList<JSFunction, FinalizeHookedFunction>
(cx, FINALIZE_FUNCTION, &emptyArenas);
}
#if JS_HAS_XML_SUPPORT
FinalizeArenaList<JSXML, FinalizeXML>(cx, FINALIZE_XML, &emptyArenas);
#endif
TIMESTAMP(gcTimer.sweepObjectEnd);
/*
* We sweep the deflated cache before we finalize the strings so the
* cache can safely use js_IsAboutToBeFinalized..
*/
rt->deflatedStringCache->sweep(cx);
FinalizeArenaList<JSString, FinalizeString>
(cx, FINALIZE_STRING, &emptyArenas);
for (unsigned i = FINALIZE_EXTERNAL_STRING0;
i <= FINALIZE_EXTERNAL_STRING_LAST;
++i) {
FinalizeArenaList<JSString, FinalizeExternalString>
(cx, i, &emptyArenas);
}
TIMESTAMP(gcTimer.sweepStringEnd);
ap = &rt->gcDoubleArenaList.head;
METER((nlivearenas = 0, nkilledarenas = 0, nthings = 0));
while ((a = *ap) != NULL) {
if (!a->info.hasMarkedDoubles) {
/* No marked double values in the arena. */
*ap = a->info.prev;
a->info.prev = emptyArenas;
emptyArenas = a;
METER(nkilledarenas++);
} else {
#ifdef JS_GCMETER
for (jsuword offset = 0;
offset != DOUBLES_PER_ARENA * sizeof(jsdouble);
offset += sizeof(jsdouble)) {
if (IsMarkedGCThing(a, offset))
METER(nthings++);
}
METER(nlivearenas++);
#endif
a->info.hasMarkedDoubles = false;
ap = &a->info.prev;
}
}
METER(UpdateArenaStats(&rt->gcStats.doubleArenaStats,
nlivearenas, nkilledarenas, nthings));
rt->gcDoubleArenaList.cursor = rt->gcDoubleArenaList.head;
TIMESTAMP(gcTimer.sweepDoubleEnd);
/*
* Sweep the runtime's property tree after finalizing objects, in case any
* had watchpoints referencing tree nodes.
*/
js::SweepScopeProperties(cx);
/*
* 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);
/*
* Destroy arenas after we finished the sweeping so finalizers can safely
* use js_IsAboutToBeFinalized().
*/
DestroyGCArenas(rt, emptyArenas);
TIMESTAMP(gcTimer.sweepDestroyEnd);
#ifdef JS_THREADSAFE
cx->submitDeallocatorTask();
#endif
if (rt->gcCallback)
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
#ifdef DEBUG_srcnotesize
{ extern void DumpSrcNoteSizeHist();
DumpSrcNoteSizeHist();
printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes);
}
#endif
#ifdef JS_SCOPE_DEPTH_METER
{ static FILE *fp;
if (!fp)
fp = fopen("/tmp/scopedepth.stats", "w");
if (fp) {
JS_DumpBasicStats(&rt->protoLookupDepthStats, "proto-lookup depth", fp);
JS_DumpBasicStats(&rt->scopeSearchDepthStats, "scope-search depth", fp);
JS_DumpBasicStats(&rt->hostenvScopeDepthStats, "hostenv scope depth", fp);
JS_DumpBasicStats(&rt->lexicalScopeDepthStats, "lexical scope depth", fp);
putc('\n', fp);
fflush(fp);
}
}
#endif /* JS_SCOPE_DEPTH_METER */
#ifdef JS_DUMP_LOOP_STATS
{ static FILE *lsfp;
if (!lsfp)
lsfp = fopen("/tmp/loopstats", "w");
if (lsfp) {
JS_DumpBasicStats(&rt->loopStats, "loops", lsfp);
fflush(lsfp);
}
}
#endif /* JS_DUMP_LOOP_STATS */
GC(cx, gckind GCTIMER_ARG);
#ifdef JS_TRACER
out:
out:
#endif
JS_LOCK_GC(rt);