mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-24 06:20:43 -08:00
Clang 10 turns on -Wimplicit-int-float-conversion, and on 64-bit platforms this issues a warning that implicit conversions to double from Size and unsigned long "may lose precision". This commit adds casts to (double) for all such conversions. The loss of precision is either impossible in practice (because a double can represent all integers up to 2**53, which is about 9 petabytes, well beyond the addressing capabilities for current CPUs), or else acceptable, because we are accumulating an approximate quantity like "collection work" or "fill size" (that has to cope with loss of precision due in any case), or computing a threshold like the spare commit limit where it is acceptable for it to be somewhat approximate.
930 lines
23 KiB
C
930 lines
23 KiB
C
/* locus.c: LOCUS MANAGER
|
|
*
|
|
* $Id$
|
|
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
|
*
|
|
* DESIGN
|
|
*
|
|
* <design/arenavm> and <design/locus> for basic locus stuff.
|
|
* <design/trace> for chains. <design/strategy> for the
|
|
* collection strategy.
|
|
*/
|
|
|
|
#include "locus.h"
|
|
#include "ring.h"
|
|
#include "mpm.h"
|
|
#include "mpstd.h"
|
|
#include <float.h> /* for DBL_MAX */
|
|
|
|
SRCID(locus, "$Id$");
|
|
|
|
|
|
/* LocusPrefCheck -- check the consistency of a locus preference */
|
|
|
|
Bool LocusPrefCheck(LocusPref pref)
|
|
{
|
|
CHECKS(LocusPref, pref);
|
|
CHECKL(BoolCheck(pref->high));
|
|
/* zones can't be checked because it's arbitrary. */
|
|
/* avoid can't be checked because it's arbitrary. */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* LocusPrefDefault -- return a locus preference representing the defaults */
|
|
|
|
static LocusPrefStruct locusPrefDefault = LocusPrefDEFAULT;
|
|
|
|
LocusPref LocusPrefDefault(void)
|
|
{
|
|
return &locusPrefDefault;
|
|
}
|
|
|
|
/* LocusPrefInit -- initialise a locus preference to the defaults */
|
|
|
|
void LocusPrefInit(LocusPref pref)
|
|
{
|
|
(void)mps_lib_memcpy(pref, &locusPrefDefault, sizeof(LocusPrefStruct));
|
|
}
|
|
|
|
|
|
/* LocusPrefExpress -- express a locus preference */
|
|
|
|
void LocusPrefExpress(LocusPref pref, LocusPrefKind kind, void *p)
|
|
{
|
|
AVERT(LocusPref, pref);
|
|
AVER(pref != &locusPrefDefault);
|
|
|
|
switch(kind) {
|
|
case LocusPrefHIGH:
|
|
AVER(p == NULL);
|
|
pref->high = TRUE;
|
|
break;
|
|
|
|
case LocusPrefLOW:
|
|
AVER(p == NULL);
|
|
pref->high = FALSE;
|
|
break;
|
|
|
|
case LocusPrefZONESET:
|
|
AVER(p != NULL);
|
|
pref->zones = *(ZoneSet *)p;
|
|
break;
|
|
|
|
default:
|
|
/* Unknown kinds are ignored for binary compatibility. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* LocusPrefDescribe -- describe a locus preference */
|
|
|
|
Res LocusPrefDescribe(LocusPref pref, mps_lib_FILE *stream, Count depth)
|
|
{
|
|
Res res;
|
|
|
|
if (!TESTT(LocusPref, pref))
|
|
return ResFAIL;
|
|
if (stream == NULL)
|
|
return ResFAIL;
|
|
|
|
res = WriteF(stream, depth,
|
|
"LocusPref $P {\n", (WriteFP)pref,
|
|
" high $S\n", WriteFYesNo(pref->high),
|
|
" zones $B\n", (WriteFB)pref->zones,
|
|
" avoid $B\n", (WriteFB)pref->avoid,
|
|
"} LocusPref $P\n", (WriteFP)pref,
|
|
NULL);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* GenDescCheck -- check a GenDesc */
|
|
|
|
ATTRIBUTE_UNUSED
|
|
Bool GenDescCheck(GenDesc gen)
|
|
{
|
|
CHECKS(GenDesc, gen);
|
|
/* nothing to check for zones */
|
|
CHECKL(gen->capacity > 0);
|
|
CHECKL(gen->mortality >= 0.0);
|
|
CHECKL(gen->mortality <= 1.0);
|
|
CHECKD_NOSIG(Ring, &gen->locusRing);
|
|
CHECKD_NOSIG(Ring, &gen->segRing);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* GenParamCheck -- check consistency of generation parameters */
|
|
|
|
ATTRIBUTE_UNUSED
|
|
static Bool GenParamCheck(GenParamStruct *params)
|
|
{
|
|
CHECKL(params != NULL);
|
|
CHECKL(params->capacity > 0);
|
|
CHECKL(params->capacity <= SizeMAX / 1024);
|
|
CHECKL(params->mortality >= 0.0);
|
|
CHECKL(params->mortality <= 1.0);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* GenDescInit -- initialize a generation in a chain */
|
|
|
|
static void GenDescInit(Arena arena, GenDesc gen, GenParamStruct *params)
|
|
{
|
|
TraceId ti;
|
|
|
|
AVER(arena != NULL); /* might not be initialized yet. */
|
|
AVER(gen != NULL);
|
|
AVER(GenParamCheck(params));
|
|
|
|
gen->serial = arena->genSerial;
|
|
++ arena->genSerial;
|
|
gen->zones = ZoneSetEMPTY;
|
|
gen->capacity = params->capacity * 1024;
|
|
gen->mortality = params->mortality;
|
|
RingInit(&gen->locusRing);
|
|
RingInit(&gen->segRing);
|
|
gen->activeTraces = TraceSetEMPTY;
|
|
for (ti = 0; ti < TraceLIMIT; ++ti)
|
|
RingInit(&gen->trace[ti].traceRing);
|
|
gen->sig = GenDescSig;
|
|
AVERT(GenDesc, gen);
|
|
EVENT5(GenInit, arena, gen, gen->serial, gen->capacity, gen->mortality);
|
|
}
|
|
|
|
|
|
/* GenDescFinish -- finish a generation in a chain */
|
|
|
|
static void GenDescFinish(Arena arena, GenDesc gen)
|
|
{
|
|
TraceId ti;
|
|
|
|
AVER(arena != NULL); /* might be being finished */
|
|
AVERT(GenDesc, gen);
|
|
|
|
EVENT3(GenFinish, arena, gen, gen->serial);
|
|
gen->sig = SigInvalid;
|
|
RingFinish(&gen->locusRing);
|
|
RingFinish(&gen->segRing);
|
|
AVER(gen->activeTraces == TraceSetEMPTY); /* <design/check/#.common> */
|
|
for (ti = 0; ti < TraceLIMIT; ++ti)
|
|
RingFinish(&gen->trace[ti].traceRing);
|
|
}
|
|
|
|
|
|
/* GenDescNewSize -- return effective size of generation */
|
|
|
|
Size GenDescNewSize(GenDesc gen)
|
|
{
|
|
Size size = 0;
|
|
Ring node, nextNode;
|
|
|
|
AVERT(GenDesc, gen);
|
|
|
|
RING_FOR(node, &gen->locusRing, nextNode) {
|
|
PoolGen pgen = RING_ELT(PoolGen, genRing, node);
|
|
AVERT(PoolGen, pgen);
|
|
size += pgen->newSize;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
|
|
/* genDescTraceStart -- notify generation of start of a trace */
|
|
|
|
void GenDescStartTrace(GenDesc gen, Trace trace)
|
|
{
|
|
GenTrace genTrace;
|
|
|
|
AVERT(GenDesc, gen);
|
|
AVERT(Trace, trace);
|
|
|
|
AVER(!TraceSetIsMember(gen->activeTraces, trace));
|
|
gen->activeTraces = TraceSetAdd(gen->activeTraces, trace);
|
|
genTrace = &gen->trace[trace->ti];
|
|
AVER(RingIsSingle(&genTrace->traceRing));
|
|
RingAppend(&trace->genRing, &genTrace->traceRing);
|
|
genTrace->condemned = 0;
|
|
genTrace->forwarded = 0;
|
|
genTrace->preservedInPlace = 0;
|
|
}
|
|
|
|
|
|
/* genDescEndTrace -- notify generation of end of a trace */
|
|
|
|
void GenDescEndTrace(GenDesc gen, Trace trace)
|
|
{
|
|
GenTrace genTrace;
|
|
Size survived;
|
|
|
|
AVERT(GenDesc, gen);
|
|
AVERT(Trace, trace);
|
|
|
|
AVER(TraceSetIsMember(gen->activeTraces, trace));
|
|
gen->activeTraces = TraceSetDel(gen->activeTraces, trace);
|
|
genTrace = &gen->trace[trace->ti];
|
|
RingRemove(&genTrace->traceRing);
|
|
survived = genTrace->forwarded + genTrace->preservedInPlace;
|
|
AVER(survived <= genTrace->condemned);
|
|
|
|
if (genTrace->condemned > 0) {
|
|
double mortality = 1.0 - (double)survived / (double)genTrace->condemned;
|
|
double alpha = LocusMortalityALPHA;
|
|
gen->mortality = gen->mortality * (1 - alpha) + mortality * alpha;
|
|
EVENT8(TraceEndGen, trace->arena, trace, gen, genTrace->condemned,
|
|
genTrace->forwarded, genTrace->preservedInPlace, mortality,
|
|
gen->mortality);
|
|
}
|
|
}
|
|
|
|
|
|
/* GenDescCondemned -- memory in a generation was condemned for a trace */
|
|
|
|
void GenDescCondemned(GenDesc gen, Trace trace, Size size)
|
|
{
|
|
GenTrace genTrace;
|
|
|
|
AVERT(GenDesc, gen);
|
|
AVERT(Trace, trace);
|
|
|
|
genTrace = &gen->trace[trace->ti];
|
|
genTrace->condemned += size;
|
|
trace->condemned += size;
|
|
}
|
|
|
|
|
|
/* GenDescSurvived -- memory in a generation survived a trace */
|
|
|
|
void GenDescSurvived(GenDesc gen, Trace trace, Size forwarded,
|
|
Size preservedInPlace)
|
|
{
|
|
GenTrace genTrace;
|
|
|
|
AVERT(GenDesc, gen);
|
|
AVERT(Trace, trace);
|
|
|
|
genTrace = &gen->trace[trace->ti];
|
|
genTrace->forwarded += forwarded;
|
|
genTrace->preservedInPlace += preservedInPlace;
|
|
trace->forwardedSize += forwarded;
|
|
trace->preservedInPlaceSize += preservedInPlace;
|
|
}
|
|
|
|
|
|
/* GenDescTotalSize -- return total size of generation */
|
|
|
|
Size GenDescTotalSize(GenDesc gen)
|
|
{
|
|
Size size = 0;
|
|
Ring node, nextNode;
|
|
|
|
AVERT(GenDesc, gen);
|
|
|
|
RING_FOR(node, &gen->locusRing, nextNode) {
|
|
PoolGen pgen = RING_ELT(PoolGen, genRing, node);
|
|
AVERT(PoolGen, pgen);
|
|
size += pgen->totalSize;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
|
|
/* GenDescDescribe -- describe a generation in a chain */
|
|
|
|
Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth)
|
|
{
|
|
Index i;
|
|
Res res;
|
|
Ring node, nextNode;
|
|
|
|
if (!TESTT(GenDesc, gen))
|
|
return ResFAIL;
|
|
if (stream == NULL)
|
|
return ResFAIL;
|
|
|
|
res = WriteF(stream, depth,
|
|
"GenDesc $P {\n", (WriteFP)gen,
|
|
" zones $B\n", (WriteFB)gen->zones,
|
|
" capacity $U\n", (WriteFW)gen->capacity,
|
|
" mortality $D\n", (WriteFD)gen->mortality,
|
|
" activeTraces $B\n", (WriteFB)gen->activeTraces,
|
|
NULL);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
for (i = 0; i < NELEMS(gen->trace); ++i) {
|
|
GenTrace genTrace = &gen->trace[i];
|
|
res = WriteF(stream, depth + 2,
|
|
"trace $U {\n", (WriteFW)i,
|
|
" condemned $U\n", (WriteFW)genTrace->condemned,
|
|
" forwarded $U\n", (WriteFW)genTrace->forwarded,
|
|
" preservedInPlace $U\n", (WriteFW)genTrace->preservedInPlace,
|
|
"}\n", NULL);
|
|
if (res != ResOK)
|
|
return res;
|
|
}
|
|
|
|
RING_FOR(node, &gen->locusRing, nextNode) {
|
|
PoolGen pgen = RING_ELT(PoolGen, genRing, node);
|
|
res = PoolGenDescribe(pgen, stream, depth + 2);
|
|
if (res != ResOK)
|
|
return res;
|
|
}
|
|
|
|
res = WriteF(stream, depth, "} GenDesc $P\n", (WriteFP)gen, NULL);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* ChainInit -- initialize a generation chain */
|
|
|
|
static void ChainInit(ChainStruct *chain, Arena arena, GenDescStruct *gens,
|
|
Count genCount)
|
|
{
|
|
AVER(chain != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(gens != NULL);
|
|
AVER(genCount > 0);
|
|
|
|
chain->arena = arena;
|
|
RingInit(&chain->chainRing);
|
|
chain->genCount = genCount;
|
|
chain->gens = gens;
|
|
chain->sig = ChainSig;
|
|
|
|
AVERT(Chain, chain);
|
|
|
|
RingAppend(&arena->chainRing, &chain->chainRing);
|
|
}
|
|
|
|
|
|
/* ChainCreate -- create a generation chain */
|
|
|
|
Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount,
|
|
GenParamStruct *params)
|
|
{
|
|
size_t i;
|
|
Size size;
|
|
Chain chain;
|
|
GenDescStruct *gens;
|
|
Res res;
|
|
void *p;
|
|
|
|
AVER(chainReturn != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(genCount > 0);
|
|
AVER(params != NULL);
|
|
|
|
size = sizeof(ChainStruct) + genCount * sizeof(GenDescStruct);
|
|
res = ControlAlloc(&p, arena, size);
|
|
if (res != ResOK)
|
|
return res;
|
|
chain = p;
|
|
gens = PointerAdd(p, sizeof(ChainStruct));
|
|
|
|
for (i = 0; i < genCount; ++i)
|
|
GenDescInit(arena, &gens[i], ¶ms[i]);
|
|
ChainInit(chain, arena, gens, genCount);
|
|
|
|
*chainReturn = chain;
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* ChainCheck -- check a chain */
|
|
|
|
Bool ChainCheck(Chain chain)
|
|
{
|
|
size_t i;
|
|
|
|
CHECKS(Chain, chain);
|
|
CHECKU(Arena, chain->arena);
|
|
CHECKD_NOSIG(Ring, &chain->chainRing);
|
|
CHECKL(chain->genCount > 0);
|
|
for (i = 0; i < chain->genCount; ++i) {
|
|
CHECKD(GenDesc, &chain->gens[i]);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* ChainDestroy -- destroy a chain */
|
|
|
|
void ChainDestroy(Chain chain)
|
|
{
|
|
Arena arena;
|
|
Size size;
|
|
size_t genCount;
|
|
size_t i;
|
|
|
|
AVERT(Chain, chain);
|
|
|
|
arena = chain->arena;
|
|
genCount = chain->genCount;
|
|
RingRemove(&chain->chainRing);
|
|
chain->sig = SigInvalid;
|
|
for (i = 0; i < genCount; ++i)
|
|
GenDescFinish(arena, &chain->gens[i]);
|
|
|
|
RingFinish(&chain->chainRing);
|
|
|
|
size = sizeof(ChainStruct) + genCount * sizeof(GenDescStruct);
|
|
ControlFree(arena, chain, size);
|
|
}
|
|
|
|
|
|
/* ChainGens -- return the number of generation in chain */
|
|
|
|
size_t ChainGens(Chain chain)
|
|
{
|
|
AVERT(Chain, chain);
|
|
return chain->genCount;
|
|
}
|
|
|
|
|
|
/* ChainGen -- return a generation in a chain, or the arena top generation */
|
|
|
|
GenDesc ChainGen(Chain chain, Index gen)
|
|
{
|
|
AVERT(Chain, chain);
|
|
AVER(gen <= chain->genCount);
|
|
|
|
if (gen < chain->genCount)
|
|
return &chain->gens[gen];
|
|
else
|
|
return &chain->arena->topGen;
|
|
}
|
|
|
|
|
|
/* ChainDeferral -- time until next ephemeral GC for this chain */
|
|
|
|
double ChainDeferral(Chain chain)
|
|
{
|
|
double time = DBL_MAX;
|
|
size_t i;
|
|
|
|
AVERT(Chain, chain);
|
|
|
|
for (i = 0; i < chain->genCount; ++i) {
|
|
double genTime;
|
|
GenDesc gen = &chain->gens[i];
|
|
if (gen->activeTraces != TraceSetEMPTY)
|
|
return DBL_MAX;
|
|
genTime = (double)gen->capacity - (double)GenDescNewSize(&chain->gens[i]);
|
|
if (genTime < time)
|
|
time = genTime;
|
|
}
|
|
|
|
return time;
|
|
}
|
|
|
|
|
|
/* ChainDescribe -- describe a chain */
|
|
|
|
Res ChainDescribe(Chain chain, mps_lib_FILE *stream, Count depth)
|
|
{
|
|
Res res;
|
|
size_t i;
|
|
|
|
if (!TESTT(Chain, chain))
|
|
return ResFAIL;
|
|
if (stream == NULL)
|
|
return ResFAIL;
|
|
|
|
res = WriteF(stream, depth,
|
|
"Chain $P {\n", (WriteFP)chain,
|
|
" arena $P\n", (WriteFP)chain->arena,
|
|
NULL);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
for (i = 0; i < chain->genCount; ++i) {
|
|
res = GenDescDescribe(&chain->gens[i], stream, depth + 2);
|
|
if (res != ResOK)
|
|
return res;
|
|
}
|
|
|
|
res = WriteF(stream, depth,
|
|
"} Chain $P\n", (WriteFP)chain,
|
|
NULL);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* PoolGenInit -- initialize a PoolGen */
|
|
|
|
Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool)
|
|
{
|
|
/* Can't check pgen, because it's not been initialized. */
|
|
AVER(pgen != NULL);
|
|
AVERT(GenDesc, gen);
|
|
AVERT(Pool, pool);
|
|
AVER(PoolHasAttr(pool, AttrGC));
|
|
|
|
pgen->pool = pool;
|
|
pgen->gen = gen;
|
|
RingInit(&pgen->genRing);
|
|
pgen->segs = 0;
|
|
pgen->totalSize = 0;
|
|
pgen->freeSize = 0;
|
|
pgen->bufferedSize = 0;
|
|
pgen->newSize = 0;
|
|
pgen->oldSize = 0;
|
|
pgen->newDeferredSize = 0;
|
|
pgen->oldDeferredSize = 0;
|
|
pgen->sig = PoolGenSig;
|
|
AVERT(PoolGen, pgen);
|
|
|
|
RingAppend(&gen->locusRing, &pgen->genRing);
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* PoolGenFinish -- finish a PoolGen */
|
|
|
|
void PoolGenFinish(PoolGen pgen)
|
|
{
|
|
AVERT(PoolGen, pgen);
|
|
AVER(pgen->segs == 0);
|
|
AVER(pgen->totalSize == 0);
|
|
AVER(pgen->freeSize == 0);
|
|
AVER(pgen->bufferedSize == 0);
|
|
AVER(pgen->newSize == 0);
|
|
AVER(pgen->newDeferredSize == 0);
|
|
AVER(pgen->oldSize == 0);
|
|
AVER(pgen->oldDeferredSize == 0);
|
|
|
|
pgen->sig = SigInvalid;
|
|
RingRemove(&pgen->genRing);
|
|
}
|
|
|
|
|
|
/* PoolGenCheck -- check a PoolGen */
|
|
|
|
Bool PoolGenCheck(PoolGen pgen)
|
|
{
|
|
CHECKS(PoolGen, pgen);
|
|
/* nothing to check about serial */
|
|
CHECKU(Pool, pgen->pool);
|
|
CHECKU(GenDesc, pgen->gen);
|
|
CHECKD_NOSIG(Ring, &pgen->genRing);
|
|
CHECKL((pgen->totalSize == 0) == (pgen->segs == 0));
|
|
CHECKL(pgen->totalSize >= pgen->segs * ArenaGrainSize(PoolArena(pgen->pool)));
|
|
CHECKL(pgen->totalSize == pgen->freeSize + pgen->bufferedSize
|
|
+ pgen->newSize + pgen->oldSize
|
|
+ pgen->newDeferredSize + pgen->oldDeferredSize);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* PoolGenAccountForAlloc -- accounting for allocation of a segment */
|
|
|
|
static void PoolGenAccountForAlloc(PoolGen pgen, Size size)
|
|
{
|
|
pgen->totalSize += size;
|
|
++ pgen->segs;
|
|
pgen->freeSize += size;
|
|
}
|
|
|
|
|
|
/* PoolGenAlloc -- allocate a segment in a pool generation
|
|
*
|
|
* Allocate a segment belong to klass (which must be GCSegClass or a
|
|
* subclass), attach it to the generation, and update the accounting.
|
|
*/
|
|
|
|
Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass klass, Size size,
|
|
ArgList args)
|
|
{
|
|
LocusPrefStruct pref;
|
|
Res res;
|
|
Seg seg;
|
|
ZoneSet zones, moreZones;
|
|
Arena arena;
|
|
GenDesc gen;
|
|
|
|
AVER(segReturn != NULL);
|
|
AVERT(PoolGen, pgen);
|
|
AVERT(SegClass, klass);
|
|
AVER(IsSubclass(klass, GCSeg));
|
|
AVER(size > 0);
|
|
AVERT(ArgList, args);
|
|
|
|
arena = PoolArena(pgen->pool);
|
|
gen = pgen->gen;
|
|
zones = gen->zones;
|
|
|
|
LocusPrefInit(&pref);
|
|
pref.high = FALSE;
|
|
pref.zones = zones;
|
|
pref.avoid = ZoneSetBlacklist(arena);
|
|
res = SegAlloc(&seg, klass, &pref, size, pgen->pool, args);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
RingAppend(&gen->segRing, &SegGCSeg(seg)->genRing);
|
|
|
|
moreZones = ZoneSetUnion(zones, ZoneSetOfSeg(arena, seg));
|
|
gen->zones = moreZones;
|
|
|
|
if (!ZoneSetSuper(zones, moreZones)) {
|
|
/* Tracking the whole zoneset for each generation gives more
|
|
* understandable telemetry than just reporting the added
|
|
* zones. */
|
|
EVENT3(GenZoneSet, arena, gen, moreZones);
|
|
}
|
|
|
|
PoolGenAccountForAlloc(pgen, SegSize(seg));
|
|
|
|
*segReturn = seg;
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* PoolGenAccountForFill -- accounting for allocation within a segment
|
|
*
|
|
* Call this when the pool allocates memory to the client program via
|
|
* BufferFill.
|
|
*
|
|
* <design/strategy#.accounting.op.fill>
|
|
*/
|
|
|
|
void PoolGenAccountForFill(PoolGen pgen, Size size)
|
|
{
|
|
AVERT(PoolGen, pgen);
|
|
|
|
AVER(pgen->freeSize >= size);
|
|
pgen->freeSize -= size;
|
|
pgen->bufferedSize += size;
|
|
}
|
|
|
|
|
|
/* PoolGenAccountForEmpty -- accounting for emptying a buffer
|
|
*
|
|
* Call this when the client program returns memory to the pool via
|
|
* BufferEmpty. The deferred flag indicates whether the accounting of
|
|
* the used memory (for the purpose of scheduling collections) should
|
|
* be deferred until later.
|
|
*
|
|
* <design/strategy#.accounting.op.empty>
|
|
*/
|
|
|
|
void PoolGenAccountForEmpty(PoolGen pgen, Size used, Size unused, Bool deferred)
|
|
{
|
|
AVERT(PoolGen, pgen);
|
|
AVERT(Bool, deferred);
|
|
|
|
AVER(pgen->bufferedSize >= used + unused);
|
|
pgen->bufferedSize -= used + unused;
|
|
if (deferred) {
|
|
pgen->newDeferredSize += used;
|
|
} else {
|
|
pgen->newSize += used;
|
|
}
|
|
pgen->freeSize += unused;
|
|
}
|
|
|
|
|
|
/* PoolGenAccountForAge -- accounting for condemning
|
|
*
|
|
* Call this when memory is condemned via PoolWhiten, or when
|
|
* artificially ageing memory in PoolGenFree. The size parameter
|
|
* should be the amount of memory that is being condemned for the
|
|
* first time. The deferred flag is as for PoolGenAccountForEmpty.
|
|
*
|
|
* <design/strategy#.accounting.op.age>
|
|
*/
|
|
|
|
void PoolGenAccountForAge(PoolGen pgen, Size wasBuffered, Size wasNew,
|
|
Bool deferred)
|
|
{
|
|
AVERT(PoolGen, pgen);
|
|
AVERT(Bool, deferred);
|
|
|
|
AVER(pgen->bufferedSize >= wasBuffered);
|
|
pgen->bufferedSize -= wasBuffered;
|
|
if (deferred) {
|
|
AVER(pgen->newDeferredSize >= wasNew);
|
|
pgen->newDeferredSize -= wasNew;
|
|
pgen->oldDeferredSize += wasBuffered + wasNew;
|
|
} else {
|
|
AVER(pgen->newSize >= wasNew);
|
|
pgen->newSize -= wasNew;
|
|
pgen->oldSize += wasBuffered + wasNew;
|
|
}
|
|
}
|
|
|
|
|
|
/* PoolGenAccountForReclaim -- accounting for reclaiming
|
|
*
|
|
* Call this when reclaiming memory, passing the amount of memory that
|
|
* was reclaimed. The deferred flag is as for PoolGenAccountForEmpty.
|
|
*
|
|
* <design/strategy#.accounting.op.reclaim>
|
|
*/
|
|
|
|
void PoolGenAccountForReclaim(PoolGen pgen, Size reclaimed, Bool deferred)
|
|
{
|
|
AVERT(PoolGen, pgen);
|
|
AVERT(Bool, deferred);
|
|
|
|
if (deferred) {
|
|
AVER(pgen->oldDeferredSize >= reclaimed);
|
|
pgen->oldDeferredSize -= reclaimed;
|
|
} else {
|
|
AVER(pgen->oldSize >= reclaimed);
|
|
pgen->oldSize -= reclaimed;
|
|
}
|
|
pgen->freeSize += reclaimed;
|
|
}
|
|
|
|
|
|
/* PoolGenUndefer -- finish deferring accounting
|
|
*
|
|
* Call this when exiting ramp mode, passing the amount of old
|
|
* (condemned at least once) and new (never condemned) memory whose
|
|
* accounting was deferred (for example, during a ramp).
|
|
*
|
|
* <design/strategy#.accounting.op.undefer>
|
|
*/
|
|
|
|
void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize)
|
|
{
|
|
AVERT(PoolGen, pgen);
|
|
AVER(pgen->oldDeferredSize >= oldSize);
|
|
pgen->oldDeferredSize -= oldSize;
|
|
pgen->oldSize += oldSize;
|
|
AVER(pgen->newDeferredSize >= newSize);
|
|
pgen->newDeferredSize -= newSize;
|
|
pgen->newSize += newSize;
|
|
}
|
|
|
|
|
|
/* PoolGenAccountForSegSplit -- accounting for splitting a segment */
|
|
|
|
void PoolGenAccountForSegSplit(PoolGen pgen)
|
|
{
|
|
AVERT(PoolGen, pgen);
|
|
AVER(pgen->segs >= 1); /* must be at least one segment to split */
|
|
++ pgen->segs;
|
|
}
|
|
|
|
|
|
/* PoolGenAccountForSegMerge -- accounting for merging a segment */
|
|
|
|
void PoolGenAccountForSegMerge(PoolGen pgen)
|
|
{
|
|
AVERT(PoolGen, pgen);
|
|
AVER(pgen->segs >= 2); /* must be at least two segments to merge */
|
|
-- pgen->segs;
|
|
}
|
|
|
|
|
|
/* PoolGenAccountForFree -- accounting for the freeing of a segment */
|
|
|
|
static void PoolGenAccountForFree(PoolGen pgen, Size size,
|
|
Size oldSize, Size newSize,
|
|
Bool deferred)
|
|
{
|
|
/* Pretend to age and reclaim the contents of the segment to ensure
|
|
* that the entire segment is accounted as free. */
|
|
PoolGenAccountForAge(pgen, 0, newSize, deferred);
|
|
PoolGenAccountForReclaim(pgen, oldSize + newSize, deferred);
|
|
|
|
AVER(pgen->totalSize >= size);
|
|
pgen->totalSize -= size;
|
|
AVER(pgen->segs > 0);
|
|
-- pgen->segs;
|
|
AVER(pgen->freeSize >= size);
|
|
pgen->freeSize -= size;
|
|
}
|
|
|
|
|
|
/* PoolGenFree -- free a segment and update accounting
|
|
*
|
|
* Pass the amount of memory in the segment that is accounted as free,
|
|
* old, or new, respectively. The deferred flag is as for
|
|
* PoolGenAccountForEmpty.
|
|
*
|
|
* <design/strategy#.accounting.op.free>
|
|
*/
|
|
|
|
void PoolGenFree(PoolGen pgen, Seg seg, Size freeSize, Size oldSize,
|
|
Size newSize, Bool deferred)
|
|
{
|
|
Size size;
|
|
|
|
AVERT(PoolGen, pgen);
|
|
AVERT(Seg, seg);
|
|
|
|
size = SegSize(seg);
|
|
AVER(freeSize + oldSize + newSize == size);
|
|
|
|
PoolGenAccountForFree(pgen, size, oldSize, newSize, deferred);
|
|
|
|
RingRemove(&SegGCSeg(seg)->genRing);
|
|
|
|
SegFree(seg);
|
|
}
|
|
|
|
|
|
/* PoolGenDescribe -- describe a PoolGen */
|
|
|
|
Res PoolGenDescribe(PoolGen pgen, mps_lib_FILE *stream, Count depth)
|
|
{
|
|
Res res;
|
|
PoolClass poolClass;
|
|
|
|
if (!TESTT(PoolGen, pgen))
|
|
return ResPARAM;
|
|
if (stream == NULL)
|
|
return ResPARAM;
|
|
|
|
poolClass = ClassOfPoly(Pool, pgen->pool);
|
|
|
|
res = WriteF(stream, depth,
|
|
"PoolGen $P {\n", (WriteFP)pgen,
|
|
" pool $P ($U) \"$S\"\n",
|
|
(WriteFP)pgen->pool, (WriteFU)pgen->pool->serial,
|
|
(WriteFS)ClassName(poolClass),
|
|
" segs $U\n", (WriteFU)pgen->segs,
|
|
" totalSize $U\n", (WriteFU)pgen->totalSize,
|
|
" freeSize $U\n", (WriteFU)pgen->freeSize,
|
|
" bufferedSize $U\n", (WriteFU)pgen->bufferedSize,
|
|
" oldSize $U\n", (WriteFU)pgen->oldSize,
|
|
" oldDeferredSize $U\n", (WriteFU)pgen->oldDeferredSize,
|
|
" newSize $U\n", (WriteFU)pgen->newSize,
|
|
" newDeferredSize $U\n", (WriteFU)pgen->newDeferredSize,
|
|
"} PoolGen $P\n", (WriteFP)pgen,
|
|
NULL);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* LocusInit -- initialize the locus module */
|
|
|
|
void LocusInit(Arena arena)
|
|
{
|
|
GenParamStruct params;
|
|
|
|
AVER(arena != NULL); /* not initialized yet. */
|
|
|
|
params.capacity = 1; /* unused since top generation is not on any chain */
|
|
params.mortality = 0.5;
|
|
|
|
GenDescInit(arena, &arena->topGen, ¶ms);
|
|
EventLabelPointer(&arena->topGen, EventInternString("TopGen"));
|
|
}
|
|
|
|
|
|
/* LocusFinish -- finish the locus module */
|
|
|
|
void LocusFinish(Arena arena)
|
|
{
|
|
/* Can't check arena, because it's being finished. */
|
|
AVER(arena != NULL);
|
|
GenDescFinish(arena, &arena->topGen);
|
|
}
|
|
|
|
|
|
/* LocusCheck -- check the locus module */
|
|
|
|
Bool LocusCheck(Arena arena)
|
|
{
|
|
/* Can't check arena, because this is part of ArenaCheck. */
|
|
CHECKD(GenDesc, &arena->topGen);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* C. COPYRIGHT AND LICENSE
|
|
*
|
|
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|