Bug 782993 - Part 1: Always sweep background things at the end r=billm

This commit is contained in:
Jon Coppeard 2012-08-21 09:47:15 +01:00
parent 49cd986642
commit 0594a6f41e
5 changed files with 76 additions and 85 deletions

View File

@ -1646,6 +1646,7 @@ ArenaLists::queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind)
#ifdef JS_THREADSAFE
JS_ASSERT(!fop->runtime()->gcHelperThread.sweeping());
#endif
ArenaList *al = &arenaLists[thingKind];
if (!al->head) {
@ -1655,34 +1656,20 @@ ArenaLists::queueForBackgroundSweep(FreeOp *fop, AllocKind thingKind)
}
/*
* The state can be just-finished if we have not allocated any GC things
* from the arena list after the previous background finalization.
* The state can be done, or just-finished if we have not allocated any GC
* things from the arena list after the previous background finalization.
*/
JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE ||
backgroundFinalizeState[thingKind] == BFS_JUST_FINISHED);
if (fop->shouldFreeLater()) {
arenaListsToSweep[thingKind] = al->head;
al->clear();
backgroundFinalizeState[thingKind] = BFS_RUN;
} else {
finalizeNow(fop, thingKind);
backgroundFinalizeState[thingKind] = BFS_DONE;
}
#else /* !JS_THREADSAFE */
finalizeNow(fop, thingKind);
#endif
arenaListsToSweep[thingKind] = al->head;
al->clear();
backgroundFinalizeState[thingKind] = BFS_RUN;
}
/*static*/ void
ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgroundThread)
{
#ifdef JS_THREADSAFE
JS_ASSERT(fop->onBackgroundThread());
#endif /* JS_THREADSAFE */
JS_ASSERT(listHead);
AllocKind thingKind = listHead->getAllocKind();
JSCompartment *comp = listHead->compartment;
@ -1704,23 +1691,26 @@ ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
JS_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN);
JS_ASSERT(!*al->cursor);
/*
* We must set the state to BFS_JUST_FINISHED if we touch arenaList list,
* even if we add to the list only fully allocated arenas without any free
* things. It ensures that the allocation thread takes the GC lock and all
* writes to the free list elements are propagated. As we always take the
* GC lock when allocating new arenas from the chunks we can set the state
* to BFS_DONE if we have released all finalized arenas back to their
* chunks.
*/
if (finalized.head) {
*al->cursor = finalized.head;
if (finalized.cursor != &finalized.head)
al->cursor = finalized.cursor;
lists->backgroundFinalizeState[thingKind] = BFS_JUST_FINISHED;
} else {
lists->backgroundFinalizeState[thingKind] = BFS_DONE;
}
/*
* We must set the state to BFS_JUST_FINISHED if we are running on the
* background thread and we have touched arenaList list, even if we add to
* the list only fully allocated arenas without any free things. It ensures
* that the allocation thread takes the GC lock and all writes to the free
* list elements are propagated. As we always take the GC lock when
* allocating new arenas from the chunks we can set the state to BFS_DONE if
* we have released all finalized arenas back to their chunks.
*/
if (onBackgroundThread && finalized.head)
lists->backgroundFinalizeState[thingKind] = BFS_JUST_FINISHED;
else
lists->backgroundFinalizeState[thingKind] = BFS_DONE;
lists->arenaListsToSweep[thingKind] = NULL;
}
@ -2829,13 +2819,41 @@ ExpireChunksAndArenas(JSRuntime *rt, bool shouldShrink)
DecommitArenas(rt);
}
static void
SweepBackgroundThings(JSRuntime* rt, bool onBackgroundThread)
{
/*
* We must finalize in the correct order, see comments in
* finalizeObjects.
*/
FreeOp fop(rt, false, false);
for (int phase = 0 ; phase < BackgroundPhaseCount ; ++phase) {
for (JSCompartment *c = rt->gcSweepingCompartments; c; c = c->gcNextCompartment) {
for (int index = 0 ; index < BackgroundPhaseLength[phase] ; ++index) {
AllocKind kind = BackgroundPhases[phase][index];
ArenaHeader *arenas = c->arenas.arenaListsToSweep[kind];
if (arenas) {
ArenaLists::backgroundFinalize(&fop, arenas, onBackgroundThread);
}
}
}
}
while (JSCompartment *c = rt->gcSweepingCompartments) {
rt->gcSweepingCompartments = c->gcNextCompartment;
c->gcNextCompartment = NULL;
}
}
static void
AssertBackgroundSweepingFinshed(JSRuntime *rt)
{
for (CompartmentsIter c(rt); !c.done(); c.next()) {
JS_ASSERT(!c->gcNextCompartment);
for (unsigned i = 0 ; i < FINALIZE_LIMIT ; ++i)
for (unsigned i = 0 ; i < FINALIZE_LIMIT ; ++i) {
JS_ASSERT(!c->arenas.arenaListsToSweep[i]);
JS_ASSERT(c->arenas.doneBackgroundFinalize(AllocKind(i)));
}
}
}
@ -3089,27 +3107,7 @@ GCHelperThread::doSweep()
sweepFlag = false;
AutoUnlockGC unlock(rt);
/*
* We must finalize in the insert order, see comments in
* finalizeObjects.
*/
FreeOp fop(rt, false, true);
for (int phase = 0; phase < BackgroundPhaseCount; ++phase) {
for (JSCompartment *c = rt->gcSweepingCompartments; c; c = c->gcNextCompartment) {
for (int index = 0; index < BackgroundPhaseLength[phase]; ++index) {
AllocKind kind = BackgroundPhases[phase][index];
ArenaHeader *arenas = c->arenas.arenaListsToSweep[kind];
if (arenas) {
ArenaLists::backgroundFinalize(&fop, arenas);
}
}
}
}
while (JSCompartment *c = rt->gcSweepingCompartments) {
rt->gcSweepingCompartments = c->gcNextCompartment;
c->gcNextCompartment = NULL;
}
SweepBackgroundThings(rt, true);
if (freeCursor) {
void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
@ -3178,7 +3176,7 @@ SweepCompartments(FreeOp *fop, gcreason::Reason gcReason)
while (read < end) {
JSCompartment *compartment = *read++;
if (!compartment->hold && compartment->isCollecting() &&
if (!compartment->hold && compartment->wasGCStarted() &&
(compartment->arenas.arenaListsAreEmpty() || gcReason == gcreason::LAST_CONTEXT))
{
compartment->arenas.checkEmptyFreeLists();
@ -3828,11 +3826,22 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason gcReaso
PropertyTree::dumpShapes(rt);
#endif
/*
* Set up list of compartments for sweeping of background things.
*/
JS_ASSERT(!rt->gcSweepingCompartments);
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
c->gcNextCompartment = rt->gcSweepingCompartments;
rt->gcSweepingCompartments = c;
}
{
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DESTROY);
if (!rt->gcSweepOnBackgroundThread)
if (!rt->gcSweepOnBackgroundThread) {
rt->freeLifoAlloc.freeAll();
SweepBackgroundThings(rt, false);
}
/*
* Sweep script filenames after sweeping functions in the generic loop
@ -3867,19 +3876,6 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason gcReaso
arena->unsetAllocDuringSweep();
}
/*
* Set up list of compartments to be swept by the background thread.
*/
JS_ASSERT(!rt->gcSweepingCompartments);
if (rt->gcSweepOnBackgroundThread) {
JSCompartment **cursor = &rt->gcSweepingCompartments;
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
JS_ASSERT(!c->gcNextCompartment);
*cursor = c.get();
cursor = &c->gcNextCompartment;
}
}
for (CompartmentsIter c(rt); !c.done(); c.next()) {
c->setGCLastBytes(c->gcBytes, c->gcMallocAndFreeBytes, gckind);
if (c->wasGCStarted())

View File

@ -318,7 +318,8 @@ struct ArenaLists {
}
bool doneBackgroundFinalize(AllocKind kind) const {
return backgroundFinalizeState[kind] == BFS_DONE;
return backgroundFinalizeState[kind] == BFS_DONE ||
backgroundFinalizeState[kind] == BFS_JUST_FINISHED;
}
/*
@ -419,7 +420,7 @@ struct ArenaLists {
void queueScriptsForSweep(FreeOp *fop);
bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget);
static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead);
static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgroundThread);
private:
inline void finalizeNow(FreeOp *fop, AllocKind thingKind);

View File

@ -82,17 +82,11 @@ GetGCObjectFixedSlotsKind(size_t numFixedSlots)
return slotsToThingKind[numFixedSlots];
}
static inline bool
IsBackgroundAllocKind(AllocKind kind)
{
JS_ASSERT(kind <= FINALIZE_LAST);
return kind <= FINALIZE_OBJECT_LAST && kind % 2 == 1;
}
static inline AllocKind
GetBackgroundAllocKind(AllocKind kind)
{
JS_ASSERT(!IsBackgroundAllocKind(kind));
JS_ASSERT(!IsBackgroundFinalized(kind));
JS_ASSERT(kind <= FINALIZE_OBJECT_LAST);
return (AllocKind) (kind + 1);
}
@ -344,7 +338,7 @@ class CellIter : public CellIterImpl
* background finalization; make sure people aren't using CellIter to
* walk such allocation kinds.
*/
JS_ASSERT(!IsBackgroundAllocKind(kind));
JS_ASSERT(!IsBackgroundFinalized(kind));
if (lists->isSynchronizedFreeList(kind)) {
lists = NULL;
} else {

View File

@ -3139,8 +3139,8 @@ bool
JSObject::swap(JSContext *cx, JSObject *other)
{
// Ensure swap doesn't cause a finalizer to not be run.
JS_ASSERT(IsBackgroundAllocKind(getAllocKind()) ==
IsBackgroundAllocKind(other->getAllocKind()));
JS_ASSERT(IsBackgroundFinalized(getAllocKind()) ==
IsBackgroundFinalized(other->getAllocKind()));
if (this->compartment() == other->compartment()) {
TradeGutsReserved reserved(cx);

View File

@ -242,7 +242,7 @@ JSObject::finalize(js::FreeOp *fop)
{
js::Probes::finalizeObject(this);
if (!fop->onBackgroundThread()) {
if (!IsBackgroundFinalized(getAllocKind())) {
/*
* Finalize obj first, in case it needs map and slots. Objects with
* finalize hooks are not finalized in the background, as the class is
@ -1420,10 +1420,10 @@ CanBeFinalizedInBackground(gc::AllocKind kind, Class *clasp)
* a different thread, we change the finalize kind. For example,
* FINALIZE_OBJECT0 calls the finalizer on the main thread,
* FINALIZE_OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
* IsBackgroundAllocKind is called to prevent recursively incrementing
* IsBackgroundFinalized is called to prevent recursively incrementing
* the finalize kind; kind may already be a background finalize kind.
*/
return (!gc::IsBackgroundAllocKind(kind) && !clasp->finalize);
return (!gc::IsBackgroundFinalized(kind) && !clasp->finalize);
}
/*