mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-24 06:20:43 -08:00
Eliminating SegPrefExpress in most places, especially where there wasn’t really any preference. Eliminating special case knowledge about garbage collection from the arena. Copied from Perforce Change: 184279 ServerID: perforce.ravenbrook.com
867 lines
24 KiB
C
867 lines
24 KiB
C
/* poolmvff.c: First Fit Manual Variable Pool
|
|
*
|
|
* $Id$
|
|
* Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
|
|
* Portions copyright (C) 2002 Global Graphics Software.
|
|
*
|
|
* .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.
|
|
*
|
|
* .design: <design/poolmvff>
|
|
*
|
|
*
|
|
* TRANSGRESSIONS
|
|
*
|
|
* .trans.stat: mps_mvff_stat is a temporary hack for measurement purposes,
|
|
* see .stat below.
|
|
*/
|
|
|
|
#include "mpscmvff.h"
|
|
#include "dbgpool.h"
|
|
#include "cbs.h"
|
|
#include "freelist.h"
|
|
#include "mpm.h"
|
|
|
|
SRCID(poolmvff, "$Id$");
|
|
|
|
|
|
/* Would go in poolmvff.h if the class had any MPS-internal clients. */
|
|
extern PoolClass PoolClassMVFF(void);
|
|
|
|
|
|
/* MVFFStruct -- MVFF (Manual Variable First Fit) pool outer structure
|
|
*
|
|
* The signature is placed at the end, see
|
|
* <design/pool/#outer-structure.sig>
|
|
*/
|
|
|
|
#define MVFFSig ((Sig)0x5193FFF9) /* SIGnature MVFF */
|
|
|
|
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 */
|
|
Size avgSize; /* client estimate of allocation size */
|
|
Size total; /* total bytes in pool */
|
|
Size free; /* total free bytes in pool */
|
|
CBSStruct cbsStruct; /* free list */
|
|
FreelistStruct flStruct; /* emergency free list */
|
|
Bool firstFit; /* as opposed to last fit */
|
|
Bool slotHigh; /* prefers high part of large block */
|
|
Sig sig; /* <design/sig/> */
|
|
} MVFFStruct;
|
|
|
|
|
|
#define Pool2MVFF(pool) PARENT(MVFFStruct, poolStruct, pool)
|
|
#define MVFF2Pool(mvff) (&((mvff)->poolStruct))
|
|
#define CBSOfMVFF(mvff) (&((mvff)->cbsStruct))
|
|
#define MVFFOfCBS(cbs) PARENT(MVFFStruct, cbsStruct, cbs)
|
|
#define FreelistOfMVFF(mvff) (&((mvff)->flStruct))
|
|
#define MVFFOfFreelist(fl) PARENT(MVFFStruct, flStruct, fl)
|
|
|
|
static Bool MVFFCheck(MVFF mvff);
|
|
|
|
|
|
/* MVFFDebug -- MVFFDebug class */
|
|
|
|
typedef struct MVFFDebugStruct {
|
|
MVFFStruct mvffStruct; /* MVFF structure */
|
|
PoolDebugMixinStruct debug; /* debug mixin */
|
|
} MVFFDebugStruct;
|
|
|
|
typedef MVFFDebugStruct *MVFFDebug;
|
|
|
|
|
|
#define MVFF2MVFFDebug(mvff) PARENT(MVFFDebugStruct, mvffStruct, mvff)
|
|
#define MVFFDebug2MVFF(mvffd) (&((mvffd)->mvffStruct))
|
|
|
|
|
|
/* MVFFAddToFreeList -- Add given range to free list
|
|
*
|
|
* Updates MVFF counters for additional free space. Returns maximally
|
|
* coalesced range containing given range. Does not attempt to free
|
|
* segments (see MVFFFreeSegs).
|
|
*/
|
|
static Res MVFFAddToFreeList(Addr *baseIO, Addr *limitIO, MVFF mvff) {
|
|
Res res;
|
|
RangeStruct range, newRange;
|
|
|
|
AVER(baseIO != NULL);
|
|
AVER(limitIO != NULL);
|
|
AVERT(MVFF, mvff);
|
|
RangeInit(&range, *baseIO, *limitIO);
|
|
|
|
res = CBSInsert(&newRange, CBSOfMVFF(mvff), &range);
|
|
if (ResIsAllocFailure(res)) {
|
|
/* CBS ran out of memory for splay nodes: add range to emergency
|
|
* free list instead. */
|
|
res = FreelistInsert(&newRange, FreelistOfMVFF(mvff), &range);
|
|
}
|
|
|
|
if (res == ResOK) {
|
|
mvff->free += RangeSize(&range);
|
|
*baseIO = RangeBase(&newRange);
|
|
*limitIO = RangeLimit(&newRange);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
/* MVFFFreeSegs -- Free segments from given range
|
|
*
|
|
* Given a free range, attempts to find entire segments within
|
|
* it, and returns them to the arena, updating total size counter.
|
|
*
|
|
* This is usually called immediately after MVFFAddToFreeList.
|
|
* It is not combined with MVFFAddToFreeList because the latter
|
|
* is also called when new segments are added under MVFFAlloc.
|
|
*/
|
|
static void MVFFFreeSegs(MVFF mvff, Addr base, Addr limit)
|
|
{
|
|
Seg seg = NULL; /* suppress "may be used uninitialized" */
|
|
Arena arena;
|
|
Bool b;
|
|
Addr segLimit; /* limit of the current segment when iterating */
|
|
Addr segBase; /* base of the current segment when iterating */
|
|
Res res;
|
|
|
|
AVERT(MVFF, mvff);
|
|
AVER(base < limit);
|
|
/* Could profitably AVER that the given range is free, */
|
|
/* but the CBS doesn't provide that facility. */
|
|
|
|
if (AddrOffset(base, limit) < mvff->minSegSize)
|
|
return; /* not large enough for entire segments */
|
|
|
|
arena = PoolArena(MVFF2Pool(mvff));
|
|
b = SegOfAddr(&seg, arena, base);
|
|
AVER(b);
|
|
|
|
segBase = SegBase(seg);
|
|
segLimit = SegLimit(seg);
|
|
|
|
while(segLimit <= limit) { /* segment ends in range */
|
|
if (segBase >= base) { /* segment starts in range */
|
|
RangeStruct range, oldRange;
|
|
RangeInit(&range, segBase, segLimit);
|
|
|
|
res = CBSDelete(&oldRange, CBSOfMVFF(mvff), &range);
|
|
if (res == ResOK) {
|
|
mvff->free -= RangeSize(&range);
|
|
} else if (ResIsAllocFailure(res)) {
|
|
/* CBS ran out of memory for splay nodes, which must mean that
|
|
* there were fragments on both sides: see
|
|
* <design/cbs/#function.cbs.delete.fail>. Handle this by
|
|
* deleting the whole of oldRange (which requires no
|
|
* allocation) and re-inserting the fragments. */
|
|
RangeStruct oldRange2;
|
|
res = CBSDelete(&oldRange2, CBSOfMVFF(mvff), &oldRange);
|
|
AVER(res == ResOK);
|
|
AVER(RangesEqual(&oldRange2, &oldRange));
|
|
mvff->free -= RangeSize(&oldRange);
|
|
AVER(RangeBase(&oldRange) != segBase);
|
|
{
|
|
Addr leftBase = RangeBase(&oldRange);
|
|
Addr leftLimit = segBase;
|
|
res = MVFFAddToFreeList(&leftBase, &leftLimit, mvff);
|
|
}
|
|
AVER(RangeLimit(&oldRange) != segLimit);
|
|
{
|
|
Addr rightBase = segLimit;
|
|
Addr rightLimit = RangeLimit(&oldRange);
|
|
res = MVFFAddToFreeList(&rightBase, &rightLimit, mvff);
|
|
}
|
|
} else if (res == ResFAIL) {
|
|
/* Not found in the CBS: must be found in the Freelist. */
|
|
res = FreelistDelete(&oldRange, FreelistOfMVFF(mvff), &range);
|
|
AVER(res == ResOK);
|
|
mvff->free -= RangeSize(&range);
|
|
}
|
|
|
|
AVER(res == ResOK);
|
|
AVER(RangesNest(&oldRange, &range));
|
|
|
|
/* 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);
|
|
mvff->total -= RangeSize(&range);
|
|
}
|
|
|
|
/* Avoid calling SegNext if the next segment would fail */
|
|
/* the loop test, mainly because there might not be a */
|
|
/* next segment. */
|
|
if (segLimit == limit) /* segment ends at end of range */
|
|
break;
|
|
|
|
b = SegFindAboveAddr(&seg, arena, segBase);
|
|
AVER(b);
|
|
segBase = SegBase(seg);
|
|
segLimit = SegLimit(seg);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* MVFFAddSeg -- Allocates a new segment from the arena
|
|
*
|
|
* Allocates a new segment 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 list.
|
|
*/
|
|
static Res MVFFAddSeg(Seg *segReturn,
|
|
MVFF mvff, Size size, Bool withReservoirPermit)
|
|
{
|
|
Pool pool;
|
|
Arena arena;
|
|
Size segSize;
|
|
Seg seg;
|
|
Res res;
|
|
Align align;
|
|
Addr base, limit;
|
|
|
|
AVERT(MVFF, mvff);
|
|
AVER(size > 0);
|
|
AVER(BoolCheck(withReservoirPermit));
|
|
|
|
pool = MVFF2Pool(mvff);
|
|
arena = PoolArena(pool);
|
|
align = ArenaAlign(arena);
|
|
|
|
AVER(SizeIsAligned(size, PoolAlignment(pool)));
|
|
|
|
/* Use extendBy unless it's too small (see */
|
|
/* <design/poolmvff/#design.seg-size>). */
|
|
if (size <= mvff->extendBy)
|
|
segSize = mvff->extendBy;
|
|
else
|
|
segSize = size;
|
|
|
|
segSize = SizeAlignUp(segSize, align);
|
|
|
|
res = SegAlloc(&seg, SegClassGet(), mvff->segPref, segSize, pool,
|
|
withReservoirPermit, argsNone);
|
|
if (res != ResOK) {
|
|
/* try again for a seg 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) {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
mvff->total += segSize;
|
|
base = SegBase(seg);
|
|
limit = AddrAdd(base, segSize);
|
|
DebugPoolFreeSplat(pool, base, limit);
|
|
res = MVFFAddToFreeList(&base, &limit, mvff);
|
|
AVER(res == ResOK);
|
|
AVER(base <= SegBase(seg));
|
|
if (mvff->minSegSize > segSize) mvff->minSegSize = segSize;
|
|
|
|
/* Don't call MVFFFreeSegs; that would be silly. */
|
|
|
|
*segReturn = seg;
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* MVFFFindFirstFree -- Finds the first (or last) suitable free block
|
|
*
|
|
* Finds a free block of the given (pool aligned) size, according
|
|
* to a first (or last) fit policy controlled by the MVFF fields
|
|
* firstFit, slotHigh (for whether to allocate the top or bottom
|
|
* portion of a larger block).
|
|
*
|
|
* Will return FALSE if the free list has no large enough block.
|
|
* In particular, will not attempt to allocate a new segment.
|
|
*/
|
|
static Bool MVFFFindFirstFree(Addr *baseReturn, Addr *limitReturn,
|
|
MVFF mvff, Size size)
|
|
{
|
|
Bool foundBlock;
|
|
FindDelete findDelete;
|
|
RangeStruct range, oldRange;
|
|
|
|
AVER(baseReturn != NULL);
|
|
AVER(limitReturn != NULL);
|
|
AVERT(MVFF, mvff);
|
|
AVER(size > 0);
|
|
AVER(SizeIsAligned(size, PoolAlignment(MVFF2Pool(mvff))));
|
|
|
|
FreelistFlushToCBS(FreelistOfMVFF(mvff), CBSOfMVFF(mvff));
|
|
|
|
findDelete = mvff->slotHigh ? FindDeleteHIGH : FindDeleteLOW;
|
|
|
|
foundBlock =
|
|
(mvff->firstFit ? CBSFindFirst : CBSFindLast)
|
|
(&range, &oldRange, CBSOfMVFF(mvff), size, findDelete);
|
|
|
|
if (!foundBlock) {
|
|
/* Failed to find a block in the CBS: try the emergency free list
|
|
* as well. */
|
|
foundBlock =
|
|
(mvff->firstFit ? FreelistFindFirst : FreelistFindLast)
|
|
(&range, &oldRange, FreelistOfMVFF(mvff), size, findDelete);
|
|
}
|
|
|
|
if (foundBlock) {
|
|
*baseReturn = RangeBase(&range);
|
|
*limitReturn = RangeLimit(&range);
|
|
mvff->free -= size;
|
|
}
|
|
|
|
return foundBlock;
|
|
}
|
|
|
|
|
|
/* MVFFAlloc -- Allocate a block */
|
|
|
|
static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size,
|
|
Bool withReservoirPermit)
|
|
{
|
|
Res res;
|
|
MVFF mvff;
|
|
Addr base, limit;
|
|
Bool foundBlock;
|
|
|
|
AVERT(Pool, pool);
|
|
mvff = Pool2MVFF(pool);
|
|
AVERT(MVFF, mvff);
|
|
|
|
AVER(aReturn != NULL);
|
|
AVER(size > 0);
|
|
AVER(BoolCheck(withReservoirPermit));
|
|
|
|
size = SizeAlignUp(size, PoolAlignment(pool));
|
|
|
|
foundBlock = MVFFFindFirstFree(&base, &limit, mvff, size);
|
|
if (!foundBlock) {
|
|
Seg seg;
|
|
|
|
res = MVFFAddSeg(&seg, mvff, size, withReservoirPermit);
|
|
if (res != ResOK)
|
|
return res;
|
|
foundBlock = MVFFFindFirstFree(&base, &limit, 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 three AVERs test for intersection of two intervals. */
|
|
AVER(base >= SegBase(seg) || limit <= SegLimit(seg));
|
|
AVER(base < SegLimit(seg));
|
|
AVER(SegBase(seg) < limit);
|
|
|
|
/* We also know that the found range is no larger than the segment. */
|
|
AVER(SegSize(seg) >= AddrOffset(base, limit));
|
|
}
|
|
AVER(foundBlock);
|
|
AVER(AddrOffset(base, limit) == size);
|
|
|
|
*aReturn = base;
|
|
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* MVFFFree -- free the given block */
|
|
|
|
static void MVFFFree(Pool pool, Addr old, Size size)
|
|
{
|
|
Res res;
|
|
Addr base, limit;
|
|
MVFF mvff;
|
|
|
|
AVERT(Pool, pool);
|
|
mvff = Pool2MVFF(pool);
|
|
AVERT(MVFF, mvff);
|
|
|
|
AVER(old != (Addr)0);
|
|
AVER(AddrIsAligned(old, PoolAlignment(pool)));
|
|
AVER(size > 0);
|
|
|
|
size = SizeAlignUp(size, PoolAlignment(pool));
|
|
base = old;
|
|
limit = AddrAdd(base, size);
|
|
|
|
res = MVFFAddToFreeList(&base, &limit, mvff);
|
|
AVER(res == ResOK);
|
|
if (res == ResOK)
|
|
MVFFFreeSegs(mvff, base, limit);
|
|
|
|
return;
|
|
}
|
|
|
|
/* MVFFFindLargest -- call CBSFindLargest and then fall back to
|
|
* FreelistFindLargest if no block in the CBS was big enough. */
|
|
|
|
static Bool MVFFFindLargest(Range range, Range oldRange, MVFF mvff,
|
|
Size size, FindDelete findDelete)
|
|
{
|
|
AVER(range != NULL);
|
|
AVER(oldRange != NULL);
|
|
AVERT(MVFF, mvff);
|
|
AVER(size > 0);
|
|
AVERT(FindDelete, findDelete);
|
|
|
|
FreelistFlushToCBS(FreelistOfMVFF(mvff), CBSOfMVFF(mvff));
|
|
|
|
if (CBSFindLargest(range, oldRange, CBSOfMVFF(mvff), size, findDelete))
|
|
return TRUE;
|
|
|
|
if (FreelistFindLargest(range, oldRange, FreelistOfMVFF(mvff),
|
|
size, findDelete))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* MVFFBufferFill -- Fill the buffer
|
|
*
|
|
* Fill it with the largest block we can find.
|
|
*/
|
|
static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn,
|
|
Pool pool, Buffer buffer, Size size,
|
|
Bool withReservoirPermit)
|
|
{
|
|
Res res;
|
|
MVFF mvff;
|
|
RangeStruct range, oldRange;
|
|
Bool found;
|
|
Seg seg = NULL;
|
|
AVER(baseReturn != NULL);
|
|
AVER(limitReturn != NULL);
|
|
AVERT(Pool, pool);
|
|
mvff = Pool2MVFF(pool);
|
|
AVERT(MVFF, mvff);
|
|
AVERT(Buffer, buffer);
|
|
AVER(size > 0);
|
|
AVER(SizeIsAligned(size, PoolAlignment(pool)));
|
|
AVERT(Bool, withReservoirPermit);
|
|
|
|
found = MVFFFindLargest(&range, &oldRange, mvff, size, FindDeleteENTIRE);
|
|
if (!found) {
|
|
/* Add a new segment to the free list and try again. */
|
|
res = MVFFAddSeg(&seg, mvff, size, withReservoirPermit);
|
|
if (res != ResOK)
|
|
return res;
|
|
found = MVFFFindLargest(&range, &oldRange, mvff, size, FindDeleteENTIRE);
|
|
}
|
|
AVER(found);
|
|
|
|
AVER(RangeSize(&range) >= size);
|
|
mvff->free -= RangeSize(&range);
|
|
|
|
*baseReturn = RangeBase(&range);
|
|
*limitReturn = RangeLimit(&range);
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* MVFFBufferEmpty -- return unused portion of this buffer */
|
|
|
|
static void MVFFBufferEmpty(Pool pool, Buffer buffer,
|
|
Addr base, Addr limit)
|
|
{
|
|
Res res;
|
|
MVFF mvff;
|
|
|
|
AVERT(Pool, pool);
|
|
mvff = Pool2MVFF(pool);
|
|
AVERT(MVFF, mvff);
|
|
AVERT(Buffer, buffer);
|
|
AVER(BufferIsReady(buffer));
|
|
AVER(base <= limit);
|
|
|
|
if (base == limit)
|
|
return;
|
|
|
|
res = MVFFAddToFreeList(&base, &limit, mvff);
|
|
AVER(res == ResOK);
|
|
if (res == ResOK)
|
|
MVFFFreeSegs(mvff, base, limit);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* MVFFVarargs -- decode obsolete varargs */
|
|
|
|
static void MVFFVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs)
|
|
{
|
|
args[0].key = MPS_KEY_EXTEND_BY;
|
|
args[0].val.size = va_arg(varargs, Size);
|
|
args[1].key = MPS_KEY_MEAN_SIZE;
|
|
args[1].val.size = va_arg(varargs, Size);
|
|
args[2].key = MPS_KEY_ALIGN;
|
|
args[2].val.align = va_arg(varargs, Size); /* promoted type */
|
|
args[3].key = MPS_KEY_MVFF_SLOT_HIGH;
|
|
args[3].val.b = va_arg(varargs, Bool);
|
|
args[4].key = MPS_KEY_MVFF_ARENA_HIGH;
|
|
args[4].val.b = va_arg(varargs, Bool);
|
|
args[5].key = MPS_KEY_MVFF_FIRST_FIT;
|
|
args[5].val.b = va_arg(varargs, Bool);
|
|
args[6].key = MPS_KEY_ARGS_END;
|
|
AVER(ArgListCheck(args));
|
|
}
|
|
|
|
static void MVFFDebugVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs)
|
|
{
|
|
args[0].key = MPS_KEY_POOL_DEBUG_OPTIONS;
|
|
args[0].val.pool_debug_options = va_arg(varargs, mps_pool_debug_option_s *);
|
|
MVFFVarargs(args + 1, varargs);
|
|
}
|
|
|
|
|
|
/* MVFFInit -- initialize method for MVFF */
|
|
|
|
ARG_DEFINE_KEY(mvff_slot_high, Bool);
|
|
ARG_DEFINE_KEY(mvff_arena_high, Bool);
|
|
ARG_DEFINE_KEY(mvff_first_fit, Bool);
|
|
|
|
static Res MVFFInit(Pool pool, ArgList args)
|
|
{
|
|
Size extendBy = MVFF_EXTEND_BY_DEFAULT;
|
|
Size avgSize = MVFF_AVG_SIZE_DEFAULT;
|
|
Size align = MVFF_ALIGN_DEFAULT;
|
|
Bool slotHigh = MVFF_SLOT_HIGH_DEFAULT;
|
|
Bool arenaHigh = MVFF_ARENA_HIGH_DEFAULT;
|
|
Bool firstFit = MVFF_FIRST_FIT_DEFAULT;
|
|
MVFF mvff;
|
|
Arena arena;
|
|
Res res;
|
|
void *p;
|
|
ArgStruct arg;
|
|
|
|
AVERT(Pool, pool);
|
|
arena = PoolArena(pool);
|
|
|
|
/* .arg: class-specific additional arguments; see */
|
|
/* <design/poolmvff/#method.init> */
|
|
/* .arg.check: we do the same checks here and in MVFFCheck */
|
|
/* except for arenaHigh, which is stored only in the segPref. */
|
|
|
|
if (ArgPick(&arg, args, MPS_KEY_EXTEND_BY))
|
|
extendBy = arg.val.size;
|
|
|
|
if (ArgPick(&arg, args, MPS_KEY_MEAN_SIZE))
|
|
avgSize = arg.val.size;
|
|
|
|
if (ArgPick(&arg, args, MPS_KEY_ALIGN))
|
|
align = arg.val.align;
|
|
|
|
if (ArgPick(&arg, args, MPS_KEY_MVFF_SLOT_HIGH))
|
|
slotHigh = arg.val.b;
|
|
|
|
if (ArgPick(&arg, args, MPS_KEY_MVFF_ARENA_HIGH))
|
|
arenaHigh = arg.val.b;
|
|
|
|
if (ArgPick(&arg, args, MPS_KEY_MVFF_FIRST_FIT))
|
|
firstFit = arg.val.b;
|
|
|
|
AVER(extendBy > 0); /* .arg.check */
|
|
AVER(avgSize > 0); /* .arg.check */
|
|
AVER(avgSize <= extendBy); /* .arg.check */
|
|
AVER(SizeIsAligned(align, MPS_PF_ALIGN));
|
|
AVER(BoolCheck(slotHigh));
|
|
AVER(BoolCheck(arenaHigh));
|
|
AVER(BoolCheck(firstFit));
|
|
|
|
mvff = Pool2MVFF(pool);
|
|
|
|
mvff->extendBy = extendBy;
|
|
if (extendBy < ArenaAlign(arena))
|
|
mvff->minSegSize = ArenaAlign(arena);
|
|
else
|
|
mvff->minSegSize = extendBy;
|
|
mvff->avgSize = avgSize;
|
|
pool->alignment = align;
|
|
mvff->slotHigh = slotHigh;
|
|
mvff->firstFit = firstFit;
|
|
|
|
res = ControlAlloc(&p, arena, sizeof(SegPrefStruct), FALSE);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
mvff->segPref = (SegPref)p;
|
|
SegPrefInit(mvff->segPref);
|
|
SegPrefExpress(mvff->segPref, arenaHigh ? SegPrefHigh : SegPrefLow, NULL);
|
|
|
|
mvff->total = 0;
|
|
mvff->free = 0;
|
|
|
|
res = FreelistInit(FreelistOfMVFF(mvff), align);
|
|
if (res != ResOK)
|
|
goto failInit;
|
|
|
|
res = CBSInit(arena, CBSOfMVFF(mvff), (void *)mvff, align, TRUE, args);
|
|
if (res != ResOK)
|
|
goto failInit;
|
|
|
|
mvff->sig = MVFFSig;
|
|
AVERT(MVFF, mvff);
|
|
EVENT8(PoolInitMVFF, pool, arena, extendBy, avgSize, align,
|
|
slotHigh, arenaHigh, firstFit);
|
|
return ResOK;
|
|
|
|
failInit:
|
|
ControlFree(arena, p, sizeof(SegPrefStruct));
|
|
return res;
|
|
}
|
|
|
|
|
|
/* MVFFFinish -- finish method for MVFF */
|
|
|
|
static void MVFFFinish(Pool pool)
|
|
{
|
|
MVFF mvff;
|
|
Arena arena;
|
|
Seg seg;
|
|
Ring ring, node, nextNode;
|
|
|
|
AVERT(Pool, pool);
|
|
mvff = Pool2MVFF(pool);
|
|
AVERT(MVFF, mvff);
|
|
|
|
ring = PoolSegRing(pool);
|
|
RING_FOR(node, ring, nextNode) {
|
|
seg = SegOfPoolRing(node);
|
|
AVER(SegPool(seg) == pool);
|
|
SegFree(seg);
|
|
}
|
|
|
|
/* Could maintain mvff->total here and check it falls to zero, */
|
|
/* but that would just make the function slow. If only we had */
|
|
/* a way to do operations only if AVERs are turned on. */
|
|
|
|
arena = PoolArena(pool);
|
|
ControlFree(arena, mvff->segPref, sizeof(SegPrefStruct));
|
|
|
|
CBSFinish(CBSOfMVFF(mvff));
|
|
FreelistFinish(FreelistOfMVFF(mvff));
|
|
|
|
mvff->sig = SigInvalid;
|
|
}
|
|
|
|
|
|
/* MVFFDebugMixin - find debug mixin in class MVFFDebug */
|
|
|
|
static PoolDebugMixin MVFFDebugMixin(Pool pool)
|
|
{
|
|
MVFF mvff;
|
|
|
|
AVERT(Pool, pool);
|
|
mvff = Pool2MVFF(pool);
|
|
AVERT(MVFF, mvff);
|
|
/* Can't check MVFFDebug, because this is called during init */
|
|
return &(MVFF2MVFFDebug(mvff)->debug);
|
|
}
|
|
|
|
|
|
/* MVFFDescribe -- describe an MVFF pool */
|
|
|
|
static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream)
|
|
{
|
|
Res res;
|
|
MVFF mvff;
|
|
|
|
if (!TESTT(Pool, pool)) return ResFAIL;
|
|
mvff = Pool2MVFF(pool);
|
|
if (!TESTT(MVFF, mvff)) return ResFAIL;
|
|
if (stream == NULL) return ResFAIL;
|
|
|
|
res = WriteF(stream,
|
|
"MVFF $P {\n", (WriteFP)mvff,
|
|
" pool $P ($U)\n",
|
|
(WriteFP)pool, (WriteFU)pool->serial,
|
|
" extendBy $W\n", (WriteFW)mvff->extendBy,
|
|
" avgSize $W\n", (WriteFW)mvff->avgSize,
|
|
" total $U\n", (WriteFU)mvff->total,
|
|
" free $U\n", (WriteFU)mvff->free,
|
|
NULL);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
res = CBSDescribe(CBSOfMVFF(mvff), stream);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
res = FreelistDescribe(FreelistOfMVFF(mvff), stream);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
res = WriteF(stream, "}\n", NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
DEFINE_POOL_CLASS(MVFFPoolClass, this)
|
|
{
|
|
INHERIT_CLASS(this, AbstractAllocFreePoolClass);
|
|
PoolClassMixInBuffer(this);
|
|
this->name = "MVFF";
|
|
this->size = sizeof(MVFFStruct);
|
|
this->offset = offsetof(MVFFStruct, poolStruct);
|
|
this->varargs = MVFFVarargs;
|
|
this->init = MVFFInit;
|
|
this->finish = MVFFFinish;
|
|
this->alloc = MVFFAlloc;
|
|
this->free = MVFFFree;
|
|
this->bufferFill = MVFFBufferFill;
|
|
this->bufferEmpty = MVFFBufferEmpty;
|
|
this->describe = MVFFDescribe;
|
|
}
|
|
|
|
|
|
PoolClass PoolClassMVFF(void)
|
|
{
|
|
return MVFFPoolClassGet();
|
|
}
|
|
|
|
|
|
/* Pool class MVFFDebug */
|
|
|
|
DEFINE_POOL_CLASS(MVFFDebugPoolClass, this)
|
|
{
|
|
INHERIT_CLASS(this, MVFFPoolClass);
|
|
PoolClassMixInDebug(this);
|
|
this->name = "MVFFDBG";
|
|
this->size = sizeof(MVFFDebugStruct);
|
|
this->varargs = MVFFDebugVarargs;
|
|
this->debugMixin = MVFFDebugMixin;
|
|
}
|
|
|
|
|
|
|
|
/* MPS Interface Extensions. */
|
|
|
|
mps_class_t mps_class_mvff(void)
|
|
{
|
|
return (mps_class_t)(MVFFPoolClassGet());
|
|
}
|
|
|
|
mps_class_t mps_class_mvff_debug(void)
|
|
{
|
|
return (mps_class_t)(MVFFDebugPoolClassGet());
|
|
}
|
|
|
|
|
|
/* Total free bytes. See <design/poolmvff/#design.arena-enter> */
|
|
|
|
size_t mps_mvff_free_size(mps_pool_t mps_pool)
|
|
{
|
|
Pool pool;
|
|
MVFF mvff;
|
|
|
|
pool = (Pool)mps_pool;
|
|
AVERT(Pool, pool);
|
|
mvff = Pool2MVFF(pool);
|
|
AVERT(MVFF, mvff);
|
|
|
|
return (size_t)mvff->free;
|
|
}
|
|
|
|
/* Total owned bytes. See <design/poolmvff/#design.arena-enter> */
|
|
|
|
size_t mps_mvff_size(mps_pool_t mps_pool)
|
|
{
|
|
Pool pool;
|
|
MVFF mvff;
|
|
|
|
pool = (Pool)mps_pool;
|
|
AVERT(Pool, pool);
|
|
mvff = Pool2MVFF(pool);
|
|
AVERT(MVFF, mvff);
|
|
|
|
return (size_t)mvff->total;
|
|
}
|
|
|
|
|
|
/* MVFFCheck -- check the consistency of an MVFF structure */
|
|
|
|
static Bool MVFFCheck(MVFF mvff)
|
|
{
|
|
CHECKS(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->avgSize > 0); /* see .arg.check */
|
|
CHECKL(mvff->avgSize <= mvff->extendBy); /* see .arg.check */
|
|
CHECKL(mvff->total >= mvff->free);
|
|
CHECKL(SizeIsAligned(mvff->free, PoolAlignment(MVFF2Pool(mvff))));
|
|
CHECKL(SizeIsAligned(mvff->total, ArenaAlign(PoolArena(MVFF2Pool(mvff)))));
|
|
CHECKD(CBS, CBSOfMVFF(mvff));
|
|
CHECKD(Freelist, FreelistOfMVFF(mvff));
|
|
CHECKL(BoolCheck(mvff->slotHigh));
|
|
CHECKL(BoolCheck(mvff->firstFit));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* Return the CBS of an MVFF pool for the benefit of fotest.c. */
|
|
|
|
extern CBS _mps_mvff_cbs(mps_pool_t);
|
|
CBS _mps_mvff_cbs(mps_pool_t mps_pool) {
|
|
Pool pool;
|
|
MVFF mvff;
|
|
|
|
pool = (Pool)mps_pool;
|
|
AVERT(Pool, pool);
|
|
mvff = Pool2MVFF(pool);
|
|
AVERT(MVFF, mvff);
|
|
|
|
return CBSOfMVFF(mvff);
|
|
}
|
|
|
|
|
|
/* C. COPYRIGHT AND LICENSE
|
|
*
|
|
* Copyright (C) 2001-2002 Ravenbrook Limited <http://www.ravenbrook.com/>.
|
|
* All rights reserved. This is an open source license. Contact
|
|
* Ravenbrook for commercial licensing options.
|
|
*
|
|
* 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.
|
|
*
|
|
* 3. Redistributions in any form must be accompanied by information on how
|
|
* to obtain complete source code for this software and any accompanying
|
|
* software that uses this software. The source code must either be
|
|
* included in the distribution or be available for no more than the cost
|
|
* of distribution plus a nominal fee, and must be freely redistributable
|
|
* under reasonable conditions. For an executable file, complete source
|
|
* code means the source code for all modules it contains. It does not
|
|
* include source code for modules or files that typically accompany the
|
|
* major components of the operating system on which the executable file
|
|
* runs.
|
|
*
|
|
* 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, FITNESS FOR A PARTICULAR
|
|
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDERS AND 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.
|
|
*/
|