1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-02 02:10:46 -08:00

Mvff no longer uses segments, but instead maintains the address ranges it has acquired from the arena in a cbs.

Copied from Perforce
 Change: 185574
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Gareth Rees 2014-04-15 17:52:14 +01:00
parent 2c47aa2d70
commit 4b49fc7d57
2 changed files with 143 additions and 158 deletions

View file

@ -6,8 +6,7 @@
*
* .purpose: This is a pool class for manually managed objects of
* variable size where address-ordered first fit is an appropriate
* policy. Provision is made to allocate in reverse. This pool
* can allocate across segment boundaries.
* policy. Provision is made to allocate in reverse.
*
* .design: <design/poolmvff>
*
@ -43,12 +42,11 @@ extern PoolClass PoolClassMVFF(void);
typedef struct MVFFStruct *MVFF;
typedef struct MVFFStruct { /* MVFF pool outer structure */
PoolStruct poolStruct; /* generic structure */
SegPref segPref; /* the preferences for segments */
Size extendBy; /* segment size to extend pool by */
Size minSegSize; /* minimum size of segment */
SegPref segPref; /* the preferences for allocation */
Size extendBy; /* size of range to extend pool by */
Size avgSize; /* client estimate of allocation size */
Size total; /* total bytes in pool */
CBSStruct cbsStruct; /* free list */
CBSStruct allocCBSStruct; /* allocated memory ranges */
CBSStruct freeCBSStruct; /* free list */
FreelistStruct flStruct; /* emergency free list */
FailoverStruct foStruct; /* fail-over mechanism */
Bool firstFit; /* as opposed to last fit */
@ -59,7 +57,8 @@ typedef struct MVFFStruct { /* MVFF pool outer structure */
#define Pool2MVFF(pool) PARENT(MVFFStruct, poolStruct, pool)
#define MVFF2Pool(mvff) (&((mvff)->poolStruct))
#define CBSOfMVFF(mvff) (&((mvff)->cbsStruct.landStruct))
#define AllocCBSOfMVFF(mvff) (&((mvff)->allocCBSStruct.landStruct))
#define FreeCBSOfMVFF(mvff) (&((mvff)->freeCBSStruct.landStruct))
#define FreelistOfMVFF(mvff) (&((mvff)->flStruct.landStruct))
#define FailoverOfMVFF(mvff) (&((mvff)->foStruct.landStruct))
@ -83,8 +82,8 @@ typedef MVFFDebugStruct *MVFFDebug;
/* MVFFInsert -- add given range to free lists
*
* Updates MVFF counters for additional free space. Returns maximally
* coalesced range containing given range. Does not attempt to free
* segments (see MVFFFreeSegs).
* coalesced range containing given range. Does not attempt to return
* memory to the arena (see MVFFFreeTracts).
*/
static Res MVFFInsert(Range rangeIO, MVFF mvff) {
AVERT(Range, rangeIO);
@ -94,89 +93,67 @@ static Res MVFFInsert(Range rangeIO, MVFF mvff) {
}
/* MVFFFreeSegs -- free segments from given range
/* MVFFFreeTracts -- free tracts from given range
*
* Given a free range, attempts to find entire segments within it, and
* returns them to the arena, updating total size counter.
* Given a free range, attempts to find entire tracts within it, and
* returns them to the arena.
*
* This is usually called immediately after MVFFInsert. It is not
* combined with MVFFInsert because the latter is also called when new
* segments are added under MVFFAlloc.
*/
static void MVFFFreeSegs(MVFF mvff, Range range)
static void MVFFFreeTracts(MVFF mvff, Range range)
{
Seg seg = NULL; /* suppress "may be used uninitialized" */
Pool pool;
Arena arena;
Bool b;
Addr segLimit; /* limit of the current segment when iterating */
Addr segBase; /* base of the current segment when iterating */
RangeStruct freeRange, oldRange;
Res res;
AVERT(MVFF, mvff);
AVERT(Range, range);
/* Could profitably AVER that the given range is free, */
/* but the CBS doesn't provide that facility. */
/* Could profitably AVER that the given range is free,
* but lands don't provide that facility. */
if (RangeSize(range) < mvff->minSegSize)
return; /* not large enough for entire segments */
if (RangeSize(range) < mvff->extendBy)
return; /* not large enough to be worth returning */
arena = PoolArena(MVFF2Pool(mvff));
b = SegOfAddr(&seg, arena, RangeBase(range));
AVER(b);
pool = MVFF2Pool(mvff);
arena = PoolArena(pool);
RangeInit(&freeRange, AddrAlignUp(RangeBase(range), ArenaAlign(arena)),
AddrAlignDown(RangeLimit(range), ArenaAlign(arena)));
if (RangeIsEmpty(&freeRange))
return;
segBase = SegBase(seg);
segLimit = SegLimit(seg);
/* Delete range from allocated list. */
res = LandDelete(&oldRange, AllocCBSOfMVFF(mvff), &freeRange);
if (res != ResOK)
/* Can't delete the range, so postpone returning it to the arena. */
return;
ArenaFree(RangeBase(&freeRange), RangeSize(&freeRange), pool);
while(segLimit <= RangeLimit(range)) { /* segment ends in range */
if (segBase >= RangeBase(range)) { /* segment starts in range */
RangeStruct delRange, oldRange;
RangeInit(&delRange, segBase, segLimit);
res = LandDelete(&oldRange, FailoverOfMVFF(mvff), &delRange);
AVER(res == ResOK);
AVER(RangesNest(&oldRange, &delRange));
/* Can't free the segment earlier, because if it was on the
* Freelist rather than the CBS then it likely contains data
* that needs to be read in order to update the Freelist. */
SegFree(seg);
AVER(mvff->total >= RangeSize(&delRange));
mvff->total -= RangeSize(&delRange);
}
/* Avoid calling SegNext if the next segment would fail */
/* the loop test, mainly because there might not be a */
/* next segment. */
if (segLimit == RangeLimit(range)) /* segment ends at end of range */
break;
b = SegFindAboveAddr(&seg, arena, segBase);
AVER(b);
segBase = SegBase(seg);
segLimit = SegLimit(seg);
}
return;
/* ... and from the free list too. */
res = LandDelete(&oldRange, FailoverOfMVFF(mvff), &freeRange);
AVER(res == ResOK);
}
/* MVFFAddSeg -- Allocates a new segment from the arena
/* MVFFAddRange -- allocate a new range from the arena
*
* Allocates a new segment from the arena (with the given
* Allocate a new range from the arena (with the given
* withReservoirPermit flag) of at least the specified size. The
* specified size should be pool-aligned. Adds it to the free lists.
* specified size should be pool-aligned. Add it to the allocated and
* free lists.
*/
static Res MVFFAddSeg(Seg *segReturn,
MVFF mvff, Size size, Bool withReservoirPermit)
static Res MVFFAddRange(Range rangeReturn, MVFF mvff, Size size,
Bool withReservoirPermit)
{
Pool pool;
Arena arena;
Size segSize;
Seg seg;
Res res;
Size allocSize;
Align align;
RangeStruct range;
RangeStruct range, coalescedRange;
Addr base;
Res res;
AVERT(MVFF, mvff);
AVER(size > 0);
@ -191,36 +168,38 @@ static Res MVFFAddSeg(Seg *segReturn,
/* Use extendBy unless it's too small (see */
/* <design/poolmvff/#design.seg-size>). */
if (size <= mvff->extendBy)
segSize = mvff->extendBy;
allocSize = mvff->extendBy;
else
segSize = size;
allocSize = size;
segSize = SizeAlignUp(segSize, align);
allocSize = SizeAlignUp(allocSize, align);
res = SegAlloc(&seg, SegClassGet(), mvff->segPref, segSize, pool,
withReservoirPermit, argsNone);
res = ArenaAlloc(&base, mvff->segPref, allocSize, pool, withReservoirPermit);
if (res != ResOK) {
/* try again for a seg just large enough for object */
/* try again with a range just large enough for object */
/* see <design/poolmvff/#design.seg-fail> */
segSize = SizeAlignUp(size, align);
res = SegAlloc(&seg, SegClassGet(), mvff->segPref, segSize, pool,
withReservoirPermit, argsNone);
if (res != ResOK) {
allocSize = SizeAlignUp(size, align);
res = ArenaAlloc(&base, mvff->segPref, allocSize, pool,
withReservoirPermit);
if (res != ResOK)
return res;
}
}
mvff->total += segSize;
RangeInitSize(&range, SegBase(seg), segSize);
RangeInitSize(&range, base, allocSize);
res = LandInsert(&coalescedRange, AllocCBSOfMVFF(mvff), &range);
if (res != ResOK) {
/* Can't record this memory, so return it to the arena and fail. */
ArenaFree(base, allocSize, pool);
return res;
}
DebugPoolFreeSplat(pool, RangeBase(&range), RangeLimit(&range));
res = MVFFInsert(&range, mvff);
AVER(res == ResOK);
AVER(RangeBase(&range) <= SegBase(seg));
if (mvff->minSegSize > segSize) mvff->minSegSize = segSize;
/* Don't call MVFFFreeSegs; that would be silly. */
/* Don't call MVFFFreeTracts; that would be silly. */
*segReturn = seg;
RangeCopy(rangeReturn, &range);
return ResOK;
}
@ -278,21 +257,16 @@ static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size,
foundBlock = MVFFFindFree(&range, mvff, size);
if (!foundBlock) {
Seg seg;
RangeStruct addRange;
res = MVFFAddSeg(&seg, mvff, size, withReservoirPermit);
res = MVFFAddRange(&addRange, mvff, size, withReservoirPermit);
if (res != ResOK)
return res;
foundBlock = MVFFFindFree(&range, mvff, size);
/* We know that the found range must intersect the new segment. */
/* In particular, it doesn't necessarily lie entirely within it. */
/* The next two AVERs test for intersection of two intervals. */
AVER(RangeBase(&range) < SegLimit(seg));
AVER(SegBase(seg) < RangeLimit(&range));
/* We also know that the found range is no larger than the segment. */
AVER(SegSize(seg) >= RangeSize(&range));
AVER(foundBlock && RangesOverlap(&range, &addRange));
}
AVER(foundBlock);
AVER(RangeSize(&range) == size);
@ -324,15 +298,14 @@ static void MVFFFree(Pool pool, Addr old, Size size)
res = MVFFInsert(&range, mvff);
AVER(res == ResOK);
if (res == ResOK)
MVFFFreeSegs(mvff, &range);
return;
MVFFFreeTracts(mvff, &range);
}
/* MVFFBufferFill -- Fill the buffer
*
* Fill it with the largest block we can find.
* Fill it with the largest block we can find. This is worst-fit
* allocation policy; see <design/poolmvff/#over.buffer>.
*/
static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn,
Pool pool, Buffer buffer, Size size,
@ -340,9 +313,8 @@ static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn,
{
Res res;
MVFF mvff;
RangeStruct range, oldRange;
RangeStruct range, oldRange, newRange;
Bool found;
Seg seg = NULL;
AVER(baseReturn != NULL);
AVER(limitReturn != NULL);
AVERT(Pool, pool);
@ -353,13 +325,16 @@ static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn,
AVER(SizeIsAligned(size, PoolAlignment(pool)));
AVERT(Bool, withReservoirPermit);
found = LandFindLargest(&range, &oldRange, FailoverOfMVFF(mvff), size, FindDeleteENTIRE);
found = LandFindLargest(&range, &oldRange, FailoverOfMVFF(mvff), size,
FindDeleteENTIRE);
if (!found) {
/* Add a new segment to the free lists and try again. */
res = MVFFAddSeg(&seg, mvff, size, withReservoirPermit);
/* Add a new range to the free lists and try again. */
res = MVFFAddRange(&newRange, mvff, size, withReservoirPermit);
if (res != ResOK)
return res;
found = LandFindLargest(&range, &oldRange, FailoverOfMVFF(mvff), size, FindDeleteENTIRE);
found = LandFindLargest(&range, &oldRange, FailoverOfMVFF(mvff), size,
FindDeleteENTIRE);
AVER(found && RangesOverlap(&range, &newRange));
}
AVER(found);
@ -393,9 +368,7 @@ static void MVFFBufferEmpty(Pool pool, Buffer buffer,
res = MVFFInsert(&range, mvff);
AVER(res == ResOK);
if (res == ResOK)
MVFFFreeSegs(mvff, &range);
return;
MVFFFreeTracts(mvff, &range);
}
@ -485,9 +458,7 @@ static Res MVFFInit(Pool pool, ArgList args)
mvff->extendBy = extendBy;
if (extendBy < ArenaAlign(arena))
mvff->minSegSize = ArenaAlign(arena);
else
mvff->minSegSize = extendBy;
mvff->extendBy = ArenaAlign(arena);
mvff->avgSize = avgSize;
pool->alignment = align;
mvff->slotHigh = slotHigh;
@ -501,20 +472,23 @@ static Res MVFFInit(Pool pool, ArgList args)
SegPrefInit(mvff->segPref);
SegPrefExpress(mvff->segPref, arenaHigh ? SegPrefHigh : SegPrefLow, NULL);
mvff->total = 0;
res = LandInit(AllocCBSOfMVFF(mvff), CBSFastLandClassGet(), arena, align,
mvff, mps_args_none);
if (res != ResOK)
goto failAllocCBSInit;
res = LandInit(FreelistOfMVFF(mvff), FreelistLandClassGet(), arena, align,
mvff, mps_args_none);
if (res != ResOK)
goto failFreelistInit;
res = LandInit(CBSOfMVFF(mvff), CBSFastLandClassGet(), arena, align, mvff,
mps_args_none);
res = LandInit(FreeCBSOfMVFF(mvff), CBSFastLandClassGet(), arena, align,
mvff, mps_args_none);
if (res != ResOK)
goto failCBSInit;
goto failFreeCBSInit;
MPS_ARGS_BEGIN(foArgs) {
MPS_ARGS_ADD(foArgs, FailoverPrimary, CBSOfMVFF(mvff));
MPS_ARGS_ADD(foArgs, FailoverPrimary, FreeCBSOfMVFF(mvff));
MPS_ARGS_ADD(foArgs, FailoverSecondary, FreelistOfMVFF(mvff));
res = LandInit(FailoverOfMVFF(mvff), FailoverLandClassGet(), arena, align,
mvff, foArgs);
@ -529,10 +503,12 @@ static Res MVFFInit(Pool pool, ArgList args)
return ResOK;
failFailoverInit:
LandFinish(CBSOfMVFF(mvff));
failCBSInit:
LandFinish(FreeCBSOfMVFF(mvff));
failFreeCBSInit:
LandFinish(FreelistOfMVFF(mvff));
failFreelistInit:
LandFinish(AllocCBSOfMVFF(mvff));
failAllocCBSInit:
ControlFree(arena, p, sizeof(SegPrefStruct));
return res;
}
@ -540,38 +516,45 @@ failFreelistInit:
/* MVFFFinish -- finish method for MVFF */
static Bool mvffFinishVisitor(Bool *deleteReturn, Land land, Range range,
void *closureP, Size closureS)
{
Pool pool;
AVER(deleteReturn != NULL);
AVERT(Land, land);
AVERT(Range, range);
AVER(closureP != NULL);
pool = closureP;
AVERT(Pool, pool);
UNUSED(closureS);
ArenaFree(RangeBase(range), RangeSize(range), pool);
*deleteReturn = FALSE;
return TRUE;
}
static void MVFFFinish(Pool pool)
{
MVFF mvff;
Arena arena;
Ring ring, node, nextNode;
AVERT(Pool, pool);
mvff = Pool2MVFF(pool);
AVERT(MVFF, mvff);
mvff->sig = SigInvalid;
ring = PoolSegRing(pool);
RING_FOR(node, ring, nextNode) {
Size size;
Seg seg;
seg = SegOfPoolRing(node);
AVER(SegPool(seg) == pool);
size = AddrOffset(SegBase(seg), SegLimit(seg));
AVER(size <= mvff->total);
mvff->total -= size;
SegFree(seg);
}
LandIterate(AllocCBSOfMVFF(mvff), mvffFinishVisitor, pool, 0);
AVER(mvff->total == 0);
arena = PoolArena(pool);
ControlFree(arena, mvff->segPref, sizeof(SegPrefStruct));
/* Would like to check that LandSize(AllocCBSOfMVFF(mvff)) == 0 now,
* but CBS doesn't support deletion while iterating. */
LandFinish(FailoverOfMVFF(mvff));
LandFinish(FreelistOfMVFF(mvff));
LandFinish(CBSOfMVFF(mvff));
LandFinish(FreeCBSOfMVFF(mvff));
LandFinish(AllocCBSOfMVFF(mvff));
mvff->sig = SigInvalid;
arena = PoolArena(pool);
ControlFree(arena, mvff->segPref, sizeof(SegPrefStruct));
}
@ -607,12 +590,17 @@ static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream)
(WriteFP)pool, (WriteFU)pool->serial,
" extendBy $W\n", (WriteFW)mvff->extendBy,
" avgSize $W\n", (WriteFW)mvff->avgSize,
" total $U\n", (WriteFU)mvff->total,
" firstFit $U\n", (WriteFU)mvff->firstFit,
" slotHigh $U\n", (WriteFU)mvff->slotHigh,
NULL);
if (res != ResOK)
return res;
res = LandDescribe(CBSOfMVFF(mvff), stream);
res = LandDescribe(AllocCBSOfMVFF(mvff), stream);
if (res != ResOK)
return res;
res = LandDescribe(FreeCBSOfMVFF(mvff), stream);
if (res != ResOK)
return res;
@ -685,15 +673,13 @@ size_t mps_mvff_free_size(mps_pool_t mps_pool)
{
Pool pool;
MVFF mvff;
Land land;
pool = (Pool)mps_pool;
AVERT(Pool, pool);
mvff = Pool2MVFF(pool);
AVERT(MVFF, mvff);
land = FailoverOfMVFF(mvff);
return (size_t)LandSize(land);
return (size_t)LandSize(FailoverOfMVFF(mvff));
}
/* Total owned bytes. See <design/poolmvff/#design.arena-enter> */
@ -708,7 +694,7 @@ size_t mps_mvff_size(mps_pool_t mps_pool)
mvff = Pool2MVFF(pool);
AVERT(MVFF, mvff);
return (size_t)mvff->total;
return (size_t)LandSize(AllocCBSOfMVFF(mvff));
}
@ -720,15 +706,14 @@ static Bool MVFFCheck(MVFF mvff)
CHECKD(Pool, MVFF2Pool(mvff));
CHECKL(IsSubclassPoly(MVFF2Pool(mvff)->class, MVFFPoolClassGet()));
CHECKD(SegPref, mvff->segPref);
CHECKL(mvff->extendBy > 0); /* see .arg.check */
CHECKL(mvff->minSegSize >= ArenaAlign(PoolArena(MVFF2Pool(mvff))));
CHECKL(mvff->extendBy >= ArenaAlign(PoolArena(MVFF2Pool(mvff))));
CHECKL(mvff->avgSize > 0); /* see .arg.check */
CHECKL(mvff->avgSize <= mvff->extendBy); /* see .arg.check */
CHECKL(SizeIsAligned(mvff->total, ArenaAlign(PoolArena(MVFF2Pool(mvff)))));
CHECKD(CBS, &mvff->cbsStruct);
CHECKD(CBS, &mvff->allocCBSStruct);
CHECKD(CBS, &mvff->freeCBSStruct);
CHECKD(Freelist, &mvff->flStruct);
CHECKD(Failover, &mvff->foStruct);
CHECKL(mvff->total >= LandSize(FailoverOfMVFF(mvff)));
CHECKL(LandSize(AllocCBSOfMVFF(mvff)) >= LandSize(FailoverOfMVFF(mvff)));
CHECKL(BoolCheck(mvff->slotHigh));
CHECKL(BoolCheck(mvff->firstFit));
return TRUE;
@ -745,7 +730,7 @@ Land _mps_mvff_cbs(Pool pool) {
mvff = Pool2MVFF(pool);
AVERT(MVFF, mvff);
return CBSOfMVFF(mvff);
return FreeCBSOfMVFF(mvff);
}

View file

@ -44,9 +44,6 @@ BufferClass. This is appropriate since these buffers don't attach to
segments, and hence don't constrain buffered regions to lie within
segment boundaries.
_`.over.segments`: The pool uses the simplest segment class
(SegClass). There's no need for anything more complex.
Methods
-------
@ -89,14 +86,10 @@ _`.method.init.epdr`: To simulate the EPDR pool, specify ``extendBy``,
_`.method.init.other`: The performance characteristics of other
combinations are unknown.
_`.method.finish`: The ``PoolFinish()`` method,
_`.method.alloc`: ``PoolAlloc()`` and ``PoolFree()`` methods are
supported, implementing the policy set by the pool params (see
`.method.init`_).
_`.method.describe`: The usual describe method.
_`.method.buffer`: The buffer methods implement a worst-fit fill
strategy.
@ -119,6 +112,9 @@ the pool class, to be used in pool creation.
Implementation
--------------
_`.impl.alloc_list`: The pool stores the address ranges that it has
acquired from the arena in a CBS (see design.mps.cbs_).
_`.impl.free-list`: The pool stores its free list in a CBS (see
design.mps.cbs_), failing over in emergencies to a Freelist (see
design.mps.freelist_) when the CBS cannot allocate new control
@ -131,13 +127,14 @@ structures. This is the reason for the alignment restriction above.
Details
-------
_`.design.seg-size`: When adding a segment, we use extendBy as the
segment size unless the object won't fit, in which case we use the
object size (in both cases we align up).
_`.design.acquire-size`: When acquiring memory from the arena, we use
``extendBy`` as the unit of allocation unless the object won't fit, in
which case we use the object size (in both cases we align up to the
arena alignment).
_`.design.seg-fail`: If allocating a segment fails, we try again with
a segment size just large enough for the object we're allocating. This
is in response to request.mps.170186_.
_`.design.acquire-fail`: If allocating ``extendBy``, we try again with
an aligned size just large enough for the object we're allocating.
This is in response to request.mps.170186_.
.. _request.mps.170186: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/mps/170186
@ -162,6 +159,9 @@ Document History
- 2013-06-04 GDR_ The CBS module no longer maintains its own emergency
list, so MVFF handles the fail-over from its CBS to a Freelist.
- 2014-04-15 GDR_ The address ranges acquired from the arena are now
stored in a CBS; segments are no longer used for this purpose.
.. _RB: http://www.ravenbrook.com/consultants/rb/
.. _GDR: http://www.ravenbrook.com/consultants/gdr/