зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1017165 - Sort arenas in order of increasing free space during finalization. r=billm
This commit is contained in:
Родитель
421d3b6944
Коммит
c6daf89dfb
|
@ -700,6 +700,12 @@ class GCRuntime
|
||||||
|
|
||||||
GCHelperState helperState;
|
GCHelperState helperState;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* During incremental sweeping, this field temporarily holds the arenas of
|
||||||
|
* the current AllocKind being swept in order of increasing free space.
|
||||||
|
*/
|
||||||
|
SortedArenaList incrementalSweepList;
|
||||||
|
|
||||||
ConservativeGCData conservativeGC;
|
ConservativeGCData conservativeGC;
|
||||||
|
|
||||||
friend class js::GCHelperState;
|
friend class js::GCHelperState;
|
||||||
|
|
|
@ -39,6 +39,7 @@ namespace gc {
|
||||||
|
|
||||||
struct Arena;
|
struct Arena;
|
||||||
class ArenaList;
|
class ArenaList;
|
||||||
|
class SortedArenaList;
|
||||||
struct ArenaHeader;
|
struct ArenaHeader;
|
||||||
struct Chunk;
|
struct Chunk;
|
||||||
|
|
||||||
|
@ -597,7 +598,7 @@ struct Arena
|
||||||
void setAsFullyUnused(AllocKind thingKind);
|
void setAsFullyUnused(AllocKind thingKind);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize);
|
size_t finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(Arena) == ArenaSize, "The hardcoded arena size must match the struct size.");
|
static_assert(sizeof(Arena) == ArenaSize, "The hardcoded arena size must match the struct size.");
|
||||||
|
@ -828,7 +829,8 @@ struct Chunk
|
||||||
ArenaHeader *allocateArena(JS::Zone *zone, AllocKind kind);
|
ArenaHeader *allocateArena(JS::Zone *zone, AllocKind kind);
|
||||||
|
|
||||||
void releaseArena(ArenaHeader *aheader);
|
void releaseArena(ArenaHeader *aheader);
|
||||||
void recycleArena(ArenaHeader *aheader, ArenaList &dest, AllocKind thingKind);
|
void recycleArena(ArenaHeader *aheader, SortedArenaList &dest, AllocKind thingKind,
|
||||||
|
size_t thingsPerArena);
|
||||||
|
|
||||||
static Chunk *allocate(JSRuntime *rt);
|
static Chunk *allocate(JSRuntime *rt);
|
||||||
|
|
||||||
|
|
133
js/src/jsgc.cpp
133
js/src/jsgc.cpp
|
@ -175,6 +175,7 @@
|
||||||
|
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
|
#include "mozilla/MacroForEach.h"
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
#include "mozilla/Move.h"
|
#include "mozilla/Move.h"
|
||||||
|
|
||||||
|
@ -255,7 +256,14 @@ const AllocKind gc::slotsToThingKind[] = {
|
||||||
static_assert(JS_ARRAY_LENGTH(slotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT,
|
static_assert(JS_ARRAY_LENGTH(slotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT,
|
||||||
"We have defined a slot count for each kind.");
|
"We have defined a slot count for each kind.");
|
||||||
|
|
||||||
const uint32_t Arena::ThingSizes[] = {
|
// Assert that SortedArenaList::MinThingSize is <= the real minimum thing size.
|
||||||
|
#define CHECK_MIN_THING_SIZE_INNER(x_) \
|
||||||
|
static_assert(x_ >= SortedArenaList::MinThingSize, \
|
||||||
|
#x_ " is less than SortedArenaList::MinThingSize!");
|
||||||
|
#define CHECK_MIN_THING_SIZE(...) { __VA_ARGS__ }; /* Define the array. */ \
|
||||||
|
MOZ_FOR_EACH(CHECK_MIN_THING_SIZE_INNER, (), (__VA_ARGS__ UINT32_MAX))
|
||||||
|
|
||||||
|
const uint32_t Arena::ThingSizes[] = CHECK_MIN_THING_SIZE(
|
||||||
sizeof(JSObject), /* FINALIZE_OBJECT0 */
|
sizeof(JSObject), /* FINALIZE_OBJECT0 */
|
||||||
sizeof(JSObject), /* FINALIZE_OBJECT0_BACKGROUND */
|
sizeof(JSObject), /* FINALIZE_OBJECT0_BACKGROUND */
|
||||||
sizeof(JSObject_Slots2), /* FINALIZE_OBJECT2 */
|
sizeof(JSObject_Slots2), /* FINALIZE_OBJECT2 */
|
||||||
|
@ -278,7 +286,10 @@ const uint32_t Arena::ThingSizes[] = {
|
||||||
sizeof(JSExternalString), /* FINALIZE_EXTERNAL_STRING */
|
sizeof(JSExternalString), /* FINALIZE_EXTERNAL_STRING */
|
||||||
sizeof(JS::Symbol), /* FINALIZE_SYMBOL */
|
sizeof(JS::Symbol), /* FINALIZE_SYMBOL */
|
||||||
sizeof(jit::JitCode), /* FINALIZE_JITCODE */
|
sizeof(jit::JitCode), /* FINALIZE_JITCODE */
|
||||||
};
|
);
|
||||||
|
|
||||||
|
#undef CHECK_MIN_THING_SIZE_INNER
|
||||||
|
#undef CHECK_MIN_THING_SIZE
|
||||||
|
|
||||||
#define OFFSET(type) uint32_t(sizeof(ArenaHeader) + (ArenaSize - sizeof(ArenaHeader)) % sizeof(type))
|
#define OFFSET(type) uint32_t(sizeof(ArenaHeader) + (ArenaSize - sizeof(ArenaHeader)) % sizeof(type))
|
||||||
|
|
||||||
|
@ -450,7 +461,7 @@ Arena::setAsFullyUnused(AllocKind thingKind)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline bool
|
inline size_t
|
||||||
Arena::finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize)
|
Arena::finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize)
|
||||||
{
|
{
|
||||||
/* Enforce requirements on size of T. */
|
/* Enforce requirements on size of T. */
|
||||||
|
@ -496,7 +507,7 @@ Arena::finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize)
|
||||||
// Do nothing. The caller will update the arena header appropriately.
|
// Do nothing. The caller will update the arena header appropriately.
|
||||||
JS_ASSERT(newListTail == &newListHead);
|
JS_ASSERT(newListTail == &newListHead);
|
||||||
JS_EXTRA_POISON(data, JS_SWEPT_TENURED_PATTERN, sizeof(data));
|
JS_EXTRA_POISON(data, JS_SWEPT_TENURED_PATTERN, sizeof(data));
|
||||||
return true;
|
return nmarked;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_ASSERT(firstThingOrSuccessorOfLastMarkedThing != firstThing);
|
JS_ASSERT(firstThingOrSuccessorOfLastMarkedThing != firstThing);
|
||||||
|
@ -517,20 +528,38 @@ Arena::finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize)
|
||||||
JS_ASSERT(nfree + nmarked == thingsPerArena(thingSize));
|
JS_ASSERT(nfree + nmarked == thingsPerArena(thingSize));
|
||||||
#endif
|
#endif
|
||||||
aheader.setFirstFreeSpan(&newListHead);
|
aheader.setFirstFreeSpan(&newListHead);
|
||||||
return false;
|
return nmarked;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaList
|
||||||
|
SortedArenaList::toArenaList()
|
||||||
|
{
|
||||||
|
// Link the non-empty segment tails up to the non-empty segment heads.
|
||||||
|
size_t tailIndex = 0;
|
||||||
|
for (size_t headIndex = 1; headIndex <= thingsPerArena_; ++headIndex) {
|
||||||
|
if (headAt(headIndex)) {
|
||||||
|
segments[tailIndex].linkTo(headAt(headIndex));
|
||||||
|
tailIndex = headIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Point the tail of the final non-empty segment at null. Note that if the
|
||||||
|
// list is empty, this will just set segments[0].head to null, a noop.
|
||||||
|
segments[tailIndex].linkTo(nullptr);
|
||||||
|
// Return an ArenaList representing the flattened list.
|
||||||
|
return ArenaList(segments[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static inline bool
|
static inline bool
|
||||||
FinalizeTypedArenas(FreeOp *fop,
|
FinalizeTypedArenas(FreeOp *fop,
|
||||||
ArenaHeader **src,
|
ArenaHeader **src,
|
||||||
ArenaList &dest,
|
SortedArenaList &dest,
|
||||||
AllocKind thingKind,
|
AllocKind thingKind,
|
||||||
SliceBudget &budget)
|
SliceBudget &budget)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Finalize arenas from src list, releasing empty arenas and inserting the
|
* Finalize arenas from src list, releasing empty arenas and inserting the
|
||||||
* others into dest in an appropriate position.
|
* others into the appropriate destination size bins.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -541,22 +570,24 @@ FinalizeTypedArenas(FreeOp *fop,
|
||||||
bool releaseArenas = !InParallelSection();
|
bool releaseArenas = !InParallelSection();
|
||||||
|
|
||||||
size_t thingSize = Arena::thingSize(thingKind);
|
size_t thingSize = Arena::thingSize(thingKind);
|
||||||
|
size_t thingsPerArena = Arena::thingsPerArena(thingSize);
|
||||||
|
|
||||||
while (ArenaHeader *aheader = *src) {
|
while (ArenaHeader *aheader = *src) {
|
||||||
*src = aheader->next;
|
*src = aheader->next;
|
||||||
bool allClear = aheader->getArena()->finalize<T>(fop, thingKind, thingSize);
|
size_t nmarked = aheader->getArena()->finalize<T>(fop, thingKind, thingSize);
|
||||||
if (!allClear)
|
size_t nfree = thingsPerArena - nmarked;
|
||||||
dest.insertAtCursor(aheader);
|
|
||||||
|
if (nmarked)
|
||||||
|
dest.insertAt(aheader, nfree);
|
||||||
else if (releaseArenas)
|
else if (releaseArenas)
|
||||||
aheader->chunk()->releaseArena(aheader);
|
aheader->chunk()->releaseArena(aheader);
|
||||||
else
|
else
|
||||||
aheader->chunk()->recycleArena(aheader, dest, thingKind);
|
aheader->chunk()->recycleArena(aheader, dest, thingKind, thingsPerArena);
|
||||||
|
|
||||||
budget.step(Arena::thingsPerArena(thingSize));
|
budget.step(thingsPerArena);
|
||||||
if (budget.isOverBudget())
|
if (budget.isOverBudget())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
dest.deepCheck();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -568,7 +599,7 @@ FinalizeTypedArenas(FreeOp *fop,
|
||||||
static bool
|
static bool
|
||||||
FinalizeArenas(FreeOp *fop,
|
FinalizeArenas(FreeOp *fop,
|
||||||
ArenaHeader **src,
|
ArenaHeader **src,
|
||||||
ArenaList &dest,
|
SortedArenaList &dest,
|
||||||
AllocKind thingKind,
|
AllocKind thingKind,
|
||||||
SliceBudget &budget)
|
SliceBudget &budget)
|
||||||
{
|
{
|
||||||
|
@ -965,10 +996,11 @@ Chunk::addArenaToFreeList(JSRuntime *rt, ArenaHeader *aheader)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Chunk::recycleArena(ArenaHeader *aheader, ArenaList &dest, AllocKind thingKind)
|
Chunk::recycleArena(ArenaHeader *aheader, SortedArenaList &dest, AllocKind thingKind,
|
||||||
|
size_t thingsPerArena)
|
||||||
{
|
{
|
||||||
aheader->getArena()->setAsFullyUnused(thingKind);
|
aheader->getArena()->setAsFullyUnused(thingKind);
|
||||||
dest.insertAtCursor(aheader);
|
dest.insertAt(aheader, thingsPerArena);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1893,10 +1925,6 @@ ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind,
|
||||||
* While we still hold the GC lock get an arena from some chunk, mark it
|
* While we still hold the GC lock get an arena from some chunk, mark it
|
||||||
* as full as its single free span is moved to the free lists, and insert
|
* as full as its single free span is moved to the free lists, and insert
|
||||||
* it to the list as a fully allocated arena.
|
* it to the list as a fully allocated arena.
|
||||||
*
|
|
||||||
* We add the arena before the the head, so that after the GC the most
|
|
||||||
* recently added arena will be used first for allocations. This improves
|
|
||||||
* cache locality.
|
|
||||||
*/
|
*/
|
||||||
JS_ASSERT(al->isCursorAtEnd());
|
JS_ASSERT(al->isCursorAtEnd());
|
||||||
aheader = chunk->allocateArena(zone, thingKind);
|
aheader = chunk->allocateArena(zone, thingKind);
|
||||||
|
@ -1905,7 +1933,7 @@ ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind,
|
||||||
|
|
||||||
if (MOZ_UNLIKELY(zone->wasGCStarted()))
|
if (MOZ_UNLIKELY(zone->wasGCStarted()))
|
||||||
rt->gc.arenaAllocatedDuringGC(zone, aheader);
|
rt->gc.arenaAllocatedDuringGC(zone, aheader);
|
||||||
al->insertAtStart(aheader);
|
al->insertAtCursor(aheader);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate from a newly allocated arena. The arena will have been set up
|
* Allocate from a newly allocated arena. The arena will have been set up
|
||||||
|
@ -1978,9 +2006,14 @@ ArenaLists::forceFinalizeNow(FreeOp *fop, AllocKind thingKind)
|
||||||
ArenaHeader *arenas = arenaLists[thingKind].head();
|
ArenaHeader *arenas = arenaLists[thingKind].head();
|
||||||
arenaLists[thingKind].clear();
|
arenaLists[thingKind].clear();
|
||||||
|
|
||||||
|
size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(thingKind));
|
||||||
|
SortedArenaList finalizedSorted(thingsPerArena);
|
||||||
|
|
||||||
SliceBudget budget;
|
SliceBudget budget;
|
||||||
FinalizeArenas(fop, &arenas, arenaLists[thingKind], thingKind, budget);
|
FinalizeArenas(fop, &arenas, finalizedSorted, thingKind, budget);
|
||||||
JS_ASSERT(!arenas);
|
JS_ASSERT(!arenas);
|
||||||
|
|
||||||
|
arenaLists[thingKind] = finalizedSorted.toArenaList();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -2028,23 +2061,31 @@ ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgr
|
||||||
AllocKind thingKind = listHead->getAllocKind();
|
AllocKind thingKind = listHead->getAllocKind();
|
||||||
Zone *zone = listHead->zone;
|
Zone *zone = listHead->zone;
|
||||||
|
|
||||||
ArenaList finalized;
|
size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(thingKind));
|
||||||
|
SortedArenaList finalizedSorted(thingsPerArena);
|
||||||
|
|
||||||
SliceBudget budget;
|
SliceBudget budget;
|
||||||
FinalizeArenas(fop, &listHead, finalized, thingKind, budget);
|
FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget);
|
||||||
JS_ASSERT(!listHead);
|
JS_ASSERT(!listHead);
|
||||||
|
|
||||||
// When arenas are queued for background finalization, all
|
// When arenas are queued for background finalization, all arenas are moved
|
||||||
// arenas are moved to arenaListsToSweep[], leaving the arenaLists[] empty.
|
// to arenaListsToSweep[], leaving the arenaLists[] empty. However, new
|
||||||
// Then, if new arenas are allocated before background finalization
|
// arenas may be allocated before background finalization finishes; now that
|
||||||
// finishes they are always added to the front of the list. Therefore,
|
// finalization is complete, we want to merge these lists back together.
|
||||||
// at this point, |al|'s cursor will always be at the end of its list.
|
|
||||||
ArenaLists *lists = &zone->allocator.arenas;
|
ArenaLists *lists = &zone->allocator.arenas;
|
||||||
ArenaList *al = &lists->arenaLists[thingKind];
|
ArenaList *al = &lists->arenaLists[thingKind];
|
||||||
|
|
||||||
|
// Flatten |finalizedSorted| into a regular ArenaList.
|
||||||
|
ArenaList finalized = finalizedSorted.toArenaList();
|
||||||
|
|
||||||
|
// Store this for later, since merging may change the state of |finalized|.
|
||||||
|
bool allClear = finalized.isEmpty();
|
||||||
|
|
||||||
AutoLockGC lock(fop->runtime());
|
AutoLockGC lock(fop->runtime());
|
||||||
JS_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN);
|
JS_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN);
|
||||||
|
|
||||||
al->appendToListWithCursorAtEnd(finalized);
|
// Join |al| and |finalized| into a single list.
|
||||||
|
*al = finalized.insertListWithCursorAtEnd(*al);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We must set the state to BFS_JUST_FINISHED if we are running on the
|
* We must set the state to BFS_JUST_FINISHED if we are running on the
|
||||||
|
@ -2055,7 +2096,7 @@ ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgr
|
||||||
* allocating new arenas from the chunks we can set the state to BFS_DONE if
|
* 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.
|
* we have released all finalized arenas back to their chunks.
|
||||||
*/
|
*/
|
||||||
if (onBackgroundThread && !finalized.isEmpty())
|
if (onBackgroundThread && !allClear)
|
||||||
lists->backgroundFinalizeState[thingKind] = BFS_JUST_FINISHED;
|
lists->backgroundFinalizeState[thingKind] = BFS_JUST_FINISHED;
|
||||||
else
|
else
|
||||||
lists->backgroundFinalizeState[thingKind] = BFS_DONE;
|
lists->backgroundFinalizeState[thingKind] = BFS_DONE;
|
||||||
|
@ -4314,13 +4355,17 @@ GCRuntime::beginSweepPhase(bool lastGC)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ArenaLists::foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget)
|
ArenaLists::foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget,
|
||||||
|
SortedArenaList &sweepList)
|
||||||
{
|
{
|
||||||
if (!arenaListsToSweep[thingKind])
|
if (!FinalizeArenas(fop, &arenaListsToSweep[thingKind], sweepList, thingKind, sliceBudget))
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
ArenaList &dest = arenaLists[thingKind];
|
// Join |arenaLists[thingKind]| and |sweepList| into a single list.
|
||||||
return FinalizeArenas(fop, &arenaListsToSweep[thingKind], dest, thingKind, sliceBudget);
|
ArenaList finalized = sweepList.toArenaList();
|
||||||
|
arenaLists[thingKind] = finalized.insertListWithCursorAtEnd(arenaLists[thingKind]);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -4352,9 +4397,17 @@ GCRuntime::sweepPhase(SliceBudget &sliceBudget)
|
||||||
while (sweepKindIndex < FinalizePhaseLength[finalizePhase]) {
|
while (sweepKindIndex < FinalizePhaseLength[finalizePhase]) {
|
||||||
AllocKind kind = FinalizePhases[finalizePhase][sweepKindIndex];
|
AllocKind kind = FinalizePhases[finalizePhase][sweepKindIndex];
|
||||||
|
|
||||||
if (!zone->allocator.arenas.foregroundFinalize(&fop, kind, sliceBudget))
|
/* Set the number of things per arena for this AllocKind. */
|
||||||
|
size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(kind));
|
||||||
|
incrementalSweepList.setThingsPerArena(thingsPerArena);
|
||||||
|
|
||||||
|
if (!zone->allocator.arenas.foregroundFinalize(&fop, kind, sliceBudget,
|
||||||
|
incrementalSweepList))
|
||||||
return false; /* Yield to the mutator. */
|
return false; /* Yield to the mutator. */
|
||||||
|
|
||||||
|
/* Reset the slots of the sweep list that we used. */
|
||||||
|
incrementalSweepList.reset(thingsPerArena);
|
||||||
|
|
||||||
++sweepKindIndex;
|
++sweepKindIndex;
|
||||||
}
|
}
|
||||||
sweepKindIndex = 0;
|
sweepKindIndex = 0;
|
||||||
|
@ -5727,8 +5780,8 @@ ArenaLists::adoptArenas(JSRuntime *rt, ArenaLists *fromArenaLists)
|
||||||
#endif
|
#endif
|
||||||
ArenaList *fromList = &fromArenaLists->arenaLists[thingKind];
|
ArenaList *fromList = &fromArenaLists->arenaLists[thingKind];
|
||||||
ArenaList *toList = &arenaLists[thingKind];
|
ArenaList *toList = &arenaLists[thingKind];
|
||||||
fromList->deepCheck();
|
fromList->check();
|
||||||
toList->deepCheck();
|
toList->check();
|
||||||
ArenaHeader *next;
|
ArenaHeader *next;
|
||||||
for (ArenaHeader *fromHeader = fromList->head(); fromHeader; fromHeader = next) {
|
for (ArenaHeader *fromHeader = fromList->head(); fromHeader; fromHeader = next) {
|
||||||
// Copy fromHeader->next before releasing/reinserting.
|
// Copy fromHeader->next before releasing/reinserting.
|
||||||
|
@ -5744,7 +5797,7 @@ ArenaLists::adoptArenas(JSRuntime *rt, ArenaLists *fromArenaLists)
|
||||||
toList->insertAtCursor(fromHeader);
|
toList->insertAtCursor(fromHeader);
|
||||||
}
|
}
|
||||||
fromList->clear();
|
fromList->clear();
|
||||||
toList->deepCheck();
|
toList->check();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
172
js/src/jsgc.h
172
js/src/jsgc.h
|
@ -383,6 +383,90 @@ GetGCKindSlots(AllocKind thingKind, const Class *clasp)
|
||||||
// above locks is held.
|
// above locks is held.
|
||||||
class AutoMaybeStartBackgroundAllocation;
|
class AutoMaybeStartBackgroundAllocation;
|
||||||
|
|
||||||
|
// A single segment of a SortedArenaList. Each segment has a head and a tail,
|
||||||
|
// which track the start and end of a segment for O(1) append and concatenation.
|
||||||
|
struct SortedArenaListSegment
|
||||||
|
{
|
||||||
|
ArenaHeader *head;
|
||||||
|
ArenaHeader **tailp;
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
head = nullptr;
|
||||||
|
tailp = &head;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEmpty() const {
|
||||||
|
return tailp == &head;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appends |aheader| to this segment.
|
||||||
|
void append(ArenaHeader *aheader) {
|
||||||
|
JS_ASSERT(aheader);
|
||||||
|
JS_ASSERT_IF(head, head->getAllocKind() == aheader->getAllocKind());
|
||||||
|
*tailp = aheader;
|
||||||
|
tailp = &aheader->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Points the tail of this segment at |aheader|, which may be null.
|
||||||
|
void linkTo(ArenaHeader *aheader) {
|
||||||
|
*tailp = aheader;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A class that holds arenas in sorted order by appending arenas to specific
|
||||||
|
// segments. SortedArenaLists can be flattened to a regular ArenaList.
|
||||||
|
class SortedArenaList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// The minimum size, in bytes, of a GC thing.
|
||||||
|
static const size_t MinThingSize = 16;
|
||||||
|
|
||||||
|
static_assert(ArenaSize <= 4096, "When increasing the Arena size, please consider how"\
|
||||||
|
" this will affect the size of a SortedArenaList.");
|
||||||
|
|
||||||
|
static_assert(MinThingSize >= 16, "When decreasing the minimum thing size, please consider"\
|
||||||
|
" how this will affect the size of a SortedArenaList.");
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The maximum number of GC things that an arena can hold.
|
||||||
|
static const size_t MaxThingsPerArena = (ArenaSize - sizeof(ArenaHeader)) / MinThingSize;
|
||||||
|
|
||||||
|
size_t thingsPerArena_;
|
||||||
|
SortedArenaListSegment segments[MaxThingsPerArena + 1];
|
||||||
|
|
||||||
|
// Convenience functions to get the nth head and tail.
|
||||||
|
ArenaHeader *headAt(size_t n) { return segments[n].head; }
|
||||||
|
ArenaHeader **tailAt(size_t n) { return segments[n].tailp; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SortedArenaList(size_t thingsPerArena = MaxThingsPerArena) {
|
||||||
|
reset(thingsPerArena);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setThingsPerArena(size_t thingsPerArena) {
|
||||||
|
JS_ASSERT(thingsPerArena && thingsPerArena <= MaxThingsPerArena);
|
||||||
|
thingsPerArena_ = thingsPerArena;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resets the first |thingsPerArena| segments of this list for further use.
|
||||||
|
void reset(size_t thingsPerArena = MaxThingsPerArena) {
|
||||||
|
setThingsPerArena(thingsPerArena);
|
||||||
|
// Initialize the segments.
|
||||||
|
for (size_t i = 0; i <= thingsPerArena; ++i)
|
||||||
|
segments[i].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inserts a header, which has room for |nfree| more things, in its segment.
|
||||||
|
void insertAt(ArenaHeader *aheader, size_t nfree) {
|
||||||
|
JS_ASSERT(nfree <= thingsPerArena_);
|
||||||
|
segments[nfree].append(aheader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flattens the SortedArenaList into a regular ArenaList. Although we don't
|
||||||
|
// expect to do this more than once, note this operation is not destructive.
|
||||||
|
ArenaList toArenaList();
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Arena lists have a head and a cursor. The cursor conceptually lies on arena
|
* Arena lists have a head and a cursor. The cursor conceptually lies on arena
|
||||||
* boundaries, i.e. before the first arena, between two arenas, or after the
|
* boundaries, i.e. before the first arena, between two arenas, or after the
|
||||||
|
@ -425,11 +509,33 @@ class ArenaList {
|
||||||
ArenaHeader *head_;
|
ArenaHeader *head_;
|
||||||
ArenaHeader **cursorp_;
|
ArenaHeader **cursorp_;
|
||||||
|
|
||||||
|
void copy(const ArenaList &other) {
|
||||||
|
other.check();
|
||||||
|
head_ = other.head_;
|
||||||
|
cursorp_ = other.isCursorAtHead() ? &head_ : other.cursorp_;
|
||||||
|
check();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ArenaList() {
|
ArenaList() {
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArenaList(const ArenaList &other) {
|
||||||
|
copy(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaList &operator=(const ArenaList &other) {
|
||||||
|
copy(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaList(const SortedArenaListSegment &segment) {
|
||||||
|
head_ = segment.head;
|
||||||
|
cursorp_ = segment.isEmpty() ? &head_ : segment.tailp;
|
||||||
|
check();
|
||||||
|
}
|
||||||
|
|
||||||
// This does checking just of |head_| and |cursorp_|.
|
// This does checking just of |head_| and |cursorp_|.
|
||||||
void check() const {
|
void check() const {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -442,28 +548,6 @@ class ArenaList {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// This does checking involving all the arenas in the list.
|
|
||||||
void deepCheck() const {
|
|
||||||
#ifdef DEBUG
|
|
||||||
check();
|
|
||||||
// All full arenas must precede all non-full arenas.
|
|
||||||
//
|
|
||||||
// XXX: this is currently commented out because it fails moderately
|
|
||||||
// often. I'm not sure if this is because (a) it's not true that all
|
|
||||||
// full arenas must precede all non-full arenas, or (b) we have some
|
|
||||||
// defective list-handling code.
|
|
||||||
//
|
|
||||||
// bool havePassedFullArenas = false;
|
|
||||||
// for (ArenaHeader *aheader = head_; aheader; aheader = aheader->next) {
|
|
||||||
// if (havePassedFullArenas) {
|
|
||||||
// JS_ASSERT(aheader->hasFreeThings());
|
|
||||||
// } else if (aheader->hasFreeThings()) {
|
|
||||||
// havePassedFullArenas = true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
head_ = nullptr;
|
head_ = nullptr;
|
||||||
cursorp_ = &head_;
|
cursorp_ = &head_;
|
||||||
|
@ -481,6 +565,11 @@ class ArenaList {
|
||||||
return head_;
|
return head_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isCursorAtHead() const {
|
||||||
|
check();
|
||||||
|
return cursorp_ == &head_;
|
||||||
|
}
|
||||||
|
|
||||||
bool isCursorAtEnd() const {
|
bool isCursorAtEnd() const {
|
||||||
check();
|
check();
|
||||||
return !*cursorp_;
|
return !*cursorp_;
|
||||||
|
@ -515,33 +604,19 @@ class ArenaList {
|
||||||
check();
|
check();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This inserts |a| at the start of the list, and doesn't change the
|
// This inserts |other|, which must be full, at the cursor of |this|.
|
||||||
// cursor.
|
ArenaList &insertListWithCursorAtEnd(const ArenaList &other) {
|
||||||
void insertAtStart(ArenaHeader *a) {
|
|
||||||
check();
|
check();
|
||||||
a->next = head_;
|
other.check();
|
||||||
if (isEmpty())
|
JS_ASSERT(other.isCursorAtEnd());
|
||||||
cursorp_ = &a->next; // The cursor remains null.
|
if (other.isCursorAtHead())
|
||||||
head_ = a;
|
return *this;
|
||||||
|
// Insert the full arenas of |other| after those of |this|.
|
||||||
|
*other.cursorp_ = *cursorp_;
|
||||||
|
*cursorp_ = other.head_;
|
||||||
|
cursorp_ = other.cursorp_;
|
||||||
check();
|
check();
|
||||||
}
|
return *this;
|
||||||
|
|
||||||
// Appends |list|. |this|'s cursor must be at the end.
|
|
||||||
void appendToListWithCursorAtEnd(ArenaList &other) {
|
|
||||||
JS_ASSERT(isCursorAtEnd());
|
|
||||||
deepCheck();
|
|
||||||
other.deepCheck();
|
|
||||||
if (!other.isEmpty()) {
|
|
||||||
// Because |this|'s cursor is at the end, |cursorp_| points to the
|
|
||||||
// list-ending null. So this assignment appends |other| to |this|.
|
|
||||||
*cursorp_ = other.head_;
|
|
||||||
|
|
||||||
// If |other|'s cursor isn't at the start of the list, then update
|
|
||||||
// |this|'s cursor accordingly.
|
|
||||||
if (other.cursorp_ != &other.head_)
|
|
||||||
cursorp_ = other.cursorp_;
|
|
||||||
}
|
|
||||||
deepCheck();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -800,7 +875,8 @@ class ArenaLists
|
||||||
void queueScriptsForSweep(FreeOp *fop);
|
void queueScriptsForSweep(FreeOp *fop);
|
||||||
void queueJitCodeForSweep(FreeOp *fop);
|
void queueJitCodeForSweep(FreeOp *fop);
|
||||||
|
|
||||||
bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget);
|
bool foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget,
|
||||||
|
SortedArenaList &sweepList);
|
||||||
static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgroundThread);
|
static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, bool onBackgroundThread);
|
||||||
|
|
||||||
void wipeDuringParallelExecution(JSRuntime *rt);
|
void wipeDuringParallelExecution(JSRuntime *rt);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче