From b6b306bb484970bbc949529b576f7ec4911eed06 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Mon, 24 Feb 2014 23:40:50 +0000 Subject: [PATCH 01/43] Pass range bases directly as treekeys, speeding up cbscompare and most cbs operations. Copied from Perforce Change: 184500 ServerID: perforce.ravenbrook.com --- mps/code/cbs.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/mps/code/cbs.c b/mps/code/cbs.c index f20896b97b7..1938e74683d 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -31,7 +31,14 @@ typedef struct CBSBlockStruct *CBSBlock; #define cbsBlockOfNode(_node) PARENT(CBSBlockStruct, node, (_node)) #define treeOfCBS(cbs) (&((cbs)->tree)) #define nodeOfCBSBlock(block) (&((block)->node)) -#define keyOfCBSBlock(block) (&((block)->base)) + +/* We pass the block base directly as a TreeKey (void *) assuming that + Addr can be encoded, and possibly breaking . + On an exotic platform where this isn't true, pass the address of base. + i.e. add an & */ +#define keyOfCBSBlock(block) ((TreeKey)(block)->base) +#define keyOfBaseVar(baseVar) ((TreeKey)(baseVar)) +#define baseOfKey(key) ((Addr)(key)) /* cbsEnter, cbsLeave -- Avoid re-entrance @@ -102,9 +109,10 @@ static Compare cbsCompare(Tree node, TreeKey key) Addr base1, base2, limit2; CBSBlock cbsBlock; - AVER(node != NULL); + AVERT_CRITICAL(Tree, node); + AVER_CRITICAL(key != NULL); - base1 = *(Addr *)key; + base1 = baseOfKey(key); cbsBlock = cbsBlockOfNode(node); base2 = cbsBlock->base; limit2 = cbsBlock->limit; @@ -410,7 +418,7 @@ static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range) limit = RangeLimit(range); METER_ACC(cbs->treeSearch, cbs->treeSize); - b = SplayTreeNeighbours(&leftSplay, &rightSplay, treeOfCBS(cbs), &base); + b = SplayTreeNeighbours(&leftSplay, &rightSplay, treeOfCBS(cbs), keyOfBaseVar(base)); if (!b) { res = ResFAIL; goto fail; @@ -528,7 +536,7 @@ static Res cbsDeleteFromTree(Range rangeReturn, CBS cbs, Range range) limit = RangeLimit(range); METER_ACC(cbs->treeSearch, cbs->treeSize); - if (!SplayTreeFind(&node, treeOfCBS(cbs), (void *)&base)) { + if (!SplayTreeFind(&node, treeOfCBS(cbs), keyOfBaseVar(base))) { res = ResFAIL; goto failSplayTreeSearch; } From f3c8926e045956be1c3a55ff67cedcb0314163bb Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 30 Mar 2014 18:10:33 +0100 Subject: [PATCH 02/43] Branching master to branch/2014-03-30/addrset. Copied from Perforce Change: 185093 ServerID: perforce.ravenbrook.com From d2cbfda452bc7be43973e4dd0006d835d08e9923 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 1 Apr 2014 19:51:55 +0100 Subject: [PATCH 03/43] First pass at implementation of lands (collections of address ranges). 100% boilerplate! Copied from Perforce Change: 185131 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 135 ++++++++--------- mps/code/cbs.c | 329 ++++++++++++++++++++++++----------------- mps/code/cbs.h | 36 +---- mps/code/eventdef.h | 6 +- mps/code/fbmtest.c | 31 ++-- mps/code/fotest.c | 8 +- mps/code/freelist.c | 15 +- mps/code/freelist.h | 3 +- mps/code/land.c | 348 ++++++++++++++++++++++++++++++++++++++++++++ mps/code/locus.c | 1 + mps/code/mpm.h | 28 +++- mps/code/mpmst.h | 54 ++++++- mps/code/mpmtypes.h | 21 ++- mps/code/mps.c | 1 + mps/code/poolmv2.c | 44 +++--- mps/code/poolmvff.c | 32 ++-- mps/code/range.h | 2 - mps/code/tract.c | 22 +-- 18 files changed, 800 insertions(+), 316 deletions(-) create mode 100644 mps/code/land.c diff --git a/mps/code/arena.c b/mps/code/arena.c index d5833c65441..b63ef84748a 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -19,7 +19,7 @@ SRCID(arena, "$Id$"); #define ArenaControlPool(arena) MV2Pool(&(arena)->controlPoolStruct) #define ArenaCBSBlockPool(arena) (&(arena)->freeCBSBlockPoolStruct.poolStruct) -#define ArenaFreeCBS(arena) (&(arena)->freeCBSStruct) +#define ArenaFreeLand(arena) ((Land)&(arena)->freeLandStruct) /* Forward declarations */ @@ -153,9 +153,9 @@ Bool ArenaCheck(Arena arena) CHECKL(LocusCheck(arena)); - CHECKL(BoolCheck(arena->hasFreeCBS)); - if (arena->hasFreeCBS) - CHECKL(CBSCheck(ArenaFreeCBS(arena))); + CHECKL(BoolCheck(arena->hasFreeLand)); + if (arena->hasFreeLand) + CHECKL(LandCheck(ArenaFreeLand(arena))); return TRUE; } @@ -198,7 +198,7 @@ Res ArenaInit(Arena arena, ArenaClass class, Align alignment, ArgList args) arena->poolReady = FALSE; /* */ arena->lastTract = NULL; arena->lastTractBase = NULL; - arena->hasFreeCBS = FALSE; + arena->hasFreeLand = FALSE; arena->freeZones = ZoneSetUNIV; arena->zoned = zoned; @@ -214,11 +214,12 @@ Res ArenaInit(Arena arena, ArenaClass class, Align alignment, ArgList args) goto failGlobalsInit; arena->sig = ArenaSig; + AVERT(Arena, arena); /* Initialise a pool to hold the arena's CBS blocks. This pool can't be allowed to extend itself using ArenaAlloc because it is used during ArenaAlloc, so MFSExtendSelf is set to FALSE. Failures to extend are - handled where the CBS is used. */ + handled where the Land is used. */ MPS_ARGS_BEGIN(piArgs) { MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSBlockStruct)); @@ -231,18 +232,19 @@ Res ArenaInit(Arena arena, ArenaClass class, Align alignment, ArgList args) if (res != ResOK) goto failMFSInit; - /* Initialise the freeCBS. */ - MPS_ARGS_BEGIN(cbsiArgs) { - MPS_ARGS_ADD(cbsiArgs, CBSBlockPool, ArenaCBSBlockPool(arena)); - MPS_ARGS_DONE(cbsiArgs); - res = CBSInit(ArenaFreeCBS(arena), arena, arena, alignment, - /* fastFind */ TRUE, arena->zoned, cbsiArgs); - } MPS_ARGS_END(cbsiArgs); + /* Initialise the freeLand. */ + MPS_ARGS_BEGIN(landiArgs) { + MPS_ARGS_ADD(landiArgs, CBSBlockPool, ArenaCBSBlockPool(arena)); + MPS_ARGS_ADD(landiArgs, CBSFastFind, TRUE); + MPS_ARGS_ADD(landiArgs, CBSZoned, arena->zoned); + MPS_ARGS_DONE(landiArgs); + res = LandInit(ArenaFreeLand(arena), CBSLandClassGet(), arena, alignment, arena, landiArgs); + } MPS_ARGS_END(landiArgs); AVER(res == ResOK); /* no allocation, no failure expected */ if (res != ResOK) - goto failCBSInit; - /* Note that although freeCBS is initialised, it doesn't have any memory - for its blocks, so hasFreeCBS remains FALSE until later. */ + goto failLandInit; + /* Note that although freeLand is initialised, it doesn't have any memory + for its blocks, so hasFreeLand remains FALSE until later. */ /* initialize the reservoir, */ res = ReservoirInit(&arena->reservoirStruct, arena); @@ -253,8 +255,8 @@ Res ArenaInit(Arena arena, ArenaClass class, Align alignment, ArgList args) return ResOK; failReservoirInit: - CBSFinish(ArenaFreeCBS(arena)); -failCBSInit: + LandFinish(ArenaFreeLand(arena)); +failLandInit: PoolFinish(ArenaCBSBlockPool(arena)); failMFSInit: GlobalsFinish(ArenaGlobals(arena)); @@ -304,15 +306,15 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) goto failStripeSize; } - /* With the primary chunk initialised we can add page memory to the freeCBS + /* With the primary chunk initialised we can add page memory to the freeLand that describes the free address space in the primary chunk. */ - arena->hasFreeCBS = TRUE; - res = ArenaFreeCBSInsert(arena, + arena->hasFreeLand = TRUE; + res = ArenaFreeLandInsert(arena, PageIndexBase(arena->primary, arena->primary->allocBase), arena->primary->limit); if (res != ResOK) - goto failPrimaryCBS; + goto failPrimaryLand; res = ControlInit(arena); if (res != ResOK) @@ -329,7 +331,7 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) failGlobalsCompleteCreate: ControlFinish(arena); failControlInit: -failPrimaryCBS: +failPrimaryLand: failStripeSize: (*class->finish)(arena); failInit: @@ -378,11 +380,11 @@ void ArenaDestroy(Arena arena) arena->poolReady = FALSE; ControlFinish(arena); - /* We must tear down the freeCBS before the chunks, because pages + /* We must tear down the freeLand before the chunks, because pages containing CBS blocks might be allocated in those chunks. */ - AVER(arena->hasFreeCBS); - arena->hasFreeCBS = FALSE; - CBSFinish(ArenaFreeCBS(arena)); + AVER(arena->hasFreeLand); + arena->hasFreeLand = FALSE; + LandFinish(ArenaFreeLand(arena)); /* The CBS block pool can't free its own memory via ArenaFree because that would use the ZonedCBS. */ @@ -601,9 +603,10 @@ Res ControlDescribe(Arena arena, mps_lib_FILE *stream) /* arenaAllocPage -- allocate one page from the arena * - * This is a primitive allocator used to allocate pages for the arena CBS. - * It is called rarely and can use a simple search. It may not use the - * CBS or any pool, because it is used as part of the bootstrap. + * This is a primitive allocator used to allocate pages for the arena + * Land. It is called rarely and can use a simple search. It may not + * use the Land or any pool, because it is used as part of the + * bootstrap. */ static Res arenaAllocPageInChunk(Addr *baseReturn, Chunk chunk, Pool pool) @@ -685,7 +688,7 @@ static Res arenaExtendCBSBlockPool(Range pageRangeReturn, Arena arena) return ResOK; } -/* arenaExcludePage -- exclude CBS block pool's page from CBSs +/* arenaExcludePage -- exclude CBS block pool's page from Land * * Exclude the page we specially allocated for the CBS block pool * so that it doesn't get reallocated. @@ -696,20 +699,20 @@ static void arenaExcludePage(Arena arena, Range pageRange) RangeStruct oldRange; Res res; - res = CBSDelete(&oldRange, ArenaFreeCBS(arena), pageRange); - AVER(res == ResOK); /* we just gave memory to the CBSs */ + res = LandDelete(&oldRange, ArenaFreeLand(arena), pageRange); + AVER(res == ResOK); /* we just gave memory to the Land */ } -/* arenaCBSInsert -- add a block to an arena CBS, extending pool if necessary +/* arenaLandInsert -- add a block to an arena Land, extending pool if necessary * - * The arena's CBSs can't get memory in the usual way because they are used + * The arena's Land can't get memory in the usual way because they are used * in the basic allocator, so we allocate pages specially. * * Only fails if it can't get a page for the block pool. */ -static Res arenaCBSInsert(Range rangeReturn, Arena arena, Range range) +static Res arenaLandInsert(Range rangeReturn, Arena arena, Range range) { Res res; @@ -717,17 +720,17 @@ static Res arenaCBSInsert(Range rangeReturn, Arena arena, Range range) AVERT(Arena, arena); AVERT(Range, range); - res = CBSInsert(rangeReturn, ArenaFreeCBS(arena), range); + res = LandInsert(rangeReturn, ArenaFreeLand(arena), range); - if (res == ResLIMIT) { /* freeCBS MFS pool ran out of blocks */ + if (res == ResLIMIT) { /* freeLand MFS pool ran out of blocks */ RangeStruct pageRange; res = arenaExtendCBSBlockPool(&pageRange, arena); if (res != ResOK) return res; /* .insert.exclude: Must insert before exclude so that we can bootstrap when the zoned CBS is empty. */ - res = CBSInsert(rangeReturn, ArenaFreeCBS(arena), range); - AVER(res == ResOK); /* we just gave memory to the CBSs */ + res = LandInsert(rangeReturn, ArenaFreeLand(arena), range); + AVER(res == ResOK); /* we just gave memory to the Land */ arenaExcludePage(arena, &pageRange); } @@ -735,16 +738,16 @@ static Res arenaCBSInsert(Range rangeReturn, Arena arena, Range range) } -/* ArenaFreeCBSInsert -- add a block to arena CBS, maybe stealing memory +/* ArenaFreeLandInsert -- add a block to arena Land, maybe stealing memory * - * See arenaCBSInsert. This function may only be applied to mapped pages - * and may steal them to store CBS nodes if it's unable to allocate + * See arenaLandInsert. This function may only be applied to mapped pages + * and may steal them to store Land nodes if it's unable to allocate * space for CBS nodes. * * IMPORTANT: May update rangeIO. */ -static void arenaCBSInsertSteal(Range rangeReturn, Arena arena, Range rangeIO) +static void arenaLandInsertSteal(Range rangeReturn, Arena arena, Range rangeIO) { Res res; @@ -752,7 +755,7 @@ static void arenaCBSInsertSteal(Range rangeReturn, Arena arena, Range rangeIO) AVERT(Arena, arena); AVERT(Range, rangeIO); - res = arenaCBSInsert(rangeReturn, arena, rangeIO); + res = arenaLandInsert(rangeReturn, arena, rangeIO); if (res != ResOK) { Addr pageBase; @@ -773,22 +776,22 @@ static void arenaCBSInsertSteal(Range rangeReturn, Arena arena, Range rangeIO) MFSExtend(ArenaCBSBlockPool(arena), pageBase, ArenaAlign(arena)); /* Try again. */ - res = CBSInsert(rangeReturn, ArenaFreeCBS(arena), rangeIO); - AVER(res == ResOK); /* we just gave memory to the CBS */ + res = LandInsert(rangeReturn, ArenaFreeLand(arena), rangeIO); + AVER(res == ResOK); /* we just gave memory to the Land */ } - AVER(res == ResOK); /* not expecting other kinds of error from the CBS */ + AVER(res == ResOK); /* not expecting other kinds of error from the Land */ } -/* ArenaFreeCBSInsert -- add block to free CBS, extending pool if necessary +/* ArenaFreeLandInsert -- add block to free Land, extending pool if necessary * * The inserted block of address space may not abut any existing block. * This restriction ensures that we don't coalesce chunks and allocate * object across the boundary, preventing chunk deletion. */ -Res ArenaFreeCBSInsert(Arena arena, Addr base, Addr limit) +Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit) { RangeStruct range, oldRange; Res res; @@ -796,7 +799,7 @@ Res ArenaFreeCBSInsert(Arena arena, Addr base, Addr limit) AVERT(Arena, arena); RangeInit(&range, base, limit); - res = arenaCBSInsert(&oldRange, arena, &range); + res = arenaLandInsert(&oldRange, arena, &range); if (res != ResOK) return res; @@ -809,7 +812,7 @@ Res ArenaFreeCBSInsert(Arena arena, Addr base, Addr limit) } -/* ArenaFreeCBSDelete -- remove a block from free CBS, extending pool if necessary +/* ArenaFreeLandDelete -- remove a block from free Land, extending pool if necessary * * This is called from ChunkFinish in order to remove address space from * the arena. @@ -820,13 +823,13 @@ Res ArenaFreeCBSInsert(Arena arena, Addr base, Addr limit) * so we can't test that path. */ -void ArenaFreeCBSDelete(Arena arena, Addr base, Addr limit) +void ArenaFreeLandDelete(Arena arena, Addr base, Addr limit) { RangeStruct range, oldRange; Res res; RangeInit(&range, base, limit); - res = CBSDelete(&oldRange, ArenaFreeCBS(arena), &range); + res = LandDelete(&oldRange, ArenaFreeLand(arena), &range); /* Shouldn't be any other kind of failure because we were only deleting a non-coalesced block. See .chunk.no-coalesce and @@ -835,7 +838,7 @@ void ArenaFreeCBSDelete(Arena arena, Addr base, Addr limit) } -static Res arenaAllocFromCBS(Tract *tractReturn, ZoneSet zones, Bool high, +static Res arenaAllocFromLand(Tract *tractReturn, ZoneSet zones, Bool high, Size size, Pool pool) { Arena arena; @@ -858,7 +861,7 @@ static Res arenaAllocFromCBS(Tract *tractReturn, ZoneSet zones, Bool high, /* Step 1. Find a range of address space. */ - res = CBSFindInZones(&range, &oldRange, ArenaFreeCBS(arena), + res = LandFindInZones(&range, &oldRange, ArenaFreeLand(arena), size, zones, high); if (res == ResLIMIT) { /* found block, but couldn't store info */ @@ -867,7 +870,7 @@ static Res arenaAllocFromCBS(Tract *tractReturn, ZoneSet zones, Bool high, if (res != ResOK) /* disastrously short on memory */ return res; arenaExcludePage(arena, &pageRange); - res = CBSFindInZones(&range, &oldRange, ArenaFreeCBS(arena), + res = LandFindInZones(&range, &oldRange, ArenaFreeLand(arena), size, zones, high); AVER(res != ResLIMIT); } @@ -901,7 +904,7 @@ static Res arenaAllocFromCBS(Tract *tractReturn, ZoneSet zones, Bool high, failMark: { - Res insertRes = arenaCBSInsert(&oldRange, arena, &range); + Res insertRes = arenaLandInsert(&oldRange, arena, &range); AVER(insertRes == ResOK); /* We only just deleted it. */ /* If the insert does fail, we lose some address space permanently. */ } @@ -942,10 +945,10 @@ static Res arenaAllocPolicy(Tract *tractReturn, Arena arena, SegPref pref, } } - /* Plan A: allocate from the free CBS in the requested zones */ + /* Plan A: allocate from the free Land in the requested zones */ zones = ZoneSetDiff(pref->zones, pref->avoid); if (zones != ZoneSetEMPTY) { - res = arenaAllocFromCBS(&tract, zones, pref->high, size, pool); + res = arenaAllocFromLand(&tract, zones, pref->high, size, pool); if (res == ResOK) goto found; } @@ -957,7 +960,7 @@ static Res arenaAllocPolicy(Tract *tractReturn, Arena arena, SegPref pref, See also job003384. */ moreZones = ZoneSetUnion(pref->zones, ZoneSetDiff(arena->freeZones, pref->avoid)); if (moreZones != zones) { - res = arenaAllocFromCBS(&tract, moreZones, pref->high, size, pool); + res = arenaAllocFromLand(&tract, moreZones, pref->high, size, pool); if (res == ResOK) goto found; } @@ -968,13 +971,13 @@ static Res arenaAllocPolicy(Tract *tractReturn, Arena arena, SegPref pref, if (res != ResOK) return res; if (zones != ZoneSetEMPTY) { - res = arenaAllocFromCBS(&tract, zones, pref->high, size, pool); + res = arenaAllocFromLand(&tract, zones, pref->high, size, pool); if (res == ResOK) goto found; } if (moreZones != zones) { zones = ZoneSetUnion(zones, ZoneSetDiff(arena->freeZones, pref->avoid)); - res = arenaAllocFromCBS(&tract, moreZones, pref->high, size, pool); + res = arenaAllocFromLand(&tract, moreZones, pref->high, size, pool); if (res == ResOK) goto found; } @@ -986,7 +989,7 @@ static Res arenaAllocPolicy(Tract *tractReturn, Arena arena, SegPref pref, /* TODO: log an event for this */ evenMoreZones = ZoneSetDiff(ZoneSetUNIV, pref->avoid); if (evenMoreZones != moreZones) { - res = arenaAllocFromCBS(&tract, evenMoreZones, pref->high, size, pool); + res = arenaAllocFromLand(&tract, evenMoreZones, pref->high, size, pool); if (res == ResOK) goto found; } @@ -995,7 +998,7 @@ static Res arenaAllocPolicy(Tract *tractReturn, Arena arena, SegPref pref, common ambiguous bit patterns pin them down, causing the zone check to give even more false positives permanently, and possibly retaining garbage indefinitely. */ - res = arenaAllocFromCBS(&tract, ZoneSetUNIV, pref->high, size, pool); + res = arenaAllocFromLand(&tract, ZoneSetUNIV, pref->high, size, pool); if (res == ResOK) goto found; @@ -1113,7 +1116,7 @@ void ArenaFree(Addr base, Size size, Pool pool) RangeInit(&range, base, limit); - arenaCBSInsertSteal(&oldRange, arena, &range); /* may update range */ + arenaLandInsertSteal(&oldRange, arena, &range); /* may update range */ (*arena->class->free)(RangeBase(&range), RangeSize(&range), pool); diff --git a/mps/code/cbs.c b/mps/code/cbs.c index c57c322d267..bf02c9d7737 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -26,6 +26,7 @@ SRCID(cbs, "$Id$"); #define CBSBlockSize(block) AddrOffset((block)->base, (block)->limit) +#define cbsOfLand(land) ((CBS)(land)) #define cbsSplay(cbs) (&((cbs)->splayTreeStruct)) #define cbsOfSplay(_splay) PARENT(CBSStruct, splayTreeStruct, _splay) #define cbsBlockTree(block) (&((block)->treeStruct)) @@ -65,16 +66,14 @@ Bool CBSCheck(CBS cbs) { /* See .enter-leave.simple. */ CHECKS(CBS, cbs); - CHECKL(cbs != NULL); + CHECKL(LandCheck(&cbs->landStruct)); CHECKD(SplayTree, cbsSplay(cbs)); /* nothing to check about treeSize */ CHECKD(Pool, cbs->blockPool); - CHECKU(Arena, cbs->arena); CHECKL(BoolCheck(cbs->fastFind)); CHECKL(BoolCheck(cbs->inCBS)); CHECKL(BoolCheck(cbs->ownPool)); CHECKL(BoolCheck(cbs->zoned)); - /* No MeterCheck */ return TRUE; } @@ -212,7 +211,7 @@ static void cbsUpdateZonedNode(SplayTree splay, Tree tree) cbsUpdateNode(splay, tree); block = cbsBlockOfTree(tree); - arena = cbsOfSplay(splay)->arena; + arena = cbsOfSplay(splay)->landStruct.arena; zones = ZoneSetOfRange(arena, CBSBlockBase(block), CBSBlockLimit(block)); if (TreeHasLeft(tree)) @@ -225,29 +224,34 @@ static void cbsUpdateZonedNode(SplayTree splay, Tree tree) } -/* CBSInit -- Initialise a CBS structure +/* cbsInit -- Initialise a CBS structure * * See . */ ARG_DEFINE_KEY(cbs_extend_by, Size); ARG_DEFINE_KEY(cbs_block_pool, Pool); +ARG_DEFINE_KEY(cbs_fast_find, Bool); +ARG_DEFINE_KEY(cbs_zoned, Bool); -Res CBSInit(CBS cbs, Arena arena, void *owner, Align alignment, - Bool fastFind, Bool zoned, ArgList args) +static Res cbsInit(Land land, ArgList args) { + CBS cbs; + LandClass super; Size extendBy = CBS_EXTEND_BY_DEFAULT; Bool extendSelf = TRUE; + Bool fastFind = FALSE; + Bool zoned = FALSE; ArgStruct arg; Res res; Pool blockPool = NULL; SplayUpdateNodeMethod update; - AVERT(Arena, arena); - AVER(cbs != NULL); - AVER(AlignCheck(alignment)); - AVER(BoolCheck(fastFind)); - AVER(BoolCheck(zoned)); + AVERT(Land, land); + super = LAND_SUPERCLASS(CBSLandClass); + res = (*super->init)(land, args); + if (res != ResOK) + return res; if (ArgPick(&arg, args, CBSBlockPool)) blockPool = arg.val.pool; @@ -255,6 +259,10 @@ Res CBSInit(CBS cbs, Arena arena, void *owner, Align alignment, extendBy = arg.val.size; if (ArgPick(&arg, args, MFSExtendSelf)) extendSelf = arg.val.b; + if (ArgPick(&arg, args, CBSFastFind)) + fastFind = arg.val.b; + if (ArgPick(&arg, args, CBSZoned)) + zoned = arg.val.b; update = SplayTrivUpdate; if (fastFind) @@ -264,6 +272,7 @@ Res CBSInit(CBS cbs, Arena arena, void *owner, Align alignment, update = cbsUpdateZonedNode; } + cbs = cbsOfLand(land); SplayTreeInit(cbsSplay(cbs), cbsCompare, cbsKey, update); if (blockPool != NULL) { @@ -274,7 +283,7 @@ Res CBSInit(CBS cbs, Arena arena, void *owner, Align alignment, MPS_ARGS_ADD(pcArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSBlockStruct)); MPS_ARGS_ADD(pcArgs, MPS_KEY_EXTEND_BY, extendBy); MPS_ARGS_ADD(pcArgs, MFSExtendSelf, extendSelf); - res = PoolCreate(&cbs->blockPool, arena, PoolClassMFS(), pcArgs); + res = PoolCreate(&cbs->blockPool, LandArena(land), PoolClassMFS(), pcArgs); } MPS_ARGS_END(pcArgs); if (res != ResOK) return res; @@ -282,10 +291,8 @@ Res CBSInit(CBS cbs, Arena arena, void *owner, Align alignment, } cbs->treeSize = 0; - cbs->arena = arena; cbs->fastFind = fastFind; cbs->zoned = zoned; - cbs->alignment = alignment; cbs->inCBS = TRUE; METER_INIT(cbs->treeSearch, "size of tree", (void *)cbs); @@ -293,7 +300,6 @@ Res CBSInit(CBS cbs, Arena arena, void *owner, Align alignment, cbs->sig = CBSSig; AVERT(CBS, cbs); - EVENT2(CBSInit, cbs, owner); cbsLeave(cbs); return ResOK; } @@ -304,8 +310,12 @@ Res CBSInit(CBS cbs, Arena arena, void *owner, Align alignment, * See . */ -void CBSFinish(CBS cbs) +static void cbsFinish(Land land) { + CBS cbs; + + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); cbsEnter(cbs); @@ -427,8 +437,9 @@ static void cbsBlockInsert(CBS cbs, CBSBlock block) /* cbsInsertIntoTree -- Insert a range into the tree */ -static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range) +static Res cbsInsertIntoTree(Range rangeReturn, Land land, Range range) { + CBS cbs; Bool b; Res res; Addr base, limit, newBase, newLimit; @@ -438,10 +449,11 @@ static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range) Size oldSize; AVER(rangeReturn != NULL); - AVERT(CBS, cbs); + AVERT(Land, land); AVERT(Range, range); - AVER(RangeIsAligned(range, cbs->alignment)); + AVER(RangeIsAligned(range, LandAlignment(land))); + cbs = cbsOfLand(land); base = RangeBase(range); limit = RangeLimit(range); @@ -522,7 +534,7 @@ static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range) } -/* CBSInsert -- Insert a range into the CBS +/* cbsInsert -- Insert a range into the CBS * * See . * @@ -530,18 +542,21 @@ static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range) * abut an existing range. */ -Res CBSInsert(Range rangeReturn, CBS cbs, Range range) +static Res cbsInsert(Range rangeReturn, Land land, Range range) { + CBS cbs; Res res; + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); cbsEnter(cbs); AVER(rangeReturn != NULL); AVERT(Range, range); - AVER(RangeIsAligned(range, cbs->alignment)); + AVER(RangeIsAligned(range, LandAlignment(land))); - res = cbsInsertIntoTree(rangeReturn, cbs, range); + res = cbsInsertIntoTree(rangeReturn, land, range); cbsLeave(cbs); return res; @@ -550,18 +565,20 @@ Res CBSInsert(Range rangeReturn, CBS cbs, Range range) /* cbsDeleteFromTree -- delete blocks from the tree */ -static Res cbsDeleteFromTree(Range rangeReturn, CBS cbs, Range range) +static Res cbsDeleteFromTree(Range rangeReturn, Land land, Range range) { + CBS cbs; Res res; CBSBlock cbsBlock; Tree tree; Addr base, limit, oldBase, oldLimit; Size oldSize; + AVERT(Land, land); + cbs = cbsOfLand(land); AVER(rangeReturn != NULL); - AVERT(CBS, cbs); AVERT(Range, range); - AVER(RangeIsAligned(range, cbs->alignment)); + AVER(RangeIsAligned(range, LandAlignment(land))); base = RangeBase(range); limit = RangeLimit(range); @@ -626,7 +643,7 @@ static Res cbsDeleteFromTree(Range rangeReturn, CBS cbs, Range range) } -/* CBSDelete -- Remove a range from a CBS +/* cbsDelete -- Remove a range from a CBS * * See . * @@ -634,18 +651,21 @@ static Res cbsDeleteFromTree(Range rangeReturn, CBS cbs, Range range) * an existing range. */ -Res CBSDelete(Range rangeReturn, CBS cbs, Range range) +static Res cbsDelete(Range rangeReturn, Land land, Range range) { - Res res; + CBS cbs; + Res res; + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); cbsEnter(cbs); AVER(rangeReturn != NULL); AVERT(Range, range); - AVER(RangeIsAligned(range, cbs->alignment)); + AVER(RangeIsAligned(range, LandAlignment(land))); - res = cbsDeleteFromTree(rangeReturn, cbs, range); + res = cbsDeleteFromTree(rangeReturn, land, range); cbsLeave(cbs); return res; @@ -683,7 +703,7 @@ static Res cbsSplayNodeDescribe(Tree tree, mps_lib_FILE *stream) } -/* CBSIterate -- iterate over all blocks in CBS +/* cbsIterate -- iterate over all blocks in CBS * * Applies a visitor to all isolated contiguous ranges in a CBS. * It receives a pointer, ``Size`` closure pair to pass on to the @@ -699,8 +719,8 @@ static Res cbsSplayNodeDescribe(Tree tree, mps_lib_FILE *stream) */ typedef struct CBSIterateClosure { - CBS cbs; - CBSVisitor iterate; + Land land; + LandVisitor iterate; void *closureP; Size closureS; } CBSIterateClosure; @@ -710,24 +730,28 @@ static Bool cbsIterateVisit(Tree tree, void *closureP, Size closureS) CBSIterateClosure *closure = closureP; RangeStruct range; CBSBlock cbsBlock; - CBS cbs = closure->cbs; + Land land = closure->land; + CBS cbs = cbsOfLand(land); UNUSED(closureS); cbsBlock = cbsBlockOfTree(tree); RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock)); - if (!closure->iterate(cbs, &range, closure->closureP, closure->closureS)) + if (!closure->iterate(land, &range, closure->closureP, closure->closureS)) return FALSE; METER_ACC(cbs->treeSearch, cbs->treeSize); return TRUE; } -void CBSIterate(CBS cbs, CBSVisitor visitor, - void *closureP, Size closureS) +static void cbsIterate(Land land, LandVisitor visitor, + void *closureP, Size closureS) { + CBS cbs; SplayTree splay; CBSIterateClosure closure; + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); cbsEnter(cbs); AVER(FUNCHECK(visitor)); @@ -737,7 +761,7 @@ void CBSIterate(CBS cbs, CBSVisitor visitor, /* searches and meter it. */ METER_ACC(cbs->treeSearch, cbs->treeSize); - closure.cbs = cbs; + closure.land = land; closure.iterate = visitor; closure.closureP = closureP; closure.closureS = closureS; @@ -766,7 +790,7 @@ Bool FindDeleteCheck(FindDelete findDelete) /* cbsFindDeleteRange -- delete appropriate range of block found */ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Range range, Size size, + Land land, Range range, Size size, FindDelete findDelete) { Bool callDelete = TRUE; @@ -774,11 +798,11 @@ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVERT(CBS, cbs); + AVERT(Land, land); AVERT(Range, range); - AVER(RangeIsAligned(range, cbs->alignment)); + AVER(RangeIsAligned(range, LandAlignment(land))); AVER(size > 0); - AVER(SizeIsAligned(size, cbs->alignment)); + AVER(SizeIsAligned(size, LandAlignment(land))); AVER(RangeSize(range) >= size); AVERT(FindDelete, findDelete); @@ -812,7 +836,7 @@ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, if (callDelete) { Res res; - res = cbsDeleteFromTree(oldRangeReturn, cbs, rangeReturn); + res = cbsDeleteFromTree(oldRangeReturn, land, rangeReturn); /* Can't have run out of memory, because all our callers pass in blocks that were just found in the tree, and we only deleted from one end of the block, so cbsDeleteFromTree did not @@ -824,19 +848,22 @@ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, /* CBSFindFirst -- find the first block of at least the given size */ -Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete) +static Bool cbsFindFirst(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + CBS cbs; Bool found; Tree tree; + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); cbsEnter(cbs); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVER(size > 0); - AVER(SizeIsAligned(size, cbs->alignment)); + AVER(SizeIsAligned(size, LandAlignment(land))); AVER(cbs->fastFind); AVERT(FindDelete, findDelete); @@ -850,7 +877,7 @@ Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, AVER(CBSBlockSize(block) >= size); RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); AVER(RangeSize(&range) >= size); - cbsFindDeleteRange(rangeReturn, oldRangeReturn, cbs, &range, + cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range, size, findDelete); } @@ -858,8 +885,10 @@ Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, return found; } -/* CBSFindFirstInZones -- find the first block of at least the given size - that lies entirely within a zone set */ +/* cbsFindInZones -- find a block of at least the given size that lies + * entirely within a zone set. (The first such block, if high is + * FALSE, or the last, if high is TRUE.) + */ typedef struct cbsTestNodeInZonesClosureStruct { Size size; @@ -902,90 +931,25 @@ static Bool cbsTestTreeInZones(SplayTree splay, Tree tree, ZoneSetInter(block->zones, closure->zoneSet) != ZoneSetEMPTY; } -Res CBSFindInZones(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, - ZoneSet zoneSet, Bool high) -{ - Tree tree; - cbsTestNodeInZonesClosureStruct closure; - Res res; - CBSFindMethod cbsFind; - SplayFindMethod splayFind; - - AVER(rangeReturn != NULL); - AVER(oldRangeReturn != NULL); - AVERT(CBS, cbs); - /* AVER(ZoneSetCheck(zoneSet)); */ - AVER(BoolCheck(high)); - - cbsFind = high ? CBSFindLast : CBSFindFirst; - splayFind = high ? SplayFindLast : SplayFindFirst; - - if (zoneSet == ZoneSetEMPTY) - return ResFAIL; - if (zoneSet == ZoneSetUNIV) { - FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; - if (cbsFind(rangeReturn, oldRangeReturn, cbs, size, fd)) - return ResOK; - else - return ResFAIL; - } - if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(cbs->arena)) - return ResFAIL; - - /* It would be nice if there were a neat way to eliminate all runs of - zones in zoneSet too small for size.*/ - - cbsEnter(cbs); - - closure.arena = cbs->arena; - closure.zoneSet = zoneSet; - closure.size = size; - closure.high = high; - if (splayFind(&tree, cbsSplay(cbs), - cbsTestNodeInZones, - cbsTestTreeInZones, - &closure, sizeof(closure))) { - CBSBlock block = cbsBlockOfTree(tree); - RangeStruct rangeStruct, oldRangeStruct; - - AVER(CBSBlockBase(block) <= closure.base); - AVER(AddrOffset(closure.base, closure.limit) >= size); - AVER(ZoneSetSub(ZoneSetOfRange(cbs->arena, closure.base, closure.limit), zoneSet)); - AVER(closure.limit <= CBSBlockLimit(block)); - - if (!high) - RangeInit(&rangeStruct, closure.base, AddrAdd(closure.base, size)); - else - RangeInit(&rangeStruct, AddrSub(closure.limit, size), closure.limit); - res = cbsDeleteFromTree(&oldRangeStruct, cbs, &rangeStruct); - if (res == ResOK) { /* enough memory to split block */ - RangeCopy(rangeReturn, &rangeStruct); - RangeCopy(oldRangeReturn, &oldRangeStruct); - } - } else - res = ResFAIL; - - cbsLeave(cbs); - return res; -} - - -/* CBSFindLast -- find the last block of at least the given size */ - -Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete) + +/* cbsFindLast -- find the last block of at least the given size */ + +static Bool cbsFindLast(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + CBS cbs; Bool found; Tree tree; + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); cbsEnter(cbs); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVER(size > 0); - AVER(SizeIsAligned(size, cbs->alignment)); + AVER(SizeIsAligned(size, LandAlignment(land))); AVER(cbs->fastFind); AVERT(FindDelete, findDelete); @@ -999,7 +963,7 @@ Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn, AVER(CBSBlockSize(block) >= size); RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); AVER(RangeSize(&range) >= size); - cbsFindDeleteRange(rangeReturn, oldRangeReturn, cbs, &range, + cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range, size, findDelete); } @@ -1008,13 +972,16 @@ Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn, } -/* CBSFindLargest -- find the largest block in the CBS */ +/* cbsFindLargest -- find the largest block in the CBS */ -Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete) +static Bool cbsFindLargest(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + CBS cbs; Bool found = FALSE; + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); cbsEnter(cbs); @@ -1039,7 +1006,7 @@ Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn, AVER(CBSBlockSize(block) >= maxSize); RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); AVER(RangeSize(&range) >= maxSize); - cbsFindDeleteRange(rangeReturn, oldRangeReturn, cbs, &range, + cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range, maxSize, findDelete); } } @@ -1049,15 +1016,91 @@ Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn, } -/* CBSDescribe -- describe a CBS +static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, + ZoneSet zoneSet, Bool high) +{ + CBS cbs; + Tree tree; + cbsTestNodeInZonesClosureStruct closure; + Res res; + LandFindMethod landFind; + SplayFindMethod splayFind; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + cbs = cbsOfLand(land); + AVERT(CBS, cbs); + /* AVER(ZoneSetCheck(zoneSet)); */ + AVER(BoolCheck(high)); + + landFind = high ? cbsFindLast : cbsFindFirst; + splayFind = high ? SplayFindLast : SplayFindFirst; + + if (zoneSet == ZoneSetEMPTY) + return ResFAIL; + if (zoneSet == ZoneSetUNIV) { + FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; + if ((*landFind)(rangeReturn, oldRangeReturn, land, size, fd)) + return ResOK; + else + return ResFAIL; + } + if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) + return ResFAIL; + + /* It would be nice if there were a neat way to eliminate all runs of + zones in zoneSet too small for size.*/ + + cbsEnter(cbs); + + closure.arena = LandArena(land); + closure.zoneSet = zoneSet; + closure.size = size; + closure.high = high; + if (splayFind(&tree, cbsSplay(cbs), + cbsTestNodeInZones, + cbsTestTreeInZones, + &closure, sizeof(closure))) { + CBSBlock block = cbsBlockOfTree(tree); + RangeStruct rangeStruct, oldRangeStruct; + + AVER(CBSBlockBase(block) <= closure.base); + AVER(AddrOffset(closure.base, closure.limit) >= size); + AVER(ZoneSetSub(ZoneSetOfRange(LandArena(land), closure.base, closure.limit), zoneSet)); + AVER(closure.limit <= CBSBlockLimit(block)); + + if (!high) + RangeInit(&rangeStruct, closure.base, AddrAdd(closure.base, size)); + else + RangeInit(&rangeStruct, AddrSub(closure.limit, size), closure.limit); + res = cbsDeleteFromTree(&oldRangeStruct, land, &rangeStruct); + if (res == ResOK) { /* enough memory to split block */ + RangeCopy(rangeReturn, &rangeStruct); + RangeCopy(oldRangeReturn, &oldRangeStruct); + } + } else + res = ResFAIL; + + cbsLeave(cbs); + return res; +} + + +/* cbsDescribe -- describe a CBS * * See . */ -Res CBSDescribe(CBS cbs, mps_lib_FILE *stream) +static Res cbsDescribe(Land land, mps_lib_FILE *stream) { + CBS cbs; Res res; + if (!TESTT(Land, land)) + return ResFAIL; + cbs = cbsOfLand(land); if (!TESTT(CBS, cbs)) return ResFAIL; if (stream == NULL) @@ -1065,7 +1108,6 @@ Res CBSDescribe(CBS cbs, mps_lib_FILE *stream) res = WriteF(stream, "CBS $P {\n", (WriteFP)cbs, - " alignment: $U\n", (WriteFU)cbs->alignment, " blockPool: $P\n", (WriteFP)cbsBlockPool(cbs), " fastFind: $U\n", (WriteFU)cbs->fastFind, " inCBS: $U\n", (WriteFU)cbs->inCBS, @@ -1084,6 +1126,27 @@ Res CBSDescribe(CBS cbs, mps_lib_FILE *stream) } +typedef LandClassStruct CBSLandClassStruct; + +DEFINE_CLASS(CBSLandClass, class) +{ + INHERIT_CLASS(class, LandClass); + class->name = "CBS"; + class->size = sizeof(CBSStruct); + class->init = cbsInit; + class->finish = cbsFinish; + class->insert = cbsInsert; + class->delete = cbsDelete; + class->iterate = cbsIterate; + class->findFirst = cbsFindFirst; + class->findLast = cbsFindLast; + class->findLargest = cbsFindLargest; + class->findInZones = cbsFindInZones; + class->describe = cbsDescribe; +} + + + /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2001-2013 Ravenbrook Limited . diff --git a/mps/code/cbs.h b/mps/code/cbs.h index e425bd80cf8..170c41d496b 100644 --- a/mps/code/cbs.h +++ b/mps/code/cbs.h @@ -15,7 +15,6 @@ #include "range.h" #include "splay.h" - /* TODO: There ought to be different levels of CBS block with inheritance so that CBSs without fastFind don't allocate the maxSize and zones fields, and CBSs without zoned don't allocate the zones field. */ @@ -29,40 +28,21 @@ typedef struct CBSBlockStruct { ZoneSet zones; /* union zone set of all ranges in sub-tree */ } CBSBlockStruct; - -typedef struct CBSStruct *CBS; -typedef Bool (*CBSVisitor)(CBS cbs, Range range, - void *closureP, Size closureS); - extern Bool CBSCheck(CBS cbs); +extern CBSLandClass CBSLandClassGet(void); + extern const struct mps_key_s _mps_key_cbs_block_pool; #define CBSBlockPool (&_mps_key_cbs_block_pool) #define CBSBlockPool_FIELD pool +extern const struct mps_key_s _mps_key_cbs_fast_find; +#define CBSFastFind (&_mps_key_cbs_fast_find) +#define CBSFastFind_FIELD b +extern const struct mps_key_s _mps_key_cbs_zoned; +#define CBSZoned (&_mps_key_cbs_zoned) +#define CBSZoned_FIELD b /* TODO: Passing booleans to affect behaviour is ugly and error-prone. */ -extern Res CBSInit(CBS cbs, Arena arena, void *owner, Align alignment, - Bool fastFind, Bool zoned, ArgList args); -extern void CBSFinish(CBS cbs); - -extern Res CBSInsert(Range rangeReturn, CBS cbs, Range range); -extern Res CBSDelete(Range rangeReturn, CBS cbs, Range range); -extern void CBSIterate(CBS cbs, CBSVisitor visitor, - void *closureP, Size closureS); - -extern Res CBSDescribe(CBS cbs, mps_lib_FILE *stream); - -typedef Bool (*CBSFindMethod)(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete); -extern Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete); -extern Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete); -extern Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete); - -extern Res CBSFindInZones(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, ZoneSet zoneSet, Bool high); #endif /* cbs_h */ diff --git a/mps/code/eventdef.h b/mps/code/eventdef.h index cc3b3d61c21..0ff0a608b58 100644 --- a/mps/code/eventdef.h +++ b/mps/code/eventdef.h @@ -96,7 +96,7 @@ EVENT(X, PoolFinish , 0x0016, TRUE, Pool) \ EVENT(X, PoolAlloc , 0x0017, TRUE, Object) \ EVENT(X, PoolFree , 0x0018, TRUE, Object) \ - EVENT(X, CBSInit , 0x0019, TRUE, Pool) \ + EVENT(X, LandInit , 0x0019, TRUE, Pool) \ EVENT(X, Intern , 0x001a, TRUE, User) \ EVENT(X, Label , 0x001b, TRUE, User) \ EVENT(X, TraceStart , 0x001c, TRUE, Trace) \ @@ -311,8 +311,8 @@ PARAM(X, 1, A, old) \ PARAM(X, 2, W, size) -#define EVENT_CBSInit_PARAMS(PARAM, X) \ - PARAM(X, 0, P, cbs) \ +#define EVENT_LandInit_PARAMS(PARAM, X) \ + PARAM(X, 0, P, land) \ PARAM(X, 1, P, owner) #define EVENT_Intern_PARAMS(PARAM, X) \ diff --git a/mps/code/fbmtest.c b/mps/code/fbmtest.c index f98c49032a9..b4072d929af 100644 --- a/mps/code/fbmtest.c +++ b/mps/code/fbmtest.c @@ -56,7 +56,7 @@ typedef struct FBMStateStruct { BT allocTable; Addr block; union { - CBS cbs; + Land land; Freelist fl; } the; } FBMStateStruct, *FBMState; @@ -83,7 +83,7 @@ static Index (indexOfAddr)(FBMState state, Addr a) static void describe(FBMState state) { switch (state->type) { case FBMTypeCBS: - die(CBSDescribe(state->the.cbs, mps_lib_get_stdout()), "CBSDescribe"); + die(LandDescribe(state->the.land, mps_lib_get_stdout()), "LandDescribe"); break; case FBMTypeFreelist: die(FreelistDescribe(state->the.fl, mps_lib_get_stdout()), "FreelistDescribe"); @@ -125,10 +125,10 @@ static Bool checkCallback(Range range, void *closureP, Size closureS) } -static Bool checkCBSCallback(CBS cbs, Range range, +static Bool checkCBSCallback(Land land, Range range, void *closureP, Size closureS) { - UNUSED(cbs); + UNUSED(land); return checkCallback(range, closureP, closureS); } @@ -151,7 +151,7 @@ static void check(FBMState state) switch (state->type) { case FBMTypeCBS: - CBSIterate(state->the.cbs, checkCBSCallback, (void *)&closure, 0); + LandIterate(state->the.land, checkCBSCallback, (void *)&closure, 0); break; case FBMTypeFreelist: FreelistIterate(state->the.fl, checkFLCallback, (void *)&closure, 0); @@ -305,7 +305,7 @@ static void allocate(FBMState state, Addr base, Addr limit) RangeInit(&range, base, limit); switch (state->type) { case FBMTypeCBS: - res = CBSDelete(&oldRange, state->the.cbs, &range); + res = LandDelete(&oldRange, state->the.land, &range); break; case FBMTypeFreelist: res = FreelistDelete(&oldRange, state->the.fl, &range); @@ -381,7 +381,7 @@ static void deallocate(FBMState state, Addr base, Addr limit) RangeInit(&range, base, limit); switch (state->type) { case FBMTypeCBS: - res = CBSInsert(&freeRange, state->the.cbs, &range); + res = LandInsert(&freeRange, state->the.land, &range); break; case FBMTypeFreelist: res = FreelistInsert(&freeRange, state->the.fl, &range); @@ -459,8 +459,8 @@ static void find(FBMState state, Size size, Bool high, FindDelete findDelete) switch (state->type) { case FBMTypeCBS: - found = (high ? CBSFindLast : CBSFindFirst) - (&foundRange, &oldRange, state->the.cbs, size * state->align, findDelete); + found = (high ? LandFindLast : LandFindFirst) + (&foundRange, &oldRange, state->the.land, size * state->align, findDelete); break; case FBMTypeFreelist: found = (high ? FreelistFindLast : FreelistFindFirst) @@ -558,6 +558,7 @@ extern int main(int argc, char *argv[]) BT allocTable; FreelistStruct flStruct; CBSStruct cbsStruct; + Land land = (Land)&cbsStruct; Align align; testlib_init(argc, argv); @@ -585,16 +586,18 @@ extern int main(int argc, char *argv[]) (char *)dummyBlock + ArraySize); } - die((mps_res_t)CBSInit(&cbsStruct, arena, arena, align, - /* fastFind */ TRUE, /* zoned */ FALSE, mps_args_none), - "failed to initialise CBS"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, CBSFastFind, TRUE); + die((mps_res_t)LandInit(land, CBSLandClassGet(), arena, align, NULL, args), + "failed to initialise CBS"); + } MPS_ARGS_END(args); state.type = FBMTypeCBS; state.align = align; state.block = dummyBlock; state.allocTable = allocTable; - state.the.cbs = &cbsStruct; + state.the.land = land; test(&state, nCBSOperations); - CBSFinish(&cbsStruct); + LandFinish(land); die((mps_res_t)FreelistInit(&flStruct, align), "failed to initialise Freelist"); diff --git a/mps/code/fotest.c b/mps/code/fotest.c index 4025d20a2c6..bef621362d7 100644 --- a/mps/code/fotest.c +++ b/mps/code/fotest.c @@ -38,8 +38,8 @@ /* Accessors for the CBS used to implement a pool. */ -extern CBS _mps_mvff_cbs(mps_pool_t); -extern CBS _mps_mvt_cbs(mps_pool_t); +extern Land _mps_mvff_cbs(mps_pool_t); +extern Land _mps_mvt_cbs(mps_pool_t); /* "OOM" pool class -- dummy alloc/free pool class whose alloc() @@ -180,7 +180,7 @@ int main(int argc, char *argv[]) die(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), "create MVFF"); } MPS_ARGS_END(args); { - CBS cbs = _mps_mvff_cbs(pool); + CBS cbs = (CBS)_mps_mvff_cbs(pool); die(stress(randomSizeAligned, alignment, pool, cbs), "stress MVFF"); } mps_pool_destroy(pool); @@ -199,7 +199,7 @@ int main(int argc, char *argv[]) die(mps_pool_create_k(&pool, arena, mps_class_mvt(), args), "create MVFF"); } MPS_ARGS_END(args); { - CBS cbs = _mps_mvt_cbs(pool); + CBS cbs = (CBS)_mps_mvt_cbs(pool); die(stress(randomSizeAligned, alignment, pool, cbs), "stress MVT"); } mps_pool_destroy(pool); diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 6260451ff59..a299dafb6e6 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -6,7 +6,6 @@ * .sources: . */ -#include "cbs.h" #include "freelist.h" #include "mpm.h" @@ -589,22 +588,22 @@ Res FreelistDescribe(Freelist fl, mps_lib_FILE *stream) /* freelistFlushIterateMethod -- Iterate method for - * FreelistFlushToCBS. Attempst to insert the range into the CBS. + * FreelistFlushToLand. Attempst to insert the range into the Land. */ static Bool freelistFlushIterateMethod(Bool *deleteReturn, Range range, void *closureP, Size closureS) { Res res; RangeStruct newRange; - CBS cbs; + Land land; AVER(deleteReturn != NULL); AVERT(Range, range); AVER(closureP != NULL); UNUSED(closureS); - cbs = closureP; - res = CBSInsert(&newRange, cbs, range); + land = closureP; + res = LandInsert(&newRange, land, range); if (res == ResOK) { *deleteReturn = TRUE; return TRUE; @@ -615,12 +614,12 @@ static Bool freelistFlushIterateMethod(Bool *deleteReturn, Range range, } -void FreelistFlushToCBS(Freelist fl, CBS cbs) +void FreelistFlushToLand(Freelist fl, Land land) { AVERT(Freelist, fl); - AVERT(CBS, cbs); + AVERT(Land, land); - FreelistIterate(fl, freelistFlushIterateMethod, cbs, 0); + FreelistIterate(fl, freelistFlushIterateMethod, land, 0); } diff --git a/mps/code/freelist.h b/mps/code/freelist.h index 1bb9840c8c9..b9aea9bdf6c 100644 --- a/mps/code/freelist.h +++ b/mps/code/freelist.h @@ -9,7 +9,6 @@ #ifndef freelist_h #define freelist_h -#include "cbs.h" #include "mpmtypes.h" #include "range.h" @@ -46,7 +45,7 @@ extern Bool FreelistFindLast(Range rangeReturn, Range oldRangeReturn, extern Bool FreelistFindLargest(Range rangeReturn, Range oldRangeReturn, Freelist fl, Size size, FindDelete findDelete); -extern void FreelistFlushToCBS(Freelist fl, CBS cbs); +extern void FreelistFlushToLand(Freelist fl, Land land); #endif /* freelist.h */ diff --git a/mps/code/land.c b/mps/code/land.c new file mode 100644 index 00000000000..3abd9c41e15 --- /dev/null +++ b/mps/code/land.c @@ -0,0 +1,348 @@ +/* land.c: LAND (COLLECTION OF ADDRESS RANGES) IMPLEMENTATION + * + * $Id: //info.ravenbrook.com/project/mps/branch/2014-03-30/land/code/land.c#1 $ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * .design: + */ + +#include "mpm.h" + +SRCID(land, "$Id$"); + + +Bool LandCheck(Land land) +{ + CHECKS(Land, land); + CHECKU(Arena, land->arena); + CHECKL(AlignCheck(land->alignment)); + return TRUE; +} + +Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args) +{ + Res res; + + AVER(land != NULL); + AVERT(LandClass, class); + AVER(AlignCheck(alignment)); + + land->alignment = alignment; + land->arena = arena; + land->class = class; + land->sig = LandSig; + + AVERT(Land, land); + + res = (*class->init)(land, args); + if (res != ResOK) + goto failInit; + + EVENT2(LandInit, land, owner); + return ResOK; + + failInit: + land->sig = SigInvalid; + return res; +} + +Res LandCreate(Land *landReturn, Arena arena, LandClass class, Align alignment, void *owner, ArgList args) +{ + Res res; + Land land; + void *p; + + AVER(landReturn != NULL); + AVERT(Arena, arena); + AVERT(LandClass, class); + + res = ControlAlloc(&p, arena, class->size, + /* withReservoirPermit */ FALSE); + if (res != ResOK) + goto failAlloc; + land = p; + + res = LandInit(land, class, arena, alignment, owner, args); + if (res != ResOK) + goto failInit; + + *landReturn = land; + return ResOK; + +failInit: + ControlFree(arena, land, class->size); +failAlloc: + return res; +} + +void LandDestroy(Land land) +{ + Arena arena; + LandClass class; + + AVERT(Land, land); + arena = land->arena; + class = land->class; + AVERT(LandClass, class); + LandFinish(land); + ControlFree(arena, land, class->size); +} + +void LandFinish(Land land) +{ + AVERT(Land, land); + (*land->class->finish)(land); +} + +Res LandInsert(Range rangeReturn, Land land, Range range) +{ + AVER(rangeReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + AVER(RangeIsAligned(range, land->alignment)); + + return (*land->class->insert)(rangeReturn, land, range); +} + +Res LandDelete(Range rangeReturn, Land land, Range range) +{ + AVER(rangeReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + AVER(RangeIsAligned(range, land->alignment)); + + return (*land->class->delete)(rangeReturn, land, range); +} + +void LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +{ + AVERT(Land, land); + AVER(FUNCHECK(visitor)); + + (*land->class->iterate)(land, visitor, closureP, closureS); +} + +Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + AVER(SizeIsAligned(size, land->alignment)); + AVER(FindDeleteCheck(findDelete)); + + return (*land->class->findFirst)(rangeReturn, oldRangeReturn, land, size, + findDelete); +} + +Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + AVER(SizeIsAligned(size, land->alignment)); + AVER(FindDeleteCheck(findDelete)); + + return (*land->class->findLast)(rangeReturn, oldRangeReturn, land, size, + findDelete); +} + +Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + AVER(SizeIsAligned(size, land->alignment)); + AVER(FindDeleteCheck(findDelete)); + + return (*land->class->findLargest)(rangeReturn, oldRangeReturn, land, size, + findDelete); +} + +Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) +{ + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + AVER(SizeIsAligned(size, land->alignment)); + /* AVER(ZoneSetCheck(zoneSet)); */ + AVER(BoolCheck(high)); + + return (*land->class->findInZones)(rangeReturn, oldRangeReturn, land, size, + zoneSet, high); +} + +Res LandDescribe(Land land, mps_lib_FILE *stream) +{ + Res res; + + if (!TESTT(Land, land)) return ResFAIL; + if (stream == NULL) return ResFAIL; + + res = WriteF(stream, + "Land $P {\n", (WriteFP)land, + " class $P", (WriteFP)land->class, + " (\"$S\")\n", land->class->name, + " arena $P\n", (WriteFP)land->arena, + " align $U\n", (WriteFU)land->alignment, + NULL); + if (res != ResOK) + return res; + + res = (*land->class->describe)(land, stream); + if (res != ResOK) + return res; + + res = WriteF(stream, "} Land $P\n", (WriteFP)land, NULL); + return ResOK; +} + + +Bool LandClassCheck(LandClass class) +{ + CHECKL(ProtocolClassCheck(&class->protocol)); + CHECKL(class->name != NULL); /* Should be <=6 char C identifier */ + CHECKL(class->size >= sizeof(LandStruct)); + CHECKL(FUNCHECK(class->init)); + CHECKL(FUNCHECK(class->finish)); + CHECKL(FUNCHECK(class->insert)); + CHECKL(FUNCHECK(class->delete)); + CHECKL(FUNCHECK(class->findFirst)); + CHECKL(FUNCHECK(class->findLast)); + CHECKL(FUNCHECK(class->findLargest)); + CHECKL(FUNCHECK(class->findInZones)); + CHECKL(FUNCHECK(class->describe)); + CHECKS(LandClass, class); + return TRUE; +} + + +static Res landTrivInit(Land land, ArgList args) +{ + AVERT(Land, land); + AVER(ArgListCheck(args)); + UNUSED(args); + return ResOK; +} + +static void landTrivFinish(Land land) +{ + AVERT(Land, land); + NOOP; +} + +static Res landNoInsert(Range rangeReturn, Land land, Range range) +{ + AVER(rangeReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + return ResUNIMPL; +} + +static Res landNoDelete(Range rangeReturn, Land land, Range range) +{ + AVER(rangeReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + return ResUNIMPL; +} + +static void landNoIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +{ + AVERT(Land, land); + AVER(visitor != NULL); + UNUSED(closureP); + UNUSED(closureS); + NOOP; +} + +static Bool landNoFind(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + UNUSED(size); + AVER(FindDeleteCheck(findDelete)); + return ResUNIMPL; +} + +static Res landNoFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) +{ + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + UNUSED(size); + UNUSED(zoneSet); + AVER(BoolCheck(high)); + return ResUNIMPL; +} + +static Res landTrivDescribe(Land land, mps_lib_FILE *stream) +{ + if (!TESTT(Land, land)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + /* dispatching function does it all */ + return ResOK; +} + +DEFINE_CLASS(LandClass, class) +{ + INHERIT_CLASS(&class->protocol, ProtocolClass); + class->name = "LAND"; + class->size = sizeof(LandStruct); + class->init = landTrivInit; + class->finish = landTrivFinish; + class->insert = landNoInsert; + class->delete = landNoDelete; + class->iterate = landNoIterate; + class->findFirst = landNoFind; + class->findLast = landNoFind; + class->findLargest = landNoFind; + class->findInZones = landNoFindInZones; + class->describe = landTrivDescribe; + class->sig = LandClassSig; + AVERT(LandClass, class); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * 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. + */ diff --git a/mps/code/locus.c b/mps/code/locus.c index f0ba7415cde..a3d7986deb3 100644 --- a/mps/code/locus.c +++ b/mps/code/locus.c @@ -480,6 +480,7 @@ void LocusInit(Arena arena) gen->proflow = 0.0; RingInit(&gen->locusRing); gen->sig = GenDescSig; + AVERT(GenDesc, gen); } diff --git a/mps/code/mpm.h b/mps/code/mpm.h index da45d9aedd2..4e65ea83ed4 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -490,8 +490,8 @@ extern void ArenaFinish(Arena arena); extern Res ArenaDescribe(Arena arena, mps_lib_FILE *stream); extern Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream); extern Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context); -extern Res ArenaFreeCBSInsert(Arena arena, Addr base, Addr limit); -extern void ArenaFreeCBSDelete(Arena arena, Addr base, Addr limit); +extern Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit); +extern void ArenaFreeLandDelete(Arena arena, Addr base, Addr limit); extern Bool GlobalsCheck(Globals arena); @@ -992,6 +992,30 @@ extern Size VMReserved(VM vm); extern Size VMMapped(VM vm); +/* Land Interface -- see */ + +extern Bool LandCheck(Land land); +#define LandArena(land) ((land)->arena) +#define LandAlignment(land) ((land)->alignment) + +extern Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args); +extern Res LandCreate(Land *landReturn, Arena arena, LandClass class, Align alignment, void *owner, ArgList args); +extern void LandDestroy(Land land); +extern void LandFinish(Land land); +extern Res LandInsert(Range rangeReturn, Land land, Range range); +extern Res LandDelete(Range rangeReturn, Land land, Range range); +extern void LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS); +extern Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete); +extern Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete); +extern Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete); +extern Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high); +extern Res LandDescribe(Land land, mps_lib_FILE *stream); + +extern Bool LandClassCheck(LandClass class); +extern LandClass LandClassGet(void); +#define LAND_SUPERCLASS(className) ((LandClass)SUPERCLASS(className)) + + /* Stack Probe */ extern void StackProbe(Size depth); diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 85e95a78ec1..8d7a414465b 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -604,7 +604,50 @@ typedef struct GlobalsStruct { } GlobalsStruct; +/* LandClassStruct -- land class structure + * + * See . + */ + +#define LandClassSig ((Sig)0x5197A4DC) /* SIGnature LAND Class */ + +typedef struct LandClassStruct { + ProtocolClassStruct protocol; + const char *name; /* class name string */ + size_t size; /* size of outer structure */ + LandInitMethod init; /* initialize the land */ + LandFinishMethod finish; /* finish the land */ + LandInsertMethod insert; /* insert a range into the land */ + LandDeleteMethod delete; /* delete a range from the land */ + LandIterateMethod iterate; /* iterate over ranges in the land */ + LandFindMethod findFirst; /* find first range of given size */ + LandFindMethod findLast; /* find last range of given size */ + LandFindMethod findLargest; /* find largest range */ + LandFindInZonesMethod findInZones; /* find first range of given size in zone set */ + LandDescribeMethod describe; /* describe the land */ + Sig sig; /* .class.end-sig */ +} LandClassStruct; + + +/* LandStruct -- generic land structure + * + * See , + */ + +#define LandSig ((Sig)0x5197A4D9) /* SIGnature LAND */ + +typedef struct LandStruct { + Sig sig; /* */ + LandClass class; /* land class structure */ + Arena arena; /* owning arena */ + Align alignment; /* alignment of addresses */ +} LandStruct; + + /* CBSStruct -- coalescing block structure + * + * CBS is a subclass of Land that maintains a collection of disjoint + * ranges in a splay tree. * * See . */ @@ -612,18 +655,17 @@ typedef struct GlobalsStruct { #define CBSSig ((Sig)0x519CB599) /* SIGnature CBS */ typedef struct CBSStruct { + LandStruct landStruct; /* superclass fields come first */ SplayTreeStruct splayTreeStruct; STATISTIC_DECL(Count treeSize); - Arena arena; - Pool blockPool; - Align alignment; + Pool blockPool; /* pool that manages blocks */ Bool fastFind; /* maintain and use size property? */ Bool zoned; /* maintain and use zone property? */ Bool inCBS; /* prevent reentrance */ Bool ownPool; /* did we create blockPool? */ /* meters for sizes of search structures at each op */ METER_DECL(treeSearch); - Sig sig; /* sig at end because embeded */ + Sig sig; /* sig at end because embedded */ } CBSStruct; @@ -661,9 +703,9 @@ typedef struct mps_arena_s { Serial chunkSerial; /* next chunk number */ ChunkCacheEntryStruct chunkCache; /* just one entry */ - Bool hasFreeCBS; /* Is freeCBS available? */ + Bool hasFreeLand; /* Is freeLand available? */ MFSStruct freeCBSBlockPoolStruct; - CBSStruct freeCBSStruct; + CBSStruct freeLandStruct; ZoneSet freeZones; /* zones not yet allocated */ Bool zoned; /* use zoned allocation? */ diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index 9aac8322b35..8abc43bb111 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -109,7 +109,13 @@ typedef struct AllocPatternStruct *AllocPattern; typedef struct AllocFrameStruct *AllocFrame; /* */ typedef struct ReservoirStruct *Reservoir; /* */ typedef struct StackContextStruct *StackContext; -typedef unsigned FindDelete; /* */ +typedef struct RangeStruct *Range; /* */ +typedef struct LandStruct *Land; /* */ +typedef struct LandClassStruct *LandClass; /* */ +typedef LandClass CBSLandClass; /* */ +typedef struct CBSStruct *CBS; /* */ +typedef LandClass FreelistClass; /* */ +typedef unsigned FindDelete; /* */ /* Arena*Method -- see */ @@ -262,6 +268,19 @@ typedef struct TraceStartMessageStruct *TraceStartMessage; typedef struct TraceMessageStruct *TraceMessage; /* trace end */ +/* Land*Method -- see */ + +typedef Res (*LandInitMethod)(Land land, ArgList args); +typedef void (*LandFinishMethod)(Land land); +typedef Res (*LandInsertMethod)(Range rangeReturn, Land land, Range range); +typedef Res (*LandDeleteMethod)(Range rangeReturn, Land land, Range range); +typedef Bool (*LandVisitor)(Land land, Range range, void *closureP, Size closureS); +typedef void (*LandIterateMethod)(Land land, LandVisitor visitor, void *closureP, Size closureS); +typedef Bool (*LandFindMethod)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete); +typedef Res (*LandFindInZonesMethod)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high); +typedef Res (*LandDescribeMethod)(Land land, mps_lib_FILE *stream); + + /* CONSTANTS */ diff --git a/mps/code/mps.c b/mps/code/mps.c index 0a8f29be9ba..34c7a9b49cd 100644 --- a/mps/code/mps.c +++ b/mps/code/mps.c @@ -75,6 +75,7 @@ #include "range.c" #include "freelist.c" #include "sa.c" +#include "land.c" /* Additional pool classes */ diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index d9d063dc39e..f3de433a5ef 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -51,7 +51,7 @@ static Res MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, MVT mvt, Size min); static Bool MVTCheckFit(Addr base, Addr limit, Size min, Arena arena); static ABQ MVTABQ(MVT mvt); -static CBS MVTCBS(MVT mvt); +static Land MVTCBS(MVT mvt); static Freelist MVTFreelist(MVT mvt); @@ -168,9 +168,9 @@ static ABQ MVTABQ(MVT mvt) } -static CBS MVTCBS(MVT mvt) +static Land MVTCBS(MVT mvt) { - return &mvt->cbsStruct; + return (Land)(&mvt->cbsStruct); } @@ -269,8 +269,10 @@ static Res MVTInit(Pool pool, ArgList args) if (abqDepth < 3) abqDepth = 3; - res = CBSInit(MVTCBS(mvt), arena, (void *)mvt, align, - /* fastFind */ FALSE, /* zoned */ FALSE, args); + MPS_ARGS_BEGIN(landiArgs) { + MPS_ARGS_ADD(landiArgs, CBSFastFind, TRUE); + res = LandInit(MVTCBS(mvt), CBSLandClassGet(), arena, align, mvt, landiArgs); + } MPS_ARGS_END(landiArgs); if (res != ResOK) goto failCBS; @@ -348,7 +350,7 @@ static Res MVTInit(Pool pool, ArgList args) failFreelist: ABQFinish(arena, MVTABQ(mvt)); failABQ: - CBSFinish(MVTCBS(mvt)); + LandFinish(MVTCBS(mvt)); failCBS: AVER(res != ResOK); return res; @@ -422,7 +424,7 @@ static void MVTFinish(Pool pool) /* Finish the Freelist, ABQ and CBS structures */ FreelistFinish(MVTFreelist(mvt)); ABQFinish(arena, MVTABQ(mvt)); - CBSFinish(MVTCBS(mvt)); + LandFinish(MVTCBS(mvt)); } @@ -808,10 +810,10 @@ static Res MVTInsert(MVT mvt, Addr base, Addr limit) /* Attempt to flush the Freelist to the CBS to give maximum * opportunities for coalescence. */ - FreelistFlushToCBS(MVTFreelist(mvt), MVTCBS(mvt)); + FreelistFlushToLand(MVTFreelist(mvt), MVTCBS(mvt)); RangeInit(&range, base, limit); - res = CBSInsert(&newRange, MVTCBS(mvt), &range); + res = LandInsert(&newRange, MVTCBS(mvt), &range); if (ResIsAllocFailure(res)) { /* CBS ran out of memory for splay nodes: add range to emergency * free list instead. */ @@ -845,7 +847,7 @@ static Res MVTDelete(MVT mvt, Addr base, Addr limit) AVER(base < limit); RangeInit(&range, base, limit); - res = CBSDelete(&rangeOld, MVTCBS(mvt), &range); + res = LandDelete(&rangeOld, MVTCBS(mvt), &range); if (ResIsAllocFailure(res)) { /* CBS ran out of memory for splay nodes, which must mean that * there were fragments on both sides: see @@ -853,7 +855,7 @@ static Res MVTDelete(MVT mvt, Addr base, Addr limit) * deleting the whole of rangeOld (which requires no * allocation) and re-inserting the fragments. */ RangeStruct rangeOld2; - res = CBSDelete(&rangeOld2, MVTCBS(mvt), &rangeOld); + res = LandDelete(&rangeOld2, MVTCBS(mvt), &rangeOld); AVER(res == ResOK); AVER(RangesEqual(&rangeOld2, &rangeOld)); AVER(RangeBase(&rangeOld) != base); @@ -1043,7 +1045,7 @@ static Res MVTDescribe(Pool pool, mps_lib_FILE *stream) NULL); if(res != ResOK) return res; - res = CBSDescribe(MVTCBS(mvt), stream); + res = LandDescribe(MVTCBS(mvt), stream); if(res != ResOK) return res; res = ABQDescribe(MVTABQ(mvt), (ABQDescribeElement)RangeDescribe, stream); @@ -1285,11 +1287,11 @@ static Bool MVTRefillCallback(MVT mvt, Range range) return MVTReserve(mvt, range); } -static Bool MVTCBSRefillCallback(CBS cbs, Range range, +static Bool MVTCBSRefillCallback(Land land, Range range, void *closureP, Size closureS) { MVT mvt; - AVERT(CBS, cbs); + AVERT(Land, land); mvt = closureP; AVERT(MVT, mvt); UNUSED(closureS); @@ -1324,7 +1326,7 @@ static void MVTRefillABQIfEmpty(MVT mvt, Size size) if (mvt->abqOverflow && ABQIsEmpty(MVTABQ(mvt))) { mvt->abqOverflow = FALSE; METER_ACC(mvt->refills, size); - CBSIterate(MVTCBS(mvt), &MVTCBSRefillCallback, mvt, 0); + LandIterate(MVTCBS(mvt), &MVTCBSRefillCallback, mvt, 0); FreelistIterate(MVTFreelist(mvt), &MVTFreelistRefillCallback, mvt, 0); } } @@ -1387,11 +1389,11 @@ static Bool MVTContingencyCallback(MVTContigency cl, Range range) return TRUE; } -static Bool MVTCBSContingencyCallback(CBS cbs, Range range, +static Bool MVTCBSContingencyCallback(Land land, Range range, void *closureP, Size closureS) { MVTContigency cl = closureP; - UNUSED(cbs); + AVERT(Land, land); UNUSED(closureS); return MVTContingencyCallback(cl, range); } @@ -1421,9 +1423,9 @@ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, cls.steps = 0; cls.hardSteps = 0; - FreelistFlushToCBS(MVTFreelist(mvt), MVTCBS(mvt)); + FreelistFlushToLand(MVTFreelist(mvt), MVTCBS(mvt)); - CBSIterate(MVTCBS(mvt), MVTCBSContingencyCallback, (void *)&cls, 0); + LandIterate(MVTCBS(mvt), MVTCBSContingencyCallback, (void *)&cls, 0); FreelistIterate(MVTFreelist(mvt), MVTFreelistContingencyCallback, (void *)&cls, 0); if (!cls.found) @@ -1472,8 +1474,8 @@ static Bool MVTCheckFit(Addr base, Addr limit, Size min, Arena arena) /* Return the CBS of an MVT pool for the benefit of fotest.c. */ -extern CBS _mps_mvt_cbs(mps_pool_t); -CBS _mps_mvt_cbs(mps_pool_t mps_pool) { +extern Land _mps_mvt_cbs(mps_pool_t); +Land _mps_mvt_cbs(mps_pool_t mps_pool) { Pool pool; MVT mvt; diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index e1d1d094d38..f1679e5acaa 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -58,7 +58,7 @@ typedef struct MVFFStruct { /* MVFF pool outer structure */ #define Pool2MVFF(pool) PARENT(MVFFStruct, poolStruct, pool) #define MVFF2Pool(mvff) (&((mvff)->poolStruct)) -#define CBSOfMVFF(mvff) (&((mvff)->cbsStruct)) +#define CBSOfMVFF(mvff) ((Land)&((mvff)->cbsStruct)) #define MVFFOfCBS(cbs) PARENT(MVFFStruct, cbsStruct, cbs) #define FreelistOfMVFF(mvff) (&((mvff)->flStruct)) #define MVFFOfFreelist(fl) PARENT(MVFFStruct, flStruct, fl) @@ -95,7 +95,7 @@ static Res MVFFAddToFreeList(Addr *baseIO, Addr *limitIO, MVFF mvff) { AVERT(MVFF, mvff); RangeInit(&range, *baseIO, *limitIO); - res = CBSInsert(&newRange, CBSOfMVFF(mvff), &range); + res = LandInsert(&newRange, CBSOfMVFF(mvff), &range); if (ResIsAllocFailure(res)) { /* CBS ran out of memory for splay nodes: add range to emergency * free list instead. */ @@ -150,7 +150,7 @@ static void MVFFFreeSegs(MVFF mvff, Addr base, Addr limit) RangeStruct range, oldRange; RangeInit(&range, segBase, segLimit); - res = CBSDelete(&oldRange, CBSOfMVFF(mvff), &range); + res = LandDelete(&oldRange, CBSOfMVFF(mvff), &range); if (res == ResOK) { mvff->free -= RangeSize(&range); } else if (ResIsAllocFailure(res)) { @@ -160,7 +160,7 @@ static void MVFFFreeSegs(MVFF mvff, Addr base, Addr limit) * deleting the whole of oldRange (which requires no * allocation) and re-inserting the fragments. */ RangeStruct oldRange2; - res = CBSDelete(&oldRange2, CBSOfMVFF(mvff), &oldRange); + res = LandDelete(&oldRange2, CBSOfMVFF(mvff), &oldRange); AVER(res == ResOK); AVER(RangesEqual(&oldRange2, &oldRange)); mvff->free -= RangeSize(&oldRange); @@ -297,12 +297,12 @@ static Bool MVFFFindFirstFree(Addr *baseReturn, Addr *limitReturn, AVER(size > 0); AVER(SizeIsAligned(size, PoolAlignment(MVFF2Pool(mvff)))); - FreelistFlushToCBS(FreelistOfMVFF(mvff), CBSOfMVFF(mvff)); + FreelistFlushToLand(FreelistOfMVFF(mvff), CBSOfMVFF(mvff)); findDelete = mvff->slotHigh ? FindDeleteHIGH : FindDeleteLOW; foundBlock = - (mvff->firstFit ? CBSFindFirst : CBSFindLast) + (mvff->firstFit ? LandFindFirst : LandFindLast) (&range, &oldRange, CBSOfMVFF(mvff), size, findDelete); if (!foundBlock) { @@ -411,9 +411,9 @@ static Bool MVFFFindLargest(Range range, Range oldRange, MVFF mvff, AVER(size > 0); AVERT(FindDelete, findDelete); - FreelistFlushToCBS(FreelistOfMVFF(mvff), CBSOfMVFF(mvff)); + FreelistFlushToLand(FreelistOfMVFF(mvff), CBSOfMVFF(mvff)); - if (CBSFindLargest(range, oldRange, CBSOfMVFF(mvff), size, findDelete)) + if (LandFindLargest(range, oldRange, CBSOfMVFF(mvff), size, findDelete)) return TRUE; if (FreelistFindLargest(range, oldRange, FreelistOfMVFF(mvff), @@ -602,8 +602,10 @@ static Res MVFFInit(Pool pool, ArgList args) if (res != ResOK) goto failInit; - res = CBSInit(CBSOfMVFF(mvff), arena, (void *)mvff, align, - /* fastFind */ TRUE, /* zoned */ FALSE, args); + MPS_ARGS_BEGIN(landiArgs) { + MPS_ARGS_ADD(landiArgs, CBSFastFind, TRUE); + res = LandInit(CBSOfMVFF(mvff), CBSLandClassGet(), arena, align, mvff, landiArgs); + } MPS_ARGS_END(landiArgs); if (res != ResOK) goto failInit; @@ -646,7 +648,7 @@ static void MVFFFinish(Pool pool) arena = PoolArena(pool); ControlFree(arena, mvff->segPref, sizeof(SegPrefStruct)); - CBSFinish(CBSOfMVFF(mvff)); + LandFinish(CBSOfMVFF(mvff)); FreelistFinish(FreelistOfMVFF(mvff)); mvff->sig = SigInvalid; @@ -691,7 +693,7 @@ static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream) if (res != ResOK) return res; - res = CBSDescribe(CBSOfMVFF(mvff), stream); + res = LandDescribe(CBSOfMVFF(mvff), stream); if (res != ResOK) return res; @@ -802,7 +804,7 @@ static Bool MVFFCheck(MVFF mvff) 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(Land, CBSOfMVFF(mvff)); CHECKD(Freelist, FreelistOfMVFF(mvff)); CHECKL(BoolCheck(mvff->slotHigh)); CHECKL(BoolCheck(mvff->firstFit)); @@ -812,8 +814,8 @@ static Bool MVFFCheck(MVFF mvff) /* 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) { +extern Land _mps_mvff_cbs(mps_pool_t); +Land _mps_mvff_cbs(mps_pool_t mps_pool) { Pool pool; MVFF mvff; diff --git a/mps/code/range.h b/mps/code/range.h index 4c5b87854da..c996276cca6 100644 --- a/mps/code/range.h +++ b/mps/code/range.h @@ -21,8 +21,6 @@ /* Prototypes */ -typedef struct RangeStruct *Range; - #define RangeBase(range) ((range)->base) #define RangeLimit(range) ((range)->limit) #define RangeSize(range) (AddrOffset(RangeBase(range), RangeLimit(range))) diff --git a/mps/code/tract.c b/mps/code/tract.c index 69ae479cf1a..542206daa69 100644 --- a/mps/code/tract.c +++ b/mps/code/tract.c @@ -210,25 +210,25 @@ Res ChunkInit(Chunk chunk, Arena arena, /* Add the chunk's free address space to the arena's freeCBS, so that we can allocate from it. */ - if (arena->hasFreeCBS) { - res = ArenaFreeCBSInsert(arena, - PageIndexBase(chunk, chunk->allocBase), - chunk->limit); + if (arena->hasFreeLand) { + res = ArenaFreeLandInsert(arena, + PageIndexBase(chunk, chunk->allocBase), + chunk->limit); if (res != ResOK) - goto failCBSInsert; + goto failLandInsert; } chunk->sig = ChunkSig; AVERT(Chunk, chunk); /* As part of the bootstrap, the first created chunk becomes the primary - chunk. This step allows AreaFreeCBSInsert to allocate pages. */ + chunk. This step allows AreaFreeLandInsert to allocate pages. */ if (arena->primary == NULL) arena->primary = chunk; return ResOK; -failCBSInsert: +failLandInsert: (arena->class->chunkFinish)(chunk); /* .no-clean: No clean-ups needed past this point for boot, as we will discard the chunk. */ @@ -248,10 +248,10 @@ void ChunkFinish(Chunk chunk) chunk->sig = SigInvalid; RingRemove(&chunk->chunkRing); - if (ChunkArena(chunk)->hasFreeCBS) - ArenaFreeCBSDelete(ChunkArena(chunk), - PageIndexBase(chunk, chunk->allocBase), - chunk->limit); + if (ChunkArena(chunk)->hasFreeLand) + ArenaFreeLandDelete(ChunkArena(chunk), + PageIndexBase(chunk, chunk->allocBase), + chunk->limit); if (chunk->arena->primary == chunk) chunk->arena->primary = NULL; From a929adf067487daac7131c31b3ba27d23bc8c859 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 1 Apr 2014 21:26:07 +0100 Subject: [PATCH 04/43] Add land.c to list of modules, and missing header range.h. Copied from Perforce Change: 185134 ServerID: perforce.ravenbrook.com --- mps/code/comm.gmk | 52 +++++++++++++++++++++++++++++++++++++++----- mps/code/commpre.nmk | 1 + mps/code/land.c | 1 + 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/mps/code/comm.gmk b/mps/code/comm.gmk index 7e68f45bfd9..ff915aaa5e3 100644 --- a/mps/code/comm.gmk +++ b/mps/code/comm.gmk @@ -164,12 +164,52 @@ FMTDYTST = fmtdy.c fmtno.c fmtdytst.c FMTHETST = fmthe.c fmtdy.c fmtno.c fmtdytst.c PLINTH = mpsliban.c mpsioan.c EVENTPROC = eventcnv.c table.c -MPMCOMMON = abq.c arena.c arenacl.c arenavm.c arg.c boot.c bt.c \ - buffer.c cbs.c dbgpool.c dbgpooli.c event.c format.c \ - freelist.c global.c ld.c locus.c message.c meter.c mpm.c mpsi.c \ - pool.c poolabs.c poolmfs.c poolmrg.c poolmv.c protocol.c range.c \ - ref.c reserv.c ring.c root.c sa.c sac.c seg.c shield.c splay.c ss.c \ - table.c trace.c traceanc.c tract.c tree.c walk.c +MPMCOMMON = \ + abq.c \ + arena.c \ + arenacl.c \ + arenavm.c \ + arg.c \ + boot.c \ + bt.c \ + buffer.c \ + cbs.c \ + dbgpool.c \ + dbgpooli.c \ + event.c \ + format.c \ + freelist.c \ + global.c \ + land.c \ + ld.c \ + locus.c \ + message.c \ + meter.c \ + mpm.c \ + mpsi.c \ + pool.c \ + poolabs.c \ + poolmfs.c \ + poolmrg.c \ + poolmv.c \ + protocol.c \ + range.c \ + ref.c \ + reserv.c \ + ring.c \ + root.c \ + sa.c \ + sac.c \ + seg.c \ + shield.c \ + splay.c \ + ss.c \ + table.c \ + trace.c \ + traceanc.c \ + tract.c \ + tree.c \ + walk.c MPM = $(MPMCOMMON) $(MPMPF) diff --git a/mps/code/commpre.nmk b/mps/code/commpre.nmk index 8d81ef13ccd..0c8e68a90f9 100644 --- a/mps/code/commpre.nmk +++ b/mps/code/commpre.nmk @@ -126,6 +126,7 @@ MPMCOMMON=\ \ \ \ + \ \ \ \ diff --git a/mps/code/land.c b/mps/code/land.c index 3abd9c41e15..991739f7b7f 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -7,6 +7,7 @@ */ #include "mpm.h" +#include "range.h" SRCID(land, "$Id$"); From 6beb2ed5f2d3e6bb066b297d74eb3d6d83ebd715 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 1 Apr 2014 23:35:23 +0100 Subject: [PATCH 05/43] First draft of land design. Copied from Perforce Change: 185146 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 2 +- mps/design/cbs.txt | 218 +++++---------------- mps/design/index.txt | 1 + mps/design/land.txt | 292 +++++++++++++++++++++++++++++ mps/manual/source/design/index.rst | 1 + 5 files changed, 341 insertions(+), 173 deletions(-) create mode 100644 mps/design/land.txt diff --git a/mps/code/arena.c b/mps/code/arena.c index b63ef84748a..8f0bfcf43ff 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -19,7 +19,7 @@ SRCID(arena, "$Id$"); #define ArenaControlPool(arena) MV2Pool(&(arena)->controlPoolStruct) #define ArenaCBSBlockPool(arena) (&(arena)->freeCBSBlockPoolStruct.poolStruct) -#define ArenaFreeLand(arena) ((Land)&(arena)->freeLandStruct) +#define ArenaFreeLand(arena) (&(arena)->freeLandStruct.landStruct) /* Forward declarations */ diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt index 6531cd02090..051889087d9 100644 --- a/mps/design/cbs.txt +++ b/mps/design/cbs.txt @@ -29,50 +29,24 @@ high level communication with the client about the size of contiguous ranges, and detection of protocol violations. -Definitions ------------ - -_`.def.range`: A (contiguous) *range* of addresses is a semi-open -interval on address space. - -_`.def.isolated`: A contiguous range is *isolated* with respect to -some property it has, if adjacent elements do not have that property. - - Requirements ------------ -_`.req.set`: Must maintain a set of addresses. +In addition to the generic land requirements (see +design.mps.land.req), the CBS must satisfy: _`.req.fast`: Common operations must have a low amortized cost. -_`.req.add`: Must be able to add address ranges to the set. - -_`.req.remove`: Must be able to remove address ranges from the set. - -_`.req.size`: Must report concisely to the client when isolated -contiguous ranges of at least a certain size appear and disappear. - -_`.req.iterate`: Must support the iteration of all isolated -contiguous ranges. This will not be a common operation. - -_`.req.protocol`: Must detect protocol violations. - -_`.req.debug`: Must support debugging of client code. - _`.req.small`: Must have a small space overhead for the storage of typical subsets of address space and not have abysmal overhead for the storage of any subset of address space. -_`.req.align`: Must support an alignment (the alignment of all -addresses specifying ranges) of down to ``sizeof(void *)`` without -losing memory. - Interface --------- -_`.header`: CBS is used through impl.h.cbs. +_`.land`: The interface to CBS is the generic functions for the *land* +abstract data type. See `design.mps.land `_. External types @@ -80,147 +54,51 @@ External types ``typedef struct CBSStruct *CBS`` -_`.type.cbs`: ``CBS`` is the main data structure for manipulating a -CBS. It is intended that a ``CBSStruct`` be embedded in another -structure. No convenience functions are provided for the allocation or -deallocation of the CBS. - -``typedef Bool (*CBSIterateMethod)(CBS cbs, Range range, void *closureP, Size closureS)`` - -_`.type.cbs.iterate.method`: Type ``CBSIterateMethod`` is a callback -function that may be passed to ``CBSIterate()``. It is called for -every isolated contiguous range in address order. The function must -returns a ``Bool`` indicating whether to continue with the iteration. +_`.type.cbs`: A ``CBSStruct`` may be embedded in another structure, or +you can create it using ``LandCreate()``. External functions .................. -``Res CBSInit(Arena arena, CBS cbs, void *owner, Align alignment, Bool fastFind, ArgList args)`` +``LandClass CBSLandClassGet(void)`` -_`.function.cbs.init`: ``CBSInit()`` is the function that initialises -the CBS structure. It performs allocation in the supplied arena. The -parameter ``owner`` is passed to ``MeterInit()``, an ``alignment`` -indicates the alignment of ranges to be maintained. An initialised CBS -contains no ranges. - -``fastFind``, if set, causes the CBS to maintain, for each subtree, -the size of the largest block in that subtree. This must be true if -any of the ``CBSFindFirst()``, ``CBSFindLast()``, or -``CBSFindLargest()`` functions are going to be used on the CBS. - -``CBSInit()`` may take one keyword argument: - -* ``MPS_KEY_CBS_EXTEND_BY`` (type ``Size``; default 4096) is the size - of segment that the CBS will request from the arena in which to - allocate its ``CBSBlock`` structures. - -``void CBSFinish(CBS cbs)`` - -_`.function.cbs.finish`: ``CBSFinish()`` is the function that finishes -the CBS structure and discards any other resources associated with the -CBS. - -``Res CBSInsert(Range rangeReturn, CBS cbs, Range range)`` - -_`.function.cbs.insert`: If any part of ``range`` is already in the -CBS, then leave it unchanged and return ``ResFAIL``. Otherwise, -attempt to insert ``range`` into the CBS. If the insertion succeeds, -then update ``rangeReturn`` to describe the contiguous isolated range -containing the inserted range (this may differ from ``range`` if there -was coalescence on either side) and return ``ResOK``. If the insertion -fails, return a result code indicating allocation failure. - -_`.function.cbs.insert.fail`: Insertion of a valid range (that is, one -that does not overlap with any range in the CBS) can only fail if the -new range is isolated and the allocation of the necessary data -structure to represent it failed. +_`.function.class`: The function ``CBSLandClassGet()`` returns the CBS +class, a subclass of ``LandClass`` suitable for passing to +``LandCreate()`` or ``LandInit()``. -``Res CBSDelete(Range rangeReturn, CBS cbs, Range range)`` +Keyword arguments +................. -_`.function.cbs.delete`: If any part of the range is not in the CBS, -then leave the CBS unchanged and return ``ResFAIL``. Otherwise, update -``rangeReturn`` to describe the contiguous isolated range that -contains ``range`` (this may differ from ``range`` if there are -fragments on either side) and attempt to delete the range from the -CBS. If the deletion succeeds, return ``ResOK``. If the deletion -fails, return a result code indicating allocation failure. +When initializing a CBS, ``LandCreate()`` and ``LandInit()`` take the +following optional keyword arguments: -_`.function.cbs.delete.fail`: Deletion of a valid range (that is, one -that is wholly contained in the CBS) can only fail if there are -fragments on both sides and the allocation of the necessary data -structures to represent them fails. +* ``CBSBlockPool`` (type ``Pool``) is the pool from which the CBS + block descriptors will be allocated. If omitted, a new MFS pool is + created for this purpose. -_`.function.cbs.delete.return`: ``CBSDelete()`` returns the contiguous -isolated range that contains ``range`` even if the deletion fails. -This is so that the caller can try deleting the whole block (which is -guaranteed to succeed) and managing the fragments using a fallback -strategy. +* ``MPS_KEY_CBS_EXTEND_BY`` (type ``Size``; default 4096) is passed as + the ``MPS_KEY_EXTEND_BY`` keyword argument to ``PoolCreate()`` if a + block descriptor pool is created. It specifies the size of segment + that the block descriptor pool will request from the arena. -``void CBSIterate(CBS cbs, CBSIterateMethod iterate, void *closureP, Size closureS)`` +* ``MFSExtendSelf`` (type ``Bool``; default ``TRUE``) is passed to + ``PoolCreate()`` if a block descriptor pool is created. If ``TRUE``, + the block descriptor pool automatically extends itself when out of + space; if ``FALSE``, the pool returns ``ResLIMIT`` in this case. + (This feature is used by the arena to bootstrap its own CBS of free + memory.) -_`.function.cbs.iterate`: ``CBSIterate()`` is the function used to -iterate all isolated contiguous ranges in a CBS. It receives a -pointer, ``Size`` closure pair to pass on to the iterator method, -and an iterator method to invoke on every range in address order. If -the iterator method returns ``FALSE``, then the iteration is -terminated. +* ``CBSFastFind`` (type ``Bool``; default ``FALSE``). If ``TRUE``, + causes the CBS to maintain, for each subtree, the size of the + largest block in that subtree. This enables the ``LandFindFirst()``, + ``LandFindLast()``, and ``LandFindLargest()`` generic functions. -``Res CBSDescribe(CBS cbs, mps_lib_FILE *stream)`` - -_`.function.cbs.describe`: ``CBSDescribe()`` is a function that prints -a textual representation of the CBS to the given stream, indicating -the contiguous ranges in order, as well as the structure of the -underlying splay tree implementation. It is provided for debugging -purposes only. - -``Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, CBS cbs, Size size, FindDelete findDelete)`` - -_`.function.cbs.find.first`: Locate the first block (in address order) -within the CBS of at least the specified size, update ``rangeReturn`` -to describe that range, and return ``TRUE``. If there is no such -block, it returns ``FALSE``. - -In addition, optionally delete the top, bottom, or all of the found -range, depending on the ``findDelete`` argument. This saves a separate -call to ``CBSDelete()``, and uses the knowledge of exactly where we -found the range. The value of ``findDelete`` must come from this -enumeration:: - - enum { - FindDeleteNONE, /* don't delete after finding */ - FindDeleteLOW, /* delete size bytes from low end of block */ - FindDeleteHIGH, /* delete size bytes from high end of block */ - FindDeleteENTIRE /* delete entire range */ - }; - -The original contiguous isolated range in which the range was found is -returned via the ``oldRangeReturn`` argument. (If ``findDelete`` is -``FindDeleteNONE`` or ``FindDeleteENTIRE``, then this will be -identical to the range returned via the ``rangeReturn`` argument.) - -``CBSFindFirst()`` requires that ``fastFind`` was true when -``CBSInit()`` was called. - -``Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn, CBS cbs, Size size, FindDelete findDelete)`` - -_`.function.cbs.find.last`: Like ``CBSFindFirst()``, except that it -finds the last block in address order. - -``Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn, CBS cbs, Size size, FindDelete findDelete)`` - -_`.function.cbs.find.largest`: Locate the largest block within the -CBS, and if that block is at least as big as ``size``, return its -range via the ``rangeReturn`` argument, and return ``TRUE``. If there -are no blocks in the CBS at least as large as ``size``, return -``FALSE``. Pass 0 for ``size`` if you want the largest block -unconditionally. - -Like ``CBSFindFirst()``, optionally delete the range (specifying -``FindDeleteLOW`` or ``FindDeleteHIGH`` has the same effect as -``FindDeleteENTIRE``). This feature requires that ``fastFind`` was -true when ``CBSInit()`` was called. +* ``CBSZoned`` (type ``Bool``; default ``FALSE``). If ``TRUE``, caused + the CBS to maintain, for each subtree, the union of the zone sets of + all ranges in that subtree. This enables the ``LandFindInZones()`` + generic function. Implementation @@ -241,19 +119,19 @@ for comparison is the base of another range. .. _design.mps.splay: splay -_`.impl.splay.fast-find`: ``CBSFindFirst()`` and ``CBSFindLast()`` use +_`.impl.splay.fast-find`: ``cbsFindFirst()`` and ``cbsFindLast()`` use the update/refresh facility of splay trees to store, in each ``CBSBlock``, an accurate summary of the maximum block size in the tree rooted at the corresponding splay node. This allows rapid location of the first or last suitable block, and very rapid failure if there is no suitable block. -_`.impl.find-largest`: ``CBSFindLargest()`` simply finds out the size +_`.impl.find-largest`: ``cbsFindLargest()`` simply finds out the size of the largest block in the CBS from the root of the tree, using ``SplayRoot()``, and does ``SplayFindFirst()`` for a block of that size. This takes time proportional to the logarithm of the size of the free list, so it's about the best you can do without maintaining a -separate priority queue, just to do ``CBSFindLargest()``. +separate priority queue, just to do ``cbsFindLargest()``. Low memory behaviour @@ -261,10 +139,10 @@ Low memory behaviour _`.impl.low-mem`: When the CBS tries to allocate a new ``CBSBlock`` structure for a new isolated range as a result of either -``CBSInsert()`` or ``CBSDelete()``, and there is insufficient memory +``LandInsert()`` or ``LandDelete()``, and there is insufficient memory to allocation the ``CBSBlock`` structure, then the range is not added -to the CBS or deleted from it, and the call to ``CBSInsert()`` or -``CBSDelete()`` returns ``ResMEMORY``. +to the CBS or deleted from it, and the call to ``LandInsert()`` or +``LandDelete()`` returns ``ResMEMORY``. The CBS block @@ -285,15 +163,8 @@ Testing _`.test`: The following testing will be performed on this module: -_`.test.cbstest`: There is a stress test for this module in -impl.c.cbstest. This allocates a large block of memory and then -simulates the allocation and deallocation of ranges within this block -using both a ``CBS`` and a ``BT``. It makes both valid and invalid -requests, and compares the ``CBS`` response to the correct behaviour -as determined by the ``BT``. It also iterates the ranges in the -``CBS``, comparing them to the ``BT``. It also invokes the -``CBSDescribe()`` method, but makes no automatic test of the resulting -output. It does not currently test the callbacks. +_`.test.fbmtest`: A generic test for land implementations. See +design.mps.land.fbmtest. _`.test.pool`: Several pools (currently MVT_ and MVFF_) are implemented on top of a CBS. These pool are subject to testing in development, QA, @@ -359,6 +230,9 @@ Document History talking about the deleted "emergency" free list allocator. Documented ``fastFind`` argument to ``CBSInit()``. +- 2014-04-01 GDR_ Moved generic material to design.mps.land. + Documented new keyword arguments. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/mps/design/index.txt b/mps/design/index.txt index b47c04bbcc6..251d598a102 100644 --- a/mps/design/index.txt +++ b/mps/design/index.txt @@ -59,6 +59,7 @@ guide.impl.c.format_ Coding standard: conventions for the general format of C interface-c_ The design of the Memory Pool System interface to C io_ The design of the MPS I/O subsystem keyword-arguments_ The design of the MPS mechanism for passing arguments by keyword. +land_ Lands (collections of address ranges) lib_ The design of the Memory Pool System library interface lock_ The design of the lock module locus_ The design for the locus manager diff --git a/mps/design/land.txt b/mps/design/land.txt new file mode 100644 index 00000000000..fcbdde02ab1 --- /dev/null +++ b/mps/design/land.txt @@ -0,0 +1,292 @@ +.. mode: -*- rst -*- + +Lands +===== + +:Tag: design.mps.land +:Author: Gareth Rees +:Date: 2014-04-01 +:Status: incomplete design +:Revision: $Id$ +:Copyright: See section `Copyright and License`_. + + +Introduction +------------ + +_`.intro`: This is the design of the *land* abstract data type, which +represents a collection of contiguous address ranges. + +_`.readership`: This document is intended for any MPS developer. + +_`.source`: `design.mps.cbs `_, `design.mps.freelist `_. + +_`.overview`: Collections of address ranges are used in several places +in the MPS: the arena stores a set of mapped address ranges; pools +store sets of address ranges which have been acquired from the arena +and sets of address ranges that are available for allocation. The +*land* abstract data type makes it easy to try out different +implementations with different performance characteristics and other +attributes. + +_`.name`: The name is inspired by *rangeland* meaning *group of +ranges* (where *ranges* is used in the sense *grazing areas*). + + +Definitions +----------- + +_`.def.range`: A (contiguous) *range* of addresses is a semi-open +interval on address space. + +_`.def.isolated`: A contiguous range is *isolated* with respect to +some property it has, if adjacent elements do not have that property. + + +Requirements +------------ + +_`.req.set`: Must maintain a set of addresses. + +_`.req.add`: Must be able to add address ranges to the set. + +_`.req.remove`: Must be able to remove address ranges from the set. + +_`.req.size`: Must report concisely to the client when isolated +contiguous ranges of at least a certain size appear and disappear. + +_`.req.iterate`: Must support the iteration of all isolated +contiguous ranges. + +_`.req.protocol`: Must detect protocol violations. + +_`.req.debug`: Must support debugging of client code. + +_`.req.align`: Must support an alignment (the alignment of all +addresses specifying ranges) of down to ``sizeof(void *)`` without +losing memory. + + +Interface +--------- + +Types +..... + +``typedef LandStruct *Land;`` + +_`.type.land`: The type of a generic land instance. + +``typedef Bool (*LandVisitor)(Land land, Range range, void *closureP, Size closureS);`` + +_`.type.visitor`: Type ``LandVisitor`` is a callback function that +may be passed to ``LandIterate()``. It is called for every isolated +contiguous range in address order. The function must return a +``Bool`` indicating whether to continue with the iteration. + + +Generic functions +................. + +``Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args)`` + +_`.function.init`: ``LandInit()`` initializes the land structure for +the given class. The land will perform allocation (if necessary -- not +all land classes need to allocate) in the supplied arena. The +``alignment`` parameter is the alignment of the address ranges that +will be stored and retrieved from the land. The parameter ``owner`` is +output as a parameter to the ``LandInit`` event. The newly initialized +land contains no ranges. + +``Res LandCreate(Land *landReturn, Arena arena, LandClass class, Align alignment, void *owner, ArgList args)`` + +_`.function.create`: ``LandCreate()`` allocates memory for a land +structure of the given class in ``arena``, and then passes all +parameters to ``LandInit()``. + +``void LandDestroy(Land land)`` + +_`.function.destroy`: ``LandDestroy()`` calls ``LandFinish()`` to +finish the land structure, and then frees its memory. + +``void LandFinish(Land land)`` + +_`.function.finish`: ``LandFinish()`` finishes the land structure and +discards any other resources associated with the land. + +``Res LandInsert(Range rangeReturn, Land land, Range range)`` + +_`.function.insert`: If any part of ``range`` is already in the +land, then leave it unchanged and return ``ResFAIL``. Otherwise, +attempt to insert ``range`` into the land. If the insertion succeeds, +then update ``rangeReturn`` to describe the contiguous isolated range +containing the inserted range (this may differ from ``range`` if there +was coalescence on either side) and return ``ResOK``. If the insertion +fails, return a result code indicating allocation failure. + +_`.function.insert.fail`: Insertion of a valid range (that is, one +that does not overlap with any range in the land) can only fail if the +new range is isolated and the allocation of the necessary data +structure to represent it failed. + +``Res LandDelete(Range rangeReturn, Land land, Range range)`` + +_`.function.delete`: If any part of the range is not in the land, +then leave the land unchanged and return ``ResFAIL``. Otherwise, update +``rangeReturn`` to describe the contiguous isolated range that +contains ``range`` (this may differ from ``range`` if there are +fragments on either side) and attempt to delete the range from the +land. If the deletion succeeds, return ``ResOK``. If the deletion +fails, return a result code indicating allocation failure. + +_`.function.delete.fail`: Deletion of a valid range (that is, one +that is wholly contained in the land) can only fail if there are +fragments on both sides and the allocation of the necessary data +structures to represent them fails. + +_`.function.delete.return`: ``LandDelete()`` returns the contiguous +isolated range that contains ``range`` even if the deletion fails. +This is so that the caller can try deleting the whole block (which is +guaranteed to succeed) and managing the fragments using a fallback +strategy. + +``void LandIterate(Land land, LandIterateMethod iterate, void *closureP, Size closureS)`` + +_`.function.iterate`: ``LandIterate()`` is the function used to +iterate all isolated contiguous ranges in a land. It receives a +pointer, ``Size`` closure pair to pass on to the iterator method, +and an iterator method to invoke on every range in address order. If +the iterator method returns ``FALSE``, then the iteration is +terminated. + +``Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)`` + +_`.function.find.first`: Locate the first block (in address order) +within the land of at least the specified size, update ``rangeReturn`` +to describe that range, and return ``TRUE``. If there is no such +block, it returns ``FALSE``. + +In addition, optionally delete the top, bottom, or all of the found +range, depending on the ``findDelete`` argument. This saves a separate +call to ``LandDelete()``, and uses the knowledge of exactly where we +found the range. The value of ``findDelete`` must come from this +enumeration:: + + enum { + FindDeleteNONE, /* don't delete after finding */ + FindDeleteLOW, /* delete size bytes from low end of block */ + FindDeleteHIGH, /* delete size bytes from high end of block */ + FindDeleteENTIRE /* delete entire range */ + }; + +The original contiguous isolated range in which the range was found is +returned via the ``oldRangeReturn`` argument. (If ``findDelete`` is +``FindDeleteNONE`` or ``FindDeleteENTIRE``, then this will be +identical to the range returned via the ``rangeReturn`` argument.) + +``Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)`` + +_`.function.find.last`: Like ``LandFindFirst()``, except that it +finds the last block in address order. + +``Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)`` + +_`.function.find.largest`: Locate the largest block within the +land, and if that block is at least as big as ``size``, return its +range via the ``rangeReturn`` argument, and return ``TRUE``. If there +are no blocks in the land at least as large as ``size``, return +``FALSE``. Pass 0 for ``size`` if you want the largest block +unconditionally. + +Like ``LandFindFirst()``, optionally delete the range (specifying +``FindDeleteLOW`` or ``FindDeleteHIGH`` has the same effect as +``FindDeleteENTIRE``), and return the original contiguous isolated +range in which the range was found via the ``oldRangeReturn`` +argument. + +``Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high)`` + +_`.function.find.zones`: Locate a block at least as big as ``size`` +that lies entirely within the ``zoneSet``, return its range via the +``rangeReturn`` argument, and return ``TRUE``. (The first such block, +if ``high`` is ``FALSE``, or the last, if ``high`` is ``TRUE``.) If +there is no such block, , return ``FALSE``. + +Delete the range as for ``LandFindFirst()`` and ``LastFindLast()`` +(with the effect of ``FindDeleteLOW`` if ``high`` is ``FALSE`` and the +effect of ``FindDeleteHIGH`` if ``high`` is ``TRUE``), and return the +original contiguous isolated range in which the range was found via +the ``oldRangeReturn`` argument. + +``Res LandDescribe(Land land, mps_lib_FILE *stream)`` + +_`.function.describe`: ``LandDescribe()`` is a function that prints +a textual representation of the land to the given stream, indicating +the contiguous ranges in order, as well as the structure of the +underlying splay tree implementation. It is provided for debugging +purposes only. + + +Testing +------- + +_`.test.fbmtest`: There is a stress test for implementations of this +interface in impl.c.fbmtest. This allocates a large block of memory +and then simulates the allocation and deallocation of ranges within +this block using both a ``Land`` and a ``BT``. It makes both valid and +invalid requests, and compares the ``Land`` response to the correct +behaviour as determined by the ``BT``. It iterates the ranges in the +``Land``, comparing them to the ``BT``. It invokes the +``LandDescribe()`` generic function, but makes no automatic test of +the resulting output. + + +Document History +---------------- + +- 2014-04-01 GDR_ Created based on `design.mps.cbs `_. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 2014 Ravenbrook Limited. 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: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. 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. + +#. 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.** diff --git a/mps/manual/source/design/index.rst b/mps/manual/source/design/index.rst index 3cf6719ac74..f87cfdadaa2 100644 --- a/mps/manual/source/design/index.rst +++ b/mps/manual/source/design/index.rst @@ -14,6 +14,7 @@ Design guide.hex.trans guide.impl.c.format keyword-arguments + land range ring sig From 1d8238419adcf0e8998a73dc3de6607fd97be767 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 1 Apr 2014 23:39:03 +0100 Subject: [PATCH 06/43] Landiargs -> liargs for terseness and consistency. Copied from Perforce Change: 185147 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 14 +++++++------- mps/code/poolmv2.c | 8 ++++---- mps/code/poolmvff.c | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/mps/code/arena.c b/mps/code/arena.c index 8f0bfcf43ff..afdb815f232 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -233,13 +233,13 @@ Res ArenaInit(Arena arena, ArenaClass class, Align alignment, ArgList args) goto failMFSInit; /* Initialise the freeLand. */ - MPS_ARGS_BEGIN(landiArgs) { - MPS_ARGS_ADD(landiArgs, CBSBlockPool, ArenaCBSBlockPool(arena)); - MPS_ARGS_ADD(landiArgs, CBSFastFind, TRUE); - MPS_ARGS_ADD(landiArgs, CBSZoned, arena->zoned); - MPS_ARGS_DONE(landiArgs); - res = LandInit(ArenaFreeLand(arena), CBSLandClassGet(), arena, alignment, arena, landiArgs); - } MPS_ARGS_END(landiArgs); + MPS_ARGS_BEGIN(liArgs) { + MPS_ARGS_ADD(liArgs, CBSBlockPool, ArenaCBSBlockPool(arena)); + MPS_ARGS_ADD(liArgs, CBSFastFind, TRUE); + MPS_ARGS_ADD(liArgs, CBSZoned, arena->zoned); + MPS_ARGS_DONE(liArgs); + res = LandInit(ArenaFreeLand(arena), CBSLandClassGet(), arena, alignment, arena, liArgs); + } MPS_ARGS_END(liArgs); AVER(res == ResOK); /* no allocation, no failure expected */ if (res != ResOK) goto failLandInit; diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index f3de433a5ef..03cd5bc4765 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -269,10 +269,10 @@ static Res MVTInit(Pool pool, ArgList args) if (abqDepth < 3) abqDepth = 3; - MPS_ARGS_BEGIN(landiArgs) { - MPS_ARGS_ADD(landiArgs, CBSFastFind, TRUE); - res = LandInit(MVTCBS(mvt), CBSLandClassGet(), arena, align, mvt, landiArgs); - } MPS_ARGS_END(landiArgs); + MPS_ARGS_BEGIN(liArgs) { + MPS_ARGS_ADD(liArgs, CBSFastFind, TRUE); + res = LandInit(MVTCBS(mvt), CBSLandClassGet(), arena, align, mvt, liArgs); + } MPS_ARGS_END(liArgs); if (res != ResOK) goto failCBS; diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index f1679e5acaa..fa5250c7011 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -602,10 +602,10 @@ static Res MVFFInit(Pool pool, ArgList args) if (res != ResOK) goto failInit; - MPS_ARGS_BEGIN(landiArgs) { - MPS_ARGS_ADD(landiArgs, CBSFastFind, TRUE); - res = LandInit(CBSOfMVFF(mvff), CBSLandClassGet(), arena, align, mvff, landiArgs); - } MPS_ARGS_END(landiArgs); + MPS_ARGS_BEGIN(liArgs) { + MPS_ARGS_ADD(liArgs, CBSFastFind, TRUE); + res = LandInit(CBSOfMVFF(mvff), CBSLandClassGet(), arena, align, mvff, liArgs); + } MPS_ARGS_END(liArgs); if (res != ResOK) goto failInit; From 5314260bc807a7fb8dfcb18a724c17b39a0564fc Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 2 Apr 2014 12:16:38 +0100 Subject: [PATCH 07/43] Avoid type puns. Copied from Perforce Change: 185151 ServerID: perforce.ravenbrook.com --- mps/code/fbmtest.c | 2 +- mps/code/poolmv2.c | 2 +- mps/code/poolmvff.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mps/code/fbmtest.c b/mps/code/fbmtest.c index b4072d929af..97776deab40 100644 --- a/mps/code/fbmtest.c +++ b/mps/code/fbmtest.c @@ -558,7 +558,7 @@ extern int main(int argc, char *argv[]) BT allocTable; FreelistStruct flStruct; CBSStruct cbsStruct; - Land land = (Land)&cbsStruct; + Land land = &cbsStruct.landStruct; Align align; testlib_init(argc, argv); diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index 03cd5bc4765..05252eaca8f 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -170,7 +170,7 @@ static ABQ MVTABQ(MVT mvt) static Land MVTCBS(MVT mvt) { - return (Land)(&mvt->cbsStruct); + return &mvt->cbsStruct.landStruct; } diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index fa5250c7011..8e0f985b757 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -58,7 +58,7 @@ typedef struct MVFFStruct { /* MVFF pool outer structure */ #define Pool2MVFF(pool) PARENT(MVFFStruct, poolStruct, pool) #define MVFF2Pool(mvff) (&((mvff)->poolStruct)) -#define CBSOfMVFF(mvff) ((Land)&((mvff)->cbsStruct)) +#define CBSOfMVFF(mvff) (&((mvff)->cbsStruct.landStruct)) #define MVFFOfCBS(cbs) PARENT(MVFFStruct, cbsStruct, cbs) #define FreelistOfMVFF(mvff) (&((mvff)->flStruct)) #define MVFFOfFreelist(fl) PARENT(MVFFStruct, flStruct, fl) From b409ae89f60d7cab3d67e77cc970c017fbbd6cff Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 2 Apr 2014 14:01:18 +0100 Subject: [PATCH 08/43] Turn freelist into a land class. Copied from Perforce Change: 185155 ServerID: perforce.ravenbrook.com --- mps/code/cbs.c | 12 +- mps/code/comm.gmk | 8 +- mps/code/commpost.nmk | 6 +- mps/code/commpre.nmk | 2 +- mps/code/freelist.c | 167 ++++++++++++---------- mps/code/freelist.h | 36 +---- mps/code/land.c | 122 ++++++++++++++++ mps/code/{fbmtest.c => landtest.c} | 188 ++++++++----------------- mps/code/mpm.h | 1 + mps/code/mpmst.h | 20 ++- mps/code/mpmtypes.h | 8 +- mps/code/mps.xcodeproj/project.pbxproj | 30 ++-- mps/code/poolmv2.c | 104 +++++--------- mps/code/poolmvff.c | 35 +++-- mps/design/cbs.txt | 55 ++++++-- mps/design/freelist.txt | 184 ++++++------------------ mps/design/land.txt | 44 +++--- mps/tool/testrun.bat | 2 +- mps/tool/testrun.sh | 2 +- 19 files changed, 492 insertions(+), 534 deletions(-) rename mps/code/{fbmtest.c => landtest.c} (79%) diff --git a/mps/code/cbs.c b/mps/code/cbs.c index bf02c9d7737..bfd21af7746 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -26,7 +26,7 @@ SRCID(cbs, "$Id$"); #define CBSBlockSize(block) AddrOffset((block)->base, (block)->limit) -#define cbsOfLand(land) ((CBS)(land)) +#define cbsOfLand(land) PARENT(CBSStruct, landStruct, land) #define cbsSplay(cbs) (&((cbs)->splayTreeStruct)) #define cbsOfSplay(_splay) PARENT(CBSStruct, splayTreeStruct, _splay) #define cbsBlockTree(block) (&((block)->treeStruct)) @@ -720,7 +720,7 @@ static Res cbsSplayNodeDescribe(Tree tree, mps_lib_FILE *stream) typedef struct CBSIterateClosure { Land land; - LandVisitor iterate; + LandVisitor visitor; void *closureP; Size closureS; } CBSIterateClosure; @@ -732,12 +732,16 @@ static Bool cbsIterateVisit(Tree tree, void *closureP, Size closureS) CBSBlock cbsBlock; Land land = closure->land; CBS cbs = cbsOfLand(land); + Bool delete = FALSE; + Bool cont = TRUE; UNUSED(closureS); cbsBlock = cbsBlockOfTree(tree); RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock)); - if (!closure->iterate(land, &range, closure->closureP, closure->closureS)) + cont = (*closure->visitor)(&delete, land, &range, closure->closureP, closure->closureS); + AVER(!delete); + if (!cont) return FALSE; METER_ACC(cbs->treeSearch, cbs->treeSize); return TRUE; @@ -762,7 +766,7 @@ static void cbsIterate(Land land, LandVisitor visitor, METER_ACC(cbs->treeSearch, cbs->treeSize); closure.land = land; - closure.iterate = visitor; + closure.visitor = visitor; closure.closureP = closureP; closure.closureS = closureS; (void)TreeTraverse(SplayTreeRoot(splay), splay->compare, splay->nodeKey, diff --git a/mps/code/comm.gmk b/mps/code/comm.gmk index ff915aaa5e3..beeb542fca9 100644 --- a/mps/code/comm.gmk +++ b/mps/code/comm.gmk @@ -280,11 +280,11 @@ TEST_TARGETS=\ djbench \ exposet0 \ expt825 \ - fbmtest \ finalcv \ finaltest \ fotest \ gcbench \ + landtest \ locbwcss \ lockcov \ locusss \ @@ -452,9 +452,6 @@ $(PFM)/$(VARIETY)/exposet0: $(PFM)/$(VARIETY)/exposet0.o \ $(PFM)/$(VARIETY)/expt825: $(PFM)/$(VARIETY)/expt825.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a -$(PFM)/$(VARIETY)/fbmtest: $(PFM)/$(VARIETY)/fbmtest.o \ - $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a - $(PFM)/$(VARIETY)/finalcv: $(PFM)/$(VARIETY)/finalcv.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a @@ -467,6 +464,9 @@ $(PFM)/$(VARIETY)/fotest: $(PFM)/$(VARIETY)/fotest.o \ $(PFM)/$(VARIETY)/gcbench: $(PFM)/$(VARIETY)/gcbench.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/landtest: $(PFM)/$(VARIETY)/landtest.o \ + $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/locbwcss: $(PFM)/$(VARIETY)/locbwcss.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a diff --git a/mps/code/commpost.nmk b/mps/code/commpost.nmk index 2406131db0c..78095f33714 100644 --- a/mps/code/commpost.nmk +++ b/mps/code/commpost.nmk @@ -158,9 +158,6 @@ $(PFM)\$(VARIETY)\exposet0.exe: $(PFM)\$(VARIETY)\exposet0.obj \ $(PFM)\$(VARIETY)\expt825.exe: $(PFM)\$(VARIETY)\expt825.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\fbmtest.exe: $(PFM)\$(VARIETY)\fbmtest.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - $(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) @@ -170,6 +167,9 @@ $(PFM)\$(VARIETY)\finaltest.exe: $(PFM)\$(VARIETY)\finaltest.obj \ $(PFM)\$(VARIETY)\fotest.exe: $(PFM)\$(VARIETY)\fotest.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\landtest.exe: $(PFM)\$(VARIETY)\landtest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\locbwcss.exe: $(PFM)\$(VARIETY)\locbwcss.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) diff --git a/mps/code/commpre.nmk b/mps/code/commpre.nmk index 0c8e68a90f9..75dbdad446e 100644 --- a/mps/code/commpre.nmk +++ b/mps/code/commpre.nmk @@ -66,10 +66,10 @@ TEST_TARGETS=\ bttest.exe \ exposet0.exe \ expt825.exe \ - fbmtest.exe \ finalcv.exe \ finaltest.exe \ fotest.exe \ + landtest.exe \ locbwcss.exe \ lockcov.exe \ lockutw3.exe \ diff --git a/mps/code/freelist.c b/mps/code/freelist.c index a299dafb6e6..14091c02558 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -12,10 +12,14 @@ SRCID(freelist, "$Id$"); +#define freelistOfLand(land) PARENT(FreelistStruct, landStruct, land) +#define freelistAlignment(fl) LandAlignment(&fl->landStruct) + + typedef union FreelistBlockUnion { struct { FreelistBlock next; /* tagged with low bit 1 */ - /* limit is (char *)this + fl->alignment */ + /* limit is (char *)this + freelistAlignment(fl) */ } small; struct { FreelistBlock next; @@ -50,7 +54,7 @@ static Addr FreelistBlockLimit(Freelist fl, FreelistBlock block) { AVERT(Freelist, fl); if (FreelistBlockIsSmall(block)) { - return AddrAdd(FreelistBlockBase(block), fl->alignment); + return AddrAdd(FreelistBlockBase(block), freelistAlignment(fl)); } else { return block->large.limit; } @@ -104,7 +108,7 @@ static void FreelistBlockSetLimit(Freelist fl, FreelistBlock block, Addr limit) AVERT(Freelist, fl); AVERT(FreelistBlock, block); - AVER(AddrIsAligned(limit, fl->alignment)); + AVER(AddrIsAligned(limit, freelistAlignment(fl))); AVER(FreelistBlockBase(block) < limit); size = AddrOffset(block, limit); @@ -127,9 +131,9 @@ static FreelistBlock FreelistBlockInit(Freelist fl, Addr base, Addr limit) AVERT(Freelist, fl); AVER(base != NULL); - AVER(AddrIsAligned(base, fl->alignment)); + AVER(AddrIsAligned(base, freelistAlignment(fl))); AVER(base < limit); - AVER(AddrIsAligned(limit, fl->alignment)); + AVER(AddrIsAligned(limit, freelistAlignment(fl))); block = (FreelistBlock)base; block->small.next = FreelistTagSet(NULL); @@ -141,21 +145,34 @@ static FreelistBlock FreelistBlockInit(Freelist fl, Addr base, Addr limit) Bool FreelistCheck(Freelist fl) { + Land land; CHECKS(Freelist, fl); + land = &fl->landStruct; + CHECKL(LandCheck(land)); /* See */ - CHECKL(AlignIsAligned(fl->alignment, freelistMinimumAlignment)); + CHECKL(AlignIsAligned(LandAlignment(land), freelistMinimumAlignment)); CHECKL((fl->list == NULL) == (fl->listSize == 0)); return TRUE; } -Res FreelistInit(Freelist fl, Align alignment) +static Res freelistInit(Land land, ArgList args) { + Freelist fl; + LandClass super; + Res res; + + AVERT(Land, land); + super = LAND_SUPERCLASS(FreelistLandClass); + res = (*super->init)(land, args); + if (res != ResOK) + return res; + /* See */ - if (!AlignIsAligned(alignment, freelistMinimumAlignment)) + if (!AlignIsAligned(LandAlignment(land), freelistMinimumAlignment)) return ResPARAM; - fl->alignment = alignment; + fl = freelistOfLand(land); fl->list = NULL; fl->listSize = 0; @@ -165,8 +182,12 @@ Res FreelistInit(Freelist fl, Align alignment) } -void FreelistFinish(Freelist fl) +static void freelistFinish(Land land) { + Freelist fl; + + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); fl->sig = SigInvalid; fl->list = NULL; @@ -200,16 +221,19 @@ static void freelistBlockSetPrevNext(Freelist fl, FreelistBlock prev, } -Res FreelistInsert(Range rangeReturn, Freelist fl, Range range) +static Res freelistInsert(Range rangeReturn, Land land, Range range) { + Freelist fl; FreelistBlock prev, cur, next, new; Addr base, limit; Bool coalesceLeft, coalesceRight; AVER(rangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); AVERT(Range, range); - AVER(RangeIsAligned(range, fl->alignment)); + AVER(RangeIsAligned(range, freelistAlignment(fl))); base = RangeBase(range); limit = RangeLimit(range); @@ -281,7 +305,7 @@ static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl, AVER(rangeReturn != NULL); AVERT(Freelist, fl); AVERT(Range, range); - AVER(RangeIsAligned(range, fl->alignment)); + AVER(RangeIsAligned(range, freelistAlignment(fl))); AVER(prev == NULL || FreelistBlockNext(prev) == block); AVERT(FreelistBlock, block); AVER(FreelistBlockBase(block) <= RangeBase(range)); @@ -319,12 +343,15 @@ static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl, } -Res FreelistDelete(Range rangeReturn, Freelist fl, Range range) +static Res freelistDelete(Range rangeReturn, Land land, Range range) { + Freelist fl; FreelistBlock prev, cur, next; Addr base, limit; AVER(rangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); AVERT(Range, range); @@ -357,13 +384,16 @@ Res FreelistDelete(Range rangeReturn, Freelist fl, Range range) } -void FreelistIterate(Freelist fl, FreelistIterateMethod iterate, - void *closureP, Size closureS) +static void freelistIterate(Land land, LandVisitor visitor, + void *closureP, Size closureS) { + Freelist fl; FreelistBlock prev, cur, next; + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); - AVER(FUNCHECK(iterate)); + AVER(FUNCHECK(visitor)); prev = NULL; cur = fl->list; @@ -372,7 +402,7 @@ void FreelistIterate(Freelist fl, FreelistIterateMethod iterate, RangeStruct range; Bool cont; RangeInit(&range, FreelistBlockBase(cur), FreelistBlockLimit(fl, cur)); - cont = (*iterate)(&delete, &range, closureP, closureS); + cont = (*visitor)(&delete, land, &range, closureP, closureS); next = FreelistBlockNext(cur); if (delete) { freelistBlockSetPrevNext(fl, prev, next, -1); @@ -405,7 +435,7 @@ static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn, AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVERT(Freelist, fl); - AVER(SizeIsAligned(size, fl->alignment)); + AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); AVER(prev == NULL || FreelistBlockNext(prev) == block); AVERT(FreelistBlock, block); @@ -445,15 +475,18 @@ static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn, } -Bool FreelistFindFirst(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete) +static Bool freelistFindFirst(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + Freelist fl; FreelistBlock prev, cur, next; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); - AVER(SizeIsAligned(size, fl->alignment)); + AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); prev = NULL; @@ -473,17 +506,20 @@ Bool FreelistFindFirst(Range rangeReturn, Range oldRangeReturn, } -Bool FreelistFindLast(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete) +static Bool freelistFindLast(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + Freelist fl; Bool found = FALSE; FreelistBlock prev, cur, next; FreelistBlock foundPrev = NULL, foundCur = NULL; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); - AVER(SizeIsAligned(size, fl->alignment)); + AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); prev = NULL; @@ -507,15 +543,18 @@ Bool FreelistFindLast(Range rangeReturn, Range oldRangeReturn, } -Bool FreelistFindLargest(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete) +static Bool freelistFindLargest(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + Freelist fl; Bool found = FALSE; FreelistBlock prev, cur, next; FreelistBlock bestPrev = NULL, bestCur = NULL; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); AVERT(FindDelete, findDelete); @@ -541,19 +580,21 @@ Bool FreelistFindLargest(Range rangeReturn, Range oldRangeReturn, } -/* freelistDescribeIterateMethod -- Iterate method for - * FreelistDescribe. Writes a decription of the range into the stream - * pointed to by 'closureP'. +/* freelistDescribeVisitor -- visitor method for freelistDescribe. + * + * Writes a decription of the range into the stream pointed to by + * closureP. */ -static Bool freelistDescribeIterateMethod(Bool *deleteReturn, Range range, - void *closureP, Size closureS) +static Bool freelistDescribeVisitor(Bool *deleteReturn, Land land, Range range, + void *closureP, Size closureS) { Res res; mps_lib_FILE *stream = closureP; - AVER(deleteReturn != NULL); - AVERT(Range, range); - AVER(stream != NULL); + if (deleteReturn == NULL) return FALSE; + if (!TESTT(Land, land)) return FALSE; + if (!TESTT(Range, range)) return FALSE; + if (stream == NULL) return FALSE; UNUSED(closureS); res = WriteF(stream, @@ -562,64 +603,48 @@ static Bool freelistDescribeIterateMethod(Bool *deleteReturn, Range range, " {$U}\n", (WriteFU)RangeSize(range), NULL); - *deleteReturn = FALSE; return res == ResOK; } -Res FreelistDescribe(Freelist fl, mps_lib_FILE *stream) +static Res freelistDescribe(Land land, mps_lib_FILE *stream) { + Freelist fl; Res res; + if (!TESTT(Land, land)) return ResFAIL; + fl = freelistOfLand(land); if (!TESTT(Freelist, fl)) return ResFAIL; if (stream == NULL) return ResFAIL; res = WriteF(stream, "Freelist $P {\n", (WriteFP)fl, - " alignment = $U\n", (WriteFU)fl->alignment, " listSize = $U\n", (WriteFU)fl->listSize, NULL); - FreelistIterate(fl, freelistDescribeIterateMethod, stream, 0); + LandIterate(land, freelistDescribeVisitor, stream, 0); res = WriteF(stream, "}\n", NULL); return res; } -/* freelistFlushIterateMethod -- Iterate method for - * FreelistFlushToLand. Attempst to insert the range into the Land. - */ -static Bool freelistFlushIterateMethod(Bool *deleteReturn, Range range, - void *closureP, Size closureS) +typedef LandClassStruct FreelistLandClassStruct; + +DEFINE_CLASS(FreelistLandClass, class) { - Res res; - RangeStruct newRange; - Land land; - - AVER(deleteReturn != NULL); - AVERT(Range, range); - AVER(closureP != NULL); - UNUSED(closureS); - - land = closureP; - res = LandInsert(&newRange, land, range); - if (res == ResOK) { - *deleteReturn = TRUE; - return TRUE; - } else { - *deleteReturn = FALSE; - return FALSE; - } -} - - -void FreelistFlushToLand(Freelist fl, Land land) -{ - AVERT(Freelist, fl); - AVERT(Land, land); - - FreelistIterate(fl, freelistFlushIterateMethod, land, 0); + INHERIT_CLASS(class, LandClass); + class->name = "FREELIST"; + class->size = sizeof(FreelistStruct); + class->init = freelistInit; + class->finish = freelistFinish; + class->insert = freelistInsert; + class->delete = freelistDelete; + class->iterate = freelistIterate; + class->findFirst = freelistFindFirst; + class->findLast = freelistFindLast; + class->findLargest = freelistFindLargest; + class->describe = freelistDescribe; } diff --git a/mps/code/freelist.h b/mps/code/freelist.h index b9aea9bdf6c..1ba46ae338d 100644 --- a/mps/code/freelist.h +++ b/mps/code/freelist.h @@ -10,42 +10,10 @@ #define freelist_h #include "mpmtypes.h" -#include "range.h" -#define FreelistSig ((Sig)0x519F6331) /* SIGnature FREEL */ +extern Bool FreelistCheck(Freelist freelist); -typedef struct FreelistStruct *Freelist; -typedef union FreelistBlockUnion *FreelistBlock; - -typedef Bool (*FreelistIterateMethod)(Bool *deleteReturn, Range range, - void *closureP, Size closureS); - -typedef struct FreelistStruct { - Sig sig; - Align alignment; - FreelistBlock list; - Count listSize; -} FreelistStruct; - -extern Bool FreelistCheck(Freelist fl); -extern Res FreelistInit(Freelist fl, Align alignment); -extern void FreelistFinish(Freelist fl); - -extern Res FreelistInsert(Range rangeReturn, Freelist fl, Range range); -extern Res FreelistDelete(Range rangeReturn, Freelist fl, Range range); -extern Res FreelistDescribe(Freelist fl, mps_lib_FILE *stream); - -extern void FreelistIterate(Freelist abq, FreelistIterateMethod iterate, - void *closureP, Size closureS); - -extern Bool FreelistFindFirst(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete); -extern Bool FreelistFindLast(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete); -extern Bool FreelistFindLargest(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete); - -extern void FreelistFlushToLand(Freelist fl, Land land); +extern FreelistLandClass FreelistLandClassGet(void); #endif /* freelist.h */ diff --git a/mps/code/land.c b/mps/code/land.c index 991739f7b7f..e06f0060e36 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -12,6 +12,8 @@ SRCID(land, "$Id$"); +/* LandCheck -- check land */ + Bool LandCheck(Land land) { CHECKS(Land, land); @@ -20,6 +22,12 @@ Bool LandCheck(Land land) return TRUE; } + +/* LandInit -- initialize land + * + * See + */ + Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args) { Res res; @@ -47,6 +55,12 @@ Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *own return res; } + +/* LandCreate -- allocate and initialize land + * + * See + */ + Res LandCreate(Land *landReturn, Arena arena, LandClass class, Align alignment, void *owner, ArgList args) { Res res; @@ -76,6 +90,12 @@ Res LandCreate(Land *landReturn, Arena arena, LandClass class, Align alignment, return res; } + +/* LandDestroy -- finish and deallocate land + * + * See + */ + void LandDestroy(Land land) { Arena arena; @@ -89,12 +109,27 @@ void LandDestroy(Land land) ControlFree(arena, land, class->size); } + +/* LandFinish -- finish land + * + * See + */ + void LandFinish(Land land) { AVERT(Land, land); + (*land->class->finish)(land); + + land->sig = SigInvalid; } + +/* LandInsert -- insert range of addresses into land + * + * See + */ + Res LandInsert(Range rangeReturn, Land land, Range range) { AVER(rangeReturn != NULL); @@ -105,6 +140,12 @@ Res LandInsert(Range rangeReturn, Land land, Range range) return (*land->class->insert)(rangeReturn, land, range); } + +/* LandDelete -- delete range of addresses from land + * + * See + */ + Res LandDelete(Range rangeReturn, Land land, Range range) { AVER(rangeReturn != NULL); @@ -115,6 +156,12 @@ Res LandDelete(Range rangeReturn, Land land, Range range) return (*land->class->delete)(rangeReturn, land, range); } + +/* LandIterate -- iterate over isolated ranges of addresses in land + * + * See + */ + void LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) { AVERT(Land, land); @@ -123,6 +170,12 @@ void LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) (*land->class->iterate)(land, visitor, closureP, closureS); } + +/* LandFindFirst -- find first range of given size + * + * See + */ + Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { AVER(rangeReturn != NULL); @@ -135,6 +188,12 @@ Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size findDelete); } + +/* LandFindLast -- find last range of given size + * + * See + */ + Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { AVER(rangeReturn != NULL); @@ -147,6 +206,12 @@ Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, findDelete); } + +/* LandFindLargest -- find largest range of at least given size + * + * See + */ + Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { AVER(rangeReturn != NULL); @@ -159,6 +224,12 @@ Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size si findDelete); } + +/* LandFindInSize -- find range of given size in set of zones + * + * See + */ + Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) { AVER(rangeReturn != NULL); @@ -172,6 +243,12 @@ Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size siz zoneSet, high); } + +/* LandDescribe -- describe land for debugging + * + * See + */ + Res LandDescribe(Land land, mps_lib_FILE *stream) { Res res; @@ -198,6 +275,51 @@ Res LandDescribe(Land land, mps_lib_FILE *stream) } +/* landFlushVisitor -- visitor for LandFlush. + * + * closureP argument is the destination Land. Attempt to insert the + * range into the destination. + */ +static Bool landFlushVisitor(Bool *deleteReturn, Land land, Range range, + void *closureP, Size closureS) +{ + Res res; + RangeStruct newRange; + Land dest; + + AVER(deleteReturn != NULL); + AVERT(Range, range); + AVER(closureP != NULL); + UNUSED(closureS); + + dest = closureP; + res = LandInsert(&newRange, land, range); + if (res == ResOK) { + *deleteReturn = TRUE; + return TRUE; + } else { + *deleteReturn = FALSE; + return FALSE; + } +} + + +/* LandFlush -- move ranges from src to dest + * + * See + */ + +void LandFlush(Land dest, Land src) +{ + AVERT(Land, dest); + AVERT(Land, src); + + LandIterate(src, landFlushVisitor, dest, 0); +} + + +/* LandClassCheck -- check land class */ + Bool LandClassCheck(LandClass class) { CHECKL(ProtocolClassCheck(&class->protocol)); diff --git a/mps/code/fbmtest.c b/mps/code/landtest.c similarity index 79% rename from mps/code/fbmtest.c rename to mps/code/landtest.c index 97776deab40..5ca7305ebda 100644 --- a/mps/code/fbmtest.c +++ b/mps/code/landtest.c @@ -1,19 +1,16 @@ -/* fbmtest.c: FREE BLOCK MANAGEMENT TEST +/* landtest.c: LAND TEST * - * $Id$ + * $Id$ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * - * The MPS contains two free block management modules: + * The MPS contains two land implementations: * - * 1. the CBS (Coalescing Block Structure) module maintains free - * blocks in a splay tree for fast access with a cost in storage; + * 1. the CBS (Coalescing Block Structure) module maintains blocks in + * a splay tree for fast access with a cost in storage; * - * 2. the Freelist module maintains free blocks in an address-ordered + * 2. the Freelist module maintains blocks in an address-ordered * singly linked list for zero storage overhead with a cost in * performance. - * - * The two modules present identical interfaces, so we apply the same - * test cases to both. */ #include "cbs.h" @@ -28,7 +25,7 @@ #include #include -SRCID(fbmtest, "$Id$"); +SRCID(landtest, "$Id$"); #define ArraySize ((Size)123456) @@ -43,64 +40,46 @@ static Count NAllocateTried, NAllocateSucceeded, NDeallocateTried, static int verbose = 0; -typedef unsigned FBMType; -enum { - FBMTypeCBS = 1, - FBMTypeFreelist, - FBMTypeLimit -}; - -typedef struct FBMStateStruct { - FBMType type; +typedef struct TestStateStruct { Align align; BT allocTable; Addr block; - union { - Land land; - Freelist fl; - } the; -} FBMStateStruct, *FBMState; + Land land; +} TestStateStruct, *TestState; -typedef struct CheckFBMClosureStruct { - FBMState state; +typedef struct CheckTestClosureStruct { + TestState state; Addr limit; Addr oldLimit; -} CheckFBMClosureStruct, *CheckFBMClosure; +} CheckTestClosureStruct, *CheckTestClosure; -static Addr (addrOfIndex)(FBMState state, Index i) +static Addr (addrOfIndex)(TestState state, Index i) { return AddrAdd(state->block, (i * state->align)); } -static Index (indexOfAddr)(FBMState state, Addr a) +static Index (indexOfAddr)(TestState state, Addr a) { return (Index)(AddrOffset(state->block, a) / state->align); } -static void describe(FBMState state) { - switch (state->type) { - case FBMTypeCBS: - die(LandDescribe(state->the.land, mps_lib_get_stdout()), "LandDescribe"); - break; - case FBMTypeFreelist: - die(FreelistDescribe(state->the.fl, mps_lib_get_stdout()), "FreelistDescribe"); - break; - default: - cdie(0, "invalid state->type"); - break; - } +static void describe(TestState state) { + die(LandDescribe(state->land, mps_lib_get_stdout()), "LandDescribe"); } -static Bool checkCallback(Range range, void *closureP, Size closureS) +static Bool checkVisitor(Bool *deleteReturn, Land land, Range range, + void *closureP, Size closureS) { Addr base, limit; - CheckFBMClosure cl = (CheckFBMClosure)closureP; + CheckTestClosure cl = closureP; - UNUSED(closureS); + Insist(deleteReturn != NULL); + testlib_unused(land); + testlib_unused(closureS); Insist(cl != NULL); base = RangeBase(range); @@ -124,42 +103,15 @@ static Bool checkCallback(Range range, void *closureP, Size closureS) return TRUE; } - -static Bool checkCBSCallback(Land land, Range range, - void *closureP, Size closureS) +static void check(TestState state) { - UNUSED(land); - return checkCallback(range, closureP, closureS); -} - - -static Bool checkFLCallback(Bool *deleteReturn, Range range, - void *closureP, Size closureS) -{ - *deleteReturn = FALSE; - return checkCallback(range, closureP, closureS); -} - - -static void check(FBMState state) -{ - CheckFBMClosureStruct closure; + CheckTestClosureStruct closure; closure.state = state; closure.limit = addrOfIndex(state, ArraySize); closure.oldLimit = state->block; - switch (state->type) { - case FBMTypeCBS: - LandIterate(state->the.land, checkCBSCallback, (void *)&closure, 0); - break; - case FBMTypeFreelist: - FreelistIterate(state->the.fl, checkFLCallback, (void *)&closure, 0); - break; - default: - cdie(0, "invalid state->type"); - return; - } + LandIterate(state->land, checkVisitor, (void *)&closure, 0); if (closure.oldLimit == state->block) Insist(BTIsSetRange(state->allocTable, 0, @@ -245,7 +197,7 @@ static Index lastEdge(BT bt, Size size, Index base) * all either set or reset. */ -static void randomRange(Addr *baseReturn, Addr *limitReturn, FBMState state) +static void randomRange(Addr *baseReturn, Addr *limitReturn, TestState state) { Index base; /* the start of our range */ Index end; /* an edge (i.e. different from its predecessor) */ @@ -267,7 +219,7 @@ static void randomRange(Addr *baseReturn, Addr *limitReturn, FBMState state) } -static void allocate(FBMState state, Addr base, Addr limit) +static void allocate(TestState state, Addr base, Addr limit) { Res res; Index ib, il; /* Indexed for base and limit */ @@ -295,25 +247,15 @@ static void allocate(FBMState state, Addr base, Addr limit) total = AddrOffset(outerBase, outerLimit); /* TODO: check these values */ - UNUSED(left); - UNUSED(right); - UNUSED(total); + testlib_unused(left); + testlib_unused(right); + testlib_unused(total); } else { outerBase = outerLimit = NULL; } RangeInit(&range, base, limit); - switch (state->type) { - case FBMTypeCBS: - res = LandDelete(&oldRange, state->the.land, &range); - break; - case FBMTypeFreelist: - res = FreelistDelete(&oldRange, state->the.fl, &range); - break; - default: - cdie(0, "invalid state->type"); - return; - } + res = LandDelete(&oldRange, state->land, &range); if (verbose) { printf("allocate: [%p,%p) -- %s\n", @@ -335,7 +277,7 @@ static void allocate(FBMState state, Addr base, Addr limit) } -static void deallocate(FBMState state, Addr base, Addr limit) +static void deallocate(TestState state, Addr base, Addr limit) { Res res; Index ib, il; @@ -373,23 +315,13 @@ static void deallocate(FBMState state, Addr base, Addr limit) total = AddrOffset(outerBase, outerLimit); /* TODO: check these values */ - UNUSED(left); - UNUSED(right); - UNUSED(total); + testlib_unused(left); + testlib_unused(right); + testlib_unused(total); } RangeInit(&range, base, limit); - switch (state->type) { - case FBMTypeCBS: - res = LandInsert(&freeRange, state->the.land, &range); - break; - case FBMTypeFreelist: - res = FreelistInsert(&freeRange, state->the.fl, &range); - break; - default: - cdie(0, "invalid state->type"); - return; - } + res = LandInsert(&freeRange, state->land, &range); if (verbose) { printf("deallocate: [%p,%p) -- %s\n", @@ -412,7 +344,7 @@ static void deallocate(FBMState state, Addr base, Addr limit) } -static void find(FBMState state, Size size, Bool high, FindDelete findDelete) +static void find(TestState state, Size size, Bool high, FindDelete findDelete) { Bool expected, found; Index expectedBase, expectedLimit; @@ -453,23 +385,12 @@ static void find(FBMState state, Size size, Bool high, FindDelete findDelete) } /* TODO: check these values */ - UNUSED(oldSize); - UNUSED(newSize); + testlib_unused(oldSize); + testlib_unused(newSize); } - switch (state->type) { - case FBMTypeCBS: - found = (high ? LandFindLast : LandFindFirst) - (&foundRange, &oldRange, state->the.land, size * state->align, findDelete); - break; - case FBMTypeFreelist: - found = (high ? FreelistFindLast : FreelistFindFirst) - (&foundRange, &oldRange, state->the.fl, size * state->align, findDelete); - break; - default: - cdie(0, "invalid state->type"); - return; - } + found = (high ? LandFindLast : LandFindFirst) + (&foundRange, &oldRange, state->land, size * state->align, findDelete); if (verbose) { printf("find %s %lu: ", high ? "last" : "first", @@ -505,7 +426,7 @@ static void find(FBMState state, Size size, Bool high, FindDelete findDelete) return; } -static void test(FBMState state, unsigned n) { +static void test(TestState state, unsigned n) { Addr base, limit; unsigned i; Size size; @@ -538,7 +459,7 @@ static void test(FBMState state, unsigned n) { find(state, size, high, findDelete); break; default: - cdie(0, "invalid state->type"); + cdie(0, "invalid rnd(3)"); return; } if ((i + 1) % 1000 == 0) @@ -551,14 +472,14 @@ static void test(FBMState state, unsigned n) { extern int main(int argc, char *argv[]) { mps_arena_t mpsArena; - Arena arena; /* the ANSI arena which we use to allocate the BT */ - FBMStateStruct state; + Arena arena; + TestStateStruct state; void *p; Addr dummyBlock; BT allocTable; - FreelistStruct flStruct; CBSStruct cbsStruct; - Land land = &cbsStruct.landStruct; + FreelistStruct flStruct; + Land land; Align align; testlib_init(argc, argv); @@ -586,25 +507,26 @@ extern int main(int argc, char *argv[]) (char *)dummyBlock + ArraySize); } + land = &cbsStruct.landStruct; MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, CBSFastFind, TRUE); die((mps_res_t)LandInit(land, CBSLandClassGet(), arena, align, NULL, args), "failed to initialise CBS"); } MPS_ARGS_END(args); - state.type = FBMTypeCBS; state.align = align; state.block = dummyBlock; state.allocTable = allocTable; - state.the.land = land; + state.land = land; test(&state, nCBSOperations); LandFinish(land); - die((mps_res_t)FreelistInit(&flStruct, align), + land = &flStruct.landStruct; + die((mps_res_t)LandInit(land, FreelistLandClassGet(), arena, align, NULL, + mps_args_none), "failed to initialise Freelist"); - state.type = FBMTypeFreelist; - state.the.fl = &flStruct; + state.land = land; test(&state, nFLOperations); - FreelistFinish(&flStruct); + LandFinish(land); mps_arena_destroy(arena); diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 4e65ea83ed4..7096618f565 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -1010,6 +1010,7 @@ extern Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Siz extern Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete); extern Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high); extern Res LandDescribe(Land land, mps_lib_FILE *stream); +extern void LandFlush(Land dest, Land src); extern Bool LandClassCheck(LandClass class); extern LandClass LandClassGet(void); diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 8d7a414465b..6679dbafd75 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -665,10 +665,28 @@ typedef struct CBSStruct { Bool ownPool; /* did we create blockPool? */ /* meters for sizes of search structures at each op */ METER_DECL(treeSearch); - Sig sig; /* sig at end because embedded */ + Sig sig; /* .class.end-sig */ } CBSStruct; +/* FreelistStruct -- address-ordered freelist + * + * Freelist is a subclass of Land that maintains a collection of + * disjoint ranges in an address-ordered freelist. + * + * See . + */ + +#define FreelistSig ((Sig)0x519F6331) /* SIGnature FREEL */ + +typedef struct FreelistStruct { + LandStruct landStruct; /* superclass fields come first */ + FreelistBlock list; + Count listSize; + Sig sig; /* .class.end-sig */ +} FreelistStruct; + + /* ArenaStruct -- generic arena * * See . */ diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index 8abc43bb111..424216ee0b3 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -112,10 +112,12 @@ typedef struct StackContextStruct *StackContext; typedef struct RangeStruct *Range; /* */ typedef struct LandStruct *Land; /* */ typedef struct LandClassStruct *LandClass; /* */ +typedef unsigned FindDelete; /* */ typedef LandClass CBSLandClass; /* */ typedef struct CBSStruct *CBS; /* */ -typedef LandClass FreelistClass; /* */ -typedef unsigned FindDelete; /* */ +typedef LandClass FreelistLandClass; /* */ +typedef struct FreelistStruct *Freelist; /* */ +typedef union FreelistBlockUnion *FreelistBlock; /* */ /* Arena*Method -- see */ @@ -274,7 +276,7 @@ typedef Res (*LandInitMethod)(Land land, ArgList args); typedef void (*LandFinishMethod)(Land land); typedef Res (*LandInsertMethod)(Range rangeReturn, Land land, Range range); typedef Res (*LandDeleteMethod)(Range rangeReturn, Land land, Range range); -typedef Bool (*LandVisitor)(Land land, Range range, void *closureP, Size closureS); +typedef Bool (*LandVisitor)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS); typedef void (*LandIterateMethod)(Land land, LandVisitor visitor, void *closureP, Size closureS); typedef Bool (*LandFindMethod)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete); typedef Res (*LandFindInZonesMethod)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high); diff --git a/mps/code/mps.xcodeproj/project.pbxproj b/mps/code/mps.xcodeproj/project.pbxproj index d9ce4fdd401..c987b57b4e9 100644 --- a/mps/code/mps.xcodeproj/project.pbxproj +++ b/mps/code/mps.xcodeproj/project.pbxproj @@ -105,7 +105,7 @@ 2291A5DB175CB05F001D4920 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; 2291A5DD175CB05F001D4920 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; 2291A5E4175CB076001D4920 /* exposet0.c in Sources */ = {isa = PBXBuildFile; fileRef = 2291A5AA175CAA9B001D4920 /* exposet0.c */; }; - 2291A5ED175CB5E2001D4920 /* fbmtest.c in Sources */ = {isa = PBXBuildFile; fileRef = 2291A5E9175CB4EC001D4920 /* fbmtest.c */; }; + 2291A5ED175CB5E2001D4920 /* landtest.c in Sources */ = {isa = PBXBuildFile; fileRef = 2291A5E9175CB4EC001D4920 /* landtest.c */; }; 22B2BC2E18B6434F00C33E63 /* mps.c in Sources */ = {isa = PBXBuildFile; fileRef = 31A47BA3156C1E130039B1C2 /* mps.c */; }; 22B2BC3718B6437C00C33E63 /* scheme-advanced.c in Sources */ = {isa = PBXBuildFile; fileRef = 22B2BC2B18B6434000C33E63 /* scheme-advanced.c */; }; 22FA176916E8D6FC0098B23F /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; }; @@ -659,7 +659,7 @@ containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; proxyType = 1; remoteGlobalIDString = 3114A64B156E9596001E0AA3; - remoteInfo = fbmtest; + remoteInfo = landtest; }; 3114A674156E9619001E0AA3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -1249,7 +1249,7 @@ 2291A5BD175CAB2F001D4920 /* awlutth */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = awlutth; sourceTree = BUILT_PRODUCTS_DIR; }; 2291A5D1175CAFCA001D4920 /* expt825 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = expt825; sourceTree = BUILT_PRODUCTS_DIR; }; 2291A5E3175CB05F001D4920 /* exposet0 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = exposet0; sourceTree = BUILT_PRODUCTS_DIR; }; - 2291A5E9175CB4EC001D4920 /* fbmtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fbmtest.c; sourceTree = ""; }; + 2291A5E9175CB4EC001D4920 /* landtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = landtest.c; sourceTree = ""; }; 2291A5EA175CB503001D4920 /* abq.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = abq.h; sourceTree = ""; }; 2291A5EB175CB53E001D4920 /* range.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = range.c; sourceTree = ""; }; 2291A5EC175CB53E001D4920 /* range.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = range.h; sourceTree = ""; }; @@ -1299,7 +1299,7 @@ 3114A633156E94DB001E0AA3 /* abqtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = abqtest; sourceTree = BUILT_PRODUCTS_DIR; }; 3114A63D156E94EA001E0AA3 /* abqtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = abqtest.c; sourceTree = ""; }; 3114A645156E9525001E0AA3 /* abq.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = abq.c; sourceTree = ""; }; - 3114A64C156E9596001E0AA3 /* fbmtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fbmtest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3114A64C156E9596001E0AA3 /* landtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = landtest; sourceTree = BUILT_PRODUCTS_DIR; }; 3114A662156E95D9001E0AA3 /* btcv */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = btcv; sourceTree = BUILT_PRODUCTS_DIR; }; 3114A66C156E95EB001E0AA3 /* btcv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = btcv.c; sourceTree = ""; }; 3114A67C156E9668001E0AA3 /* mv2test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mv2test; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2004,7 +2004,7 @@ 3114A613156E944A001E0AA3 /* bttest.c */, 2291A5AA175CAA9B001D4920 /* exposet0.c */, 2291A5AB175CAA9B001D4920 /* expt825.c */, - 2291A5E9175CB4EC001D4920 /* fbmtest.c */, + 2291A5E9175CB4EC001D4920 /* landtest.c */, 3114A5CD156E9369001E0AA3 /* finalcv.c */, 3114A5E5156E93B9001E0AA3 /* finaltest.c */, 3124CAC6156BE48D00753214 /* fmtdy.c */, @@ -2100,7 +2100,7 @@ 3114A605156E9430001E0AA3 /* bttest */, 3114A61C156E9485001E0AA3 /* teletest */, 3114A633156E94DB001E0AA3 /* abqtest */, - 3114A64C156E9596001E0AA3 /* fbmtest */, + 3114A64C156E9596001E0AA3 /* landtest */, 3114A662156E95D9001E0AA3 /* btcv */, 3114A67C156E9668001E0AA3 /* mv2test */, 3114A695156E971B001E0AA3 /* messtest */, @@ -2725,9 +2725,9 @@ productReference = 3114A633156E94DB001E0AA3 /* abqtest */; productType = "com.apple.product-type.tool"; }; - 3114A64B156E9596001E0AA3 /* fbmtest */ = { + 3114A64B156E9596001E0AA3 /* landtest */ = { isa = PBXNativeTarget; - buildConfigurationList = 3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "fbmtest" */; + buildConfigurationList = 3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "landtest" */; buildPhases = ( 3114A648156E9596001E0AA3 /* Sources */, 3114A649156E9596001E0AA3 /* Frameworks */, @@ -2738,9 +2738,9 @@ dependencies = ( 3114A659156E95B1001E0AA3 /* PBXTargetDependency */, ); - name = fbmtest; - productName = fbmtest; - productReference = 3114A64C156E9596001E0AA3 /* fbmtest */; + name = landtest; + productName = landtest; + productReference = 3114A64C156E9596001E0AA3 /* landtest */; productType = "com.apple.product-type.tool"; }; 3114A661156E95D9001E0AA3 /* btcv */ = { @@ -3120,7 +3120,7 @@ 318DA8C31892B0F30089718C /* djbench */, 2291A5D3175CB05F001D4920 /* exposet0 */, 2291A5C1175CAFCA001D4920 /* expt825 */, - 3114A64B156E9596001E0AA3 /* fbmtest */, + 3114A64B156E9596001E0AA3 /* landtest */, 3114A5BC156E9315001E0AA3 /* finalcv */, 3114A5D5156E93A0001E0AA3 /* finaltest */, 224CC78C175E1821002FF81B /* fotest */, @@ -3421,7 +3421,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2291A5ED175CB5E2001D4920 /* fbmtest.c in Sources */, + 2291A5ED175CB5E2001D4920 /* landtest.c in Sources */, 3114A672156E95F6001E0AA3 /* testlib.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3908,7 +3908,7 @@ }; 3114A65B156E95B4001E0AA3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 3114A64B156E9596001E0AA3 /* fbmtest */; + target = 3114A64B156E9596001E0AA3 /* landtest */; targetProxy = 3114A65A156E95B4001E0AA3 /* PBXContainerItemProxy */; }; 3114A675156E9619001E0AA3 /* PBXTargetDependency */ = { @@ -5460,7 +5460,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "fbmtest" */ = { + 3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "landtest" */ = { isa = XCConfigurationList; buildConfigurations = ( 3114A654156E9596001E0AA3 /* Debug */, diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index 05252eaca8f..adc0abf426f 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -52,7 +52,7 @@ static Res MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, static Bool MVTCheckFit(Addr base, Addr limit, Size min, Arena arena); static ABQ MVTABQ(MVT mvt); static Land MVTCBS(MVT mvt); -static Freelist MVTFreelist(MVT mvt); +static Land MVTFreelist(MVT mvt); /* Types */ @@ -174,9 +174,9 @@ static Land MVTCBS(MVT mvt) } -static Freelist MVTFreelist(MVT mvt) +static Land MVTFreelist(MVT mvt) { - return &mvt->flStruct; + return &mvt->flStruct.landStruct; } @@ -280,7 +280,8 @@ static Res MVTInit(Pool pool, ArgList args) if (res != ResOK) goto failABQ; - res = FreelistInit(MVTFreelist(mvt), align); + res = LandInit(MVTFreelist(mvt), FreelistLandClassGet(), arena, align, mvt, + mps_args_none); if (res != ResOK) goto failFreelist; @@ -422,7 +423,7 @@ static void MVTFinish(Pool pool) } /* Finish the Freelist, ABQ and CBS structures */ - FreelistFinish(MVTFreelist(mvt)); + LandFinish(MVTFreelist(mvt)); ABQFinish(arena, MVTABQ(mvt)); LandFinish(MVTCBS(mvt)); } @@ -810,14 +811,14 @@ static Res MVTInsert(MVT mvt, Addr base, Addr limit) /* Attempt to flush the Freelist to the CBS to give maximum * opportunities for coalescence. */ - FreelistFlushToLand(MVTFreelist(mvt), MVTCBS(mvt)); + LandFlush(MVTCBS(mvt), MVTFreelist(mvt)); RangeInit(&range, base, limit); res = LandInsert(&newRange, MVTCBS(mvt), &range); if (ResIsAllocFailure(res)) { /* CBS ran out of memory for splay nodes: add range to emergency * free list instead. */ - res = FreelistInsert(&newRange, MVTFreelist(mvt), &range); + res = LandInsert(&newRange, MVTFreelist(mvt), &range); } if (res != ResOK) return res; @@ -866,7 +867,7 @@ static Res MVTDelete(MVT mvt, Addr base, Addr limit) AVER(res == ResOK); } else if (res == ResFAIL) { /* Not found in the CBS: try the Freelist. */ - res = FreelistDelete(&rangeOld, MVTFreelist(mvt), &range); + res = LandDelete(&rangeOld, MVTFreelist(mvt), &range); } if (res != ResOK) return res; @@ -1051,7 +1052,7 @@ static Res MVTDescribe(Pool pool, mps_lib_FILE *stream) res = ABQDescribe(MVTABQ(mvt), (ABQDescribeElement)RangeDescribe, stream); if(res != ResOK) return res; - res = FreelistDescribe(MVTFreelist(mvt), stream); + res = LandDescribe(MVTFreelist(mvt), stream); if(res != ResOK) return res; res = METER_WRITE(mvt->segAllocs, stream); @@ -1272,13 +1273,16 @@ static Bool MVTReturnSegs(MVT mvt, Range range, Arena arena) } -/* MVTRefillCallback -- called from CBSIterate or FreelistIterate at - * the behest of MVTRefillABQIfEmpty - */ -static Bool MVTRefillCallback(MVT mvt, Range range) +static Bool MVTRefillVisitor(Bool *deleteReturn, Land land, Range range, + void *closureP, Size closureS) { - AVERT(ABQ, MVTABQ(mvt)); - AVERT(Range, range); + MVT mvt; + + AVER(deleteReturn != NULL); + AVERT(Land, land); + mvt = closureP; + AVERT(MVT, mvt); + UNUSED(closureS); if (RangeSize(range) < mvt->reuseSize) return TRUE; @@ -1287,29 +1291,6 @@ static Bool MVTRefillCallback(MVT mvt, Range range) return MVTReserve(mvt, range); } -static Bool MVTCBSRefillCallback(Land land, Range range, - void *closureP, Size closureS) -{ - MVT mvt; - AVERT(Land, land); - mvt = closureP; - AVERT(MVT, mvt); - UNUSED(closureS); - return MVTRefillCallback(mvt, range); -} - -static Bool MVTFreelistRefillCallback(Bool *deleteReturn, Range range, - void *closureP, Size closureS) -{ - MVT mvt; - mvt = closureP; - AVERT(MVT, mvt); - UNUSED(closureS); - AVER(deleteReturn != NULL); - *deleteReturn = FALSE; - return MVTRefillCallback(mvt, range); -} - /* MVTRefillABQIfEmpty -- refill the ABQ from the CBS and the Freelist if * it is empty */ @@ -1326,8 +1307,8 @@ static void MVTRefillABQIfEmpty(MVT mvt, Size size) if (mvt->abqOverflow && ABQIsEmpty(MVTABQ(mvt))) { mvt->abqOverflow = FALSE; METER_ACC(mvt->refills, size); - LandIterate(MVTCBS(mvt), &MVTCBSRefillCallback, mvt, 0); - FreelistIterate(MVTFreelist(mvt), &MVTFreelistRefillCallback, mvt, 0); + LandIterate(MVTCBS(mvt), &MVTRefillVisitor, mvt, 0); + LandIterate(MVTFreelist(mvt), &MVTRefillVisitor, mvt, 0); } } @@ -1348,19 +1329,26 @@ typedef struct MVTContigencyStruct } MVTContigencyStruct; -/* MVTContingencyCallback -- called from CBSIterate or FreelistIterate - * at the behest of MVTContingencySearch. +/* MVTContingencyVisitor -- called from LandIterate at the behest of + * MVTContingencySearch. */ -static Bool MVTContingencyCallback(MVTContigency cl, Range range) + +static Bool MVTContingencyVisitor(Bool *deleteReturn, Land land, Range range, + void *closureP, Size closureS) { MVT mvt; Size size; Addr base, limit; + MVTContigency cl; - AVER(cl != NULL); + AVER(deleteReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + AVER(closureP != NULL); + cl = closureP; mvt = cl->mvt; AVERT(MVT, mvt); - AVERT(Range, range); + UNUSED(closureS); base = RangeBase(range); limit = RangeLimit(range); @@ -1389,25 +1377,6 @@ static Bool MVTContingencyCallback(MVTContigency cl, Range range) return TRUE; } -static Bool MVTCBSContingencyCallback(Land land, Range range, - void *closureP, Size closureS) -{ - MVTContigency cl = closureP; - AVERT(Land, land); - UNUSED(closureS); - return MVTContingencyCallback(cl, range); -} - -static Bool MVTFreelistContingencyCallback(Bool *deleteReturn, Range range, - void *closureP, Size closureS) -{ - MVTContigency cl = closureP; - UNUSED(closureS); - AVER(deleteReturn != NULL); - *deleteReturn = FALSE; - return MVTContingencyCallback(cl, range); -} - /* MVTContingencySearch -- search the CBS and the Freelist for a block * of size min */ @@ -1423,11 +1392,10 @@ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, cls.steps = 0; cls.hardSteps = 0; - FreelistFlushToLand(MVTFreelist(mvt), MVTCBS(mvt)); + LandFlush(MVTCBS(mvt), MVTFreelist(mvt)); - LandIterate(MVTCBS(mvt), MVTCBSContingencyCallback, (void *)&cls, 0); - FreelistIterate(MVTFreelist(mvt), MVTFreelistContingencyCallback, - (void *)&cls, 0); + LandIterate(MVTCBS(mvt), MVTContingencyVisitor, (void *)&cls, 0); + LandIterate(MVTFreelist(mvt), MVTContingencyVisitor, (void *)&cls, 0); if (!cls.found) return FALSE; diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index 8e0f985b757..bbdb40fa510 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -59,9 +59,7 @@ 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 MVFFOfCBS(cbs) PARENT(MVFFStruct, cbsStruct, cbs) -#define FreelistOfMVFF(mvff) (&((mvff)->flStruct)) -#define MVFFOfFreelist(fl) PARENT(MVFFStruct, flStruct, fl) +#define FreelistOfMVFF(mvff) (&((mvff)->flStruct.landStruct)) static Bool MVFFCheck(MVFF mvff); @@ -99,7 +97,7 @@ static Res MVFFAddToFreeList(Addr *baseIO, Addr *limitIO, MVFF mvff) { if (ResIsAllocFailure(res)) { /* CBS ran out of memory for splay nodes: add range to emergency * free list instead. */ - res = FreelistInsert(&newRange, FreelistOfMVFF(mvff), &range); + res = LandInsert(&newRange, FreelistOfMVFF(mvff), &range); } if (res == ResOK) { @@ -178,7 +176,7 @@ static void MVFFFreeSegs(MVFF mvff, Addr base, Addr limit) } } else if (res == ResFAIL) { /* Not found in the CBS: must be found in the Freelist. */ - res = FreelistDelete(&oldRange, FreelistOfMVFF(mvff), &range); + res = LandDelete(&oldRange, FreelistOfMVFF(mvff), &range); AVER(res == ResOK); mvff->free -= RangeSize(&range); } @@ -297,7 +295,7 @@ static Bool MVFFFindFirstFree(Addr *baseReturn, Addr *limitReturn, AVER(size > 0); AVER(SizeIsAligned(size, PoolAlignment(MVFF2Pool(mvff)))); - FreelistFlushToLand(FreelistOfMVFF(mvff), CBSOfMVFF(mvff)); + LandFlush(CBSOfMVFF(mvff), FreelistOfMVFF(mvff)); findDelete = mvff->slotHigh ? FindDeleteHIGH : FindDeleteLOW; @@ -309,7 +307,7 @@ static Bool MVFFFindFirstFree(Addr *baseReturn, Addr *limitReturn, /* Failed to find a block in the CBS: try the emergency free list * as well. */ foundBlock = - (mvff->firstFit ? FreelistFindFirst : FreelistFindLast) + (mvff->firstFit ? LandFindFirst : LandFindLast) (&range, &oldRange, FreelistOfMVFF(mvff), size, findDelete); } @@ -411,13 +409,12 @@ static Bool MVFFFindLargest(Range range, Range oldRange, MVFF mvff, AVER(size > 0); AVERT(FindDelete, findDelete); - FreelistFlushToLand(FreelistOfMVFF(mvff), CBSOfMVFF(mvff)); + LandFlush(CBSOfMVFF(mvff), FreelistOfMVFF(mvff)); if (LandFindLargest(range, oldRange, CBSOfMVFF(mvff), size, findDelete)) return TRUE; - if (FreelistFindLargest(range, oldRange, FreelistOfMVFF(mvff), - size, findDelete)) + if (LandFindLargest(range, oldRange, FreelistOfMVFF(mvff), size, findDelete)) return TRUE; return FALSE; @@ -598,16 +595,16 @@ static Res MVFFInit(Pool pool, ArgList args) mvff->total = 0; mvff->free = 0; - res = FreelistInit(FreelistOfMVFF(mvff), align); + res = LandInit(FreelistOfMVFF(mvff), FreelistLandClassGet(), arena, align, mvff, mps_args_none); if (res != ResOK) - goto failInit; + goto failFreelistInit; MPS_ARGS_BEGIN(liArgs) { MPS_ARGS_ADD(liArgs, CBSFastFind, TRUE); res = LandInit(CBSOfMVFF(mvff), CBSLandClassGet(), arena, align, mvff, liArgs); } MPS_ARGS_END(liArgs); if (res != ResOK) - goto failInit; + goto failCBSInit; mvff->sig = MVFFSig; AVERT(MVFF, mvff); @@ -615,7 +612,9 @@ static Res MVFFInit(Pool pool, ArgList args) slotHigh, arenaHigh, firstFit); return ResOK; -failInit: +failCBSInit: + LandFinish(FreelistOfMVFF(mvff)); +failFreelistInit: ControlFree(arena, p, sizeof(SegPrefStruct)); return res; } @@ -649,7 +648,7 @@ static void MVFFFinish(Pool pool) ControlFree(arena, mvff->segPref, sizeof(SegPrefStruct)); LandFinish(CBSOfMVFF(mvff)); - FreelistFinish(FreelistOfMVFF(mvff)); + LandFinish(FreelistOfMVFF(mvff)); mvff->sig = SigInvalid; } @@ -697,7 +696,7 @@ static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream) if (res != ResOK) return res; - res = FreelistDescribe(FreelistOfMVFF(mvff), stream); + res = LandDescribe(FreelistOfMVFF(mvff), stream); if (res != ResOK) return res; @@ -804,8 +803,8 @@ static Bool MVFFCheck(MVFF mvff) CHECKL(mvff->total >= mvff->free); CHECKL(SizeIsAligned(mvff->free, PoolAlignment(MVFF2Pool(mvff)))); CHECKL(SizeIsAligned(mvff->total, ArenaAlign(PoolArena(MVFF2Pool(mvff))))); - CHECKD(Land, CBSOfMVFF(mvff)); - CHECKD(Freelist, FreelistOfMVFF(mvff)); + CHECKD(CBS, &mvff->cbsStruct); + CHECKD(Freelist, &mvff->flStruct); CHECKL(BoolCheck(mvff->slotHigh)); CHECKL(BoolCheck(mvff->firstFit)); return TRUE; diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt index 051889087d9..5ee2348fe54 100644 --- a/mps/design/cbs.txt +++ b/mps/design/cbs.txt @@ -20,7 +20,10 @@ eager coalescence. _`.readership`: This document is intended for any MM developer. -_`.source`: design.mps.poolmv2, design.mps.poolmvff. +_`.source`: design.mps.poolmv2_, design.mps.poolmvff_. + +.. _design.mps.poolmv2: poolmv2 +.. _design.mps.poolmvff: poolmvff _`.overview`: The "coalescing block structure" is a set of addresses (or a subset of address space), with provision for efficient @@ -33,7 +36,9 @@ Requirements ------------ In addition to the generic land requirements (see -design.mps.land.req), the CBS must satisfy: +design.mps.land_), the CBS must satisfy: + +.. _design.mps.land: land _`.req.fast`: Common operations must have a low amortized cost. @@ -45,8 +50,9 @@ storage of any subset of address space. Interface --------- -_`.land`: The interface to CBS is the generic functions for the *land* -abstract data type. See `design.mps.land `_. +_`.land`: CBS is an implementation of the *land* abstract data type, +so the interface consists of the generic functions for lands. See +design.mps.land_. External types @@ -54,8 +60,9 @@ External types ``typedef struct CBSStruct *CBS`` -_`.type.cbs`: A ``CBSStruct`` may be embedded in another structure, or -you can create it using ``LandCreate()``. +_`.type.cbs`: The type of coalescing block structures. A ``CBSStruct`` +may be embedded in another structure, or you can create it using +``LandCreate()``. External functions @@ -101,6 +108,25 @@ following optional keyword arguments: generic function. +Limitations +........... + +_`.limit.find`: CBS does not support the ``LandFindFirst()``, +``LandFindLast()``, and ``LandFindLargest()`` generic functions unless +the ``CBSFastFind`` keyword argument was set to ``TRUE``. + +_`.limit.zones`: CBS does not support the ``LandFindInZones()`` +generic function unless the ``CBSFastFind`` and ``CBSZoned`` keyword +arguments were both set to ``TRUE``. + +_`.limit.iterate`: CBS does not support visitors setting +``deleteReturn`` to ``TRUE`` when iterating over ranges with +``LandIterate()``. + +_`.limit.flush`: CBS cannot be used as the source in a call to +``LandFlush()``. + + Implementation -------------- @@ -108,7 +134,6 @@ _`.impl`: This section is concerned with describing various aspects of the implementation. It does not form part of the interface definition. - Splay tree .......... @@ -163,12 +188,12 @@ Testing _`.test`: The following testing will be performed on this module: -_`.test.fbmtest`: A generic test for land implementations. See -design.mps.land.fbmtest. +_`.test.land`: A generic test for land implementations. See +design.mps.land.test. -_`.test.pool`: Several pools (currently MVT_ and MVFF_) are implemented -on top of a CBS. These pool are subject to testing in development, QA, -and are/will be heavily exercised by customers. +_`.test.pool`: The arena and two pools (MVT_ and MVFF_) are +implemented on top of a CBS. These are subject to testing in +development, QA, and are heavily exercised by customers. .. _MVT: poolmvt .. _MVFF: poolmvff @@ -177,9 +202,9 @@ and are/will be heavily exercised by customers. Notes for future development ---------------------------- -_`.future.not-splay`: The initial implementation of CBSs is based on -splay trees. It could be revised to use any other data structure that -meets the requirements (especially `.req.fast`_). +_`.future.not-splay`: The implementation of CBSs is based on splay +trees. It could be revised to use other data structures that meet the +requirements (especially `.req.fast`_). _`.future.hybrid`: It would be possible to attenuate the problem of `.risk.overhead`_ (below) by using a single word bit set to represent diff --git a/mps/design/freelist.txt b/mps/design/freelist.txt index bc859cd3e32..680a143c1a5 100644 --- a/mps/design/freelist.txt +++ b/mps/design/freelist.txt @@ -40,174 +40,53 @@ When memory becomes available again to allocate control structures, the free lists can be "flushed" back into the more efficient data structures. -_`.bg`: The free list allocator was formerly part of the Coalescing -Block Structure module (see design.mps.cbs) but it was split into its -own module because this makes it: - -#. simpler (no need to interact with CBS) and thus more maintainable; -#. possible to test directly (no need to create a CBS and then force - its control pool to run out of memory); and -#. usable as a fallback allocator in other pools (not just in pools - that use CBS). - - -Definitions ------------ - -_`.def.range`: A (contiguous) *range* of addresses is a semi-open -interval on address space. - -_`.def.isolated`: A contiguous range is *isolated* with respect to -some property it has, if adjacent elements do not have that property. - Requirements ------------ -_`.req.set`: Must maintain a set of free address ranges. +In addition to the generic land requirements (see design.mps.land_), +free lists must satisfy: -_`.req.add`: Must be able to add free address ranges to the set. - -_`.req.remove`: Must be able to remove address ranges from the set (in -particular, when memory is allocated). - -_`.req.iterate`: Must support the iteration of all isolated contiguous -ranges. - -_`.req.protocol`: Must detect protocol violations. - -_`.req.align`: Must support an alignment (the alignment of all -addresses specifying ranges) of down to ``sizeof(void *)`` without -losing memory. +.. _design.mps.land: land _`.req.zero-overhead`: Must have zero space overhead for the storage of any set of free blocks, so that it can be used to manage memory when no memory can be allocated for control structures. -_`.req.source`: This set of requirements is derived from those of the -CBS module (see design.mps.cbs.req), except that there is no -equivalent of design.mps.cbs.req.fast, and design.mps.cbs.req.small -has been replaced with `.req.zero-overhead`_. - Interface --------- +_`.land`: Free lists are an implementation of the *land* abstract data +type, so the interface consists of the generic functions for lands. +See design.mps.land_. + Types ..... ``typedef struct FreelistStruct *Freelist`` -_`.type.freelist`: The type of free lists. The structure -``FreelistStruct`` is declared in the header so that it can be inlined -in other structures, but you should not depend on its details. - -``typedef Bool (*FreelistIterateMethod)(Bool *deleteReturn, Freelist fl, Range range, void *closureP, Size closureS)`` - -_`.type.iterate.method`: A callback function that may be passed to -``FreelistIterate()``. It is called for every isolated contiguous -range in address order, and with the closure arguments that were -originally passed to ``FreelistIterate()``. It must update -``*deleteReturn`` to ``TRUE`` if the range must be deleted from the -free lists, or ``FALSE`` if the range must be kept. The function must -return ``TRUE`` if the iteration must continue, and ``FALSE`` if the -iteration must stop (after possibly deleting the current range). +_`.type.freelist`: The type of free lists. A ``FreelistStruct`` may be +embedded in another structure, or you can create it using +``LandCreate()``. -Functions -......... +External functions +.................. -``Res FreelistInit(Freelist fl, Align alignment)`` +``LandClass FreelistLandClassGet(void)`` -_`.function.init`: Initialize the ``Freelist`` structure pointed to by -``fl``. The argument ``alignment`` is the alignment of address ranges -to be maintained. An initialised free list contains no address ranges. +_`.function.class`: The function ``FreelistLandClassGet()`` returns +the free list class, a subclass of ``LandClass`` suitable for passing +to ``LandCreate()`` or ``LandInit()``. -``void FreelistFinish(Freelist fl)`` -_`.function.finish`: Finish the free list pointed to by ``fl``. - -``Res FreelistInsert(Range rangeReturn, Freelist fl, Range range)`` - -_`.function.insert`: If any part of ``range`` is already in the free -list ``fl``, then leave the free list unchanged and return -``ResFAIL``. Otherwise, insert ``range`` into the free list ``fl``; -update ``rangeReturn`` to describe the contiguous isolated range -containing the inserted range (this may differ from ``range`` if there -was coalescence on either side) and return ``ResOK``. - -``Res FreelistDelete(Range rangeReturn, Freelist fl, Range range)`` - -_`.function.delete`: If any part of the range is not in the free list, -then leave the free list unchanged and return ``ResFAIL``. Otherwise, -remove ``range`` from the free list and update ``rangeReturn`` to -describe the contiguous isolated range that formerly contained the -deleted range (this may differ from ``range`` if there were fragments -left on either side), and return ``ResOK``. - -``void FreelistIterate(Freelist fl, FreelistIterateMethod iterate, void *closureP, Size closureS)`` - -_`.function.iterate`: Iterate all isolated contiguous ranges in the -free list ``fl`` in address order, calling ``iterate`` for each one. -See ``FreelistIterateMethod`` for details. - -``Bool FreelistFindFirst(Range rangeReturn, Range oldRangeReturn, Freelist fl, Size size, FindDelete findDelete)`` - -_`.function.find.first`: Locate the first isolated contiguous range in -address order, within the free list ``fl``, of at least ``size`` -bytes, update ``rangeReturn`` to that range, and return ``TRUE``. If -there is no such continuous range, return ``FALSE``. - -In addition, optionally delete the found range from the free list, -depending on the ``findDelete`` argument. This saves a separate call -to ``FreelistDelete()``, and uses the knowledge of exactly where we -found the range. The value of ``findDelete`` must come from this -enumeration:: - - enum { - FindDeleteNONE, /* don't delete after finding */ - FindDeleteLOW, /* delete size bytes from low end of block */ - FindDeleteHIGH, /* delete size bytes from high end of block */ - FindDeleteENTIRE /* delete entire range */ - }; - -The original contiguous isolated range in which the range was found is -returned via the ``oldRangeReturn`` argument. (If ``findDelete`` is -``FindDeleteNONE`` or ``FindDeleteENTIRE``, then this will be -identical to the range returned via the ``rangeReturn`` argument.) - -``Bool FreelistFindLast(Range rangeReturn, Range oldRangeReturn, Freelist fl, Size size, FindDelete findDelete)`` - -_`.function.find.last`: Like ``FreelistFindFirst()``, except that it -finds the last block in address order. - -``Bool FreelistFindLargest(Range rangeReturn, Range oldRangeReturn, Freelist fl, Size, size, FindDelete findDelete)`` - -_`.function.find.largest`: Locate the largest block within the free -list ``fl``, and if that block is at least as big as ``size``, return -its range via the ``rangeReturn`` argument, and return ``TRUE``. If -there are no blocks in the free list at least as large as ``size``, -return ``FALSE``. Pass 0 for ``size`` if you want the largest block -unconditionally. - -Like ``FreelistFindFirst()``, optionally delete the range from the -free list. (Always the whole range: specifying ``FindDeleteLOW`` or -``FindDeleteHIGH`` has the same effect as ``FindDeleteENTIRE``). - -``void FreelistFlushToCBS(Freelist fl, CBS cbs)`` - -Remove free address ranges from the free list ``fl`` and add them to -the Coalescing Block Structure ``cbs``. Continue until a call to -``CBSInsert()`` fails, or until the free list is empty, whichever -happens first. - -``Res FreelistDescribe(Freelist fl, mps_lib_FILE *stream)`` - -_`.function.describe`: Print a textual representation of the free -list ``fl`` to the given stream, indicating the contiguous ranges in -order. It is provided for debugging purposes only. +Keyword arguments +................. +When initializing a free list, ``LandCreate()`` and ``LandInit()`` +take no keyword arguments. Pass ``mps_args_none``. Implementation @@ -246,6 +125,23 @@ do this, such as using another tag to indicate the last block in the list, but these would be more complicated.) +Testing +------- + +_`.test`: The following testing will be performed on this module: + +_`.test.land`: A generic test for land implementations. See +design.mps.land.test. + +_`.test.pool`: Two pools (MVT_ and MVFF_) use free lists as a fallback +when low on memory. These are subject to testing in development, QA, +and are heavily exercised by customers. + +.. _MVT: poolmvt +.. _MVFF: poolmvff + + + Opportunities for improvement ----------------------------- @@ -255,8 +151,8 @@ exceed the recorded size of the list. _`.improve.maxsize`: We could maintain the maximum size of any range on the list, and use that to make an early exit from -``FreelistFindLargest()``. It's not clear that this would actually be -an improvement. +``LandFindLargest()``. It's not clear that this would actually be an +improvement. @@ -265,6 +161,8 @@ Document History - 2013-05-18 GDR_ Initial draft based on CBS "emergency block" design. +- 2014-04-01 GDR_ Moved generic material to design.mps.land_. + .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/mps/design/land.txt b/mps/design/land.txt index fcbdde02ab1..8c12c9f5a96 100644 --- a/mps/design/land.txt +++ b/mps/design/land.txt @@ -77,12 +77,15 @@ Types _`.type.land`: The type of a generic land instance. -``typedef Bool (*LandVisitor)(Land land, Range range, void *closureP, Size closureS);`` +``typedef Bool (*LandVisitor)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS);`` -_`.type.visitor`: Type ``LandVisitor`` is a callback function that -may be passed to ``LandIterate()``. It is called for every isolated -contiguous range in address order. The function must return a -``Bool`` indicating whether to continue with the iteration. +_`.type.visitor`: Type ``LandVisitor`` is a callback function that may +be passed to ``LandIterate()``. It is called for every isolated +contiguous range in address order. The function must return a ``Bool`` +indicating whether to continue with the iteration. It may additionally +update ``*deleteReturn`` to ``TRUE`` if the range must be deleted from +the land, or ``FALSE`` if the range must be kept. (The default is to +keep the range, and not all land classes support deletion.) Generic functions @@ -220,25 +223,28 @@ the ``oldRangeReturn`` argument. ``Res LandDescribe(Land land, mps_lib_FILE *stream)`` -_`.function.describe`: ``LandDescribe()`` is a function that prints -a textual representation of the land to the given stream, indicating -the contiguous ranges in order, as well as the structure of the -underlying splay tree implementation. It is provided for debugging -purposes only. +_`.function.describe`: ``LandDescribe()`` prints a textual +representation of the land to the given stream, indicating the +contiguous ranges in order, as well as the structure of the underlying +splay tree implementation. It is provided for debugging purposes only. + +``void LandFlush(Land dest, Land src)`` + +_`.function.flush`: Delete ranges of addresses from ``src`` and insert +them into ``dest``, so long as ``LandInsert()`` remains successful. Testing ------- -_`.test.fbmtest`: There is a stress test for implementations of this -interface in impl.c.fbmtest. This allocates a large block of memory -and then simulates the allocation and deallocation of ranges within -this block using both a ``Land`` and a ``BT``. It makes both valid and -invalid requests, and compares the ``Land`` response to the correct -behaviour as determined by the ``BT``. It iterates the ranges in the -``Land``, comparing them to the ``BT``. It invokes the -``LandDescribe()`` generic function, but makes no automatic test of -the resulting output. +_`.test`: There is a stress test for implementations of this interface +in impl.c.landtest. This allocates a large block of memory and then +simulates the allocation and deallocation of ranges within this block +using both a ``Land`` and a ``BT``. It makes both valid and invalid +requests, and compares the ``Land`` response to the correct behaviour +as determined by the ``BT``. It iterates the ranges in the ``Land``, +comparing them to the ``BT``. It invokes the ``LandDescribe()`` +generic function, but makes no automatic test of the resulting output. Document History diff --git a/mps/tool/testrun.bat b/mps/tool/testrun.bat index e2b16a00260..4b713f5c748 100755 --- a/mps/tool/testrun.bat +++ b/mps/tool/testrun.bat @@ -32,10 +32,10 @@ set ALL_TEST_CASES=^ btcv.exe ^ exposet0.exe ^ expt825.exe ^ - fbmtest.exe ^ finalcv.exe ^ finaltest.exe ^ fotest.exe ^ + landtest.exe ^ locbwcss.exe ^ lockcov.exe ^ lockutw3.exe ^ diff --git a/mps/tool/testrun.sh b/mps/tool/testrun.sh index 9cecd1c6fd5..4264388d0f6 100755 --- a/mps/tool/testrun.sh +++ b/mps/tool/testrun.sh @@ -29,10 +29,10 @@ ALL_TEST_CASES=" btcv exposet0 expt825 - fbmtest finalcv finaltest fotest + landtest locbwcss lockcov locusss From 0b159dc6501166607a4b0713399e9c36fcfb85bf Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 3 Apr 2014 12:52:23 +0100 Subject: [PATCH 09/43] New module failover implements a fail-over allocator as a land class. Use Failover in MVT and MVFF. Test Failover in landtest. Implementation of LandFindInZones for Freelist (untested). Remove signature from RangeStruct so we can embed it without a space cost. Copied from Perforce Change: 185196 ServerID: perforce.ravenbrook.com --- mps/code/cbs.c | 33 +-- mps/code/cbs.h | 4 +- mps/code/comm.gmk | 1 + mps/code/commpre.nmk | 1 + mps/code/failover.c | 322 +++++++++++++++++++++++++ mps/code/failover.h | 69 ++++++ mps/code/freelist.c | 131 ++++++++-- mps/code/freelist.h | 4 +- mps/code/land.c | 36 ++- mps/code/landtest.c | 43 +++- mps/code/mpm.h | 4 +- mps/code/mpmst.h | 24 +- mps/code/mpmtypes.h | 7 +- mps/code/mps.c | 1 + mps/code/mps.xcodeproj/project.pbxproj | 16 +- mps/code/poolmv2.c | 117 ++++----- mps/code/poolmvff.c | 227 +++++++---------- mps/code/range.c | 8 +- mps/code/range.h | 8 +- mps/design/cbs.txt | 27 ++- mps/design/failover.txt | 150 ++++++++++++ mps/design/index.txt | 13 +- mps/design/land.txt | 51 +++- mps/design/poolmvff.txt | 8 +- mps/design/range.txt | 10 +- mps/design/splay.txt | 12 +- mps/manual/source/design/index.rst | 1 + 27 files changed, 991 insertions(+), 337 deletions(-) create mode 100644 mps/code/failover.c create mode 100644 mps/code/failover.h create mode 100644 mps/design/failover.txt diff --git a/mps/code/cbs.c b/mps/code/cbs.c index bfd21af7746..3e99e3ca195 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -66,7 +66,7 @@ Bool CBSCheck(CBS cbs) { /* See .enter-leave.simple. */ CHECKS(CBS, cbs); - CHECKL(LandCheck(&cbs->landStruct)); + CHECKD(Land, &cbs->landStruct); CHECKD(SplayTree, cbsSplay(cbs)); /* nothing to check about treeSize */ CHECKD(Pool, cbs->blockPool); @@ -226,7 +226,7 @@ static void cbsUpdateZonedNode(SplayTree splay, Tree tree) /* cbsInit -- Initialise a CBS structure * - * See . + * See . */ ARG_DEFINE_KEY(cbs_extend_by, Size); @@ -307,7 +307,7 @@ static Res cbsInit(Land land, ArgList args) /* CBSFinish -- Finish a CBS structure * - * See . + * See . */ static void cbsFinish(Land land) @@ -645,7 +645,7 @@ static Res cbsDeleteFromTree(Range rangeReturn, Land land, Range range) /* cbsDelete -- Remove a range from a CBS * - * See . + * See . * * .delete.alloc: Will only allocate a block if the range splits * an existing range. @@ -715,7 +715,7 @@ static Res cbsSplayNodeDescribe(Tree tree, mps_lib_FILE *stream) * This is because CBSIterate uses TreeTraverse, which does not permit * modification, for speed and to avoid perturbing the splay tree balance. * - * See . + * See . */ typedef struct CBSIterateClosure { @@ -777,20 +777,6 @@ static void cbsIterate(Land land, LandVisitor visitor, } -/* FindDeleteCheck -- check method for a FindDelete value */ - -Bool FindDeleteCheck(FindDelete findDelete) -{ - CHECKL(findDelete == FindDeleteNONE - || findDelete == FindDeleteLOW - || findDelete == FindDeleteHIGH - || findDelete == FindDeleteENTIRE); - UNUSED(findDelete); /* */ - - return TRUE; -} - - /* cbsFindDeleteRange -- delete appropriate range of block found */ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, @@ -1094,7 +1080,7 @@ static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, /* cbsDescribe -- describe a CBS * - * See . + * See . */ static Res cbsDescribe(Land land, mps_lib_FILE *stream) @@ -1129,10 +1115,7 @@ static Res cbsDescribe(Land land, mps_lib_FILE *stream) return res; } - -typedef LandClassStruct CBSLandClassStruct; - -DEFINE_CLASS(CBSLandClass, class) +DEFINE_LAND_CLASS(CBSLandClass, class) { INHERIT_CLASS(class, LandClass); class->name = "CBS"; @@ -1147,10 +1130,10 @@ DEFINE_CLASS(CBSLandClass, class) class->findLargest = cbsFindLargest; class->findInZones = cbsFindInZones; class->describe = cbsDescribe; + AVERT(LandClass, class); } - /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2001-2013 Ravenbrook Limited . diff --git a/mps/code/cbs.h b/mps/code/cbs.h index 170c41d496b..e6a3dd13850 100644 --- a/mps/code/cbs.h +++ b/mps/code/cbs.h @@ -28,9 +28,11 @@ typedef struct CBSBlockStruct { ZoneSet zones; /* union zone set of all ranges in sub-tree */ } CBSBlockStruct; +typedef struct CBSStruct *CBS; + extern Bool CBSCheck(CBS cbs); -extern CBSLandClass CBSLandClassGet(void); +extern LandClass CBSLandClassGet(void); extern const struct mps_key_s _mps_key_cbs_block_pool; #define CBSBlockPool (&_mps_key_cbs_block_pool) diff --git a/mps/code/comm.gmk b/mps/code/comm.gmk index beeb542fca9..44264ed0acf 100644 --- a/mps/code/comm.gmk +++ b/mps/code/comm.gmk @@ -177,6 +177,7 @@ MPMCOMMON = \ dbgpool.c \ dbgpooli.c \ event.c \ + failover.c \ format.c \ freelist.c \ global.c \ diff --git a/mps/code/commpre.nmk b/mps/code/commpre.nmk index 75dbdad446e..82b51bff382 100644 --- a/mps/code/commpre.nmk +++ b/mps/code/commpre.nmk @@ -123,6 +123,7 @@ MPMCOMMON=\ \ \ \ + \ \ \ \ diff --git a/mps/code/failover.c b/mps/code/failover.c new file mode 100644 index 00000000000..ca0d15cad1c --- /dev/null +++ b/mps/code/failover.c @@ -0,0 +1,322 @@ +/* failover.c: FAILOVER IMPLEMENTATION + * + * $Id$ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * .design: + */ + +#include "failover.h" +#include "mpm.h" +#include "range.h" + +SRCID(failover, "$Id$"); + + +#define failoverOfLand(land) PARENT(FailoverStruct, landStruct, land) + + +ARG_DEFINE_KEY(failover_primary, Pointer); +ARG_DEFINE_KEY(failover_secondary, Pointer); + + +Bool FailoverCheck(Failover fo) +{ + CHECKS(Failover, fo); + CHECKD(Land, &fo->landStruct); + CHECKD(Land, fo->primary); + CHECKD(Land, fo->secondary); + return TRUE; +} + + +static Res failoverInit(Land land, ArgList args) +{ + Failover fo; + LandClass super; + Land primary, secondary; + ArgStruct arg; + Res res; + + AVERT(Land, land); + super = LAND_SUPERCLASS(FailoverLandClass); + res = (*super->init)(land, args); + if (res != ResOK) + return res; + + ArgRequire(&arg, args, FailoverPrimary); + primary = arg.val.p; + ArgRequire(&arg, args, FailoverSecondary); + secondary = arg.val.p; + + fo = failoverOfLand(land); + fo->primary = primary; + fo->secondary = secondary; + fo->sig = FailoverSig; + AVERT(Failover, fo); + return ResOK; +} + + +static void failoverFinish(Land land) +{ + Failover fo; + + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + + fo->sig = SigInvalid; +} + + +static Res failoverInsert(Range rangeReturn, Land land, Range range) +{ + Failover fo; + Res res; + + AVER(rangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVERT(Range, range); + + /* Provide more opportunities for coalescence. See + * . + */ + LandFlush(fo->primary, fo->secondary); + + res = LandInsert(rangeReturn, fo->primary, range); + if (ResIsAllocFailure(res)) { + /* primary ran out of memory: try secondary instead. */ + res = LandInsert(rangeReturn, fo->secondary, range); + } + + return res; +} + + +static Res failoverDelete(Range rangeReturn, Land land, Range range) +{ + Failover fo; + Res res; + RangeStruct oldRange, dummyRange, left, right; + + AVER(rangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVERT(Range, range); + + /* Prefer efficient search in the primary. See + * . + */ + LandFlush(fo->primary, fo->secondary); + + res = LandDelete(&oldRange, fo->primary, range); + + if (res == ResFAIL) { + /* Range not found in primary: try secondary. */ + return LandDelete(rangeReturn, fo->secondary, range); + } else if (ResIsAllocFailure(res)) { + /* Range was found in primary, but couldn't be deleted because the + * primary is out of memory. Delete the whole of oldRange, and + * re-insert the fragments (which might end up in the secondary). + * See . + */ + res = LandDelete(&dummyRange, fo->primary, &oldRange); + if (res != ResOK) + return res; + + AVER(RangesEqual(&oldRange, &dummyRange)); + RangeInit(&left, RangeBase(&oldRange), RangeBase(range)); + if (!RangeEmpty(&left)) { + res = LandInsert(&dummyRange, land, &left); + AVER(res == ResOK); + } + RangeInit(&right, RangeLimit(range), RangeLimit(&oldRange)); + if (!RangeEmpty(&right)) { + res = LandInsert(&dummyRange, land, &right); + AVER(res == ResOK); + } + } + if (res == ResOK) { + AVER(RangesNest(&oldRange, range)); + RangeCopy(rangeReturn, &oldRange); + } + return res; +} + + +static void failoverIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +{ + Failover fo; + + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVER(visitor != NULL); + + LandIterate(fo->primary, visitor, closureP, closureS); + LandIterate(fo->secondary, visitor, closureP, closureS); +} + + +static Bool failoverFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + Failover fo; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVERT(FindDelete, findDelete); + + /* See . */ + LandFlush(fo->primary, fo->secondary); + + return LandFindFirst(rangeReturn, oldRangeReturn, fo->primary, size, findDelete) + || LandFindFirst(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete); +} + + +static Bool failoverFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + Failover fo; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVERT(FindDelete, findDelete); + + /* See . */ + LandFlush(fo->primary, fo->secondary); + + return LandFindLast(rangeReturn, oldRangeReturn, fo->primary, size, findDelete) + || LandFindLast(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete); +} + + +static Bool failoverFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + Failover fo; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVERT(FindDelete, findDelete); + + /* See . */ + LandFlush(fo->primary, fo->secondary); + + return LandFindLargest(rangeReturn, oldRangeReturn, fo->primary, size, findDelete) + || LandFindLargest(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete); +} + + +static Bool failoverFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) +{ + Failover fo; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + /* AVERT(ZoneSet, zoneSet); */ + AVERT(Bool, high); + + /* See . */ + LandFlush(fo->primary, fo->secondary); + + return LandFindInZones(rangeReturn, oldRangeReturn, fo->primary, size, zoneSet, high) + || LandFindInZones(rangeReturn, oldRangeReturn, fo->secondary, size, zoneSet, high); +} + + +static Res failoverDescribe(Land land, mps_lib_FILE *stream) +{ + Failover fo; + Res res; + + if (!TESTT(Land, land)) return ResFAIL; + fo = failoverOfLand(land); + if (!TESTT(Failover, fo)) return ResFAIL; + if (stream == NULL) return ResFAIL; + + res = WriteF(stream, + "Failover $P {\n", (WriteFP)fo, + " primary = $P ($S)\n", (WriteFP)fo->primary, + fo->primary->class->name, + " secondary = $P ($S)\n", (WriteFP)fo->secondary, + fo->secondary->class->name, + "}\n", NULL); + + return res; +} + + +DEFINE_LAND_CLASS(FailoverLandClass, class) +{ + INHERIT_CLASS(class, LandClass); + class->name = "FAILOVER"; + class->size = sizeof(FailoverStruct); + class->init = failoverInit; + class->finish = failoverFinish; + class->insert = failoverInsert; + class->delete = failoverDelete; + class->iterate = failoverIterate; + class->findFirst = failoverFindFirst; + class->findLast = failoverFindLast; + class->findLargest = failoverFindLargest; + class->findInZones = failoverFindInZones; + class->describe = failoverDescribe; + AVERT(LandClass, class); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * 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. + */ diff --git a/mps/code/failover.h b/mps/code/failover.h new file mode 100644 index 00000000000..56e6149e05e --- /dev/null +++ b/mps/code/failover.h @@ -0,0 +1,69 @@ +/* failover.h: FAILOVER ALLOCATOR INTERFACE + * + * $Id$ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * .source: . + */ + +#ifndef failover_h +#define failover_h + +#include "mpmtypes.h" + +typedef struct FailoverStruct *Failover; + +extern Bool FailoverCheck(Failover failover); + +extern LandClass FailoverLandClassGet(void); + +extern const struct mps_key_s _mps_key_failover_primary; +#define FailoverPrimary (&_mps_key_failover_primary) +#define FailoverPrimary_FIELD p +extern const struct mps_key_s _mps_key_failover_secondary; +#define FailoverSecondary (&_mps_key_failover_secondary) +#define FailoverSecondary_FIELD p + +#endif /* failover.h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * 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. + */ diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 14091c02558..4706212bcc0 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -8,6 +8,7 @@ #include "freelist.h" #include "mpm.h" +#include "range.h" SRCID(freelist, "$Id$"); @@ -22,19 +23,36 @@ typedef union FreelistBlockUnion { /* limit is (char *)this + freelistAlignment(fl) */ } small; struct { - FreelistBlock next; + FreelistBlock next; /* not tagged (low bit 0) */ Addr limit; } large; } FreelistBlockUnion; -/* See */ +/* freelistMinimumAlignment -- the minimum allowed alignment for the + * address ranges in a free list: see + */ + #define freelistMinimumAlignment ((Align)sizeof(FreelistBlock)) +/* FreelistTag -- return the tag of word */ + #define FreelistTag(word) ((word) & 1) + + +/* FreelistTagSet -- return word updated with the tag set */ + #define FreelistTagSet(word) ((FreelistBlock)((Word)(word) | 1)) + + +/* FreelistTagReset -- return word updated with the tag reset */ + #define FreelistTagReset(word) ((FreelistBlock)((Word)(word) & ~(Word)1)) + + +/* FreelistTagCopy -- return 'to' updated to have the same tag as 'from' */ + #define FreelistTagCopy(to, from) ((FreelistBlock)((Word)(to) | FreelistTag((Word)(from)))) @@ -148,7 +166,7 @@ Bool FreelistCheck(Freelist fl) Land land; CHECKS(Freelist, fl); land = &fl->landStruct; - CHECKL(LandCheck(land)); + CHECKD(Land, land); /* See */ CHECKL(AlignIsAligned(LandAlignment(land), freelistMinimumAlignment)); CHECKL((fl->list == NULL) == (fl->listSize == 0)); @@ -195,12 +213,14 @@ static void freelistFinish(Land land) /* freelistBlockSetPrevNext -- update list of blocks + * * If prev and next are both NULL, make the block list empty. * Otherwise, if prev is NULL, make next the first block in the list. * Otherwise, if next is NULL, make prev the last block in the list. * Otherwise, make next follow prev in the list. * Update the count of blocks by 'delta'. */ + static void freelistBlockSetPrevNext(Freelist fl, FreelistBlock prev, FreelistBlock next, int delta) { @@ -289,12 +309,14 @@ static Res freelistInsert(Range rangeReturn, Land land, Range range) } -/* freelistDeleteFromBlock -- delete 'range' from 'block' (it is known - * to be a subset of that block); update 'rangeReturn' to the original - * range of 'block' and update the block list accordingly: 'prev' is - * the block on the list just before 'block', or NULL if 'block' is - * the first block on the list. +/* freelistDeleteFromBlock -- delete range from block + * + * range must be a subset of block. Update rangeReturn to be the + * original range of block and update the block list accordingly: prev + * is on the list just before block, or NULL if block is the first + * block on the list. */ + static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl, Range range, FreelistBlock prev, FreelistBlock block) @@ -416,14 +438,17 @@ static void freelistIterate(Land land, LandVisitor visitor, } -/* freelistFindDeleteFromBlock -- Find a chunk of 'size' bytes in - * 'block' (which is known to be at least that big) and possibly - * delete that chunk according to the instruction in 'findDelete'. - * Return the range of that chunk in 'rangeReturn'. Return the - * original range of the block in 'oldRangeReturn'. Update the block - * list accordingly, using 'prev' which is the previous block in the - * list, or NULL if 'block' is the first block in the list. +/* freelistFindDeleteFromBlock -- delete size bytes from block + * + * Find a chunk of size bytes in block (which is known to be at least + * that big) and possibly delete that chunk according to the + * instruction in findDelete. Return the range of that chunk in + * rangeReturn. Return the original range of the block in + * oldRangeReturn. Update the block list accordingly, using prev, + * which is previous in list or NULL if block is the first block in + * the list. */ + static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn, Freelist fl, Size size, FindDelete findDelete, @@ -580,11 +605,77 @@ static Bool freelistFindLargest(Range rangeReturn, Range oldRangeReturn, } -/* freelistDescribeVisitor -- visitor method for freelistDescribe. +static Res freelistFindInZones(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, + ZoneSet zoneSet, Bool high) +{ + Freelist fl; + LandFindMethod landFind; + RangeInZoneSet search; + Bool found = FALSE; + FreelistBlock prev, cur, next; + FreelistBlock foundPrev = NULL, foundCur = NULL; + RangeStruct foundRange; + + AVER(FALSE); /* TODO: this code is completely untested! */ + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); + AVERT(Freelist, fl); + /* AVERT(ZoneSet, zoneSet); */ + AVERT(Bool, high); + + landFind = high ? cbsFindLast : cbsFindFirst; + search = high ? RangeInZoneSetLast : RangeInZoneSetFirst; + + if (zoneSet == ZoneSetEMPTY) + return ResFAIL; + if (zoneSet == ZoneSetUNIV) { + FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; + if ((*landFind)(rangeReturn, oldRangeReturn, land, size, fd)) + return ResOK; + else + return ResFAIL; + } + if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) + return ResFAIL; + + prev = NULL; + cur = fl->list; + while (cur) { + Addr base, limit; + if ((*search)(&base, &limit, FreelistBlockBase(cur), + FreelistBlockLimit(fl, cur), + LandArena(land), zoneSet, size)) + { + found = TRUE; + foundPrev = prev; + foundCur = cur; + RangeInit(&foundRange, base, limit); + if (!high) + break; + } + next = FreelistBlockNext(cur); + prev = cur; + cur = next; + } + + if (!found) + return ResFAIL; + + freelistDeleteFromBlock(oldRangeReturn, fl, &foundRange, foundPrev, foundCur); + RangeCopy(rangeReturn, &foundRange); + return ResOK; +} + + +/* freelistDescribeVisitor -- visitor method for freelistDescribe * * Writes a decription of the range into the stream pointed to by * closureP. */ + static Bool freelistDescribeVisitor(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS) { @@ -593,7 +684,7 @@ static Bool freelistDescribeVisitor(Bool *deleteReturn, Land land, Range range, if (deleteReturn == NULL) return FALSE; if (!TESTT(Land, land)) return FALSE; - if (!TESTT(Range, range)) return FALSE; + if (!RangeCheck(range)) return FALSE; if (stream == NULL) return FALSE; UNUSED(closureS); @@ -629,9 +720,7 @@ static Res freelistDescribe(Land land, mps_lib_FILE *stream) } -typedef LandClassStruct FreelistLandClassStruct; - -DEFINE_CLASS(FreelistLandClass, class) +DEFINE_LAND_CLASS(FreelistLandClass, class) { INHERIT_CLASS(class, LandClass); class->name = "FREELIST"; @@ -644,7 +733,9 @@ DEFINE_CLASS(FreelistLandClass, class) class->findFirst = freelistFindFirst; class->findLast = freelistFindLast; class->findLargest = freelistFindLargest; + class->findInZones = freelistFindInZones; class->describe = freelistDescribe; + AVERT(LandClass, class); } diff --git a/mps/code/freelist.h b/mps/code/freelist.h index 1ba46ae338d..c46ab57bc15 100644 --- a/mps/code/freelist.h +++ b/mps/code/freelist.h @@ -11,9 +11,11 @@ #include "mpmtypes.h" +typedef struct FreelistStruct *Freelist; + extern Bool FreelistCheck(Freelist freelist); -extern FreelistLandClass FreelistLandClassGet(void); +extern LandClass FreelistLandClassGet(void); #endif /* freelist.h */ diff --git a/mps/code/land.c b/mps/code/land.c index e06f0060e36..fe759d85410 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -12,11 +12,26 @@ SRCID(land, "$Id$"); +/* FindDeleteCheck -- check method for a FindDelete value */ + +Bool FindDeleteCheck(FindDelete findDelete) +{ + CHECKL(findDelete == FindDeleteNONE + || findDelete == FindDeleteLOW + || findDelete == FindDeleteHIGH + || findDelete == FindDeleteENTIRE); + UNUSED(findDelete); /* */ + + return TRUE; +} + + /* LandCheck -- check land */ Bool LandCheck(Land land) { CHECKS(Land, land); + CHECKD(LandClass, land->class); CHECKU(Arena, land->arena); CHECKL(AlignCheck(land->alignment)); return TRUE; @@ -34,8 +49,8 @@ Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *own AVER(land != NULL); AVERT(LandClass, class); - AVER(AlignCheck(alignment)); - + AVERT(Align, alignment); + land->alignment = alignment; land->arena = arena; land->class = class; @@ -236,8 +251,8 @@ Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size siz AVER(oldRangeReturn != NULL); AVERT(Land, land); AVER(SizeIsAligned(size, land->alignment)); - /* AVER(ZoneSetCheck(zoneSet)); */ - AVER(BoolCheck(high)); + /* AVER(ZoneSet, zoneSet); */ + AVERT(Bool, high); return (*land->class->findInZones)(rangeReturn, oldRangeReturn, land, size, zoneSet, high); @@ -288,12 +303,13 @@ static Bool landFlushVisitor(Bool *deleteReturn, Land land, Range range, Land dest; AVER(deleteReturn != NULL); + AVERT(Land, land); AVERT(Range, range); AVER(closureP != NULL); UNUSED(closureS); dest = closureP; - res = LandInsert(&newRange, land, range); + res = LandInsert(&newRange, dest, range); if (res == ResOK) { *deleteReturn = TRUE; return TRUE; @@ -434,18 +450,18 @@ DEFINE_CLASS(LandClass, class) * Copyright (C) 2014 Ravenbrook Limited . * 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 @@ -456,7 +472,7 @@ DEFINE_CLASS(LandClass, class) * 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 diff --git a/mps/code/landtest.c b/mps/code/landtest.c index 5ca7305ebda..4698a9f1aaf 100644 --- a/mps/code/landtest.c +++ b/mps/code/landtest.c @@ -14,6 +14,7 @@ */ #include "cbs.h" +#include "failover.h" #include "freelist.h" #include "mpm.h" #include "mps.h" @@ -34,6 +35,7 @@ SRCID(landtest, "$Id$"); * the former. */ #define nCBSOperations ((Size)125000) #define nFLOperations ((Size)12500) +#define nFOOperations ((Size)12500) static Count NAllocateTried, NAllocateSucceeded, NDeallocateTried, NDeallocateSucceeded; @@ -479,7 +481,10 @@ extern int main(int argc, char *argv[]) BT allocTable; CBSStruct cbsStruct; FreelistStruct flStruct; - Land land; + FailoverStruct foStruct; + Land cbs = &cbsStruct.landStruct; + Land fl = &flStruct.landStruct; + Land fo = &foStruct.landStruct; Align align; testlib_init(argc, argv); @@ -507,26 +512,46 @@ extern int main(int argc, char *argv[]) (char *)dummyBlock + ArraySize); } - land = &cbsStruct.landStruct; MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, CBSFastFind, TRUE); - die((mps_res_t)LandInit(land, CBSLandClassGet(), arena, align, NULL, args), + die((mps_res_t)LandInit(cbs, CBSLandClassGet(), arena, align, NULL, args), "failed to initialise CBS"); } MPS_ARGS_END(args); state.align = align; state.block = dummyBlock; state.allocTable = allocTable; - state.land = land; + state.land = cbs; test(&state, nCBSOperations); - LandFinish(land); + LandFinish(cbs); - land = &flStruct.landStruct; - die((mps_res_t)LandInit(land, FreelistLandClassGet(), arena, align, NULL, + die((mps_res_t)LandInit(fl, FreelistLandClassGet(), arena, align, NULL, mps_args_none), "failed to initialise Freelist"); - state.land = land; + state.land = fl; test(&state, nFLOperations); - LandFinish(land); + LandFinish(fl); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, CBSFastFind, TRUE); + die((mps_res_t)LandInit(cbs, CBSLandClassGet(), arena, align, + NULL, args), + "failed to initialise CBS"); + } MPS_ARGS_END(args); + die((mps_res_t)LandInit(fl, FreelistLandClassGet(), arena, align, NULL, + mps_args_none), + "failed to initialise Freelist"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, FailoverPrimary, cbs); + MPS_ARGS_ADD(args, FailoverSecondary, fl); + die((mps_res_t)LandInit(fo, FailoverLandClassGet(), arena, align, NULL, + args), + "failed to initialise Failover"); + } MPS_ARGS_END(args); + state.land = fo; + test(&state, nFOOperations); + LandFinish(fo); + LandFinish(fl); + LandFinish(cbs); mps_arena_destroy(arena); diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 7096618f565..69765515fe5 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -807,7 +807,7 @@ extern AllocPattern AllocPatternRamp(void); extern AllocPattern AllocPatternRampCollectAll(void); -/* FindDelete -- see and */ +/* FindDelete -- see */ extern Bool FindDeleteCheck(FindDelete findDelete); @@ -1015,6 +1015,8 @@ extern void LandFlush(Land dest, Land src); extern Bool LandClassCheck(LandClass class); extern LandClass LandClassGet(void); #define LAND_SUPERCLASS(className) ((LandClass)SUPERCLASS(className)) +#define DEFINE_LAND_CLASS(className, var) \ + DEFINE_ALIAS_CLASS(className, LandClass, var) /* Stack Probe */ diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 6679dbafd75..35684a52950 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -646,8 +646,8 @@ typedef struct LandStruct { /* CBSStruct -- coalescing block structure * - * CBS is a subclass of Land that maintains a collection of disjoint - * ranges in a splay tree. + * CBS is a Land implementation that maintains a collection of + * disjoint ranges in a splay tree. * * See . */ @@ -669,6 +669,24 @@ typedef struct CBSStruct { } CBSStruct; +/* FailoverStruct -- fail over from one land to another + * + * Failover is a Land implementation that combines two other Lands, + * using primary until it fails, and then using secondary. + * + * See . + */ + +#define FailoverSig ((Sig)0x519FA170) /* SIGnature FAILOver */ + +typedef struct FailoverStruct { + LandStruct landStruct; /* superclass fields come first */ + Land primary; /* use this land normally */ + Land secondary; /* but use this one if primary fails */ + Sig sig; /* .class.end-sig */ +} FailoverStruct; + + /* FreelistStruct -- address-ordered freelist * * Freelist is a subclass of Land that maintains a collection of @@ -679,6 +697,8 @@ typedef struct CBSStruct { #define FreelistSig ((Sig)0x519F6331) /* SIGnature FREEL */ +typedef union FreelistBlockUnion *FreelistBlock; + typedef struct FreelistStruct { LandStruct landStruct; /* superclass fields come first */ FreelistBlock list; diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index 424216ee0b3..72b09af099e 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -113,11 +113,6 @@ typedef struct RangeStruct *Range; /* */ typedef struct LandStruct *Land; /* */ typedef struct LandClassStruct *LandClass; /* */ typedef unsigned FindDelete; /* */ -typedef LandClass CBSLandClass; /* */ -typedef struct CBSStruct *CBS; /* */ -typedef LandClass FreelistLandClass; /* */ -typedef struct FreelistStruct *Freelist; /* */ -typedef union FreelistBlockUnion *FreelistBlock; /* */ /* Arena*Method -- see */ @@ -439,7 +434,7 @@ enum { }; -/* FindDelete operations -- see and */ +/* FindDelete operations -- see */ enum { FindDeleteNONE = 1, /* don't delete after finding */ diff --git a/mps/code/mps.c b/mps/code/mps.c index 34c7a9b49cd..f404855310e 100644 --- a/mps/code/mps.c +++ b/mps/code/mps.c @@ -76,6 +76,7 @@ #include "freelist.c" #include "sa.c" #include "land.c" +#include "failover.c" /* Additional pool classes */ diff --git a/mps/code/mps.xcodeproj/project.pbxproj b/mps/code/mps.xcodeproj/project.pbxproj index c987b57b4e9..03cbf5c2bc4 100644 --- a/mps/code/mps.xcodeproj/project.pbxproj +++ b/mps/code/mps.xcodeproj/project.pbxproj @@ -42,11 +42,11 @@ 22B2BC3D18B643B300C33E63 /* PBXTargetDependency */, 2291A5E6175CB207001D4920 /* PBXTargetDependency */, 2291A5E8175CB20E001D4920 /* PBXTargetDependency */, - 3114A65B156E95B4001E0AA3 /* PBXTargetDependency */, 3114A5CC156E932C001E0AA3 /* PBXTargetDependency */, 3114A5EA156E93C4001E0AA3 /* PBXTargetDependency */, 224CC79D175E187C002FF81B /* PBXTargetDependency */, 22B2BC3F18B643B700C33E63 /* PBXTargetDependency */, + 3114A65B156E95B4001E0AA3 /* PBXTargetDependency */, 2231BB6D18CA986B002D6322 /* PBXTargetDependency */, 31D60034156D3D5A00337B26 /* PBXTargetDependency */, 2231BB6F18CA986D002D6322 /* PBXTargetDependency */, @@ -1258,6 +1258,11 @@ 2291A5F0175CB7A4001D4920 /* testlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testlib.h; sourceTree = ""; }; 22B2BC2B18B6434000C33E63 /* scheme-advanced.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "scheme-advanced.c"; path = "../example/scheme/scheme-advanced.c"; sourceTree = ""; }; 22B2BC3618B6434F00C33E63 /* scheme-advanced */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "scheme-advanced"; sourceTree = BUILT_PRODUCTS_DIR; }; + 22C5C99A18EC6AEC004C63D4 /* failover.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = failover.c; sourceTree = ""; }; + 22C5C99B18EC6AEC004C63D4 /* failover.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = failover.h; sourceTree = ""; }; + 22C5C99C18EC6AEC004C63D4 /* land.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = land.c; sourceTree = ""; }; + 22DD93E118ED815F00240DD2 /* failover.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = failover.txt; path = ../design/failover.txt; sourceTree = ""; }; + 22DD93E218ED815F00240DD2 /* land.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = land.txt; path = ../design/land.txt; sourceTree = ""; }; 22FA177516E8D6FC0098B23F /* amcssth */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = amcssth; sourceTree = BUILT_PRODUCTS_DIR; }; 22FA177616E8D7A80098B23F /* amcssth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amcssth.c; sourceTree = ""; }; 2D07B96C1636FC7200DB751B /* eventsql.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = eventsql.c; sourceTree = ""; }; @@ -1927,6 +1932,7 @@ 31160D9C1899540D0071EB17 /* config.txt */, 31160D9D1899540D0071EB17 /* critical-path.txt */, 31160D9E1899540D0071EB17 /* diag.txt */, + 22DD93E118ED815F00240DD2 /* failover.txt */, 31160D9F1899540D0071EB17 /* finalize.txt */, 31160DA01899540D0071EB17 /* fix.txt */, 31160DA11899540D0071EB17 /* freelist.txt */, @@ -1936,6 +1942,7 @@ 31160DA51899540D0071EB17 /* interface-c.txt */, 31160DA61899540D0071EB17 /* io.txt */, 31160DA71899540D0071EB17 /* keyword-arguments.txt */, + 22DD93E218ED815F00240DD2 /* land.txt */, 31160DA81899540D0071EB17 /* lib.txt */, 31160DA91899540D0071EB17 /* lock.txt */, 31160DAA1899540D0071EB17 /* locus.txt */, @@ -2004,7 +2011,6 @@ 3114A613156E944A001E0AA3 /* bttest.c */, 2291A5AA175CAA9B001D4920 /* exposet0.c */, 2291A5AB175CAA9B001D4920 /* expt825.c */, - 2291A5E9175CB4EC001D4920 /* landtest.c */, 3114A5CD156E9369001E0AA3 /* finalcv.c */, 3114A5E5156E93B9001E0AA3 /* finaltest.c */, 3124CAC6156BE48D00753214 /* fmtdy.c */, @@ -2012,6 +2018,7 @@ 3124CAE4156BE6D500753214 /* fmthe.c */, 3124CACC156BE4C200753214 /* fmtno.c */, 224CC79E175E3202002FF81B /* fotest.c */, + 2291A5E9175CB4EC001D4920 /* landtest.c */, 2231BB6818CA9834002D6322 /* locbwcss.c */, 31D60036156D3E0200337B26 /* lockcov.c */, 2231BB6918CA983C002D6322 /* locusss.c */, @@ -2152,10 +2159,13 @@ 311F2F5917398AE900C15B6A /* eventcom.h */, 311F2F5A17398AE900C15B6A /* eventdef.h */, 311F2F5C17398AE900C15B6A /* eventrep.h */, + 22C5C99A18EC6AEC004C63D4 /* failover.c */, + 22C5C99B18EC6AEC004C63D4 /* failover.h */, 31EEAC1A156AB2B200714D05 /* format.c */, 2291A5EE175CB768001D4920 /* freelist.c */, 2291A5EF175CB768001D4920 /* freelist.h */, 31EEAC07156AB27B00714D05 /* global.c */, + 22C5C99C18EC6AEC004C63D4 /* land.c */, 31EEAC2B156AB2F200714D05 /* ld.c */, 311F2F5E17398B0E00C15B6A /* lock.h */, 31EEAC08156AB27B00714D05 /* locus.c */, @@ -3120,11 +3130,11 @@ 318DA8C31892B0F30089718C /* djbench */, 2291A5D3175CB05F001D4920 /* exposet0 */, 2291A5C1175CAFCA001D4920 /* expt825 */, - 3114A64B156E9596001E0AA3 /* landtest */, 3114A5BC156E9315001E0AA3 /* finalcv */, 3114A5D5156E93A0001E0AA3 /* finaltest */, 224CC78C175E1821002FF81B /* fotest */, 6313D46718A400B200EB03EF /* gcbench */, + 3114A64B156E9596001E0AA3 /* landtest */, 2231BB4C18CA97D8002D6322 /* locbwcss */, 31D60026156D3D3E00337B26 /* lockcov */, 2231BB5A18CA97DC002D6322 /* locusss */, diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index adc0abf426f..6baa0322a53 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -53,6 +53,7 @@ static Bool MVTCheckFit(Addr base, Addr limit, Size min, Arena arena); static ABQ MVTABQ(MVT mvt); static Land MVTCBS(MVT mvt); static Land MVTFreelist(MVT mvt); +static Land MVTFailover(MVT mvt); /* Types */ @@ -62,6 +63,7 @@ typedef struct MVTStruct PoolStruct poolStruct; CBSStruct cbsStruct; /* The coalescing block structure */ FreelistStruct flStruct; /* The emergency free list structure */ + FailoverStruct foStruct; /* The fail-over mechanism */ ABQStruct abqStruct; /* The available block queue */ /* */ Size minSize; /* Pool parameter */ @@ -180,6 +182,12 @@ static Land MVTFreelist(MVT mvt) } +static Land MVTFailover(MVT mvt) +{ + return &mvt->foStruct.landStruct; +} + + /* Methods */ @@ -276,14 +284,23 @@ static Res MVTInit(Pool pool, ArgList args) if (res != ResOK) goto failCBS; - res = ABQInit(arena, MVTABQ(mvt), (void *)mvt, abqDepth, sizeof(RangeStruct)); - if (res != ResOK) - goto failABQ; - res = LandInit(MVTFreelist(mvt), FreelistLandClassGet(), arena, align, mvt, mps_args_none); if (res != ResOK) goto failFreelist; + + MPS_ARGS_BEGIN(foArgs) { + MPS_ARGS_ADD(foArgs, FailoverPrimary, MVTCBS(mvt)); + MPS_ARGS_ADD(foArgs, FailoverSecondary, MVTFreelist(mvt)); + res = LandInit(MVTFailover(mvt), FailoverLandClassGet(), arena, align, mvt, + foArgs); + } MPS_ARGS_END(foArgs); + if (res != ResOK) + goto failFailover; + + res = ABQInit(arena, MVTABQ(mvt), (void *)mvt, abqDepth, sizeof(RangeStruct)); + if (res != ResOK) + goto failABQ; pool->alignment = align; mvt->reuseSize = reuseSize; @@ -348,9 +365,11 @@ static Res MVTInit(Pool pool, ArgList args) reserveDepth, fragLimit); return ResOK; -failFreelist: - ABQFinish(arena, MVTABQ(mvt)); failABQ: + LandFinish(MVTFailover(mvt)); +failFailover: + LandFinish(MVTFreelist(mvt)); +failFreelist: LandFinish(MVTCBS(mvt)); failCBS: AVER(res != ResOK); @@ -370,6 +389,7 @@ static Bool MVTCheck(MVT mvt) CHECKD(ABQ, &mvt->abqStruct); /* CHECKL(ABQCheck(MVTABQ(mvt))); */ CHECKD(Freelist, &mvt->flStruct); + CHECKD(Failover, &mvt->foStruct); CHECKL(mvt->reuseSize >= 2 * mvt->fillSize); CHECKL(mvt->fillSize >= mvt->maxSize); CHECKL(mvt->maxSize >= mvt->meanSize); @@ -422,9 +442,10 @@ static void MVTFinish(Pool pool) SegFree(SegOfPoolRing(node)); } - /* Finish the Freelist, ABQ and CBS structures */ - LandFinish(MVTFreelist(mvt)); + /* Finish the ABQ, Failover, Freelist and CBS structures */ ABQFinish(arena, MVTABQ(mvt)); + LandFinish(MVTFailover(mvt)); + LandFinish(MVTFreelist(mvt)); LandFinish(MVTCBS(mvt)); } @@ -615,14 +636,7 @@ static Bool MVTABQFill(Addr *baseReturn, Addr *limitReturn, } -/* MVTContingencyFill -- try to fill a request from the CBS or Freelist - * - * (The CBS and Freelist are lumped together under the heading of - * "contingency" for historical reasons: the Freelist used to be part - * of the CBS. There is no principled reason why these two are - * searched at the same time: if it should prove convenient to - * separate them, go ahead.) - */ +/* MVTContingencyFill -- try to fill a request from the free lists */ static Bool MVTContingencyFill(Addr *baseReturn, Addr *limitReturn, MVT mvt, Size minSize) { @@ -711,8 +725,7 @@ static Res MVTBufferFill(Addr *baseReturn, Addr *limitReturn, METER_ACC(mvt->underflows, minSize); /* If fragmentation is acceptable, attempt to find a free block from - the CBS or Freelist. - */ + the free lists. */ if (mvt->available >= mvt->availLimit) { METER_ACC(mvt->fragLimitContingencies, minSize); if (MVTContingencyFill(baseReturn, limitReturn, mvt, minSize)) @@ -798,8 +811,8 @@ static Bool MVTReserve(MVT mvt, Range range) } -/* MVTInsert -- insert an address range into the CBS (or the Freelist - * if that fails) and update the ABQ accordingly. +/* MVTInsert -- insert an address range into the free lists and update + * the ABQ accordingly. */ static Res MVTInsert(MVT mvt, Addr base, Addr limit) { @@ -808,18 +821,9 @@ static Res MVTInsert(MVT mvt, Addr base, Addr limit) AVERT(MVT, mvt); AVER(base < limit); - - /* Attempt to flush the Freelist to the CBS to give maximum - * opportunities for coalescence. */ - LandFlush(MVTCBS(mvt), MVTFreelist(mvt)); RangeInit(&range, base, limit); - res = LandInsert(&newRange, MVTCBS(mvt), &range); - if (ResIsAllocFailure(res)) { - /* CBS ran out of memory for splay nodes: add range to emergency - * free list instead. */ - res = LandInsert(&newRange, MVTFreelist(mvt), &range); - } + res = LandInsert(&newRange, MVTFailover(mvt), &range); if (res != ResOK) return res; @@ -836,8 +840,8 @@ static Res MVTInsert(MVT mvt, Addr base, Addr limit) } -/* MVTDelete -- delete an address range from the CBS and the Freelist, - * and update the ABQ accordingly. +/* MVTDelete -- delete an address range from the free lists, and + * update the ABQ accordingly. */ static Res MVTDelete(MVT mvt, Addr base, Addr limit) { @@ -848,27 +852,7 @@ static Res MVTDelete(MVT mvt, Addr base, Addr limit) AVER(base < limit); RangeInit(&range, base, limit); - res = LandDelete(&rangeOld, MVTCBS(mvt), &range); - if (ResIsAllocFailure(res)) { - /* CBS ran out of memory for splay nodes, which must mean that - * there were fragments on both sides: see - * . Handle this by - * deleting the whole of rangeOld (which requires no - * allocation) and re-inserting the fragments. */ - RangeStruct rangeOld2; - res = LandDelete(&rangeOld2, MVTCBS(mvt), &rangeOld); - AVER(res == ResOK); - AVER(RangesEqual(&rangeOld2, &rangeOld)); - AVER(RangeBase(&rangeOld) != base); - res = MVTInsert(mvt, RangeBase(&rangeOld), base); - AVER(res == ResOK); - AVER(RangeLimit(&rangeOld) != limit); - res = MVTInsert(mvt, limit, RangeLimit(&rangeOld)); - AVER(res == ResOK); - } else if (res == ResFAIL) { - /* Not found in the CBS: try the Freelist. */ - res = LandDelete(&rangeOld, MVTFreelist(mvt), &range); - } + res = LandDelete(&rangeOld, MVTFailover(mvt), &range); if (res != ResOK) return res; AVER(RangesNest(&rangeOld, &range)); @@ -1048,12 +1032,12 @@ static Res MVTDescribe(Pool pool, mps_lib_FILE *stream) res = LandDescribe(MVTCBS(mvt), stream); if(res != ResOK) return res; - - res = ABQDescribe(MVTABQ(mvt), (ABQDescribeElement)RangeDescribe, stream); - if(res != ResOK) return res; - res = LandDescribe(MVTFreelist(mvt), stream); if(res != ResOK) return res; + res = LandDescribe(MVTFailover(mvt), stream); + if(res != ResOK) return res; + res = ABQDescribe(MVTABQ(mvt), (ABQDescribeElement)RangeDescribe, stream); + if(res != ResOK) return res; res = METER_WRITE(mvt->segAllocs, stream); if (res != ResOK) return res; @@ -1291,8 +1275,8 @@ static Bool MVTRefillVisitor(Bool *deleteReturn, Land land, Range range, return MVTReserve(mvt, range); } -/* MVTRefillABQIfEmpty -- refill the ABQ from the CBS and the Freelist if - * it is empty +/* MVTRefillABQIfEmpty -- refill the ABQ from the free lists if it is + * empty. */ static void MVTRefillABQIfEmpty(MVT mvt, Size size) { @@ -1300,15 +1284,14 @@ static void MVTRefillABQIfEmpty(MVT mvt, Size size) AVER(size > 0); /* If there have never been any overflows from the ABQ back to the - * CBS/Freelist, then there cannot be any blocks in the CBS/Freelist + * free lists, then there cannot be any blocks in the free lists * that are worth adding to the ABQ. So as an optimization, we don't * bother to look. */ if (mvt->abqOverflow && ABQIsEmpty(MVTABQ(mvt))) { mvt->abqOverflow = FALSE; METER_ACC(mvt->refills, size); - LandIterate(MVTCBS(mvt), &MVTRefillVisitor, mvt, 0); - LandIterate(MVTFreelist(mvt), &MVTRefillVisitor, mvt, 0); + LandIterate(MVTFailover(mvt), &MVTRefillVisitor, mvt, 0); } } @@ -1377,8 +1360,9 @@ static Bool MVTContingencyVisitor(Bool *deleteReturn, Land land, Range range, return TRUE; } -/* MVTContingencySearch -- search the CBS and the Freelist for a block - * of size min */ +/* MVTContingencySearch -- search the free lists for a block of size + * min. + */ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, MVT mvt, Size min) @@ -1392,10 +1376,7 @@ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, cls.steps = 0; cls.hardSteps = 0; - LandFlush(MVTCBS(mvt), MVTFreelist(mvt)); - - LandIterate(MVTCBS(mvt), MVTContingencyVisitor, (void *)&cls, 0); - LandIterate(MVTFreelist(mvt), MVTContingencyVisitor, (void *)&cls, 0); + LandIterate(MVTFailover(mvt), MVTContingencyVisitor, (void *)&cls, 0); if (!cls.found) return FALSE; diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index bbdb40fa510..818b4f932aa 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -50,6 +50,7 @@ typedef struct MVFFStruct { /* MVFF pool outer structure */ Size free; /* total free bytes in pool */ CBSStruct cbsStruct; /* free list */ FreelistStruct flStruct; /* emergency free list */ + FailoverStruct foStruct; /* fail-over mechanism */ Bool firstFit; /* as opposed to last fit */ Bool slotHigh; /* prefers high part of large block */ Sig sig; /* */ @@ -59,7 +60,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 FreelistOfMVFF(mvff) (&((mvff)->flStruct.landStruct)) +#define FreelistOfMVFF(mvff) (&((mvff)->flStruct.landStruct)) +#define FailoverOfMVFF(mvff) (&((mvff)->foStruct.landStruct)) static Bool MVFFCheck(MVFF mvff); @@ -78,48 +80,39 @@ typedef MVFFDebugStruct *MVFFDebug; #define MVFFDebug2MVFF(mvffd) (&((mvffd)->mvffStruct)) -/* MVFFAddToFreeList -- Add given range to free list +/* 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 + * 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) { +static Res MVFFInsert(Range rangeIO, MVFF mvff) { Res res; - RangeStruct range, newRange; + Size size; - AVER(baseIO != NULL); - AVER(limitIO != NULL); + AVER(rangeIO != NULL); AVERT(MVFF, mvff); - RangeInit(&range, *baseIO, *limitIO); - res = LandInsert(&newRange, CBSOfMVFF(mvff), &range); - if (ResIsAllocFailure(res)) { - /* CBS ran out of memory for splay nodes: add range to emergency - * free list instead. */ - res = LandInsert(&newRange, FreelistOfMVFF(mvff), &range); - } + size = RangeSize(rangeIO); + res = LandInsert(rangeIO, FailoverOfMVFF(mvff), rangeIO); - if (res == ResOK) { - mvff->free += RangeSize(&range); - *baseIO = RangeBase(&newRange); - *limitIO = RangeLimit(&newRange); - } + if (res == ResOK) + mvff->free += size; return res; } -/* MVFFFreeSegs -- Free segments from given range +/* 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. + * 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. + * 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, Addr base, Addr limit) +static void MVFFFreeSegs(MVFF mvff, Range range) { Seg seg = NULL; /* suppress "may be used uninitialized" */ Arena arena; @@ -129,72 +122,42 @@ static void MVFFFreeSegs(MVFF mvff, Addr base, Addr limit) Res res; AVERT(MVFF, mvff); - AVER(base < limit); + AVERT(Range, range); /* Could profitably AVER that the given range is free, */ /* but the CBS doesn't provide that facility. */ - if (AddrOffset(base, limit) < mvff->minSegSize) + if (RangeSize(range) < mvff->minSegSize) return; /* not large enough for entire segments */ arena = PoolArena(MVFF2Pool(mvff)); - b = SegOfAddr(&seg, arena, base); + b = SegOfAddr(&seg, arena, RangeBase(range)); 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 = LandDelete(&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 - * . Handle this by - * deleting the whole of oldRange (which requires no - * allocation) and re-inserting the fragments. */ - RangeStruct oldRange2; - res = LandDelete(&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 = LandDelete(&oldRange, FreelistOfMVFF(mvff), &range); - AVER(res == ResOK); - mvff->free -= RangeSize(&range); - } + 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, &range)); + 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); - mvff->total -= RangeSize(&range); + + mvff->free -= 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 == limit) /* segment ends at end of range */ + if (segLimit == RangeLimit(range)) /* segment ends at end of range */ break; b = SegFindAboveAddr(&seg, arena, segBase); @@ -210,8 +173,8 @@ static void MVFFFreeSegs(MVFF mvff, Addr base, Addr limit) /* 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. + * withReservoirPermit flag) of at least the specified size. The + * specified size should be pool-aligned. Adds it to the free lists. */ static Res MVFFAddSeg(Seg *segReturn, MVFF mvff, Size size, Bool withReservoirPermit) @@ -222,7 +185,7 @@ static Res MVFFAddSeg(Seg *segReturn, Seg seg; Res res; Align align; - Addr base, limit; + RangeStruct range; AVERT(MVFF, mvff); AVER(size > 0); @@ -257,12 +220,11 @@ static Res MVFFAddSeg(Seg *segReturn, } mvff->total += segSize; - base = SegBase(seg); - limit = AddrAdd(base, segSize); - DebugPoolFreeSplat(pool, base, limit); - res = MVFFAddToFreeList(&base, &limit, mvff); + RangeInitSize(&range, SegBase(seg), segSize); + DebugPoolFreeSplat(pool, RangeBase(&range), RangeLimit(&range)); + res = MVFFInsert(&range, mvff); AVER(res == ResOK); - AVER(base <= SegBase(seg)); + AVER(RangeBase(&range) <= SegBase(seg)); if (mvff->minSegSize > segSize) mvff->minSegSize = segSize; /* Don't call MVFFFreeSegs; that would be silly. */ @@ -272,48 +234,34 @@ static Res MVFFAddSeg(Seg *segReturn, } -/* MVFFFindFirstFree -- Finds the first (or last) suitable free block +/* MVFFFindFree -- find 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. + * Will return FALSE if the free lists have 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) +static Bool MVFFFindFree(Range rangeReturn, MVFF mvff, Size size) { Bool foundBlock; FindDelete findDelete; - RangeStruct range, oldRange; + RangeStruct oldRange; - AVER(baseReturn != NULL); - AVER(limitReturn != NULL); + AVER(rangeReturn != NULL); AVERT(MVFF, mvff); AVER(size > 0); AVER(SizeIsAligned(size, PoolAlignment(MVFF2Pool(mvff)))); - LandFlush(CBSOfMVFF(mvff), FreelistOfMVFF(mvff)); - findDelete = mvff->slotHigh ? FindDeleteHIGH : FindDeleteLOW; foundBlock = (mvff->firstFit ? LandFindFirst : LandFindLast) - (&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 ? LandFindFirst : LandFindLast) - (&range, &oldRange, FreelistOfMVFF(mvff), size, findDelete); - } + (rangeReturn, &oldRange, FailoverOfMVFF(mvff), size, findDelete); if (foundBlock) { - *baseReturn = RangeBase(&range); - *limitReturn = RangeLimit(&range); mvff->free -= size; } @@ -328,7 +276,7 @@ static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size, { Res res; MVFF mvff; - Addr base, limit; + RangeStruct range; Bool foundBlock; AVERT(Pool, pool); @@ -341,29 +289,28 @@ static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size, size = SizeAlignUp(size, PoolAlignment(pool)); - foundBlock = MVFFFindFirstFree(&base, &limit, mvff, size); + foundBlock = MVFFFindFree(&range, mvff, size); if (!foundBlock) { Seg seg; res = MVFFAddSeg(&seg, mvff, size, withReservoirPermit); if (res != ResOK) return res; - foundBlock = MVFFFindFirstFree(&base, &limit, mvff, size); + 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 three AVERs test for intersection of two intervals. */ - AVER(base >= SegBase(seg) || limit <= SegLimit(seg)); - AVER(base < SegLimit(seg)); - AVER(SegBase(seg) < limit); + /* 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) >= AddrOffset(base, limit)); + AVER(SegSize(seg) >= RangeSize(&range)); } AVER(foundBlock); - AVER(AddrOffset(base, limit) == size); + AVER(RangeSize(&range) == size); - *aReturn = base; + *aReturn = RangeBase(&range); return ResOK; } @@ -374,7 +321,7 @@ static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size, static void MVFFFree(Pool pool, Addr old, Size size) { Res res; - Addr base, limit; + RangeStruct range; MVFF mvff; AVERT(Pool, pool); @@ -385,41 +332,16 @@ static void MVFFFree(Pool pool, Addr old, Size size) AVER(AddrIsAligned(old, PoolAlignment(pool))); AVER(size > 0); - size = SizeAlignUp(size, PoolAlignment(pool)); - base = old; - limit = AddrAdd(base, size); + RangeInitSize(&range, old, SizeAlignUp(size, PoolAlignment(pool))); - res = MVFFAddToFreeList(&base, &limit, mvff); + res = MVFFInsert(&range, mvff); AVER(res == ResOK); if (res == ResOK) - MVFFFreeSegs(mvff, base, limit); + MVFFFreeSegs(mvff, &range); 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); - - LandFlush(CBSOfMVFF(mvff), FreelistOfMVFF(mvff)); - - if (LandFindLargest(range, oldRange, CBSOfMVFF(mvff), size, findDelete)) - return TRUE; - - if (LandFindLargest(range, oldRange, FreelistOfMVFF(mvff), size, findDelete)) - return TRUE; - - return FALSE; -} - /* MVFFBufferFill -- Fill the buffer * @@ -444,13 +366,13 @@ static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn, AVER(SizeIsAligned(size, PoolAlignment(pool))); AVERT(Bool, withReservoirPermit); - found = MVFFFindLargest(&range, &oldRange, mvff, size, FindDeleteENTIRE); + found = LandFindLargest(&range, &oldRange, FailoverOfMVFF(mvff), size, FindDeleteENTIRE); if (!found) { - /* Add a new segment to the free list and try again. */ + /* Add a new segment to the free lists and try again. */ res = MVFFAddSeg(&seg, mvff, size, withReservoirPermit); if (res != ResOK) return res; - found = MVFFFindLargest(&range, &oldRange, mvff, size, FindDeleteENTIRE); + found = LandFindLargest(&range, &oldRange, FailoverOfMVFF(mvff), size, FindDeleteENTIRE); } AVER(found); @@ -470,21 +392,22 @@ static void MVFFBufferEmpty(Pool pool, Buffer buffer, { Res res; MVFF mvff; + RangeStruct range; AVERT(Pool, pool); mvff = Pool2MVFF(pool); AVERT(MVFF, mvff); AVERT(Buffer, buffer); AVER(BufferIsReady(buffer)); - AVER(base <= limit); + RangeInit(&range, base, limit); - if (base == limit) + if (RangeEmpty(&range)) return; - res = MVFFAddToFreeList(&base, &limit, mvff); + res = MVFFInsert(&range, mvff); AVER(res == ResOK); if (res == ResOK) - MVFFFreeSegs(mvff, base, limit); + MVFFFreeSegs(mvff, &range); return; } @@ -606,12 +529,22 @@ static Res MVFFInit(Pool pool, ArgList args) if (res != ResOK) goto failCBSInit; + MPS_ARGS_BEGIN(foArgs) { + MPS_ARGS_ADD(foArgs, FailoverPrimary, CBSOfMVFF(mvff)); + MPS_ARGS_ADD(foArgs, FailoverSecondary, FreelistOfMVFF(mvff)); + res = LandInit(FailoverOfMVFF(mvff), FailoverLandClassGet(), arena, align, mvff, foArgs); + } MPS_ARGS_END(foArgs); + if (res != ResOK) + goto failFailoverInit; + mvff->sig = MVFFSig; AVERT(MVFF, mvff); EVENT8(PoolInitMVFF, pool, arena, extendBy, avgSize, align, slotHigh, arenaHigh, firstFit); return ResOK; +failFailoverInit: + LandFinish(CBSOfMVFF(mvff)); failCBSInit: LandFinish(FreelistOfMVFF(mvff)); failFreelistInit: @@ -647,8 +580,9 @@ static void MVFFFinish(Pool pool) arena = PoolArena(pool); ControlFree(arena, mvff->segPref, sizeof(SegPrefStruct)); - LandFinish(CBSOfMVFF(mvff)); + LandFinish(FailoverOfMVFF(mvff)); LandFinish(FreelistOfMVFF(mvff)); + LandFinish(CBSOfMVFF(mvff)); mvff->sig = SigInvalid; } @@ -805,6 +739,7 @@ static Bool MVFFCheck(MVFF mvff) CHECKL(SizeIsAligned(mvff->total, ArenaAlign(PoolArena(MVFF2Pool(mvff))))); CHECKD(CBS, &mvff->cbsStruct); CHECKD(Freelist, &mvff->flStruct); + CHECKD(Failover, &mvff->foStruct); CHECKL(BoolCheck(mvff->slotHigh)); CHECKL(BoolCheck(mvff->firstFit)); return TRUE; diff --git a/mps/code/range.c b/mps/code/range.c index 5e8049b4395..fb11ff091bf 100644 --- a/mps/code/range.c +++ b/mps/code/range.c @@ -15,7 +15,6 @@ SRCID(range, "$Id$"); Bool RangeCheck(Range range) { - CHECKS(Range, range); CHECKL(range->base != NULL); CHECKL(range->base <= range->limit); @@ -31,14 +30,17 @@ void RangeInit(Range range, Addr base, Addr limit) range->base = base; range->limit = limit; - range->sig = RangeSig; AVERT(Range, range); } +void RangeInitSize(Range range, Addr base, Size size) +{ + RangeInit(range, base, AddrAdd(base, size)); +} + void RangeFinish(Range range) { AVERT(Range, range); - range->sig = SigInvalid; range->base = range->limit = NULL; } diff --git a/mps/code/range.h b/mps/code/range.h index c996276cca6..2d93080f6aa 100644 --- a/mps/code/range.h +++ b/mps/code/range.h @@ -14,18 +14,15 @@ #include "mpmtypes.h" -/* Signatures */ - -#define RangeSig ((Sig)0x5196A493) /* SIGnature RANGE */ - - /* Prototypes */ #define RangeBase(range) ((range)->base) #define RangeLimit(range) ((range)->limit) #define RangeSize(range) (AddrOffset(RangeBase(range), RangeLimit(range))) +#define RangeEmpty(range) (RangeBase(range) == RangeLimit(range)) extern void RangeInit(Range range, Addr base, Addr limit); +extern void RangeInitSize(Range range, Addr base, Size size); extern void RangeFinish(Range range); extern Res RangeDescribe(Range range, mps_lib_FILE *stream); extern Bool RangeCheck(Range range); @@ -42,7 +39,6 @@ extern void RangeCopy(Range to, Range from); /* Types */ typedef struct RangeStruct { - Sig sig; Addr base; Addr limit; } RangeStruct; diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt index 5ee2348fe54..ec597de1abd 100644 --- a/mps/design/cbs.txt +++ b/mps/design/cbs.txt @@ -20,9 +20,9 @@ eager coalescence. _`.readership`: This document is intended for any MM developer. -_`.source`: design.mps.poolmv2_, design.mps.poolmvff_. +_`.source`: design.mps.poolmvt_, design.mps.poolmvff_. -.. _design.mps.poolmv2: poolmv2 +.. _design.mps.poolmvt: poolmvt .. _design.mps.poolmvff: poolmvff _`.overview`: The "coalescing block structure" is a set of addresses @@ -124,23 +124,19 @@ _`.limit.iterate`: CBS does not support visitors setting ``LandIterate()``. _`.limit.flush`: CBS cannot be used as the source in a call to -``LandFlush()``. +``LandFlush()``. (Because of `.limit.iterate`_.) Implementation -------------- -_`.impl`: This section is concerned with describing various aspects of -the implementation. It does not form part of the interface definition. - - Splay tree .......... -_`.impl.splay`: The CBS is principally implemented using a splay tree -(see design.mps.splay_). Each splay tree node is embedded in a -``CBSBlock`` that represents a semi-open address range. The key passed -for comparison is the base of another range. +_`.impl.splay`: The CBS is implemented using a splay tree (see +design.mps.splay_). Each splay tree node is embedded in a ``CBSBlock`` +that represents a semi-open address range. The key passed for +comparison is the base of another range. .. _design.mps.splay: splay @@ -158,6 +154,11 @@ size. This takes time proportional to the logarithm of the size of the free list, so it's about the best you can do without maintaining a separate priority queue, just to do ``cbsFindLargest()``. +_`.impl.splay.zones`: ``cbsFindInZones()`` uses the update/refresh +facility of splay trees to store, in each ``CBSBlock()``, the union of +the zones of the ranges in the tree rooted at the corresponding splay +node. This allows rapid location of a block in a set of zones. + Low memory behaviour .................... @@ -231,7 +232,7 @@ Document History ---------------- - 1998-05-01 Gavin Matthews. This document was derived from the - outline in design.mps.poolmv2(2). + outline in design.mps.poolmvt_. - 1998-07-22 Gavin Matthews. Updated in response to approval comments in change.epcore.anchovy.160040. There is too much fragmentation in @@ -255,7 +256,7 @@ Document History talking about the deleted "emergency" free list allocator. Documented ``fastFind`` argument to ``CBSInit()``. -- 2014-04-01 GDR_ Moved generic material to design.mps.land. +- 2014-04-01 GDR_ Moved generic material to design.mps.land_. Documented new keyword arguments. .. _RB: http://www.ravenbrook.com/consultants/rb/ diff --git a/mps/design/failover.txt b/mps/design/failover.txt new file mode 100644 index 00000000000..5fdb5a9b76d --- /dev/null +++ b/mps/design/failover.txt @@ -0,0 +1,150 @@ +.. mode: -*- rst -*- + +Fail-over allocator +=================== + +:Tag: design.mps.failover +:Author: Gareth Rees +:Date: 2014-04-01 +:Status: complete design +:Revision: $Id$ +:Copyright: See section `Copyright and License`_. + + +Introduction +------------ + +_`.intro`: This is the design of the fail-over allocator, a data +structure for the management of address ranges. + +_`.readership`: This document is intended for any MPS developer. + +_`.source`: design.mps.land_, design.mps.poolmvt_, design.mps.poolmvff_. + +_`.overview`: The fail-over allocator combines two *land* instances. +It stores address ranges in one of the lands (the *primary*) unless +insertion fails, in which case it falls back to the other (the +*secondary*). The purpose is to be able to combine two lands with +different properties: with a CBS_ for the primary and a Freelist_ for +the secondary, operations are fast so long as there is memory to +allocate new nodes in the CBS, but operations can continue using the +Freelist when memory is low. + +.. _CBS: cbs +.. _Freelist: freelist +.. _design.mps.land: land +.. _design.mps.poolmvt: poolmvt +.. _design.mps.poolmvff: poolmvff + + +Interface +--------- + +_`.land`: The fail-over allocator is an implementation of the *land* +abstract data type, so the interface consists of the generic functions +for lands. See design.mps.land_. + + +External types +.............. + +``typedef struct FailoverStruct *Failover`` + +_`.type.failover`: The type of fail-over allocator structures. A +``FailoverStruct`` may be embedded in another structure, or you can +create it using ``LandCreate()``. + + +External functions +.................. + +``LandClass FailoverLandClassGet(void)`` + +_`.function.class`: The function ``FailoverLandClassGet()`` returns +the fail-over allocator class, a subclass of ``LandClass`` suitable +for passing to ``LandCreate()`` or ``LandInit()``. + + +Keyword arguments +................. + +When initializing a fail-over allocator, ``LandCreate()`` and +``LandInit()`` require these two keyword arguments: + +* ``FailoverPrimary`` (type ``Land``) is the primary land. + +* ``FailoverSecondary`` (type ``Land``) is the secondary land. + + +Implementation +-------------- + +_`.impl.assume`: The implementation assumes that the primary is fast +but space-hungry (a CBS) and the secondary is slow but space-frugal (a +Freelist). This assumption is used in the following places: + +_`.impl.assume.flush`: The fail-over allocator attempts to flush the +secondary to the primary before any operation, in order to benefit +from the speed of the primary wherever possible. In the normal case +where the secondary is empty this is cheap. + +_`.impl.assume.delete`: When deletion of a range on the primary fails +due to lack of memory, we assume that this can only happen when there +are splinters on both sides of the deleted range, one of which needs +to be allocated a new node (this is the case for CBS), and that +therefore the following procedure will be effective: first, delete the +enclosing range from the primary (leaving no splinters and thus +requiring no allocation), and re-insert the splinters (failing over to +the secondary if necessary). + + + +Document History +---------------- + +- 2014-04-03 GDR_ Created. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 2014 Ravenbrook Limited. 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: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. 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. + +#. 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.** diff --git a/mps/design/index.txt b/mps/design/index.txt index 251d598a102..b14ed257d5f 100644 --- a/mps/design/index.txt +++ b/mps/design/index.txt @@ -44,13 +44,14 @@ arena_ The design of the MPS arena arenavm_ Virtual memory arena bt_ Bit tables buffer_ Allocation buffers and allocation points -cbs_ Design for coalescing block structure +cbs_ Coalescing Block Structure allocator check_ Design of checking in MPS class-interface_ Design of the pool class interface collection_ The collection framework config_ The design of MPS configuration critical-path_ The critical path through the MPS diag_ The design of MPS diagnostic feedback +failover_ Fail-over allocator finalize_ Finalization fix_ The Design of the Generic Fix Function freelist_ Free list allocator @@ -71,11 +72,11 @@ poolamc_ The design of the automatic mostly-copying memory pool c poolams_ The design of the automatic mark-and-sweep pool class poolawl_ Automatic weak linked poollo_ Leaf object pool class -poolmfs_ The design of the manual fixed small memory pool class +poolmfs_ Manual Fixed Small pool class poolmrg_ Guardian poolclass -poolmv_ The design of the manual variable memory pool class -poolmvt_ The design of a new manual-variable memory pool class -poolmvff_ Design of the manually-managed variable-size first-fit pool +poolmv_ Manual Variable pool class +poolmvt_ Manual Variable Temporal pool class +poolmvff_ Manually Variable First-Fit pool prot_ Generic design of the protection module protan_ ANSI implementation of protection module protli_ Linux implementation of protection module @@ -119,6 +120,7 @@ writef_ The design of the MPS writef function .. _config: config .. _critical-path: critical-path .. _diag: diag +.. _failover: failover .. _finalize: finalize .. _fix: fix .. _freelist: freelist @@ -127,6 +129,7 @@ writef_ The design of the MPS writef function .. _interface-c: interface-c .. _io: io .. _keyword-arguments: keyword-arguments +.. _land: land .. _lib: lib .. _lock: lock .. _locus: locus diff --git a/mps/design/land.txt b/mps/design/land.txt index 8c12c9f5a96..3a4b81abc08 100644 --- a/mps/design/land.txt +++ b/mps/design/land.txt @@ -6,7 +6,7 @@ Lands :Tag: design.mps.land :Author: Gareth Rees :Date: 2014-04-01 -:Status: incomplete design +:Status: complete design :Revision: $Id$ :Copyright: See section `Copyright and License`_. @@ -19,7 +19,7 @@ represents a collection of contiguous address ranges. _`.readership`: This document is intended for any MPS developer. -_`.source`: `design.mps.cbs `_, `design.mps.freelist `_. +_`.source`: design.mps.cbs_, design.mps.freelist_. _`.overview`: Collections of address ranges are used in several places in the MPS: the arena stores a set of mapped address ranges; pools @@ -132,6 +132,9 @@ that does not overlap with any range in the land) can only fail if the new range is isolated and the allocation of the necessary data structure to represent it failed. +_`.function.insert.alias`: It is acceptable for ``rangeReturn`` and +``range`` to share storage. + ``Res LandDelete(Range rangeReturn, Land land, Range range)`` _`.function.delete`: If any part of the range is not in the land, @@ -153,14 +156,16 @@ This is so that the caller can try deleting the whole block (which is guaranteed to succeed) and managing the fragments using a fallback strategy. +_`.function.delete.alias`: It is acceptable for ``rangeReturn`` and +``range`` to share storage. + ``void LandIterate(Land land, LandIterateMethod iterate, void *closureP, Size closureS)`` _`.function.iterate`: ``LandIterate()`` is the function used to iterate all isolated contiguous ranges in a land. It receives a -pointer, ``Size`` closure pair to pass on to the iterator method, -and an iterator method to invoke on every range in address order. If -the iterator method returns ``FALSE``, then the iteration is -terminated. +pointer, ``Size`` closure pair to pass on to the iterator method, and +an iterator method to invoke on every range. If the iterator method +returns ``FALSE``, then the iteration is terminated. ``Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)`` @@ -211,9 +216,9 @@ argument. _`.function.find.zones`: Locate a block at least as big as ``size`` that lies entirely within the ``zoneSet``, return its range via the -``rangeReturn`` argument, and return ``TRUE``. (The first such block, +``rangeReturn`` argument, and return ``ResOK``. (The first such block, if ``high`` is ``FALSE``, or the last, if ``high`` is ``TRUE``.) If -there is no such block, , return ``FALSE``. +there is no such block, , return ``ResFAIL``. Delete the range as for ``LandFindFirst()`` and ``LastFindLast()`` (with the effect of ``FindDeleteLOW`` if ``high`` is ``FALSE`` and the @@ -221,6 +226,10 @@ effect of ``FindDeleteHIGH`` if ``high`` is ``TRUE``), and return the original contiguous isolated range in which the range was found via the ``oldRangeReturn`` argument. +_`.function.find.zones.fail`: It's possible that the range can't be +deleted from the land because that would require allocation, in which +case the result code will indicate the cause of the failure. + ``Res LandDescribe(Land land, mps_lib_FILE *stream)`` _`.function.describe`: ``LandDescribe()`` prints a textual @@ -234,6 +243,30 @@ _`.function.flush`: Delete ranges of addresses from ``src`` and insert them into ``dest``, so long as ``LandInsert()`` remains successful. +Implementations +--------------- + +There are three land implementations: + +#. CBS (Coalescing Block Structure) stores ranges in a splay tree. It + has fast (logarithmic in the number of ranges) insertion, deletion + and searching, but has substantial space overhead. See + design.mps.cbs_. + +#. Freelist stores ranges in an address-ordered free list, as in + traditional ``malloc()`` implementations. Insertion, deletion, and + searching are slow (proportional to the number of ranges) but it + does not need to allocate. See design.mps.freelist_. + +#. Failover combines two lands, using one (the *primary*) until it + fails, and then falls back to the other (the *secondary*). See + design.mps.failover_. + +.. _design.mps.cbs: cbs +.. _design.mps.freelist: freelist +.. _design.mps.failover: failover + + Testing ------- @@ -250,7 +283,7 @@ generic function, but makes no automatic test of the resulting output. Document History ---------------- -- 2014-04-01 GDR_ Created based on `design.mps.cbs `_. +- 2014-04-01 GDR_ Created based on design.mps.cbs_. .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/mps/design/poolmvff.txt b/mps/design/poolmvff.txt index 2c16b80c4a0..c842fe03a6c 100644 --- a/mps/design/poolmvff.txt +++ b/mps/design/poolmvff.txt @@ -120,11 +120,13 @@ Implementation -------------- _`.impl.free-list`: The pool stores its free list in a CBS (see -//gdr-peewit/info.ravenbrook.com/project/mps/branch/2013-05-17/emergency/design/poolmvff.txt -`design.mps.cbs `_), failing over in emergencies to a Freelist -(see design.mps.freelist) when the CBS cannot allocate new control +design.mps.cbs_), failing over in emergencies to a Freelist (see +design.mps.freelist_) when the CBS cannot allocate new control structures. This is the reason for the alignment restriction above. +.. _design.mps.cbs: cbs +.. _design.mps.freelist: freelist + Details ------- diff --git a/mps/design/range.txt b/mps/design/range.txt index 86fcab775f1..fa5b4d3867c 100644 --- a/mps/design/range.txt +++ b/mps/design/range.txt @@ -24,8 +24,8 @@ Requirements ------------ _`.req.range`: A range object must be able to represent an arbitrary -range of addresses that does not include the top grain of the address -space. +range of addresses that neither starts at ``NULL`` nor includes the +top grain of the address space. _`.req.empty`: A range object must be able to represent the empty range. @@ -50,6 +50,12 @@ between ``base`` (inclusive) and ``limit`` (exclusive). It must be the case that ``base <= limit``. If ``base == limit`` then the range is empty. +``void RangeInitSize(Range range, Addr base, Size size)`` + +Initialize a range object to represent the half-open address range +between ``base`` (inclusive) and ``base + size`` (exclusive). If +``size == 0`` then the range is empty. + ``void RangeFinish(Range range)`` Finish a range object. Because a range object uses no heap resources diff --git a/mps/design/splay.txt b/mps/design/splay.txt index 8eb1a91c2a8..28aa1147222 100644 --- a/mps/design/splay.txt +++ b/mps/design/splay.txt @@ -22,9 +22,13 @@ implementation. _`.readership`: This document is intended for any MM developer. _`.source`: The primary sources for this design are [ST85]_ and -[Sleator96]_. Also as CBS is a client, design.mps.cbs. As -PoolMVFF is an indirect client, design.mps.poolmvff(1). Also, as -PoolMV2 is an (obsolescent?) indirect client, design.mps.poolmv2. +[Sleator96]_. As CBS is a client, design.mps.cbs_. As PoolMVFF is an +indirect client, design.mps.poolmvff_. Also, as PoolMVT is an indirect +client, design.mps.poolmvt_. + +.. _design.mps.cbs: cbs +.. _design.mps.poolmvt: poolmvt +.. _design.mps.poolmvff: poolmvff _`.background`: The following background documents influence the design: guide.impl.c.adt(0). @@ -89,7 +93,7 @@ Requirements ------------ _`.req`: These requirements are drawn from those implied by -design.mps.poolmv2, design.mps.poolmvff(1), design.mps.cbs(2) and +design.mps.poolmvt_, design.mps.poolmvff_, design.mps.cbs_, and general inferred MPS requirements. _`.req.order`: Must maintain a set of abstract keys which is totally diff --git a/mps/manual/source/design/index.rst b/mps/manual/source/design/index.rst index f87cfdadaa2..4c501253041 100644 --- a/mps/manual/source/design/index.rst +++ b/mps/manual/source/design/index.rst @@ -10,6 +10,7 @@ Design cbs config critical-path + failover freelist guide.hex.trans guide.impl.c.format From ce6b34aa8fd473c62a938e1ac79a16f4a4ea2620 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 3 Apr 2014 14:46:58 +0100 Subject: [PATCH 10/43] Test the failover module (both always and never failing over). Fix result code bug in failoverInsert. Test all result codes in fotest. Tidy up code and documentation. Copied from Perforce Change: 185207 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 33 +++++++++--------- mps/code/cbs.c | 9 +++-- mps/code/failover.c | 14 ++++---- mps/code/fotest.c | 40 +++++++++++++--------- mps/code/landtest.c | 74 +++++++++++++++++++++++++++++------------ mps/code/mpmst.h | 4 +-- mps/code/poolmv2.c | 39 ++++++++-------------- mps/code/poolmvff.c | 6 ++-- mps/design/cbs.txt | 2 +- mps/design/freelist.txt | 4 +-- mps/design/index.txt | 12 +++---- 11 files changed, 132 insertions(+), 105 deletions(-) diff --git a/mps/code/arena.c b/mps/code/arena.c index afdb815f232..7c0af5ffe86 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -238,7 +238,8 @@ Res ArenaInit(Arena arena, ArenaClass class, Align alignment, ArgList args) MPS_ARGS_ADD(liArgs, CBSFastFind, TRUE); MPS_ARGS_ADD(liArgs, CBSZoned, arena->zoned); MPS_ARGS_DONE(liArgs); - res = LandInit(ArenaFreeLand(arena), CBSLandClassGet(), arena, alignment, arena, liArgs); + res = LandInit(ArenaFreeLand(arena), CBSLandClassGet(), arena, + alignment, arena, liArgs); } MPS_ARGS_END(liArgs); AVER(res == ResOK); /* no allocation, no failure expected */ if (res != ResOK) @@ -310,9 +311,9 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) that describes the free address space in the primary chunk. */ arena->hasFreeLand = TRUE; res = ArenaFreeLandInsert(arena, - PageIndexBase(arena->primary, - arena->primary->allocBase), - arena->primary->limit); + PageIndexBase(arena->primary, + arena->primary->allocBase), + arena->primary->limit); if (res != ResOK) goto failPrimaryLand; @@ -704,10 +705,10 @@ static void arenaExcludePage(Arena arena, Range pageRange) } -/* arenaLandInsert -- add a block to an arena Land, extending pool if necessary +/* arenaLandInsert -- add range to arena's land, maybe extending block pool * - * The arena's Land can't get memory in the usual way because they are used - * in the basic allocator, so we allocate pages specially. + * The arena's land can't get memory in the usual way because it is + * used in the basic allocator, so we allocate pages specially. * * Only fails if it can't get a page for the block pool. */ @@ -722,7 +723,7 @@ static Res arenaLandInsert(Range rangeReturn, Arena arena, Range range) res = LandInsert(rangeReturn, ArenaFreeLand(arena), range); - if (res == ResLIMIT) { /* freeLand MFS pool ran out of blocks */ + if (res == ResLIMIT) { /* CBS block pool ran out of blocks */ RangeStruct pageRange; res = arenaExtendCBSBlockPool(&pageRange, arena); if (res != ResOK) @@ -730,7 +731,7 @@ static Res arenaLandInsert(Range rangeReturn, Arena arena, Range range) /* .insert.exclude: Must insert before exclude so that we can bootstrap when the zoned CBS is empty. */ res = LandInsert(rangeReturn, ArenaFreeLand(arena), range); - AVER(res == ResOK); /* we just gave memory to the Land */ + AVER(res == ResOK); /* we just gave memory to the CBS block pool */ arenaExcludePage(arena, &pageRange); } @@ -738,11 +739,11 @@ static Res arenaLandInsert(Range rangeReturn, Arena arena, Range range) } -/* ArenaFreeLandInsert -- add a block to arena Land, maybe stealing memory +/* ArenaFreeLandInsert -- add range to arena's land, maybe stealing memory * - * See arenaLandInsert. This function may only be applied to mapped pages - * and may steal them to store Land nodes if it's unable to allocate - * space for CBS nodes. + * See arenaLandInsert. This function may only be applied to mapped + * pages and may steal them to store Land nodes if it's unable to + * allocate space for CBS blocks. * * IMPORTANT: May update rangeIO. */ @@ -777,14 +778,14 @@ static void arenaLandInsertSteal(Range rangeReturn, Arena arena, Range rangeIO) /* Try again. */ res = LandInsert(rangeReturn, ArenaFreeLand(arena), rangeIO); - AVER(res == ResOK); /* we just gave memory to the Land */ + AVER(res == ResOK); /* we just gave memory to the CBS block pool */ } AVER(res == ResOK); /* not expecting other kinds of error from the Land */ } -/* ArenaFreeLandInsert -- add block to free Land, extending pool if necessary +/* ArenaFreeLandInsert -- add range to arena's land, maybe extending block pool * * The inserted block of address space may not abut any existing block. * This restriction ensures that we don't coalesce chunks and allocate @@ -812,7 +813,7 @@ Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit) } -/* ArenaFreeLandDelete -- remove a block from free Land, extending pool if necessary +/* ArenaFreeLandDelete -- remove range from arena's land, maybe extending block pool * * This is called from ChunkFinish in order to remove address space from * the arena. diff --git a/mps/code/cbs.c b/mps/code/cbs.c index 3e99e3ca195..37f597930a0 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -26,6 +26,7 @@ SRCID(cbs, "$Id$"); #define CBSBlockSize(block) AddrOffset((block)->base, (block)->limit) +#define cbsLand(cbs) (&((cbs)->landStruct)) #define cbsOfLand(land) PARENT(CBSStruct, landStruct, land) #define cbsSplay(cbs) (&((cbs)->splayTreeStruct)) #define cbsOfSplay(_splay) PARENT(CBSStruct, splayTreeStruct, _splay) @@ -66,7 +67,7 @@ Bool CBSCheck(CBS cbs) { /* See .enter-leave.simple. */ CHECKS(CBS, cbs); - CHECKD(Land, &cbs->landStruct); + CHECKD(Land, cbsLand(cbs)); CHECKD(SplayTree, cbsSplay(cbs)); /* nothing to check about treeSize */ CHECKD(Pool, cbs->blockPool); @@ -211,7 +212,7 @@ static void cbsUpdateZonedNode(SplayTree splay, Tree tree) cbsUpdateNode(splay, tree); block = cbsBlockOfTree(tree); - arena = cbsOfSplay(splay)->landStruct.arena; + arena = LandArena(cbsLand(cbsOfSplay(splay))); zones = ZoneSetOfRange(arena, CBSBlockBase(block), CBSBlockLimit(block)); if (TreeHasLeft(tree)) @@ -740,7 +741,7 @@ static Bool cbsIterateVisit(Tree tree, void *closureP, Size closureS) cbsBlock = cbsBlockOfTree(tree); RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock)); cont = (*closure->visitor)(&delete, land, &range, closure->closureP, closure->closureS); - AVER(!delete); + AVER(!delete); /* */ if (!cont) return FALSE; METER_ACC(cbs->treeSearch, cbs->treeSize); @@ -1101,6 +1102,8 @@ static Res cbsDescribe(Land land, mps_lib_FILE *stream) " blockPool: $P\n", (WriteFP)cbsBlockPool(cbs), " fastFind: $U\n", (WriteFU)cbs->fastFind, " inCBS: $U\n", (WriteFU)cbs->inCBS, + " ownPool: $U\n", (WriteFU)cbs->ownPool, + " zoned: $U\n", (WriteFU)cbs->zoned, " treeSize: $U\n", (WriteFU)cbs->treeSize, NULL); if (res != ResOK) return res; diff --git a/mps/code/failover.c b/mps/code/failover.c index ca0d15cad1c..b5d7588480c 100644 --- a/mps/code/failover.c +++ b/mps/code/failover.c @@ -87,10 +87,8 @@ static Res failoverInsert(Range rangeReturn, Land land, Range range) LandFlush(fo->primary, fo->secondary); res = LandInsert(rangeReturn, fo->primary, range); - if (ResIsAllocFailure(res)) { - /* primary ran out of memory: try secondary instead. */ + if (res != ResOK && res != ResFAIL) res = LandInsert(rangeReturn, fo->secondary, range); - } return res; } @@ -118,11 +116,11 @@ static Res failoverDelete(Range rangeReturn, Land land, Range range) if (res == ResFAIL) { /* Range not found in primary: try secondary. */ return LandDelete(rangeReturn, fo->secondary, range); - } else if (ResIsAllocFailure(res)) { - /* Range was found in primary, but couldn't be deleted because the - * primary is out of memory. Delete the whole of oldRange, and - * re-insert the fragments (which might end up in the secondary). - * See . + } else if (res != ResOK) { + /* Range was found in primary, but couldn't be deleted, perhaps + * because the primary is out of memory. Delete the whole of + * oldRange, and re-insert the fragments (which might end up in + * the secondary). See . */ res = LandDelete(&dummyRange, fo->primary, &oldRange); if (res != ResOK) diff --git a/mps/code/fotest.c b/mps/code/fotest.c index bef621362d7..fd3011840b9 100644 --- a/mps/code/fotest.c +++ b/mps/code/fotest.c @@ -38,28 +38,37 @@ /* Accessors for the CBS used to implement a pool. */ -extern Land _mps_mvff_cbs(mps_pool_t); -extern Land _mps_mvt_cbs(mps_pool_t); +extern Land _mps_mvff_cbs(Pool); +extern Land _mps_mvt_cbs(Pool); /* "OOM" pool class -- dummy alloc/free pool class whose alloc() - * method always returns ResMEMORY */ + * method always fails. */ -static Res OOMAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit) +static Res oomAlloc(Addr *pReturn, Pool pool, Size size, + Bool withReservoirPermit) { UNUSED(pReturn); UNUSED(pool); UNUSED(size); UNUSED(withReservoirPermit); - return ResMEMORY; + switch (rnd() % 4) { + case 0: + return ResRESOURCE; + case 1: + return ResMEMORY; + case 2: + return ResLIMIT; + default: + return ResCOMMIT_LIMIT; + } } -extern PoolClass PoolClassOOM(void); +extern PoolClass OOMPoolClassGet(void); DEFINE_POOL_CLASS(OOMPoolClass, this) { INHERIT_CLASS(this, AbstractAllocFreePoolClass); - this->alloc = OOMAlloc; + this->alloc = oomAlloc; } @@ -81,16 +90,17 @@ static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size) /* set_oom -- set blockPool of CBS to OOM or MFS according to argument. */ -static void set_oom(CBS cbs, int oom) +static void set_oom(Land land, int oom) { - cbs->blockPool->class = oom ? EnsureOOMPoolClass() : PoolClassMFS(); + CBS cbs = PARENT(CBSStruct, landStruct, land); + cbs->blockPool->class = oom ? OOMPoolClassGet() : PoolClassMFS(); } /* stress -- create an allocation point and allocate in it */ static mps_res_t stress(size_t (*size)(unsigned long, mps_align_t), - mps_align_t alignment, mps_pool_t pool, CBS cbs) + mps_align_t alignment, mps_pool_t pool, Land cbs) { mps_res_t res = MPS_RES_OK; mps_ap_t ap; @@ -180,8 +190,8 @@ int main(int argc, char *argv[]) die(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), "create MVFF"); } MPS_ARGS_END(args); { - CBS cbs = (CBS)_mps_mvff_cbs(pool); - die(stress(randomSizeAligned, alignment, pool, cbs), "stress MVFF"); + die(stress(randomSizeAligned, alignment, pool, _mps_mvff_cbs(pool)), + "stress MVFF"); } mps_pool_destroy(pool); mps_arena_destroy(arena); @@ -199,8 +209,8 @@ int main(int argc, char *argv[]) die(mps_pool_create_k(&pool, arena, mps_class_mvt(), args), "create MVFF"); } MPS_ARGS_END(args); { - CBS cbs = (CBS)_mps_mvt_cbs(pool); - die(stress(randomSizeAligned, alignment, pool, cbs), "stress MVT"); + die(stress(randomSizeAligned, alignment, pool, _mps_mvt_cbs(pool)), + "stress MVT"); } mps_pool_destroy(pool); mps_arena_destroy(arena); diff --git a/mps/code/landtest.c b/mps/code/landtest.c index 4698a9f1aaf..e820eca517f 100644 --- a/mps/code/landtest.c +++ b/mps/code/landtest.c @@ -3,7 +3,7 @@ * $Id$ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * - * The MPS contains two land implementations: + * The MPS contains three land implementations: * * 1. the CBS (Coalescing Block Structure) module maintains blocks in * a splay tree for fast access with a cost in storage; @@ -11,6 +11,9 @@ * 2. the Freelist module maintains blocks in an address-ordered * singly linked list for zero storage overhead with a cost in * performance. + * + * 3. the Failover module implements a mechanism for using CBS until + * it fails, then falling back to a Freelist. */ #include "cbs.h" @@ -20,6 +23,7 @@ #include "mps.h" #include "mpsavm.h" #include "mpstd.h" +#include "poolmfs.h" #include "testlib.h" #include @@ -479,13 +483,16 @@ extern int main(int argc, char *argv[]) void *p; Addr dummyBlock; BT allocTable; + MFSStruct blockPool; CBSStruct cbsStruct; FreelistStruct flStruct; FailoverStruct foStruct; Land cbs = &cbsStruct.landStruct; Land fl = &flStruct.landStruct; Land fo = &foStruct.landStruct; + Pool mfs = &blockPool.poolStruct; Align align; + int i; testlib_init(argc, argv); align = (1 << rnd() % 4) * MPS_PF_ALIGN; @@ -512,6 +519,8 @@ extern int main(int argc, char *argv[]) (char *)dummyBlock + ArraySize); } + /* 1. Test CBS */ + MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, CBSFastFind, TRUE); die((mps_res_t)LandInit(cbs, CBSLandClassGet(), arena, align, NULL, args), @@ -524,6 +533,8 @@ extern int main(int argc, char *argv[]) test(&state, nCBSOperations); LandFinish(cbs); + /* 2. Test Freelist */ + die((mps_res_t)LandInit(fl, FreelistLandClassGet(), arena, align, NULL, mps_args_none), "failed to initialise Freelist"); @@ -531,27 +542,46 @@ extern int main(int argc, char *argv[]) test(&state, nFLOperations); LandFinish(fl); - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, CBSFastFind, TRUE); - die((mps_res_t)LandInit(cbs, CBSLandClassGet(), arena, align, - NULL, args), - "failed to initialise CBS"); - } MPS_ARGS_END(args); - die((mps_res_t)LandInit(fl, FreelistLandClassGet(), arena, align, NULL, - mps_args_none), - "failed to initialise Freelist"); - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, FailoverPrimary, cbs); - MPS_ARGS_ADD(args, FailoverSecondary, fl); - die((mps_res_t)LandInit(fo, FailoverLandClassGet(), arena, align, NULL, - args), - "failed to initialise Failover"); - } MPS_ARGS_END(args); - state.land = fo; - test(&state, nFOOperations); - LandFinish(fo); - LandFinish(fl); - LandFinish(cbs); + /* 3. Test CBS-failing-over-to-Freelist (always failing over on + * first iteration, never failing over on second; see fotest.c for a + * test case that randomly switches fail-over on and off) + */ + + for (i = 0; i < 2; ++i) { + MPS_ARGS_BEGIN(piArgs) { + MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSBlockStruct)); + MPS_ARGS_ADD(piArgs, MPS_KEY_EXTEND_BY, ArenaAlign(arena)); + MPS_ARGS_ADD(piArgs, MFSExtendSelf, i); + MPS_ARGS_DONE(piArgs); + die(PoolInit(mfs, arena, PoolClassMFS(), piArgs), "PoolInit"); + } MPS_ARGS_END(piArgs); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, CBSFastFind, TRUE); + MPS_ARGS_ADD(args, CBSBlockPool, mfs); + die((mps_res_t)LandInit(cbs, CBSLandClassGet(), arena, align, NULL, + args), + "failed to initialise CBS"); + } MPS_ARGS_END(args); + + die((mps_res_t)LandInit(fl, FreelistLandClassGet(), arena, align, NULL, + mps_args_none), + "failed to initialise Freelist"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, FailoverPrimary, cbs); + MPS_ARGS_ADD(args, FailoverSecondary, fl); + die((mps_res_t)LandInit(fo, FailoverLandClassGet(), arena, align, NULL, + args), + "failed to initialise Failover"); + } MPS_ARGS_END(args); + + state.land = fo; + test(&state, nFOOperations); + LandFinish(fo); + LandFinish(fl); + LandFinish(cbs); + PoolFinish(mfs); + } mps_arena_destroy(arena); diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 35684a52950..a36a9e7b8e8 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -701,8 +701,8 @@ typedef union FreelistBlockUnion *FreelistBlock; typedef struct FreelistStruct { LandStruct landStruct; /* superclass fields come first */ - FreelistBlock list; - Count listSize; + FreelistBlock list; /* first block in list or NULL if empty */ + Count listSize; /* number of blocks in list */ Sig sig; /* .class.end-sig */ } FreelistStruct; diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index 6baa0322a53..bbf57d586a0 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -385,9 +385,7 @@ static Bool MVTCheck(MVT mvt) CHECKD(Pool, &mvt->poolStruct); CHECKL(mvt->poolStruct.class == MVTPoolClassGet()); CHECKD(CBS, &mvt->cbsStruct); - /* CHECKL(CBSCheck(MVTCBS(mvt))); */ CHECKD(ABQ, &mvt->abqStruct); - /* CHECKL(ABQCheck(MVTABQ(mvt))); */ CHECKD(Freelist, &mvt->flStruct); CHECKD(Failover, &mvt->foStruct); CHECKL(mvt->reuseSize >= 2 * mvt->fillSize); @@ -402,8 +400,7 @@ static Bool MVTCheck(MVT mvt) if (mvt->splinter) { CHECKL(AddrOffset(mvt->splinterBase, mvt->splinterLimit) >= mvt->minSize); - /* CHECKD(Seg, mvt->splinterSeg); */ - CHECKL(SegCheck(mvt->splinterSeg)); + CHECKD(Seg, mvt->splinterSeg); CHECKL(mvt->splinterBase >= SegBase(mvt->splinterSeg)); CHECKL(mvt->splinterLimit <= SegLimit(mvt->splinterSeg)); } @@ -1257,6 +1254,10 @@ static Bool MVTReturnSegs(MVT mvt, Range range, Arena arena) } +/* MVTRefillABQIfEmpty -- refill the ABQ from the free lists if it is + * empty. + */ + static Bool MVTRefillVisitor(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS) { @@ -1275,9 +1276,6 @@ static Bool MVTRefillVisitor(Bool *deleteReturn, Land land, Range range, return MVTReserve(mvt, range); } -/* MVTRefillABQIfEmpty -- refill the ABQ from the free lists if it is - * empty. - */ static void MVTRefillABQIfEmpty(MVT mvt, Size size) { AVERT(MVT, mvt); @@ -1296,10 +1294,9 @@ static void MVTRefillABQIfEmpty(MVT mvt, Size size) } -/* Closure for MVTContingencySearch */ -typedef struct MVTContigencyStruct *MVTContigency; +/* MVTContingencySearch -- search free lists for a block of a given size */ -typedef struct MVTContigencyStruct +typedef struct MVTContigencyClosureStruct { MVT mvt; Bool found; @@ -1309,12 +1306,7 @@ typedef struct MVTContigencyStruct /* meters */ Count steps; Count hardSteps; -} MVTContigencyStruct; - - -/* MVTContingencyVisitor -- called from LandIterate at the behest of - * MVTContingencySearch. - */ +} MVTContigencyClosureStruct, *MVTContigencyClosure; static Bool MVTContingencyVisitor(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS) @@ -1322,7 +1314,7 @@ static Bool MVTContingencyVisitor(Bool *deleteReturn, Land land, Range range, MVT mvt; Size size; Addr base, limit; - MVTContigency cl; + MVTContigencyClosure cl; AVER(deleteReturn != NULL); AVERT(Land, land); @@ -1360,14 +1352,10 @@ static Bool MVTContingencyVisitor(Bool *deleteReturn, Land land, Range range, return TRUE; } -/* MVTContingencySearch -- search the free lists for a block of size - * min. - */ - static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, MVT mvt, Size min) { - MVTContigencyStruct cls; + MVTContigencyClosureStruct cls; cls.mvt = mvt; cls.found = FALSE; @@ -1394,6 +1382,7 @@ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, /* MVTCheckFit -- verify that segment-aligned block of size min can * fit in a candidate address range. */ + static Bool MVTCheckFit(Addr base, Addr limit, Size min, Arena arena) { Seg seg; @@ -1423,12 +1412,10 @@ static Bool MVTCheckFit(Addr base, Addr limit, Size min, Arena arena) /* Return the CBS of an MVT pool for the benefit of fotest.c. */ -extern Land _mps_mvt_cbs(mps_pool_t); -Land _mps_mvt_cbs(mps_pool_t mps_pool) { - Pool pool; +extern Land _mps_mvt_cbs(Pool); +Land _mps_mvt_cbs(Pool pool) { MVT mvt; - pool = (Pool)mps_pool; AVERT(Pool, pool); mvt = Pool2MVT(pool); AVERT(MVT, mvt); diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index 818b4f932aa..a975d944cbb 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -748,12 +748,10 @@ static Bool MVFFCheck(MVFF mvff) /* Return the CBS of an MVFF pool for the benefit of fotest.c. */ -extern Land _mps_mvff_cbs(mps_pool_t); -Land _mps_mvff_cbs(mps_pool_t mps_pool) { - Pool pool; +extern Land _mps_mvff_cbs(Pool); +Land _mps_mvff_cbs(Pool pool) { MVFF mvff; - pool = (Pool)mps_pool; AVERT(Pool, pool); mvff = Pool2MVFF(pool); AVERT(MVFF, mvff); diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt index ec597de1abd..1f5d2eafb39 100644 --- a/mps/design/cbs.txt +++ b/mps/design/cbs.txt @@ -232,7 +232,7 @@ Document History ---------------- - 1998-05-01 Gavin Matthews. This document was derived from the - outline in design.mps.poolmvt_. + outline in design.mps.poolmv2(2). - 1998-07-22 Gavin Matthews. Updated in response to approval comments in change.epcore.anchovy.160040. There is too much fragmentation in diff --git a/mps/design/freelist.txt b/mps/design/freelist.txt index 680a143c1a5..7dd253ed720 100644 --- a/mps/design/freelist.txt +++ b/mps/design/freelist.txt @@ -151,8 +151,8 @@ exceed the recorded size of the list. _`.improve.maxsize`: We could maintain the maximum size of any range on the list, and use that to make an early exit from -``LandFindLargest()``. It's not clear that this would actually be an -improvement. +``freelistFindLargest()``. It's not clear that this would actually be +an improvement. diff --git a/mps/design/index.txt b/mps/design/index.txt index b14ed257d5f..41db91aa4b7 100644 --- a/mps/design/index.txt +++ b/mps/design/index.txt @@ -68,15 +68,15 @@ message_ MPS to client message protocol message-gc_ Messages sent when garbage collection begins or ends object-debug_ Debugging Features for Client Objects pool_ The design of the pool and pool class mechanisms -poolamc_ The design of the automatic mostly-copying memory pool class -poolams_ The design of the automatic mark-and-sweep pool class -poolawl_ Automatic weak linked -poollo_ Leaf object pool class +poolamc_ Automatic Mostly-Copying pool class +poolams_ Automatic Mark-and-Sweep pool class +poolawl_ Automatic Weak Linked pool class +poollo_ Leaf Object pool class poolmfs_ Manual Fixed Small pool class -poolmrg_ Guardian poolclass +poolmrg_ Manual Rank Guardian pool class poolmv_ Manual Variable pool class poolmvt_ Manual Variable Temporal pool class -poolmvff_ Manually Variable First-Fit pool +poolmvff_ Manual Variable First-Fit pool class prot_ Generic design of the protection module protan_ ANSI implementation of protection module protli_ Linux implementation of protection module From 86a50c3d1c5b835b4f423a4e015fae27cd973be8 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 3 Apr 2014 15:01:53 +0100 Subject: [PATCH 11/43] Fix file-at-a-time compilation. Copied from Perforce Change: 185210 ServerID: perforce.ravenbrook.com --- mps/code/freelist.c | 2 +- mps/code/poolmv2.c | 1 + mps/code/poolmvff.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 4706212bcc0..e46ee7ab773 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -626,7 +626,7 @@ static Res freelistFindInZones(Range rangeReturn, Range oldRangeReturn, /* AVERT(ZoneSet, zoneSet); */ AVERT(Bool, high); - landFind = high ? cbsFindLast : cbsFindFirst; + landFind = high ? freelistFindLast : freelistFindFirst; search = high ? RangeInZoneSetLast : RangeInZoneSetFirst; if (zoneSet == ZoneSetEMPTY) diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index bbf57d586a0..ca1a754041c 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -14,6 +14,7 @@ #include "mpscmvt.h" #include "abq.h" #include "cbs.h" +#include "failover.h" #include "freelist.h" #include "meter.h" #include "range.h" diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index a975d944cbb..ef5cafd2499 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -21,6 +21,7 @@ #include "mpscmvff.h" #include "dbgpool.h" #include "cbs.h" +#include "failover.h" #include "freelist.h" #include "mpm.h" From 642050880d7097cc03f45f78364274601e8596c0 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 3 Apr 2014 15:02:20 +0100 Subject: [PATCH 12/43] Compile cool before hot because the former doesn't need to optimize and so detects errors more quickly; and because the former uses file-at-a-time compilation and so can pick up where it left off instead of having to start from the beginning of mps.c. Copied from Perforce Change: 185211 ServerID: perforce.ravenbrook.com --- mps/Makefile.in | 6 +++--- mps/code/comm.gmk | 12 ++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/mps/Makefile.in b/mps/Makefile.in index 2d558588673..3071110cb11 100644 --- a/mps/Makefile.in +++ b/mps/Makefile.in @@ -34,12 +34,12 @@ install-make-build: make-install-dirs build-via-make $(INSTALL_PROGRAM) $(addprefix code/$(MPS_TARGET_NAME)/hot/Release/,$(EXTRA_TARGETS)) $(prefix)/bin build-via-xcode: - $(XCODEBUILD) -config Release $(XCODEBUILD) -config Debug + $(XCODEBUILD) -config Release clean-xcode-build: - $(XCODEBUILD) -config Release clean $(XCODEBUILD) -config Debug clean + $(XCODEBUILD) -config Release clean install-xcode-build: make-install-dirs build-via-xcode $(INSTALL_DATA) code/mps*.h $(prefix)/include/ @@ -72,7 +72,7 @@ test-make-build: @BUILD_TARGET@ $(MAKE) $(TARGET_OPTS) VARIETY=hot testrun test-xcode-build: - $(XCODEBUILD) -config Release -target testrun $(XCODEBUILD) -config Debug -target testrun + $(XCODEBUILD) -config Release -target testrun test: @TEST_TARGET@ diff --git a/mps/code/comm.gmk b/mps/code/comm.gmk index 44264ed0acf..a270296a914 100644 --- a/mps/code/comm.gmk +++ b/mps/code/comm.gmk @@ -342,17 +342,25 @@ clean: phony $(ECHO) "$(PFM): $@" rm -rf "$(PFM)" -# "target" builds some varieties of the target named in the TARGET macro. +# "target" builds some varieties of the target named in the TARGET +# macro. +# # %%VARIETY: When adding a new target, optionally add a recursive make call # for the new variety, if it should be built by default. It probably # shouldn't without a product design decision and an update of the readme # and build manual! +# +# Note that we build VARIETY=cool before VARIETY=hot because +# the former doesn't need to optimize and so detects errors more +# quickly; and because the former uses file-at-a-time compilation and +# so can pick up where it left off instead of having to start from the +# beginning of mps.c ifdef TARGET ifndef VARIETY target: phony - $(MAKE) -f $(PFM).gmk VARIETY=hot variety $(MAKE) -f $(PFM).gmk VARIETY=cool variety + $(MAKE) -f $(PFM).gmk VARIETY=hot variety endif endif From 53c8d9390b3deecffdab3d31fe676258e2b95ace Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 3 Apr 2014 16:50:51 +0100 Subject: [PATCH 13/43] Build cool variety before hot because the former doesn't need to optimize and so detects errors more quickly; and because the former uses file-at-a-time compilation and so can pick up where it left off instead of having to start from the beginning of mps.c. Copied from Perforce Change: 185213 ServerID: perforce.ravenbrook.com --- mps/code/commpost.nmk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mps/code/commpost.nmk b/mps/code/commpost.nmk index 78095f33714..e8ec7bf6b44 100644 --- a/mps/code/commpost.nmk +++ b/mps/code/commpost.nmk @@ -39,8 +39,8 @@ clean: !IFDEF TARGET !IFNDEF VARIETY target: - $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot variety $(MAKE) /nologo /f $(PFM).nmk VARIETY=cool variety + $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot variety !ENDIF !ENDIF @@ -60,8 +60,8 @@ testrun: $(TEST_TARGETS) !IFDEF VARIETY ..\tool\testrun.bat $(PFM) $(VARIETY) !ELSE - $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot testrun $(MAKE) /nologo /f $(PFM).nmk VARIETY=cool testrun + $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot testrun !ENDIF From 6432d58813a8d38e80efb38898c4a08f3019094c Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Mon, 7 Apr 2014 16:26:03 +0100 Subject: [PATCH 14/43] Fix the condition for splaynodeupdate. Copied from Perforce Change: 185307 ServerID: perforce.ravenbrook.com --- mps/code/splay.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mps/code/splay.c b/mps/code/splay.c index e07b40215cb..278f038b9b5 100644 --- a/mps/code/splay.c +++ b/mps/code/splay.c @@ -1323,7 +1323,8 @@ void SplayNodeUpdate(SplayTree splay, Tree node) { AVERT(SplayTree, splay); AVERT(Tree, node); - AVER(SplayTreeIsEmpty(splay)); /* otherwise, call SplayNodeRefresh */ + AVER(!TreeHasLeft(node)); /* otherwise, call SplayNodeRefresh */ + AVER(!TreeHasRight(node)); /* otherwise, call SplayNodeRefresh */ AVER(SplayHasUpdate(splay)); /* otherwise, why call? */ splay->updateNode(splay, node); From 262bb324f371dfd8f9b42e977ae61cfd0bef9293 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 8 Apr 2014 00:13:50 +0100 Subject: [PATCH 15/43] No keyword arguments needed in these cbsfastlandclass initializations. Copied from Perforce Change: 185329 ServerID: perforce.ravenbrook.com --- mps/code/poolmv2.c | 5 ++--- mps/code/poolmvff.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index 5343c05ec96..9d73e358a42 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -279,9 +279,8 @@ static Res MVTInit(Pool pool, ArgList args) if (abqDepth < 3) abqDepth = 3; - MPS_ARGS_BEGIN(liArgs) { - res = LandInit(MVTCBS(mvt), CBSFastLandClassGet(), arena, align, mvt, liArgs); - } MPS_ARGS_END(liArgs); + res = LandInit(MVTCBS(mvt), CBSFastLandClassGet(), arena, align, mvt, + mps_args_none); if (res != ResOK) goto failCBS; diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index 8784042111c..6a530fb94bd 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -523,9 +523,8 @@ static Res MVFFInit(Pool pool, ArgList args) if (res != ResOK) goto failFreelistInit; - MPS_ARGS_BEGIN(liArgs) { - res = LandInit(CBSOfMVFF(mvff), CBSFastLandClassGet(), arena, align, mvff, liArgs); - } MPS_ARGS_END(liArgs); + res = LandInit(CBSOfMVFF(mvff), CBSFastLandClassGet(), arena, align, mvff, + mps_args_none); if (res != ResOK) goto failCBSInit; From 4d29cf2757464e7dbc6794b1b2ac3a7dbacb8444 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 8 Apr 2014 00:14:50 +0100 Subject: [PATCH 16/43] Fix typo. Copied from Perforce Change: 185330 ServerID: perforce.ravenbrook.com --- mps/design/cbs.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt index 70cf737eaf4..7ed38d586a1 100644 --- a/mps/design/cbs.txt +++ b/mps/design/cbs.txt @@ -172,8 +172,8 @@ Low memory behaviour _`.impl.low-mem`: When the CBS tries to allocate a new ``CBSBlock`` structure for a new isolated range as a result of either ``LandInsert()`` or ``LandDelete()``, and there is insufficient memory -to allocation the block structure, then the range is not added -to the CBS or deleted from it, and the call to ``LandInsert()`` or +to allocate the block structure, then the range is not added to the +CBS or deleted from it, and the call to ``LandInsert()`` or ``LandDelete()`` returns ``ResMEMORY``. From a595c2ff20bc040c728691e26a64797142f7293c Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 10 Apr 2014 13:02:22 +0100 Subject: [PATCH 17/43] Trying to create a freelist with too-small alignment is a static programming error, not a dynamic failure condition, so aver instead of returning resparam. (see job003485). Copied from Perforce Change: 185426 ServerID: perforce.ravenbrook.com --- mps/code/freelist.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mps/code/freelist.c b/mps/code/freelist.c index e46ee7ab773..5b586a82e6a 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -187,8 +187,7 @@ static Res freelistInit(Land land, ArgList args) return res; /* See */ - if (!AlignIsAligned(LandAlignment(land), freelistMinimumAlignment)) - return ResPARAM; + AVER(AlignIsAligned(LandAlignment(land), freelistMinimumAlignment)); fl = freelistOfLand(land); fl->list = NULL; From c859df7e49fcc8bc0efc9971c28daae8e2ddbcaa Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 15 Apr 2014 13:05:10 +0100 Subject: [PATCH 18/43] Branching master to branch/2014-04-15/mvffnoseg. Copied from Perforce Change: 185560 ServerID: perforce.ravenbrook.com From 4deafee12538813b282f124e50b32ad487ce9a5b Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 15 Apr 2014 14:23:53 +0100 Subject: [PATCH 19/43] Lands maintain the total size of the address ranges they maintain. (this avoids the need to do free size accounting in mvff.) Copied from Perforce Change: 185567 ServerID: perforce.ravenbrook.com --- mps/code/land.c | 75 +++++++++++++++++++++++++++++++++++++++------ mps/code/mpm.h | 1 + mps/code/mpmst.h | 1 + mps/code/poolmvff.c | 29 ++++-------------- 4 files changed, 73 insertions(+), 33 deletions(-) diff --git a/mps/code/land.c b/mps/code/land.c index fe759d85410..6c59cb193a6 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -34,6 +34,8 @@ Bool LandCheck(Land land) CHECKD(LandClass, land->class); CHECKU(Arena, land->arena); CHECKL(AlignCheck(land->alignment)); + CHECKL(SizeIsAligned(land->size, land->alignment)); + /* too expensive to check land->size against contents */ return TRUE; } @@ -51,6 +53,7 @@ Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *own AVERT(LandClass, class); AVERT(Align, alignment); + land->size = 0; land->alignment = alignment; land->arena = arena; land->class = class; @@ -147,12 +150,21 @@ void LandFinish(Land land) Res LandInsert(Range rangeReturn, Land land, Range range) { + Res res; + Size size; + AVER(rangeReturn != NULL); AVERT(Land, land); AVERT(Range, range); AVER(RangeIsAligned(range, land->alignment)); - return (*land->class->insert)(rangeReturn, land, range); + /* rangeReturn is allowed to alias with range, so take size first. + * See */ + size = RangeSize(range); + res = (*land->class->insert)(rangeReturn, land, range); + if (res == ResOK) + land->size += size; + return res; } @@ -163,12 +175,22 @@ Res LandInsert(Range rangeReturn, Land land, Range range) Res LandDelete(Range rangeReturn, Land land, Range range) { + Res res; + Size size; + AVER(rangeReturn != NULL); AVERT(Land, land); AVERT(Range, range); AVER(RangeIsAligned(range, land->alignment)); - return (*land->class->delete)(rangeReturn, land, range); + /* rangeReturn is allowed to alias with range, so take size first. + * See */ + size = RangeSize(range); + AVER(land->size >= size); + res = (*land->class->delete)(rangeReturn, land, range); + if (res == ResOK) + land->size -= size; + return res; } @@ -193,14 +215,22 @@ void LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { + Bool res; + AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVERT(Land, land); AVER(SizeIsAligned(size, land->alignment)); AVER(FindDeleteCheck(findDelete)); - return (*land->class->findFirst)(rangeReturn, oldRangeReturn, land, size, - findDelete); + res = (*land->class->findFirst)(rangeReturn, oldRangeReturn, land, size, + findDelete); + if (res && findDelete != FindDeleteNONE) { + AVER(RangeIsAligned(rangeReturn, land->alignment)); + AVER(land->size >= RangeSize(rangeReturn)); + land->size -= RangeSize(rangeReturn); + } + return res; } @@ -211,14 +241,22 @@ Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { + Bool res; + AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVERT(Land, land); AVER(SizeIsAligned(size, land->alignment)); AVER(FindDeleteCheck(findDelete)); - return (*land->class->findLast)(rangeReturn, oldRangeReturn, land, size, - findDelete); + res = (*land->class->findLast)(rangeReturn, oldRangeReturn, land, size, + findDelete); + if (res && findDelete != FindDeleteNONE) { + AVER(RangeIsAligned(rangeReturn, land->alignment)); + AVER(land->size >= RangeSize(rangeReturn)); + land->size -= RangeSize(rangeReturn); + } + return res; } @@ -229,14 +267,22 @@ Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { + Bool res; + AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVERT(Land, land); AVER(SizeIsAligned(size, land->alignment)); AVER(FindDeleteCheck(findDelete)); - return (*land->class->findLargest)(rangeReturn, oldRangeReturn, land, size, - findDelete); + res = (*land->class->findLargest)(rangeReturn, oldRangeReturn, land, size, + findDelete); + if (res && findDelete != FindDeleteNONE) { + AVER(RangeIsAligned(rangeReturn, land->alignment)); + AVER(land->size >= RangeSize(rangeReturn)); + land->size -= RangeSize(rangeReturn); + } + return res; } @@ -247,6 +293,8 @@ Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size si Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) { + Res res; + AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVERT(Land, land); @@ -254,8 +302,14 @@ Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size siz /* AVER(ZoneSet, zoneSet); */ AVERT(Bool, high); - return (*land->class->findInZones)(rangeReturn, oldRangeReturn, land, size, - zoneSet, high); + res = (*land->class->findInZones)(rangeReturn, oldRangeReturn, land, size, + zoneSet, high); + if (res == ResOK) { + AVER(RangeIsAligned(rangeReturn, land->alignment)); + AVER(land->size >= RangeSize(rangeReturn)); + land->size -= RangeSize(rangeReturn); + } + return res; } @@ -277,6 +331,7 @@ Res LandDescribe(Land land, mps_lib_FILE *stream) " (\"$S\")\n", land->class->name, " arena $P\n", (WriteFP)land->arena, " align $U\n", (WriteFU)land->alignment, + " align $U\n", (WriteFU)land->size, NULL); if (res != ResOK) return res; diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 422327453dc..406aaf7c107 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -1002,6 +1002,7 @@ extern Size VMMapped(VM vm); extern Bool LandCheck(Land land); #define LandArena(land) ((land)->arena) #define LandAlignment(land) ((land)->alignment) +#define LandSize(land) ((land)->size) extern Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args); extern Res LandCreate(Land *landReturn, Arena arena, LandClass class, Align alignment, void *owner, ArgList args); diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 80bbd298090..7f355ed4ec8 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -641,6 +641,7 @@ typedef struct LandStruct { LandClass class; /* land class structure */ Arena arena; /* owning arena */ Align alignment; /* alignment of addresses */ + Size size; /* total size of ranges in land */ } LandStruct; diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index e50cf077f80..73bb4ff19eb 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -48,7 +48,6 @@ typedef struct MVFFStruct { /* MVFF pool outer structure */ 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 */ FailoverStruct foStruct; /* fail-over mechanism */ @@ -88,19 +87,10 @@ typedef MVFFDebugStruct *MVFFDebug; * segments (see MVFFFreeSegs). */ static Res MVFFInsert(Range rangeIO, MVFF mvff) { - Res res; - Size size; - - AVER(rangeIO != NULL); + AVERT(Range, rangeIO); AVERT(MVFF, mvff); - size = RangeSize(rangeIO); - res = LandInsert(rangeIO, FailoverOfMVFF(mvff), rangeIO); - - if (res == ResOK) - mvff->free += size; - - return res; + return LandInsert(rangeIO, FailoverOfMVFF(mvff), rangeIO); } @@ -151,7 +141,6 @@ static void MVFFFreeSegs(MVFF mvff, Range range) * that needs to be read in order to update the Freelist. */ SegFree(seg); - mvff->free -= RangeSize(&delRange); mvff->total -= RangeSize(&delRange); } @@ -262,10 +251,6 @@ static Bool MVFFFindFree(Range rangeReturn, MVFF mvff, Size size) (mvff->firstFit ? LandFindFirst : LandFindLast) (rangeReturn, &oldRange, FailoverOfMVFF(mvff), size, findDelete); - if (foundBlock) { - mvff->free -= size; - } - return foundBlock; } @@ -378,7 +363,6 @@ static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn, AVER(found); AVER(RangeSize(&range) >= size); - mvff->free -= RangeSize(&range); *baseReturn = RangeBase(&range); *limitReturn = RangeLimit(&range); @@ -517,7 +501,6 @@ static Res MVFFInit(Pool pool, ArgList args) SegPrefExpress(mvff->segPref, arenaHigh ? SegPrefHigh : SegPrefLow, NULL); mvff->total = 0; - mvff->free = 0; res = LandInit(FreelistOfMVFF(mvff), FreelistLandClassGet(), arena, align, mvff, mps_args_none); if (res != ResOK) @@ -620,7 +603,6 @@ static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream) " 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; @@ -698,13 +680,15 @@ 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)mvff->free; + return (size_t)LandSize(land); } /* Total owned bytes. See */ @@ -735,8 +719,7 @@ static Bool MVFFCheck(MVFF mvff) 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(mvff->total >= LandSize(FailoverOfMVFF(mvff))); CHECKL(SizeIsAligned(mvff->total, ArenaAlign(PoolArena(MVFF2Pool(mvff))))); CHECKD(CBS, &mvff->cbsStruct); CHECKD(Freelist, &mvff->flStruct); From 87b388040539290dbaf1c8592fcd1e300d2a89f4 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 15 Apr 2014 16:35:34 +0100 Subject: [PATCH 20/43] New generic function landsize returns the total size of ranges in a land (if the land supports it). implement it for all land classes. The MVFF pool class doesn't have to maintain its free size any more: it can just call LandSize. Move re-entrancy protection from CBS to Land. This allows us to remove some CBS functions. (But requires some adjustment in failoverDelete.) In MVFF, do more checking of mvff->total. Copied from Perforce Change: 185569 ServerID: perforce.ravenbrook.com --- mps/code/cbs.c | 158 ++++++++++++++------------------------------ mps/code/failover.c | 23 ++++++- mps/code/freelist.c | 24 ++++++- mps/code/land.c | 138 +++++++++++++++++++++++++++----------- mps/code/mpm.h | 4 +- mps/code/mpmst.h | 6 +- mps/code/mpmtypes.h | 1 + mps/code/poolmvff.c | 19 ++++-- mps/design/land.txt | 5 ++ 9 files changed, 218 insertions(+), 160 deletions(-) diff --git a/mps/code/cbs.c b/mps/code/cbs.c index 8aa21f87ca7..177388bc0cb 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -40,43 +40,20 @@ SRCID(cbs, "$Id$"); #define cbsBlockPool(cbs) RVALUE((cbs)->blockPool) -/* cbsEnter, cbsLeave -- Avoid re-entrance - * - * .enter-leave: The callbacks are restricted in what they may call. - * These functions enforce this. - * - * .enter-leave.simple: Simple queries may be called from callbacks. - */ - -static void cbsEnter(CBS cbs) -{ - /* Don't need to check as always called from interface function. */ - AVER(!cbs->inCBS); - cbs->inCBS = TRUE; - return; -} - -static void cbsLeave(CBS cbs) -{ - /* Don't need to check as always called from interface function. */ - AVER(cbs->inCBS); - cbs->inCBS = FALSE; - return; -} - - /* CBSCheck -- Check CBS */ Bool CBSCheck(CBS cbs) { /* See .enter-leave.simple. */ + Land land; CHECKS(CBS, cbs); - CHECKD(Land, cbsLand(cbs)); + land = cbsLand(cbs); + CHECKD(Land, land); CHECKD(SplayTree, cbsSplay(cbs)); - /* nothing to check about treeSize */ CHECKD(Pool, cbs->blockPool); - CHECKL(BoolCheck(cbs->inCBS)); CHECKL(BoolCheck(cbs->ownPool)); + CHECKL(SizeIsAligned(cbs->size, LandAlignment(land))); + CHECKL((cbs->size == 0) == (cbs->treeSize == 0)); return TRUE; } @@ -84,7 +61,6 @@ Bool CBSCheck(CBS cbs) static Bool CBSBlockCheck(CBSBlock block) { - /* See .enter-leave.simple. */ UNUSED(block); /* Required because there is no signature */ CHECKL(block != NULL); /* Can't use CHECKD_NOSIG because TreeEMPTY is NULL. */ @@ -277,16 +253,15 @@ static Res cbsInitComm(Land land, ArgList args, SplayUpdateNodeMethod update, cbs->ownPool = TRUE; } cbs->treeSize = 0; + cbs->size = 0; cbs->blockStructSize = blockStructSize; - cbs->inCBS = TRUE; METER_INIT(cbs->treeSearch, "size of tree", (void *)cbs); cbs->sig = CBSSig; AVERT(CBS, cbs); - cbsLeave(cbs); return ResOK; } @@ -309,7 +284,7 @@ static Res cbsInitZoned(Land land, ArgList args) } -/* CBSFinish -- Finish a CBS structure +/* cbsFinish -- Finish a CBS structure * * See . */ @@ -321,7 +296,6 @@ static void cbsFinish(Land land) AVERT(Land, land); cbs = cbsOfLand(land); AVERT(CBS, cbs); - cbsEnter(cbs); METER_EMIT(&cbs->treeSearch); @@ -333,6 +307,23 @@ static void cbsFinish(Land land) } +/* cbsSize -- total size of ranges in CBS + * + * See . + */ + +static Size cbsSize(Land land) +{ + CBS cbs; + + AVERT(Land, land); + cbs = cbsOfLand(land); + AVERT(CBS, cbs); + + return cbs->size; +} + + /* Node change operators * * These four functions are called whenever blocks are created, @@ -343,14 +334,18 @@ static void cbsFinish(Land land) static void cbsBlockDelete(CBS cbs, CBSBlock block) { Bool b; + Size size; AVERT(CBS, cbs); AVERT(CBSBlock, block); + size = CBSBlockSize(block); METER_ACC(cbs->treeSearch, cbs->treeSize); b = SplayTreeDelete(cbsSplay(cbs), cbsBlockTree(block)); AVER(b); /* expect block to be in the tree */ STATISTIC(--cbs->treeSize); + AVER(cbs->size >= size); + cbs->size -= size; /* make invalid */ block->limit = block->base; @@ -367,8 +362,10 @@ static void cbsBlockShrunk(CBS cbs, CBSBlock block, Size oldSize) newSize = CBSBlockSize(block); AVER(oldSize > newSize); + AVER(cbs->size >= oldSize - newSize); SplayNodeRefresh(cbsSplay(cbs), cbsBlockTree(block)); + cbs->size -= oldSize - newSize; } static void cbsBlockGrew(CBS cbs, CBSBlock block, Size oldSize) @@ -382,6 +379,7 @@ static void cbsBlockGrew(CBS cbs, CBSBlock block, Size oldSize) AVER(oldSize < newSize); SplayNodeRefresh(cbsSplay(cbs), cbsBlockTree(block)); + cbs->size += newSize - oldSize; } /* cbsBlockAlloc -- allocate a new block and set its base and limit, @@ -431,12 +429,19 @@ static void cbsBlockInsert(CBS cbs, CBSBlock block) b = SplayTreeInsert(cbsSplay(cbs), cbsBlockTree(block)); AVER(b); STATISTIC(++cbs->treeSize); + cbs->size += CBSBlockSize(block); } -/* cbsInsertIntoTree -- Insert a range into the tree */ +/* cbsInsert -- Insert a range into the CBS + * + * See . + * + * .insert.alloc: Will only allocate a block if the range does not + * abut an existing range. + */ -static Res cbsInsertIntoTree(Range rangeReturn, Land land, Range range) +static Res cbsInsert(Range rangeReturn, Land land, Range range) { CBS cbs; Bool b; @@ -533,38 +538,15 @@ static Res cbsInsertIntoTree(Range rangeReturn, Land land, Range range) } -/* cbsInsert -- Insert a range into the CBS +/* cbsDelete -- Remove a range from a CBS * - * See . + * See . * - * .insert.alloc: Will only allocate a block if the range does not - * abut an existing range. + * .delete.alloc: Will only allocate a block if the range splits + * an existing range. */ -static Res cbsInsert(Range rangeReturn, Land land, Range range) -{ - CBS cbs; - Res res; - - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); - cbsEnter(cbs); - - AVER(rangeReturn != NULL); - AVERT(Range, range); - AVER(RangeIsAligned(range, LandAlignment(land))); - - res = cbsInsertIntoTree(rangeReturn, land, range); - - cbsLeave(cbs); - return res; -} - - -/* cbsDeleteFromTree -- delete blocks from the tree */ - -static Res cbsDeleteFromTree(Range rangeReturn, Land land, Range range) +static Res cbsDelete(Range rangeReturn, Land land, Range range) { CBS cbs; Res res; @@ -642,35 +624,6 @@ static Res cbsDeleteFromTree(Range rangeReturn, Land land, Range range) } -/* cbsDelete -- Remove a range from a CBS - * - * See . - * - * .delete.alloc: Will only allocate a block if the range splits - * an existing range. - */ - -static Res cbsDelete(Range rangeReturn, Land land, Range range) -{ - CBS cbs; - Res res; - - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); - cbsEnter(cbs); - - AVER(rangeReturn != NULL); - AVERT(Range, range); - AVER(RangeIsAligned(range, LandAlignment(land))); - - res = cbsDeleteFromTree(rangeReturn, land, range); - - cbsLeave(cbs); - return res; -} - - static Res cbsBlockDescribe(CBSBlock block, mps_lib_FILE *stream) { Res res; @@ -813,7 +766,6 @@ static void cbsIterate(Land land, LandVisitor visitor, AVERT(Land, land); cbs = cbsOfLand(land); AVERT(CBS, cbs); - cbsEnter(cbs); AVER(FUNCHECK(visitor)); splay = cbsSplay(cbs); @@ -827,9 +779,6 @@ static void cbsIterate(Land land, LandVisitor visitor, closure.closureS = closureS; (void)TreeTraverse(SplayTreeRoot(splay), splay->compare, splay->nodeKey, cbsIterateVisit, &closure, 0); - - cbsLeave(cbs); - return; } @@ -882,10 +831,10 @@ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, if (callDelete) { Res res; - res = cbsDeleteFromTree(oldRangeReturn, land, rangeReturn); + res = cbsDelete(oldRangeReturn, land, rangeReturn); /* Can't have run out of memory, because all our callers pass in blocks that were just found in the tree, and we only - deleted from one end of the block, so cbsDeleteFromTree did not + deleted from one end of the block, so cbsDelete did not need to allocate a new block. */ AVER(res == ResOK); } @@ -905,7 +854,6 @@ static Bool cbsFindFirst(Range rangeReturn, Range oldRangeReturn, cbs = cbsOfLand(land); AVERT(CBS, cbs); AVER(IsLandSubclass(cbsLand(cbs), CBSFastLandClass)); - cbsEnter(cbs); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); @@ -927,7 +875,6 @@ static Bool cbsFindFirst(Range rangeReturn, Range oldRangeReturn, size, findDelete); } - cbsLeave(cbs); return found; } @@ -992,7 +939,6 @@ static Bool cbsFindLast(Range rangeReturn, Range oldRangeReturn, cbs = cbsOfLand(land); AVERT(CBS, cbs); AVER(IsLandSubclass(cbsLand(cbs), CBSFastLandClass)); - cbsEnter(cbs); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); @@ -1014,7 +960,6 @@ static Bool cbsFindLast(Range rangeReturn, Range oldRangeReturn, size, findDelete); } - cbsLeave(cbs); return found; } @@ -1031,7 +976,6 @@ static Bool cbsFindLargest(Range rangeReturn, Range oldRangeReturn, cbs = cbsOfLand(land); AVERT(CBS, cbs); AVER(IsLandSubclass(cbsLand(cbs), CBSFastLandClass)); - cbsEnter(cbs); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); @@ -1058,7 +1002,6 @@ static Bool cbsFindLargest(Range rangeReturn, Range oldRangeReturn, } } - cbsLeave(cbs); return found; } @@ -1101,8 +1044,6 @@ static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, /* It would be nice if there were a neat way to eliminate all runs of zones in zoneSet too small for size.*/ - cbsEnter(cbs); - closure.arena = LandArena(land); closure.zoneSet = zoneSet; closure.size = size; @@ -1123,7 +1064,7 @@ static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, RangeInit(&rangeStruct, closure.base, AddrAdd(closure.base, size)); else RangeInit(&rangeStruct, AddrSub(closure.limit, size), closure.limit); - res = cbsDeleteFromTree(&oldRangeStruct, land, &rangeStruct); + res = cbsDelete(&oldRangeStruct, land, &rangeStruct); if (res == ResOK) { /* enough memory to split block */ RangeCopy(rangeReturn, &rangeStruct); RangeCopy(oldRangeReturn, &oldRangeStruct); @@ -1131,7 +1072,6 @@ static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, } else res = ResFAIL; - cbsLeave(cbs); return res; } @@ -1158,7 +1098,6 @@ static Res cbsDescribe(Land land, mps_lib_FILE *stream) res = WriteF(stream, "CBS $P {\n", (WriteFP)cbs, " blockPool: $P\n", (WriteFP)cbsBlockPool(cbs), - " inCBS: $U\n", (WriteFU)cbs->inCBS, " ownPool: $U\n", (WriteFU)cbs->ownPool, " treeSize: $U\n", (WriteFU)cbs->treeSize, NULL); @@ -1187,6 +1126,7 @@ DEFINE_LAND_CLASS(CBSLandClass, class) class->size = sizeof(CBSStruct); class->init = cbsInit; class->finish = cbsFinish; + class->sizeMethod = cbsSize; class->insert = cbsInsert; class->delete = cbsDelete; class->iterate = cbsIterate; diff --git a/mps/code/failover.c b/mps/code/failover.c index 3f633acd34d..e8a0dbc7dae 100644 --- a/mps/code/failover.c +++ b/mps/code/failover.c @@ -70,6 +70,18 @@ static void failoverFinish(Land land) } +static Size failoverSize(Land land) +{ + Failover fo; + + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + + return LandSize(fo->primary) + LandSize(fo->secondary); +} + + static Res failoverInsert(Range rangeReturn, Land land, Range range) { Failover fo; @@ -129,12 +141,18 @@ static Res failoverDelete(Range rangeReturn, Land land, Range range) AVER(RangesEqual(&oldRange, &dummyRange)); RangeInit(&left, RangeBase(&oldRange), RangeBase(range)); if (!RangeIsEmpty(&left)) { - res = LandInsert(&dummyRange, land, &left); + /* Don't call LandInsert(..., land, ...) here: that would be + * re-entrant and fail the landEnter check. */ + res = LandInsert(&dummyRange, fo->primary, &left); + if (res != ResOK && res != ResFAIL) + res = LandInsert(&dummyRange, fo->secondary, &left); AVER(res == ResOK); } RangeInit(&right, RangeLimit(range), RangeLimit(&oldRange)); if (!RangeIsEmpty(&right)) { - res = LandInsert(&dummyRange, land, &right); + res = LandInsert(&dummyRange, fo->primary, &right); + if (res != ResOK && res != ResFAIL) + res = LandInsert(&dummyRange, fo->secondary, &right); AVER(res == ResOK); } } @@ -266,6 +284,7 @@ DEFINE_LAND_CLASS(FailoverLandClass, class) class->size = sizeof(FailoverStruct); class->init = failoverInit; class->finish = failoverFinish; + class->sizeMethod = failoverSize; class->insert = failoverInsert; class->delete = failoverDelete; class->iterate = failoverIterate; diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 5b586a82e6a..13f83bd4aad 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -168,8 +168,11 @@ Bool FreelistCheck(Freelist fl) land = &fl->landStruct; CHECKD(Land, land); /* See */ - CHECKL(AlignIsAligned(LandAlignment(land), freelistMinimumAlignment)); + CHECKL(AlignIsAligned(freelistAlignment(fl), freelistMinimumAlignment)); CHECKL((fl->list == NULL) == (fl->listSize == 0)); + CHECKL((fl->list == NULL) == (fl->size == 0)); + CHECKL(SizeIsAligned(fl->size, freelistAlignment(fl))); + return TRUE; } @@ -192,6 +195,7 @@ static Res freelistInit(Land land, ArgList args) fl = freelistOfLand(land); fl->list = NULL; fl->listSize = 0; + fl->size = 0; fl->sig = FreelistSig; AVERT(Freelist, fl); @@ -211,6 +215,17 @@ static void freelistFinish(Land land) } +static Size freelistSize(Land land) +{ + Freelist fl; + + AVERT(Land, land); + fl = freelistOfLand(land); + AVERT(Freelist, fl); + return fl->size; +} + + /* freelistBlockSetPrevNext -- update list of blocks * * If prev and next are both NULL, make the block list empty. @@ -303,6 +318,7 @@ static Res freelistInsert(Range rangeReturn, Land land, Range range) freelistBlockSetPrevNext(fl, prev, new, +1); } + fl->size += RangeSize(range); RangeInit(rangeReturn, base, limit); return ResOK; } @@ -360,6 +376,8 @@ static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl, freelistBlockSetPrevNext(fl, block, new, +1); } + AVER(fl->size >= RangeSize(range)); + fl->size -= RangeSize(range); RangeInit(rangeReturn, blockBase, blockLimit); } @@ -426,7 +444,10 @@ static void freelistIterate(Land land, LandVisitor visitor, cont = (*visitor)(&delete, land, &range, closureP, closureS); next = FreelistBlockNext(cur); if (delete) { + Size size = FreelistBlockSize(fl, cur); freelistBlockSetPrevNext(fl, prev, next, -1); + AVER(fl->size >= size); + fl->size -= size; } else { prev = cur; } @@ -726,6 +747,7 @@ DEFINE_LAND_CLASS(FreelistLandClass, class) class->size = sizeof(FreelistStruct); class->init = freelistInit; class->finish = freelistFinish; + class->sizeMethod = freelistSize; class->insert = freelistInsert; class->delete = freelistDelete; class->iterate = freelistIterate; diff --git a/mps/code/land.c b/mps/code/land.c index 6c59cb193a6..9ff8257151c 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -26,16 +26,41 @@ Bool FindDeleteCheck(FindDelete findDelete) } +/* landEnter, landLeave -- Avoid re-entrance + * + * .enter-leave: The visitor function passed to LandIterate is not + * allowed to call methods of that land. These functions enforce this. + * + * .enter-leave.simple: Some simple queries are fine to call from + * visitor functions. These are marked with the tag of this comment. + */ + +static void landEnter(Land land) +{ + /* Don't need to check as always called from interface function. */ + AVER(!land->inLand); + land->inLand = TRUE; + return; +} + +static void landLeave(Land land) +{ + /* Don't need to check as always called from interface function. */ + AVER(land->inLand); + land->inLand = FALSE; + return; +} + + /* LandCheck -- check land */ Bool LandCheck(Land land) { + /* .enter-leave.simple */ CHECKS(Land, land); CHECKD(LandClass, land->class); CHECKU(Arena, land->arena); CHECKL(AlignCheck(land->alignment)); - CHECKL(SizeIsAligned(land->size, land->alignment)); - /* too expensive to check land->size against contents */ return TRUE; } @@ -53,7 +78,7 @@ Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *own AVERT(LandClass, class); AVERT(Align, alignment); - land->size = 0; + land->inLand = TRUE; land->alignment = alignment; land->arena = arena; land->class = class; @@ -66,6 +91,7 @@ Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *own goto failInit; EVENT2(LandInit, land, owner); + landLeave(land); return ResOK; failInit: @@ -136,6 +162,7 @@ void LandDestroy(Land land) void LandFinish(Land land) { AVERT(Land, land); + landEnter(land); (*land->class->finish)(land); @@ -143,6 +170,20 @@ void LandFinish(Land land) } +/* LandSize -- return the total size of ranges in land + * + * See + */ + +Size LandSize(Land land) +{ + /* .enter-leave.simple */ + AVERT(Land, land); + + return (*land->class->sizeMethod)(land); +} + + /* LandInsert -- insert range of addresses into land * * See @@ -151,19 +192,16 @@ void LandFinish(Land land) Res LandInsert(Range rangeReturn, Land land, Range range) { Res res; - Size size; AVER(rangeReturn != NULL); AVERT(Land, land); AVERT(Range, range); AVER(RangeIsAligned(range, land->alignment)); + landEnter(land); - /* rangeReturn is allowed to alias with range, so take size first. - * See */ - size = RangeSize(range); res = (*land->class->insert)(rangeReturn, land, range); - if (res == ResOK) - land->size += size; + + landLeave(land); return res; } @@ -176,20 +214,16 @@ Res LandInsert(Range rangeReturn, Land land, Range range) Res LandDelete(Range rangeReturn, Land land, Range range) { Res res; - Size size; AVER(rangeReturn != NULL); AVERT(Land, land); AVERT(Range, range); AVER(RangeIsAligned(range, land->alignment)); + landEnter(land); - /* rangeReturn is allowed to alias with range, so take size first. - * See */ - size = RangeSize(range); - AVER(land->size >= size); res = (*land->class->delete)(rangeReturn, land, range); - if (res == ResOK) - land->size -= size; + + landLeave(land); return res; } @@ -203,8 +237,11 @@ void LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) { AVERT(Land, land); AVER(FUNCHECK(visitor)); + landEnter(land); (*land->class->iterate)(land, visitor, closureP, closureS); + + landLeave(land); } @@ -222,14 +259,12 @@ Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size AVERT(Land, land); AVER(SizeIsAligned(size, land->alignment)); AVER(FindDeleteCheck(findDelete)); + landEnter(land); res = (*land->class->findFirst)(rangeReturn, oldRangeReturn, land, size, findDelete); - if (res && findDelete != FindDeleteNONE) { - AVER(RangeIsAligned(rangeReturn, land->alignment)); - AVER(land->size >= RangeSize(rangeReturn)); - land->size -= RangeSize(rangeReturn); - } + + landLeave(land); return res; } @@ -248,14 +283,12 @@ Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, AVERT(Land, land); AVER(SizeIsAligned(size, land->alignment)); AVER(FindDeleteCheck(findDelete)); + landEnter(land); res = (*land->class->findLast)(rangeReturn, oldRangeReturn, land, size, findDelete); - if (res && findDelete != FindDeleteNONE) { - AVER(RangeIsAligned(rangeReturn, land->alignment)); - AVER(land->size >= RangeSize(rangeReturn)); - land->size -= RangeSize(rangeReturn); - } + + landLeave(land); return res; } @@ -274,14 +307,12 @@ Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size si AVERT(Land, land); AVER(SizeIsAligned(size, land->alignment)); AVER(FindDeleteCheck(findDelete)); + landEnter(land); res = (*land->class->findLargest)(rangeReturn, oldRangeReturn, land, size, findDelete); - if (res && findDelete != FindDeleteNONE) { - AVER(RangeIsAligned(rangeReturn, land->alignment)); - AVER(land->size >= RangeSize(rangeReturn)); - land->size -= RangeSize(rangeReturn); - } + + landLeave(land); return res; } @@ -301,14 +332,12 @@ Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size siz AVER(SizeIsAligned(size, land->alignment)); /* AVER(ZoneSet, zoneSet); */ AVERT(Bool, high); + landEnter(land); res = (*land->class->findInZones)(rangeReturn, oldRangeReturn, land, size, zoneSet, high); - if (res == ResOK) { - AVER(RangeIsAligned(rangeReturn, land->alignment)); - AVER(land->size >= RangeSize(rangeReturn)); - land->size -= RangeSize(rangeReturn); - } + + landLeave(land); return res; } @@ -331,7 +360,7 @@ Res LandDescribe(Land land, mps_lib_FILE *stream) " (\"$S\")\n", land->class->name, " arena $P\n", (WriteFP)land->arena, " align $U\n", (WriteFU)land->alignment, - " align $U\n", (WriteFU)land->size, + " inLand: $U\n", (WriteFU)land->inLand, NULL); if (res != ResOK) return res; @@ -424,6 +453,40 @@ static void landTrivFinish(Land land) NOOP; } +static Size landNoSize(Land land) +{ + UNUSED(land); + NOTREACHED; + return 0; +} + +/* LandSlowSize -- generic size method but slow */ + +static Bool landSizeVisitor(Bool *deleteReturn, Land land, Range range, + void *closureP, Size closureS) +{ + Size *size; + + AVER(deleteReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + AVER(closureP != NULL); + UNUSED(closureS); + + size = closureP; + *size += RangeSize(range); + *deleteReturn = FALSE; + + return TRUE; +} + +Size LandSlowSize(Land land) +{ + Size size = 0; + LandIterate(land, landSizeVisitor, &size, 0); + return size; +} + static Res landNoInsert(Range rangeReturn, Land land, Range range) { AVER(rangeReturn != NULL); @@ -486,6 +549,7 @@ DEFINE_CLASS(LandClass, class) class->name = "LAND"; class->size = sizeof(LandStruct); class->init = landTrivInit; + class->sizeMethod = landNoSize; class->finish = landTrivFinish; class->insert = landNoInsert; class->delete = landNoDelete; diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 406aaf7c107..070d749bb80 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -1002,8 +1002,7 @@ extern Size VMMapped(VM vm); extern Bool LandCheck(Land land); #define LandArena(land) ((land)->arena) #define LandAlignment(land) ((land)->alignment) -#define LandSize(land) ((land)->size) - +extern Size LandSize(Land land); extern Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args); extern Res LandCreate(Land *landReturn, Arena arena, LandClass class, Align alignment, void *owner, ArgList args); extern void LandDestroy(Land land); @@ -1018,6 +1017,7 @@ extern Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, S extern Res LandDescribe(Land land, mps_lib_FILE *stream); extern void LandFlush(Land dest, Land src); +extern Size LandSlowSize(Land land); extern Bool LandClassCheck(LandClass class); extern LandClass LandClassGet(void); #define LAND_SUPERCLASS(className) ((LandClass)SUPERCLASS(className)) diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 7f355ed4ec8..8ce4d14f01a 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -615,6 +615,7 @@ typedef struct LandClassStruct { ProtocolClassStruct protocol; const char *name; /* class name string */ size_t size; /* size of outer structure */ + LandSizeMethod sizeMethod; /* total size of ranges in land */ LandInitMethod init; /* initialize the land */ LandFinishMethod finish; /* finish the land */ LandInsertMethod insert; /* insert a range into the land */ @@ -641,7 +642,7 @@ typedef struct LandStruct { LandClass class; /* land class structure */ Arena arena; /* owning arena */ Align alignment; /* alignment of addresses */ - Size size; /* total size of ranges in land */ + Bool inLand; /* prevent reentrance */ } LandStruct; @@ -661,8 +662,8 @@ typedef struct CBSStruct { STATISTIC_DECL(Count treeSize); Pool blockPool; /* pool that manages blocks */ Size blockStructSize; /* size of block structure */ - Bool inCBS; /* prevent reentrance */ Bool ownPool; /* did we create blockPool? */ + Size size; /* total size of ranges in CBS */ /* meters for sizes of search structures at each op */ METER_DECL(treeSearch); Sig sig; /* .class.end-sig */ @@ -703,6 +704,7 @@ typedef struct FreelistStruct { LandStruct landStruct; /* superclass fields come first */ FreelistBlock list; /* first block in list or NULL if empty */ Count listSize; /* number of blocks in list */ + Size size; /* total size of ranges in list */ Sig sig; /* .class.end-sig */ } FreelistStruct; diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index d8eebb7bcd6..04f73a7e42d 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -268,6 +268,7 @@ typedef struct TraceMessageStruct *TraceMessage; /* trace end */ typedef Res (*LandInitMethod)(Land land, ArgList args); typedef void (*LandFinishMethod)(Land land); +typedef Size (*LandSizeMethod)(Land land); typedef Res (*LandInsertMethod)(Range rangeReturn, Land land, Range range); typedef Res (*LandDeleteMethod)(Range rangeReturn, Land land, Range range); typedef Bool (*LandVisitor)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS); diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index 73bb4ff19eb..4fb5c5ce724 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -141,6 +141,7 @@ static void MVFFFreeSegs(MVFF mvff, Range range) * that needs to be read in order to update the Freelist. */ SegFree(seg); + AVER(mvff->total >= RangeSize(&delRange)); mvff->total -= RangeSize(&delRange); } @@ -502,7 +503,8 @@ static Res MVFFInit(Pool pool, ArgList args) mvff->total = 0; - res = LandInit(FreelistOfMVFF(mvff), FreelistLandClassGet(), arena, align, mvff, mps_args_none); + res = LandInit(FreelistOfMVFF(mvff), FreelistLandClassGet(), arena, align, + mvff, mps_args_none); if (res != ResOK) goto failFreelistInit; @@ -514,7 +516,8 @@ static Res MVFFInit(Pool pool, ArgList args) MPS_ARGS_BEGIN(foArgs) { MPS_ARGS_ADD(foArgs, FailoverPrimary, CBSOfMVFF(mvff)); MPS_ARGS_ADD(foArgs, FailoverSecondary, FreelistOfMVFF(mvff)); - res = LandInit(FailoverOfMVFF(mvff), FailoverLandClassGet(), arena, align, mvff, foArgs); + res = LandInit(FailoverOfMVFF(mvff), FailoverLandClassGet(), arena, align, + mvff, foArgs); } MPS_ARGS_END(foArgs); if (res != ResOK) goto failFailoverInit; @@ -541,7 +544,6 @@ static void MVFFFinish(Pool pool) { MVFF mvff; Arena arena; - Seg seg; Ring ring, node, nextNode; AVERT(Pool, pool); @@ -550,14 +552,17 @@ static void MVFFFinish(Pool pool) 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); } - /* 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. */ + AVER(mvff->total == 0); arena = PoolArena(pool); ControlFree(arena, mvff->segPref, sizeof(SegPrefStruct)); @@ -719,11 +724,11 @@ static Bool MVFFCheck(MVFF mvff) 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 >= LandSize(FailoverOfMVFF(mvff))); CHECKL(SizeIsAligned(mvff->total, ArenaAlign(PoolArena(MVFF2Pool(mvff))))); CHECKD(CBS, &mvff->cbsStruct); CHECKD(Freelist, &mvff->flStruct); CHECKD(Failover, &mvff->foStruct); + CHECKL(mvff->total >= LandSize(FailoverOfMVFF(mvff))); CHECKL(BoolCheck(mvff->slotHigh)); CHECKL(BoolCheck(mvff->firstFit)); return TRUE; diff --git a/mps/design/land.txt b/mps/design/land.txt index 3a4b81abc08..11f192fa6ff 100644 --- a/mps/design/land.txt +++ b/mps/design/land.txt @@ -117,6 +117,11 @@ finish the land structure, and then frees its memory. _`.function.finish`: ``LandFinish()`` finishes the land structure and discards any other resources associated with the land. +``void LandSize(Land land)`` + +_`.function.size`: ``LandSize()`` returns the total size of the ranges +stored in the land. + ``Res LandInsert(Range rangeReturn, Land land, Range range)`` _`.function.insert`: If any part of ``range`` is already in the From 4b49fc7d579cac77ca8a8fdb1a5b2b19f79289ca Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 15 Apr 2014 17:52:14 +0100 Subject: [PATCH 21/43] 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 --- mps/code/poolmvff.c | 275 +++++++++++++++++++--------------------- mps/design/poolmvff.txt | 26 ++-- 2 files changed, 143 insertions(+), 158 deletions(-) diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index 4fb5c5ce724..365f4f02595 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -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: * @@ -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 */ /* ). */ 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 */ - 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 . */ 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 @@ static Res MVFFInit(Pool pool, ArgList args) /* 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 */ @@ -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); } diff --git a/mps/design/poolmvff.txt b/mps/design/poolmvff.txt index c842fe03a6c..b46cace73ae 100644 --- a/mps/design/poolmvff.txt +++ b/mps/design/poolmvff.txt @@ -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/ From b3a3b0b8fc21d72ca4342c7befa8a9cfb7b4e3c1 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 16 Apr 2014 10:24:26 +0100 Subject: [PATCH 22/43] Move cbsfindinzones so that diff is cleaner. Copied from Perforce Change: 185578 ServerID: perforce.ravenbrook.com --- mps/code/cbs.c | 139 ++++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 70 deletions(-) diff --git a/mps/code/cbs.c b/mps/code/cbs.c index 177388bc0cb..8531fa0673b 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -925,6 +925,75 @@ static Bool cbsTestTreeInZones(SplayTree splay, Tree tree, && ZoneSetInter(zonedBlock->zones, closure->zoneSet) != ZoneSetEMPTY; } +static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, + ZoneSet zoneSet, Bool high) +{ + CBS cbs; + Tree tree; + cbsTestNodeInZonesClosureStruct closure; + Res res; + LandFindMethod landFind; + SplayFindMethod splayFind; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + cbs = cbsOfLand(land); + AVERT(CBS, cbs); + AVER(IsLandSubclass(cbsLand(cbs), CBSZonedLandClass)); + /* AVERT(ZoneSet, zoneSet); */ + AVER(BoolCheck(high)); + + landFind = high ? cbsFindLast : cbsFindFirst; + splayFind = high ? SplayFindLast : SplayFindFirst; + + if (zoneSet == ZoneSetEMPTY) + return ResFAIL; + if (zoneSet == ZoneSetUNIV) { + FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; + if ((*landFind)(rangeReturn, oldRangeReturn, land, size, fd)) + return ResOK; + else + return ResFAIL; + } + if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) + return ResFAIL; + + /* It would be nice if there were a neat way to eliminate all runs of + zones in zoneSet too small for size.*/ + + closure.arena = LandArena(land); + closure.zoneSet = zoneSet; + closure.size = size; + closure.high = high; + if (splayFind(&tree, cbsSplay(cbs), + cbsTestNodeInZones, + cbsTestTreeInZones, + &closure, sizeof(closure))) { + CBSBlock block = cbsBlockOfTree(tree); + RangeStruct rangeStruct, oldRangeStruct; + + AVER(CBSBlockBase(block) <= closure.base); + AVER(AddrOffset(closure.base, closure.limit) >= size); + AVER(ZoneSetSub(ZoneSetOfRange(LandArena(land), closure.base, closure.limit), zoneSet)); + AVER(closure.limit <= CBSBlockLimit(block)); + + if (!high) + RangeInit(&rangeStruct, closure.base, AddrAdd(closure.base, size)); + else + RangeInit(&rangeStruct, AddrSub(closure.limit, size), closure.limit); + res = cbsDelete(&oldRangeStruct, land, &rangeStruct); + if (res == ResOK) { /* enough memory to split block */ + RangeCopy(rangeReturn, &rangeStruct); + RangeCopy(oldRangeReturn, &oldRangeStruct); + } + } else + res = ResFAIL; + + return res; +} + /* cbsFindLast -- find the last block of at least the given size */ @@ -1006,76 +1075,6 @@ static Bool cbsFindLargest(Range rangeReturn, Range oldRangeReturn, } -static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, - Land land, Size size, - ZoneSet zoneSet, Bool high) -{ - CBS cbs; - Tree tree; - cbsTestNodeInZonesClosureStruct closure; - Res res; - LandFindMethod landFind; - SplayFindMethod splayFind; - - AVER(rangeReturn != NULL); - AVER(oldRangeReturn != NULL); - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); - AVER(IsLandSubclass(cbsLand(cbs), CBSZonedLandClass)); - /* AVERT(ZoneSet, zoneSet); */ - AVER(BoolCheck(high)); - - landFind = high ? cbsFindLast : cbsFindFirst; - splayFind = high ? SplayFindLast : SplayFindFirst; - - if (zoneSet == ZoneSetEMPTY) - return ResFAIL; - if (zoneSet == ZoneSetUNIV) { - FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; - if ((*landFind)(rangeReturn, oldRangeReturn, land, size, fd)) - return ResOK; - else - return ResFAIL; - } - if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) - return ResFAIL; - - /* It would be nice if there were a neat way to eliminate all runs of - zones in zoneSet too small for size.*/ - - closure.arena = LandArena(land); - closure.zoneSet = zoneSet; - closure.size = size; - closure.high = high; - if (splayFind(&tree, cbsSplay(cbs), - cbsTestNodeInZones, - cbsTestTreeInZones, - &closure, sizeof(closure))) { - CBSBlock block = cbsBlockOfTree(tree); - RangeStruct rangeStruct, oldRangeStruct; - - AVER(CBSBlockBase(block) <= closure.base); - AVER(AddrOffset(closure.base, closure.limit) >= size); - AVER(ZoneSetSub(ZoneSetOfRange(LandArena(land), closure.base, closure.limit), zoneSet)); - AVER(closure.limit <= CBSBlockLimit(block)); - - if (!high) - RangeInit(&rangeStruct, closure.base, AddrAdd(closure.base, size)); - else - RangeInit(&rangeStruct, AddrSub(closure.limit, size), closure.limit); - res = cbsDelete(&oldRangeStruct, land, &rangeStruct); - if (res == ResOK) { /* enough memory to split block */ - RangeCopy(rangeReturn, &rangeStruct); - RangeCopy(oldRangeReturn, &oldRangeStruct); - } - } else - res = ResFAIL; - - return res; -} - - /* cbsDescribe -- describe a CBS * * See . From 29035a7f67597477faa94791489797dba831e94d Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 16 Apr 2014 11:19:55 +0100 Subject: [PATCH 23/43] Put cbsfindinzones back where it was (moving it broke the build). Copied from Perforce Change: 185584 ServerID: perforce.ravenbrook.com --- mps/code/cbs.c | 139 +++++++++++++++++++++++++------------------------ 1 file changed, 70 insertions(+), 69 deletions(-) diff --git a/mps/code/cbs.c b/mps/code/cbs.c index 8531fa0673b..177388bc0cb 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -925,75 +925,6 @@ static Bool cbsTestTreeInZones(SplayTree splay, Tree tree, && ZoneSetInter(zonedBlock->zones, closure->zoneSet) != ZoneSetEMPTY; } -static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, - Land land, Size size, - ZoneSet zoneSet, Bool high) -{ - CBS cbs; - Tree tree; - cbsTestNodeInZonesClosureStruct closure; - Res res; - LandFindMethod landFind; - SplayFindMethod splayFind; - - AVER(rangeReturn != NULL); - AVER(oldRangeReturn != NULL); - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); - AVER(IsLandSubclass(cbsLand(cbs), CBSZonedLandClass)); - /* AVERT(ZoneSet, zoneSet); */ - AVER(BoolCheck(high)); - - landFind = high ? cbsFindLast : cbsFindFirst; - splayFind = high ? SplayFindLast : SplayFindFirst; - - if (zoneSet == ZoneSetEMPTY) - return ResFAIL; - if (zoneSet == ZoneSetUNIV) { - FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; - if ((*landFind)(rangeReturn, oldRangeReturn, land, size, fd)) - return ResOK; - else - return ResFAIL; - } - if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) - return ResFAIL; - - /* It would be nice if there were a neat way to eliminate all runs of - zones in zoneSet too small for size.*/ - - closure.arena = LandArena(land); - closure.zoneSet = zoneSet; - closure.size = size; - closure.high = high; - if (splayFind(&tree, cbsSplay(cbs), - cbsTestNodeInZones, - cbsTestTreeInZones, - &closure, sizeof(closure))) { - CBSBlock block = cbsBlockOfTree(tree); - RangeStruct rangeStruct, oldRangeStruct; - - AVER(CBSBlockBase(block) <= closure.base); - AVER(AddrOffset(closure.base, closure.limit) >= size); - AVER(ZoneSetSub(ZoneSetOfRange(LandArena(land), closure.base, closure.limit), zoneSet)); - AVER(closure.limit <= CBSBlockLimit(block)); - - if (!high) - RangeInit(&rangeStruct, closure.base, AddrAdd(closure.base, size)); - else - RangeInit(&rangeStruct, AddrSub(closure.limit, size), closure.limit); - res = cbsDelete(&oldRangeStruct, land, &rangeStruct); - if (res == ResOK) { /* enough memory to split block */ - RangeCopy(rangeReturn, &rangeStruct); - RangeCopy(oldRangeReturn, &oldRangeStruct); - } - } else - res = ResFAIL; - - return res; -} - /* cbsFindLast -- find the last block of at least the given size */ @@ -1075,6 +1006,76 @@ static Bool cbsFindLargest(Range rangeReturn, Range oldRangeReturn, } +static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, + ZoneSet zoneSet, Bool high) +{ + CBS cbs; + Tree tree; + cbsTestNodeInZonesClosureStruct closure; + Res res; + LandFindMethod landFind; + SplayFindMethod splayFind; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + cbs = cbsOfLand(land); + AVERT(CBS, cbs); + AVER(IsLandSubclass(cbsLand(cbs), CBSZonedLandClass)); + /* AVERT(ZoneSet, zoneSet); */ + AVER(BoolCheck(high)); + + landFind = high ? cbsFindLast : cbsFindFirst; + splayFind = high ? SplayFindLast : SplayFindFirst; + + if (zoneSet == ZoneSetEMPTY) + return ResFAIL; + if (zoneSet == ZoneSetUNIV) { + FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; + if ((*landFind)(rangeReturn, oldRangeReturn, land, size, fd)) + return ResOK; + else + return ResFAIL; + } + if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) + return ResFAIL; + + /* It would be nice if there were a neat way to eliminate all runs of + zones in zoneSet too small for size.*/ + + closure.arena = LandArena(land); + closure.zoneSet = zoneSet; + closure.size = size; + closure.high = high; + if (splayFind(&tree, cbsSplay(cbs), + cbsTestNodeInZones, + cbsTestTreeInZones, + &closure, sizeof(closure))) { + CBSBlock block = cbsBlockOfTree(tree); + RangeStruct rangeStruct, oldRangeStruct; + + AVER(CBSBlockBase(block) <= closure.base); + AVER(AddrOffset(closure.base, closure.limit) >= size); + AVER(ZoneSetSub(ZoneSetOfRange(LandArena(land), closure.base, closure.limit), zoneSet)); + AVER(closure.limit <= CBSBlockLimit(block)); + + if (!high) + RangeInit(&rangeStruct, closure.base, AddrAdd(closure.base, size)); + else + RangeInit(&rangeStruct, AddrSub(closure.limit, size), closure.limit); + res = cbsDelete(&oldRangeStruct, land, &rangeStruct); + if (res == ResOK) { /* enough memory to split block */ + RangeCopy(rangeReturn, &rangeStruct); + RangeCopy(oldRangeReturn, &oldRangeStruct); + } + } else + res = ResFAIL; + + return res; +} + + /* cbsDescribe -- describe a CBS * * See . From de8983d66ef28ae0b07d484cecf2487bf00cec33 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 17 Apr 2014 00:11:01 +0100 Subject: [PATCH 24/43] Fix typos. Copied from Perforce Change: 185616 ServerID: perforce.ravenbrook.com --- mps/design/cbs.txt | 3 +-- mps/design/freelist.txt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt index 7ed38d586a1..c0661f2f792 100644 --- a/mps/design/cbs.txt +++ b/mps/design/cbs.txt @@ -121,7 +121,7 @@ _`.limit.find`: ``CBSLandClass`` does not support the generic functions (the subclasses do support these operations). _`.limit.zones`: ``CBSLandClass`` and ``CBSFastLandClass`` do not -support the ``LandFindInZones()`` generic function (the suclass +support the ``LandFindInZones()`` generic function (the subclass ``CBSZonedLandClass`` does support this operation). _`.limit.iterate`: CBS does not support visitors setting @@ -233,7 +233,6 @@ the size of that area. [Four words per two grains.] The CBS structure is thus suitable only for managing large enough ranges. - Document History ---------------- diff --git a/mps/design/freelist.txt b/mps/design/freelist.txt index 344d6e3b34e..f26fd37a891 100644 --- a/mps/design/freelist.txt +++ b/mps/design/freelist.txt @@ -100,7 +100,7 @@ an address-ordered singly linked free list. (As in traditional _`.impl.block`: If the free address range is large enough to contain an inline block descriptor consisting of two pointers, then the two pointers stored are to the next free range in address order (or -``NULL`` if there are no more ranges), and to the limit of current +``NULL`` if there are no more ranges), and to the limit of the current free address range, in that order. _`.impl.grain`: Otherwise, the free address range must be large enough From 3ea6e3af7de87a3a278f061f7228f1bd9e619472 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 17 Apr 2014 00:19:45 +0100 Subject: [PATCH 25/43] Use freelistend instead of null as the special value. Copied from Perforce Change: 185617 ServerID: perforce.ravenbrook.com --- mps/code/freelist.c | 96 +++++++++++++++++++++++------------------ mps/design/freelist.txt | 11 ++--- 2 files changed, 61 insertions(+), 46 deletions(-) diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 13f83bd4aad..171493544e3 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -29,6 +29,18 @@ typedef union FreelistBlockUnion { } FreelistBlockUnion; +/* freelistEND -- the end of a list + * + * The end of a list should not be represented with NULL, as this is + * ambiguous. However, freelistEND in fact a null pointer for + * performance. To check whether you have it right, try temporarily + * defining freelistEND as ((FreelistBlock)2) or similar (it must be + * an even number because of the use of a tag). + */ + +#define freelistEND ((FreelistBlock)0) + + /* freelistMinimumAlignment -- the minimum allowed alignment for the * address ranges in a free list: see */ @@ -85,7 +97,7 @@ static Bool FreelistBlockCheck(FreelistBlock block) { CHECKL(block != NULL); /* block list is address-ordered */ - CHECKL(FreelistTagReset(block->small.next) == NULL + CHECKL(FreelistTagReset(block->small.next) == freelistEND || block < FreelistTagReset(block->small.next)); CHECKL(FreelistBlockIsSmall(block) || (Addr)block < block->large.limit); @@ -93,8 +105,8 @@ static Bool FreelistBlockCheck(FreelistBlock block) } -/* FreelistBlockNext -- return the next block in the list, or NULL if - * there are no more blocks. +/* FreelistBlockNext -- return the next block in the list, or + * freelistEND if there are no more blocks. */ static FreelistBlock FreelistBlockNext(FreelistBlock block) { @@ -154,7 +166,7 @@ static FreelistBlock FreelistBlockInit(Freelist fl, Addr base, Addr limit) AVER(AddrIsAligned(limit, freelistAlignment(fl))); block = (FreelistBlock)base; - block->small.next = FreelistTagSet(NULL); + block->small.next = FreelistTagSet(freelistEND); FreelistBlockSetLimit(fl, block, limit); AVERT(FreelistBlock, block); return block; @@ -169,8 +181,8 @@ Bool FreelistCheck(Freelist fl) CHECKD(Land, land); /* See */ CHECKL(AlignIsAligned(freelistAlignment(fl), freelistMinimumAlignment)); - CHECKL((fl->list == NULL) == (fl->listSize == 0)); - CHECKL((fl->list == NULL) == (fl->size == 0)); + CHECKL((fl->list == freelistEND) == (fl->listSize == 0)); + CHECKL((fl->list == freelistEND) == (fl->size == 0)); CHECKL(SizeIsAligned(fl->size, freelistAlignment(fl))); return TRUE; @@ -193,7 +205,7 @@ static Res freelistInit(Land land, ArgList args) AVER(AlignIsAligned(LandAlignment(land), freelistMinimumAlignment)); fl = freelistOfLand(land); - fl->list = NULL; + fl->list = freelistEND; fl->listSize = 0; fl->size = 0; @@ -211,7 +223,7 @@ static void freelistFinish(Land land) fl = freelistOfLand(land); AVERT(Freelist, fl); fl->sig = SigInvalid; - fl->list = NULL; + fl->list = freelistEND; } @@ -228,9 +240,9 @@ static Size freelistSize(Land land) /* freelistBlockSetPrevNext -- update list of blocks * - * If prev and next are both NULL, make the block list empty. - * Otherwise, if prev is NULL, make next the first block in the list. - * Otherwise, if next is NULL, make prev the last block in the list. + * If prev and next are both freelistEND, make the block list empty. + * Otherwise, if prev is freelistEND, make next the first block in the list. + * Otherwise, if next is freelistEND, make prev the last block in the list. * Otherwise, make next follow prev in the list. * Update the count of blocks by 'delta'. */ @@ -240,11 +252,13 @@ static void freelistBlockSetPrevNext(Freelist fl, FreelistBlock prev, { AVERT(Freelist, fl); - if (prev) { - AVER(next == NULL || FreelistBlockLimit(fl, prev) < FreelistBlockBase(next)); - FreelistBlockSetNext(prev, next); - } else { + if (prev == freelistEND) { fl->list = next; + } else { + /* Isolated range invariant (design.mps.freelist.impl.invariant). */ + AVER(next == freelistEND + || FreelistBlockLimit(fl, prev) < FreelistBlockBase(next)); + FreelistBlockSetNext(prev, next); } if (delta < 0) { AVER(fl->listSize >= (Count)-delta); @@ -272,15 +286,15 @@ static Res freelistInsert(Range rangeReturn, Land land, Range range) base = RangeBase(range); limit = RangeLimit(range); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { + while (cur != freelistEND) { if (base < FreelistBlockLimit(fl, cur) && FreelistBlockBase(cur) < limit) return ResFAIL; /* range overlaps with cur */ if (limit <= FreelistBlockBase(cur)) break; next = FreelistBlockNext(cur); - if (next) + if (next != freelistEND) /* Isolated range invariant (design.mps.freelist.impl.invariant). */ AVER(FreelistBlockLimit(fl, cur) < FreelistBlockBase(next)); prev = cur; @@ -291,8 +305,8 @@ static Res freelistInsert(Range rangeReturn, Land land, Range range) * coalesces then it does so with prev on the left, and cur on the * right. */ - coalesceLeft = (prev && base == FreelistBlockLimit(fl, prev)); - coalesceRight = (cur && limit == FreelistBlockBase(cur)); + coalesceLeft = (prev != freelistEND && base == FreelistBlockLimit(fl, prev)); + coalesceRight = (cur != freelistEND && limit == FreelistBlockBase(cur)); if (coalesceLeft && coalesceRight) { base = FreelistBlockBase(prev); @@ -328,8 +342,8 @@ static Res freelistInsert(Range rangeReturn, Land land, Range range) * * range must be a subset of block. Update rangeReturn to be the * original range of block and update the block list accordingly: prev - * is on the list just before block, or NULL if block is the first - * block on the list. + * is on the list just before block, or freelistEND if block is the + * first block on the list. */ static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl, @@ -343,7 +357,7 @@ static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl, AVERT(Freelist, fl); AVERT(Range, range); AVER(RangeIsAligned(range, freelistAlignment(fl))); - AVER(prev == NULL || FreelistBlockNext(prev) == block); + AVER(prev == freelistEND || FreelistBlockNext(prev) == block); AVERT(FreelistBlock, block); AVER(FreelistBlockBase(block) <= RangeBase(range)); AVER(RangeLimit(range) <= FreelistBlockLimit(fl, block)); @@ -397,9 +411,9 @@ static Res freelistDelete(Range rangeReturn, Land land, Range range) base = RangeBase(range); limit = RangeLimit(range); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { + while (cur != freelistEND) { Addr blockBase, blockLimit; blockBase = FreelistBlockBase(cur); blockLimit = FreelistBlockLimit(fl, cur); @@ -434,9 +448,9 @@ static void freelistIterate(Land land, LandVisitor visitor, AVERT(Freelist, fl); AVER(FUNCHECK(visitor)); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { + while (cur != freelistEND) { Bool delete = FALSE; RangeStruct range; Bool cont; @@ -465,8 +479,8 @@ static void freelistIterate(Land land, LandVisitor visitor, * instruction in findDelete. Return the range of that chunk in * rangeReturn. Return the original range of the block in * oldRangeReturn. Update the block list accordingly, using prev, - * which is previous in list or NULL if block is the first block in - * the list. + * which is previous in list or freelistEND if block is the first + * block in the list. */ static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn, @@ -482,7 +496,7 @@ static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn, AVERT(Freelist, fl); AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); - AVER(prev == NULL || FreelistBlockNext(prev) == block); + AVER(prev == freelistEND || FreelistBlockNext(prev) == block); AVERT(FreelistBlock, block); AVER(FreelistBlockSize(fl, block) >= size); @@ -534,9 +548,9 @@ static Bool freelistFindFirst(Range rangeReturn, Range oldRangeReturn, AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { + while (cur != freelistEND) { if (FreelistBlockSize(fl, cur) >= size) { freelistFindDeleteFromBlock(rangeReturn, oldRangeReturn, fl, size, findDelete, prev, cur); @@ -557,7 +571,7 @@ static Bool freelistFindLast(Range rangeReturn, Range oldRangeReturn, Freelist fl; Bool found = FALSE; FreelistBlock prev, cur, next; - FreelistBlock foundPrev = NULL, foundCur = NULL; + FreelistBlock foundPrev = freelistEND, foundCur = freelistEND; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); @@ -567,9 +581,9 @@ static Bool freelistFindLast(Range rangeReturn, Range oldRangeReturn, AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { + while (cur != freelistEND) { if (FreelistBlockSize(fl, cur) >= size) { found = TRUE; foundPrev = prev; @@ -594,7 +608,7 @@ static Bool freelistFindLargest(Range rangeReturn, Range oldRangeReturn, Freelist fl; Bool found = FALSE; FreelistBlock prev, cur, next; - FreelistBlock bestPrev = NULL, bestCur = NULL; + FreelistBlock bestPrev = freelistEND, bestCur = freelistEND; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); @@ -603,9 +617,9 @@ static Bool freelistFindLargest(Range rangeReturn, Range oldRangeReturn, AVERT(Freelist, fl); AVERT(FindDelete, findDelete); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { + while (cur != freelistEND) { if (FreelistBlockSize(fl, cur) >= size) { found = TRUE; size = FreelistBlockSize(fl, cur); @@ -634,7 +648,7 @@ static Res freelistFindInZones(Range rangeReturn, Range oldRangeReturn, RangeInZoneSet search; Bool found = FALSE; FreelistBlock prev, cur, next; - FreelistBlock foundPrev = NULL, foundCur = NULL; + FreelistBlock foundPrev = freelistEND, foundCur = freelistEND; RangeStruct foundRange; AVER(FALSE); /* TODO: this code is completely untested! */ @@ -661,9 +675,9 @@ static Res freelistFindInZones(Range rangeReturn, Range oldRangeReturn, if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) return ResFAIL; - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { + while (cur != freelistEND) { Addr base, limit; if ((*search)(&base, &limit, FreelistBlockBase(cur), FreelistBlockLimit(fl, cur), diff --git a/mps/design/freelist.txt b/mps/design/freelist.txt index f26fd37a891..b0654468de1 100644 --- a/mps/design/freelist.txt +++ b/mps/design/freelist.txt @@ -100,12 +100,13 @@ an address-ordered singly linked free list. (As in traditional _`.impl.block`: If the free address range is large enough to contain an inline block descriptor consisting of two pointers, then the two pointers stored are to the next free range in address order (or -``NULL`` if there are no more ranges), and to the limit of the current -free address range, in that order. +``freelistEND`` if there are no more ranges), and to the limit of the +current free address range, in that order. _`.impl.grain`: Otherwise, the free address range must be large enough to contain a single pointer. The pointer stored is to the next free -range in address order, or ``NULL`` if there are no more ranges. +range in address order, or ``freelistEND`` if there are no more +ranges. _`.impl.tag`: Grains and blocks are distinguished by a one-bit tag in the low bit of the first word (the one containing the pointer to the @@ -118,8 +119,8 @@ _`.impl.merge`: When a free address range is added to the free list, it is merged with adjacent ranges so as to maintain `.impl.invariant`_. -_`.impl.rule.break`: The use of ``NULL`` to mark the end of the list -violates the rule that exceptional values should not be used to +_`.impl.rule.break`: The use of ``freelistEND`` to mark the end of the +list violates the rule that exceptional values should not be used to distinguish exeptional situations. This infraction allows the implementation to meet `.req.zero-overhead`_. (There are other ways to do this, such as using another tag to indicate the last block in the From 4252e17f9afe8d7a9015442b0969c0c9c01445d7 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 17 Apr 2014 23:39:45 +0100 Subject: [PATCH 26/43] Fix typo. Copied from Perforce Change: 185645 ServerID: perforce.ravenbrook.com --- mps/design/splay.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps/design/splay.txt b/mps/design/splay.txt index 8e8b02e292c..4290b703ac1 100644 --- a/mps/design/splay.txt +++ b/mps/design/splay.txt @@ -103,7 +103,7 @@ general inferred MPS requirements. _`.req.order`: Must maintain a set of abstract keys which is totally ordered for a comparator. -_`.req.splay`: Common operations must have low amortized cost. +_`.req.fast`: Common operations must have low amortized cost. _`.req.add`: Must be able to add new nodes. This is a common operation. From e811f6c536a1965407208a7064f6a5403a46fd00 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 23 Apr 2014 09:12:20 +0100 Subject: [PATCH 27/43] Branching master to branch/2014-04-23/awl. Copied from Perforce Change: 185751 ServerID: perforce.ravenbrook.com From 37b363fb2705a1cd3dd7a20b32da7a49d7e04cda Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 30 Apr 2014 10:16:28 +0100 Subject: [PATCH 28/43] Branching master to branch/2014-04-30/poolgen. Copied from Perforce Change: 185871 ServerID: perforce.ravenbrook.com From 0eb9ac546fe750cf27cf1ae597933be43f83263d Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 30 Apr 2014 11:18:44 +0100 Subject: [PATCH 29/43] Pool generations now refer directly to their generation (not via a chain and a generation number). Allocation into a generation now via PoolGenAlloc (not ChainAlloc). The "top generation" logic is encapsulated in the function ChainGen. Copied from Perforce Change: 185877 ServerID: perforce.ravenbrook.com --- mps/code/chain.h | 18 +++----- mps/code/eventdef.h | 11 ++--- mps/code/locus.c | 108 +++++++++++++++++++++++--------------------- mps/code/poolamc.c | 48 ++++++++++---------- mps/code/poolams.c | 14 ++---- mps/code/poolams.h | 1 - mps/code/poolawl.c | 9 ++-- mps/code/poollo.c | 20 ++++---- mps/code/segsmss.c | 17 ++++--- mps/code/trace.c | 5 +- 10 files changed, 122 insertions(+), 129 deletions(-) diff --git a/mps/code/chain.h b/mps/code/chain.h index ec5bc228ebf..eb4e9e78df7 100644 --- a/mps/code/chain.h +++ b/mps/code/chain.h @@ -44,9 +44,8 @@ typedef struct PoolGenStruct *PoolGen; typedef struct PoolGenStruct { Sig sig; - Serial nr; /* generation number */ Pool pool; /* pool this belongs to */ - Chain chain; /* chain this belongs to */ + GenDesc gen; /* generation this belongs to */ /* link in ring of all PoolGen's in this GenDesc (locus) */ RingStruct genRing; Size totalSize; /* total size of segs in gen in this pool */ @@ -84,16 +83,13 @@ extern Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace); extern void ChainStartGC(Chain chain, Trace trace); extern void ChainEndGC(Chain chain, Trace trace); extern size_t ChainGens(Chain chain); -extern Res ChainAlloc(Seg *segReturn, Chain chain, Serial genNr, - SegClass class, Size size, Pool pool, - Bool withReservoirPermit, ArgList args); - -extern Bool PoolGenCheck(PoolGen gen); -extern Res PoolGenInit(PoolGen gen, Chain chain, Serial nr, Pool pool); -extern void PoolGenFinish(PoolGen gen); -extern void PoolGenFlip(PoolGen gen); -#define PoolGenNr(gen) ((gen)->nr) +extern GenDesc ChainGen(Chain chain, Index gen); +extern Bool PoolGenCheck(PoolGen pgen); +extern Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool); +extern void PoolGenFinish(PoolGen pgen); +extern Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class, + Size size, Bool withReservoirPermit, ArgList args); #endif /* chain_h */ diff --git a/mps/code/eventdef.h b/mps/code/eventdef.h index 4ce313dcfc8..c4918d38413 100644 --- a/mps/code/eventdef.h +++ b/mps/code/eventdef.h @@ -36,8 +36,8 @@ */ #define EVENT_VERSION_MAJOR ((unsigned)1) -#define EVENT_VERSION_MEDIAN ((unsigned)1) -#define EVENT_VERSION_MINOR ((unsigned)7) +#define EVENT_VERSION_MEDIAN ((unsigned)2) +#define EVENT_VERSION_MINOR ((unsigned)0) /* EVENT_LIST -- list of event types and general properties @@ -722,9 +722,8 @@ PARAM(X, 5, D, mortality) /* mortality of generation */ \ PARAM(X, 6, W, zone) /* zone set of generation */ \ PARAM(X, 7, P, pool) /* pool */ \ - PARAM(X, 8, W, serial) /* pool gen serial number */ \ - PARAM(X, 9, W, totalSize) /* total size of pool gen */ \ - PARAM(X, 10, W, newSizeAtCreate) /* new size of pool gen at trace create */ + PARAM(X, 8, W, totalSize) /* total size of pool gen */ \ + PARAM(X, 9, W, newSizeAtCreate) /* new size of pool gen at trace create */ #define EVENT_TraceCondemnZones_PARAMS(PARAM, X) \ PARAM(X, 0, P, trace) /* the trace */ \ @@ -733,7 +732,7 @@ #define EVENT_ArenaGenZoneAdd_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) /* the arena */ \ - PARAM(X, 1, W, gen) /* the generation number */ \ + PARAM(X, 1, P, gendesc) /* the generation description */ \ PARAM(X, 2, W, zoneSet) /* the new zoneSet */ #define EVENT_ArenaUseFreeZone_PARAMS(PARAM, X) \ diff --git a/mps/code/locus.c b/mps/code/locus.c index 6e6e22128b7..942d3f1f405 100644 --- a/mps/code/locus.c +++ b/mps/code/locus.c @@ -212,7 +212,8 @@ void ChainDestroy(Chain chain) AVERT(Chain, chain); - arena = chain->arena; genCount = chain->genCount; + arena = chain->arena; + genCount = chain->genCount; RingRemove(&chain->chainRing); chain->sig = SigInvalid; for (i = 0; i < genCount; ++i) { @@ -234,55 +235,67 @@ size_t ChainGens(Chain chain) } -/* ChainAlloc -- allocate tracts in a generation */ +/* ChainGen -- return a generation in a chain, or the arena top generation */ -Res ChainAlloc(Seg *segReturn, Chain chain, Serial genNr, SegClass class, - Size size, Pool pool, Bool withReservoirPermit, - ArgList args) +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; +} + + +/* PoolGenAlloc -- allocate a segment in a pool generation */ + +Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class, Size size, + Bool withReservoirPermit, ArgList args) { SegPrefStruct pref; Res res; Seg seg; ZoneSet zones, moreZones; Arena arena; + GenDesc gen; - AVERT(Chain, chain); - AVER(genNr <= chain->genCount); + AVER(segReturn != NULL); + AVERT(PoolGen, pgen); + AVERT(SegClass, class); + AVER(size > 0); + AVERT(Bool, withReservoirPermit); + AVERT(ArgList, args); - arena = chain->arena; - if (genNr < chain->genCount) - zones = chain->gens[genNr].zones; - else - zones = arena->topGen.zones; + arena = PoolArena(pgen->pool); + gen = pgen->gen; + zones = gen->zones; SegPrefInit(&pref); pref.high = FALSE; pref.zones = zones; pref.avoid = ZoneSetBlacklist(arena); - res = SegAlloc(&seg, class, &pref, size, pool, withReservoirPermit, args); + res = SegAlloc(&seg, class, &pref, size, pgen->pool, withReservoirPermit, + args); if (res != ResOK) return res; moreZones = ZoneSetUnion(zones, ZoneSetOfSeg(arena, seg)); + gen->zones = moreZones; if (!ZoneSetSuper(zones, moreZones)) { - /* Tracking the whole zoneset for each generation number gives - * more understandable telemetry than just reporting the added + /* Tracking the whole zoneset for each generation gives more + * understandable telemetry than just reporting the added * zones. */ - EVENT3(ArenaGenZoneAdd, arena, genNr, moreZones); + EVENT3(ArenaGenZoneAdd, arena, gen, moreZones); } - if (genNr < chain->genCount) - chain->gens[genNr].zones = moreZones; - else - chain->arena->topGen.zones = moreZones; - *segReturn = seg; return ResOK; } - /* ChainDeferral -- time until next ephemeral GC for this chain */ double ChainDeferral(Chain chain) @@ -392,55 +405,48 @@ void ChainEndGC(Chain chain, Trace trace) /* PoolGenInit -- initialize a PoolGen */ -Res PoolGenInit(PoolGen gen, Chain chain, Serial nr, Pool pool) +Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool) { - /* Can't check gen, because it's not been initialized. */ - AVER(gen != NULL); - AVERT(Chain, chain); - AVER(nr <= chain->genCount); + /* Can't check pgen, because it's not been initialized. */ + AVER(pgen != NULL); + AVERT(GenDesc, gen); AVERT(Pool, pool); AVER(PoolHasAttr(pool, AttrGC)); - gen->nr = nr; - gen->pool = pool; - gen->chain = chain; - RingInit(&gen->genRing); - gen->totalSize = (Size)0; - gen->newSize = (Size)0; - gen->sig = PoolGenSig; + pgen->pool = pool; + pgen->gen = gen; + RingInit(&pgen->genRing); + pgen->totalSize = (Size)0; + pgen->newSize = (Size)0; + pgen->sig = PoolGenSig; + AVERT(PoolGen, pgen); - if(nr != chain->genCount) { - RingAppend(&chain->gens[nr].locusRing, &gen->genRing); - } else { - /* Dynamic generation is linked to the arena, not the chain. */ - RingAppend(&chain->arena->topGen.locusRing, &gen->genRing); - } - AVERT(PoolGen, gen); + RingAppend(&gen->locusRing, &pgen->genRing); return ResOK; } /* PoolGenFinish -- finish a PoolGen */ -void PoolGenFinish(PoolGen gen) +void PoolGenFinish(PoolGen pgen) { - AVERT(PoolGen, gen); + AVERT(PoolGen, pgen); - gen->sig = SigInvalid; - RingRemove(&gen->genRing); + pgen->sig = SigInvalid; + RingRemove(&pgen->genRing); } /* PoolGenCheck -- check a PoolGen */ -Bool PoolGenCheck(PoolGen gen) +Bool PoolGenCheck(PoolGen pgen) { - CHECKS(PoolGen, gen); + CHECKS(PoolGen, pgen); /* nothing to check about serial */ - CHECKU(Pool, gen->pool); - CHECKU(Chain, gen->chain); - CHECKD_NOSIG(Ring, &gen->genRing); - CHECKL(gen->newSize <= gen->totalSize); + CHECKU(Pool, pgen->pool); + CHECKU(GenDesc, pgen->gen); + CHECKD_NOSIG(Ring, &pgen->genRing); + CHECKL(pgen->newSize <= pgen->totalSize); return TRUE; } diff --git a/mps/code/poolamc.c b/mps/code/poolamc.c index aeb04454efe..6677ddc337c 100644 --- a/mps/code/poolamc.c +++ b/mps/code/poolamc.c @@ -453,7 +453,6 @@ typedef struct AMCStruct { /* */ RankSet rankSet; /* rankSet for entire pool */ RingStruct genRing; /* ring of generations */ Bool gensBooted; /* used during boot (init) */ - Chain chain; /* chain used by this pool */ size_t gens; /* number of generations */ amcGen *gen; /* (pointer to) array of generations */ amcGen nursery; /* the default mutator generation */ @@ -639,12 +638,12 @@ DEFINE_BUFFER_CLASS(amcBufClass, class) /* amcGenCreate -- create a generation */ -static Res amcGenCreate(amcGen *genReturn, AMC amc, Serial genNr) +static Res amcGenCreate(amcGen *genReturn, AMC amc, GenDesc gen) { Arena arena; Buffer buffer; Pool pool; - amcGen gen; + amcGen amcgen; Res res; void *p; @@ -654,25 +653,25 @@ static Res amcGenCreate(amcGen *genReturn, AMC amc, Serial genNr) res = ControlAlloc(&p, arena, sizeof(amcGenStruct), FALSE); if(res != ResOK) goto failControlAlloc; - gen = (amcGen)p; + amcgen = (amcGen)p; res = BufferCreate(&buffer, EnsureamcBufClass(), pool, FALSE, argsNone); if(res != ResOK) goto failBufferCreate; - res = PoolGenInit(&gen->pgen, amc->chain, genNr, pool); + res = PoolGenInit(&amcgen->pgen, gen, pool); if(res != ResOK) goto failGenInit; - RingInit(&gen->amcRing); - gen->segs = 0; - gen->forward = buffer; - gen->sig = amcGenSig; + RingInit(&amcgen->amcRing); + amcgen->segs = 0; + amcgen->forward = buffer; + amcgen->sig = amcGenSig; - AVERT(amcGen, gen); + AVERT(amcGen, amcgen); - RingAppend(&amc->genRing, &gen->amcRing); - EVENT2(AMCGenCreate, amc, gen); - *genReturn = gen; + RingAppend(&amc->genRing, &amcgen->amcRing); + EVENT2(AMCGenCreate, amc, amcgen); + *genReturn = amcgen; return ResOK; failGenInit: @@ -715,8 +714,7 @@ static Res amcGenDescribe(amcGen gen, mps_lib_FILE *stream) return ResFAIL; res = WriteF(stream, - " amcGen $P ($U) {\n", - (WriteFP)gen, (WriteFU)amcGenNr(gen), + " amcGen $P {\n", (WriteFP)gen, " buffer $P\n", gen->forward, " segs $U, totalSize $U, newSize $U\n", (WriteFU)gen->segs, @@ -798,6 +796,7 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) size_t genArraySize; size_t genCount; Bool interior = AMC_INTERIOR_DEFAULT; + Chain chain; ArgStruct arg; /* Suppress a warning about this structure not being used when there @@ -818,14 +817,14 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) ArgRequire(&arg, args, MPS_KEY_FORMAT); pool->format = arg.val.format; if (ArgPick(&arg, args, MPS_KEY_CHAIN)) - amc->chain = arg.val.chain; + chain = arg.val.chain; else - amc->chain = ArenaGlobals(arena)->defaultChain; + chain = ArenaGlobals(arena)->defaultChain; if (ArgPick(&arg, args, MPS_KEY_INTERIOR)) interior = arg.val.b; AVERT(Format, pool->format); - AVERT(Chain, amc->chain); + AVERT(Chain, chain); pool->alignment = pool->format->alignment; amc->rankSet = rankSet; @@ -861,7 +860,7 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) AVERT(AMC, amc); /* Init generations. */ - genCount = ChainGens(amc->chain); + genCount = ChainGens(chain); { void *p; @@ -871,11 +870,10 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) if(res != ResOK) goto failGensAlloc; amc->gen = p; - for(i = 0; i < genCount + 1; ++i) { - res = amcGenCreate(&amc->gen[i], amc, (Serial)i); - if(res != ResOK) { + for (i = 0; i <= genCount; ++i) { + res = amcGenCreate(&amc->gen[i], amc, ChainGen(chain, i)); + if (res != ResOK) goto failGenAlloc; - } } /* Set up forwarding buffers. */ for(i = 0; i < genCount; ++i) { @@ -1017,8 +1015,8 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, alignedSize = SizeAlignUp(size, ArenaAlign(arena)); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD_FIELD(args, amcKeySegGen, p, gen); - res = ChainAlloc(&seg, amc->chain, PoolGenNr(pgen), amcSegClassGet(), - alignedSize, pool, withReservoirPermit, args); + res = PoolGenAlloc(&seg, pgen, amcSegClassGet(), alignedSize, + withReservoirPermit, args); } MPS_ARGS_END(args); if(res != ResOK) return res; diff --git a/mps/code/poolams.c b/mps/code/poolams.c index 65b2dcf754f..17d2312ade4 100644 --- a/mps/code/poolams.c +++ b/mps/code/poolams.c @@ -691,14 +691,14 @@ static Res AMSSegCreate(Seg *segReturn, Pool pool, Size size, if (res != ResOK) goto failSize; - res = ChainAlloc(&seg, ams->chain, ams->pgen.nr, (*ams->segClass)(), - prefSize, pool, withReservoirPermit, argsNone); + res = PoolGenAlloc(&seg, &ams->pgen, (*ams->segClass)(), prefSize, + withReservoirPermit, argsNone); if (res != ResOK) { /* try to allocate one that's just large enough */ Size minSize = SizeAlignUp(size, ArenaAlign(arena)); if (minSize == prefSize) goto failSeg; - res = ChainAlloc(&seg, ams->chain, ams->pgen.nr, (*ams->segClass)(), - prefSize, pool, withReservoirPermit, argsNone); + res = PoolGenAlloc(&seg, &ams->pgen, (*ams->segClass)(), prefSize, + withReservoirPermit, argsNone); if (res != ResOK) goto failSeg; } @@ -822,8 +822,7 @@ Res AMSInitInternal(AMS ams, Format format, Chain chain, unsigned gen, pool->alignment = pool->format->alignment; ams->grainShift = SizeLog2(PoolAlignment(pool)); - ams->chain = chain; - res = PoolGenInit(&ams->pgen, ams->chain, gen, pool); + res = PoolGenInit(&ams->pgen, ChainGen(chain, gen), pool); if (res != ResOK) return res; @@ -1666,8 +1665,6 @@ static Res AMSDescribe(Pool pool, mps_lib_FILE *stream) " size $W\n", (WriteFW)ams->size, " grain shift $U\n", (WriteFU)ams->grainShift, - " chain $P\n", - (WriteFP)ams->chain, NULL); if (res != ResOK) return res; @@ -1761,7 +1758,6 @@ Bool AMSCheck(AMS ams) CHECKL(IsSubclassPoly(AMS2Pool(ams)->class, AMSPoolClassGet())); CHECKL(PoolAlignment(AMS2Pool(ams)) == ((Size)1 << ams->grainShift)); CHECKL(PoolAlignment(AMS2Pool(ams)) == AMS2Pool(ams)->format->alignment); - CHECKD(Chain, ams->chain); CHECKD(PoolGen, &ams->pgen); CHECKL(SizeIsAligned(ams->size, ArenaAlign(PoolArena(AMS2Pool(ams))))); CHECKL(FUNCHECK(ams->segSize)); diff --git a/mps/code/poolams.h b/mps/code/poolams.h index 96cec6c6c7b..dd5a85889ed 100644 --- a/mps/code/poolams.h +++ b/mps/code/poolams.h @@ -41,7 +41,6 @@ typedef Res (*AMSSegSizePolicyFunction)(Size *sizeReturn, typedef struct AMSStruct { PoolStruct poolStruct; /* generic pool structure */ Shift grainShift; /* log2 of grain size */ - Chain chain; /* chain used by this pool */ PoolGenStruct pgen; /* generation representing the pool */ Size size; /* total segment size of the pool */ AMSSegSizePolicyFunction segSize; /* SegSize policy */ diff --git a/mps/code/poolawl.c b/mps/code/poolawl.c index cbece0b41fe..46478e95bb9 100644 --- a/mps/code/poolawl.c +++ b/mps/code/poolawl.c @@ -84,7 +84,6 @@ typedef Addr (*FindDependentMethod)(Addr object); typedef struct AWLStruct { PoolStruct poolStruct; Shift alignShift; - Chain chain; /* dummy chain */ PoolGenStruct pgen; /* generation representing the pool */ Size size; /* allocated size in bytes */ Count succAccesses; /* number of successive single accesses */ @@ -472,8 +471,8 @@ static Res AWLSegCreate(AWLSeg *awlsegReturn, return ResMEMORY; MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD_FIELD(args, awlKeySegRankSet, u, rankSet); - res = ChainAlloc(&seg, awl->chain, awl->pgen.nr, AWLSegClassGet(), - size, pool, reservoirPermit, args); + res = PoolGenAlloc(&seg, &awl->pgen, AWLSegClassGet(), size, + reservoirPermit, args); } MPS_ARGS_END(args); if (res != ResOK) return res; @@ -569,9 +568,8 @@ static Res AWLInit(Pool pool, ArgList args) AVERT(Chain, chain); AVER(gen <= ChainGens(chain)); - awl->chain = chain; - res = PoolGenInit(&awl->pgen, chain, gen, pool); + res = PoolGenInit(&awl->pgen, ChainGen(chain, gen), pool); if (res != ResOK) goto failGenInit; @@ -1305,7 +1303,6 @@ static Bool AWLCheck(AWL awl) CHECKD(Pool, &awl->poolStruct); CHECKL(awl->poolStruct.class == AWLPoolClassGet()); CHECKL((Align)1 << awl->alignShift == awl->poolStruct.alignment); - CHECKD(Chain, awl->chain); /* Nothing to check about succAccesses. */ CHECKL(FUNCHECK(awl->findDependent)); /* Don't bother to check stats. */ diff --git a/mps/code/poollo.c b/mps/code/poollo.c index d8f23584fba..8836d2563d0 100644 --- a/mps/code/poollo.c +++ b/mps/code/poollo.c @@ -24,7 +24,6 @@ typedef struct LOStruct *LO; typedef struct LOStruct { PoolStruct poolStruct; /* generic pool structure */ Shift alignShift; /* log_2 of pool alignment */ - Chain chain; /* chain used by this pool */ PoolGenStruct pgen; /* generation representing the pool */ Sig sig; } LOStruct; @@ -291,9 +290,9 @@ static Res loSegCreate(LOSeg *loSegReturn, Pool pool, Size size, lo = PoolPoolLO(pool); AVERT(LO, lo); - res = ChainAlloc(&seg, lo->chain, lo->pgen.nr, EnsureLOSegClass(), - SizeAlignUp(size, ArenaAlign(PoolArena(pool))), - pool, withReservoirPermit, argsNone); + res = PoolGenAlloc(&seg, &lo->pgen, EnsureLOSegClass(), + SizeAlignUp(size, ArenaAlign(PoolArena(pool))), + withReservoirPermit, argsNone); if (res != ResOK) return res; @@ -473,9 +472,11 @@ static Res LOInit(Pool pool, ArgList args) Arena arena; Res res; ArgStruct arg; + Chain chain; unsigned gen = LO_GEN_DEFAULT; AVERT(Pool, pool); + AVERT(ArgList, args); arena = PoolArena(pool); @@ -484,22 +485,22 @@ static Res LOInit(Pool pool, ArgList args) ArgRequire(&arg, args, MPS_KEY_FORMAT); pool->format = arg.val.format; if (ArgPick(&arg, args, MPS_KEY_CHAIN)) - lo->chain = arg.val.chain; + chain = arg.val.chain; else { - lo->chain = ArenaGlobals(arena)->defaultChain; + chain = ArenaGlobals(arena)->defaultChain; gen = 1; /* avoid the nursery of the default chain by default */ } if (ArgPick(&arg, args, MPS_KEY_GEN)) gen = arg.val.u; AVERT(Format, pool->format); - AVERT(Chain, lo->chain); - AVER(gen <= ChainGens(lo->chain)); + AVERT(Chain, chain); + AVER(gen <= ChainGens(chain)); pool->alignment = pool->format->alignment; lo->alignShift = SizeLog2((Size)PoolAlignment(pool)); - res = PoolGenInit(&lo->pgen, lo->chain, gen, pool); + res = PoolGenInit(&lo->pgen, ChainGen(chain, gen), pool); if (res != ResOK) goto failGenInit; @@ -812,7 +813,6 @@ static Bool LOCheck(LO lo) CHECKL(lo->poolStruct.class == EnsureLOPoolClass()); CHECKL(ShiftCheck(lo->alignShift)); CHECKL((Align)1 << lo->alignShift == PoolAlignment(&lo->poolStruct)); - CHECKD(Chain, lo->chain); CHECKD(PoolGen, &lo->pgen); return TRUE; } diff --git a/mps/code/segsmss.c b/mps/code/segsmss.c index 978c352e0dd..e3ed646865e 100644 --- a/mps/code/segsmss.c +++ b/mps/code/segsmss.c @@ -40,7 +40,6 @@ extern PoolClass AMSTPoolClassGet(void); typedef struct AMSTStruct { AMSStruct amsStruct; /* generic AMS structure */ - Chain chain; /* chain to use */ Bool failSegs; /* fail seg splits & merges when true */ Count splits; /* count of successful segment splits */ Count merges; /* count of successful segment merges */ @@ -333,17 +332,23 @@ static Res AMSTInit(Pool pool, ArgList args) Format format; Chain chain; Res res; - static GenParamStruct genParam = { 1024, 0.2 }; + unsigned gen = AMS_GEN_DEFAULT; ArgStruct arg; AVERT(Pool, pool); + AVERT(ArgList, args); + if (ArgPick(&arg, args, MPS_KEY_CHAIN)) + chain = arg.val.chain; + else { + chain = ArenaGlobals(PoolArena(pool))->defaultChain; + gen = 1; /* avoid the nursery of the default chain by default */ + } + if (ArgPick(&arg, args, MPS_KEY_GEN)) + gen = arg.val.u; ArgRequire(&arg, args, MPS_KEY_FORMAT); format = arg.val.format; - res = ChainCreate(&chain, pool->arena, 1, &genParam); - if (res != ResOK) - return res; res = AMSInitInternal(Pool2AMS(pool), format, chain, 0, FALSE); if (res != ResOK) return res; @@ -351,7 +356,6 @@ static Res AMSTInit(Pool pool, ArgList args) ams = Pool2AMS(pool); ams->segSize = AMSTSegSizePolicy; ams->segClass = AMSTSegClassGet; - amst->chain = chain; amst->failSegs = TRUE; amst->splits = 0; amst->merges = 0; @@ -386,7 +390,6 @@ static void AMSTFinish(Pool pool) AMSFinish(pool); amst->sig = SigInvalid; - ChainDestroy(amst->chain); } diff --git a/mps/code/trace.c b/mps/code/trace.c index bd2b871fb11..999d5047a5c 100644 --- a/mps/code/trace.c +++ b/mps/code/trace.c @@ -1593,10 +1593,9 @@ static void TraceStartPoolGen(Chain chain, GenDesc desc, Bool top, Index i) Ring n, nn; RING_FOR(n, &desc->locusRing, nn) { PoolGen gen = RING_ELT(PoolGen, genRing, n); - EVENT11(TraceStartPoolGen, chain, BOOLOF(top), i, desc, + EVENT10(TraceStartPoolGen, chain, BOOLOF(top), i, desc, desc->capacity, desc->mortality, desc->zones, - gen->pool, gen->nr, gen->totalSize, - gen->newSizeAtCreate); + gen->pool, gen->totalSize, gen->newSizeAtCreate); } } From 8837b85cb1fe0f4eb04ae8a7ea39753cb8b572f3 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 30 Apr 2014 12:09:09 +0100 Subject: [PATCH 30/43] Pass gen parameter to amsinitinternal. Set up segsmss with same chain as before. Copied from Perforce Change: 185885 ServerID: perforce.ravenbrook.com --- mps/code/segsmss.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mps/code/segsmss.c b/mps/code/segsmss.c index e3ed646865e..f6e86010cd5 100644 --- a/mps/code/segsmss.c +++ b/mps/code/segsmss.c @@ -349,7 +349,7 @@ static Res AMSTInit(Pool pool, ArgList args) ArgRequire(&arg, args, MPS_KEY_FORMAT); format = arg.val.format; - res = AMSInitInternal(Pool2AMS(pool), format, chain, 0, FALSE); + res = AMSInitInternal(Pool2AMS(pool), format, chain, gen, FALSE); if (res != ResOK) return res; amst = Pool2AMST(pool); @@ -759,14 +759,19 @@ static void *test(void *arg, size_t s) mps_ap_t busy_ap; mps_addr_t busy_init; const char *indent = " "; + mps_chain_t chain; + static mps_gen_param_s genParam = {1024, 0.2}; arena = (mps_arena_t)arg; (void)s; /* unused */ die(mps_fmt_create_A(&format, arena, dylan_fmt_A()), "fmt_create"); + die(mps_chain_create(&chain, arena, 1, &genParam), "chain_create"); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + MPS_ARGS_ADD(args, MPS_KEY_GEN, 0); die(mps_pool_create_k(&pool, arena, mps_class_amst(), args), "pool_create(amst)"); } MPS_ARGS_END(args); @@ -842,6 +847,7 @@ static void *test(void *arg, size_t s) mps_root_destroy(exactRoot); mps_root_destroy(ambigRoot); mps_pool_destroy(pool); + mps_chain_destroy(chain); mps_fmt_destroy(format); return NULL; From 82db47ceac405e450864b00d15cd4fc2e5525a7f Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 30 Apr 2014 12:56:30 +0100 Subject: [PATCH 31/43] Undo seghasbuffer change, to simplify the diffs. Copied from Perforce Change: 185890 ServerID: perforce.ravenbrook.com --- mps/code/buffer.c | 2 +- mps/code/mpm.h | 1 - mps/code/poolamc.c | 33 +++++++++++++++++---------------- mps/code/poolams.c | 11 ++++++----- mps/code/poolawl.c | 13 +++++++------ mps/code/poollo.c | 13 +++++++------ mps/code/poolsnc.c | 4 ++-- mps/code/segsmss.c | 3 +-- 8 files changed, 41 insertions(+), 39 deletions(-) diff --git a/mps/code/buffer.c b/mps/code/buffer.c index c6ac0c980ce..bbae1f3eacd 100644 --- a/mps/code/buffer.c +++ b/mps/code/buffer.c @@ -1333,7 +1333,7 @@ static void segBufAttach(Buffer buffer, Addr base, Addr limit, found = SegOfAddr(&seg, arena, base); AVER(found); AVER(segbuf->seg == NULL); - AVER(!SegHasBuffer(seg)); + AVER(SegBuffer(seg) == NULL); AVER(SegBase(seg) <= base); AVER(limit <= SegLimit(seg)); diff --git a/mps/code/mpm.h b/mps/code/mpm.h index ef45cbf5b48..e732ca8ee9f 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -708,7 +708,6 @@ extern Addr (SegLimit)(Seg seg); #define SegOfPoolRing(node) (RING_ELT(Seg, poolRing, (node))) #define SegOfGreyRing(node) (&(RING_ELT(GCSeg, greyRing, (node)) \ ->segStruct)) -#define SegHasBuffer(seg) (SegBuffer(seg) != NULL) #define SegSummary(seg) (((GCSeg)(seg))->summary) #define SegSetPM(seg, mode) ((void)((seg)->pm = BS_BITFIELD(Access, (mode)))) diff --git a/mps/code/poolamc.c b/mps/code/poolamc.c index f80c50fa137..72331270583 100644 --- a/mps/code/poolamc.c +++ b/mps/code/poolamc.c @@ -169,6 +169,7 @@ static Res AMCSegInit(Seg seg, Pool pool, Addr base, Size size, static void AMCSegSketch(Seg seg, char *pbSketch, size_t cbSketch) { amcSeg amcseg; + Buffer buffer; AVER(pbSketch); AVER(cbSketch >= 5); @@ -196,8 +197,10 @@ static void AMCSegSketch(Seg seg, char *pbSketch, size_t cbSketch) pbSketch[2] = 'W'; /* White */ } - if (SegHasBuffer(seg)) { - Buffer buffer = SegBuffer(seg); + buffer = SegBuffer(seg); + if(buffer == NULL) { + pbSketch[3] = '_'; + } else { Bool mut = BufferIsMutator(buffer); Bool flipped = ((buffer->mode & BufferModeFLIPPED) != 0); Bool trapped = BufferIsTrapped(buffer); @@ -220,8 +223,6 @@ static void AMCSegSketch(Seg seg, char *pbSketch, size_t cbSketch) } else { /* I don't know what's going on! */ } - } else { - pbSketch[3] = '_'; } pbSketch[4] = '\0'; @@ -287,7 +288,7 @@ static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream) if(res != ResOK) return res; - if (SegHasBuffer(seg)) + if(SegBuffer(seg) != NULL) init = BufferGetInit(SegBuffer(seg)); else init = limit; @@ -669,7 +670,6 @@ static Res amcGenCreate(amcGen *genReturn, AMC amc, GenDesc gen) if(res != ResOK) goto failGenInit; RingInit(&amcgen->amcRing); - amcgen->segs = 0; amcgen->forward = buffer; amcgen->sig = amcGenSig; @@ -1200,6 +1200,7 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) Size condemned = 0; amcGen gen; AMC amc; + Buffer buffer; amcSeg amcseg; Res res; @@ -1208,8 +1209,8 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) AVERT(Seg, seg); amcseg = Seg2amcSeg(seg); - if (SegHasBuffer(seg)) { - Buffer buffer = SegBuffer(seg); + buffer = SegBuffer(seg); + if(buffer != NULL) { AVERT(Buffer, buffer); if(!BufferIsMutator(buffer)) { /* forwarding buffer */ @@ -1380,7 +1381,7 @@ static Res amcScanNailedOnce(Bool *totalReturn, Bool *moreReturn, NailboardClearNewNails(board); p = SegBase(seg); - while (SegHasBuffer(seg)) { + while(SegBuffer(seg) != NULL) { limit = BufferScanLimit(SegBuffer(seg)); if(p >= limit) { AVER(p == limit); @@ -1485,7 +1486,7 @@ static Res AMCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) base = AddrAdd(SegBase(seg), format->headerSize); /* */ - while (SegHasBuffer(seg)) { + while(SegBuffer(seg) != NULL) { limit = AddrAdd(BufferScanLimit(SegBuffer(seg)), format->headerSize); if(base >= limit) { @@ -1929,7 +1930,7 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) headerSize = format->headerSize; ShieldExpose(arena, seg); p = SegBase(seg); - if (SegHasBuffer(seg)) { + if(SegBuffer(seg) != NULL) { limit = BufferScanLimit(SegBuffer(seg)); } else { limit = SegLimit(seg); @@ -1982,13 +1983,13 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) /* Free the seg if we can; fixes .nailboard.limitations.middle. */ if(preservedInPlaceCount == 0 - && !SegHasBuffer(seg) + && (SegBuffer(seg) == NULL) && (SegNailed(seg) == TraceSetEMPTY)) { amcGen gen = amcSegGen(seg); /* We may not free a buffered seg. */ - AVER(!SegHasBuffer(seg)); + AVER(SegBuffer(seg) == NULL); PoolGenReclaim(&gen->pgen, SegSize(seg), Seg2amcSeg(seg)->deferred); PoolGenFree(&gen->pgen, seg); @@ -2065,7 +2066,7 @@ static void AMCReclaim(Pool pool, Trace trace, Seg seg) /* We may not free a buffered seg. (But all buffered + condemned */ /* segs should have been nailed anyway). */ - AVER(!SegHasBuffer(seg)); + AVER(SegBuffer(seg) == NULL); trace->reclaimSize += SegSize(seg); @@ -2134,7 +2135,7 @@ static void AMCWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, /* If the segment is buffered, only walk as far as the end */ /* of the initialized objects. cf. AMCScan */ - if(SegHasBuffer(seg)) + if(SegBuffer(seg) != NULL) limit = BufferScanLimit(SegBuffer(seg)); else limit = SegLimit(seg); @@ -2232,7 +2233,7 @@ static Res AMCAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr) arena = PoolArena(pool); base = SegBase(seg); - if (SegHasBuffer(seg)) { + if (SegBuffer(seg) != NULL) { /* We use BufferGetInit here (and not BufferScanLimit) because we * want to be able to find objects that have been allocated and * committed since the last flip. These objects lie between the diff --git a/mps/code/poolams.c b/mps/code/poolams.c index e3b7939a941..f822c9323d4 100644 --- a/mps/code/poolams.c +++ b/mps/code/poolams.c @@ -288,7 +288,7 @@ static void AMSSegFinish(Seg seg) ams = amsseg->ams; AVERT(AMS, ams); arena = PoolArena(AMS2Pool(ams)); - AVER(!SegHasBuffer(seg)); + AVER(SegBuffer(seg) == NULL); /* keep the destructions in step with AMSSegInit failure cases */ amsDestroyTables(ams, amsseg->allocTable, amsseg->nongreyTable, @@ -972,7 +972,7 @@ static Res AMSBufferFill(Addr *baseReturn, Addr *limitReturn, seg = AMSSeg2Seg(amsseg); if (SegRankSet(seg) == rankSet - && !SegHasBuffer(seg) + && SegBuffer(seg) == NULL /* Can't use a white or grey segment, see d.m.p.fill.colour. */ && SegWhite(seg) == TraceSetEMPTY && SegGrey(seg) == TraceSetEMPTY) @@ -1105,6 +1105,7 @@ static Res AMSWhiten(Pool pool, Trace trace, Seg seg) { AMS ams; AMSSeg amsseg; + Buffer buffer; /* the seg's buffer, if it has one */ Count uncondemned; AVERT(Pool, pool); @@ -1144,8 +1145,8 @@ static Res AMSWhiten(Pool pool, Trace trace, Seg seg) amsseg->allocTableInUse = TRUE; } - if (SegHasBuffer(seg)) { /* */ - Buffer buffer = SegBuffer(seg); + buffer = SegBuffer(seg); + if (buffer != NULL) { /* */ Index scanLimitIndex, limitIndex; scanLimitIndex = AMS_ADDR_INDEX(seg, BufferScanLimit(buffer)); limitIndex = AMS_ADDR_INDEX(seg, BufferLimit(buffer)); @@ -1631,7 +1632,7 @@ static void AMSReclaim(Pool pool, Trace trace, Seg seg) amsseg->colourTablesInUse = FALSE; SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); - if (amsseg->freeGrains == grains && !SegHasBuffer(seg)) + if (amsseg->freeGrains == grains && SegBuffer(seg) == NULL) /* No survivors */ PoolGenFree(&ams->pgen, seg); } diff --git a/mps/code/poolawl.c b/mps/code/poolawl.c index 05c4ecdc57b..d57f70877c6 100644 --- a/mps/code/poolawl.c +++ b/mps/code/poolawl.c @@ -654,7 +654,7 @@ static Res AWLBufferFill(Addr *baseReturn, Addr *limitReturn, /* Only try to allocate in the segment if it is not already */ /* buffered, and has the same ranks as the buffer. */ - if (!SegHasBuffer(seg) + if (SegBuffer(seg) == NULL && SegRankSet(seg) == BufferRankSet(buffer) && AWLGrainsSize(awl, awlseg->freeGrains) >= size && AWLSegAlloc(&base, &limit, awlseg, awl, size)) @@ -749,6 +749,7 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg) { AWL awl; AWLSeg awlseg; + Buffer buffer; Count uncondemned; /* All parameters checked by generic PoolWhiten. */ @@ -757,17 +758,17 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg) AVERT(AWL, awl); awlseg = Seg2AWLSeg(seg); AVERT(AWLSeg, awlseg); + buffer = SegBuffer(seg); /* Can only whiten for a single trace, */ /* see */ AVER(SegWhite(seg) == TraceSetEMPTY); - if (!SegHasBuffer(seg)) { + if(buffer == NULL) { awlRangeWhiten(awlseg, 0, awlseg->grains); uncondemned = (Count)0; } else { /* Whiten everything except the buffer. */ - Buffer buffer = SegBuffer(seg); Addr base = SegBase(seg); Index scanLimitIndex = awlIndexOfAddr(base, awl, BufferScanLimit(buffer)); Index limitIndex = awlIndexOfAddr(base, awl, BufferLimit(buffer)); @@ -825,7 +826,7 @@ static void AWLGrey(Pool pool, Trace trace, Seg seg) AVERT(AWLSeg, awlseg); SegSetGrey(seg, TraceSetAdd(SegGrey(seg), trace)); - if (SegHasBuffer(seg)) { + if (SegBuffer(seg) != NULL) { Addr base = SegBase(seg); Buffer buffer = SegBuffer(seg); @@ -1130,7 +1131,7 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) continue; } p = awlAddrOfIndex(base, awl, i); - if (SegHasBuffer(seg)) { + if (SegBuffer(seg) != NULL) { Buffer buffer = SegBuffer(seg); if(p == BufferScanLimit(buffer) @@ -1248,7 +1249,7 @@ static void AWLWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, Addr next; Index i; - if (SegHasBuffer(seg)) { + if (SegBuffer(seg) != NULL) { Buffer buffer = SegBuffer(seg); if (object == BufferScanLimit(buffer) && BufferScanLimit(buffer) != BufferLimit(buffer)) { diff --git a/mps/code/poollo.c b/mps/code/poollo.c index 883275b2a1c..f53beeb3226 100644 --- a/mps/code/poollo.c +++ b/mps/code/poollo.c @@ -252,7 +252,7 @@ static Bool loSegFindFree(Addr *bReturn, Addr *lReturn, AVER(agrains <= loseg->freeGrains); AVER(size <= SegSize(seg)); - if (SegHasBuffer(seg)) + if (SegBuffer(seg) != NULL) /* Don't bother trying to allocate from a buffered segment */ return FALSE; @@ -338,11 +338,11 @@ static void loSegReclaim(LOSeg loseg, Trace trace) */ p = base; while(p < limit) { + Buffer buffer = SegBuffer(seg); Addr q; Index i; - if (SegHasBuffer(seg)) { - Buffer buffer = SegBuffer(seg); + if(buffer != NULL) { marked = TRUE; if (p == BufferScanLimit(buffer) && BufferScanLimit(buffer) != BufferLimit(buffer)) { @@ -429,7 +429,7 @@ static void LOWalk(Pool pool, Seg seg, Addr next; Index j; - if (SegHasBuffer(seg)) { + if(SegBuffer(seg) != NULL) { Buffer buffer = SegBuffer(seg); if(object == BufferScanLimit(buffer) && BufferScanLimit(buffer) != BufferLimit(buffer)) { @@ -676,6 +676,7 @@ static Res LOWhiten(Pool pool, Trace trace, Seg seg) { LO lo; LOSeg loseg; + Buffer buffer; Count grains, uncondemned; AVERT(Pool, pool); @@ -691,8 +692,8 @@ static Res LOWhiten(Pool pool, Trace trace, Seg seg) grains = loSegGrains(loseg); /* Whiten allocated objects; leave free areas black. */ - if (SegHasBuffer(seg)) { - Buffer buffer = SegBuffer(seg); + buffer = SegBuffer(seg); + if (buffer != NULL) { Addr base = SegBase(seg); Index scanLimitIndex = loIndexOfAddr(base, lo, BufferScanLimit(buffer)); Index limitIndex = loIndexOfAddr(base, lo, BufferLimit(buffer)); diff --git a/mps/code/poolsnc.c b/mps/code/poolsnc.c index 9a21d0b2fba..e9afe98a96b 100644 --- a/mps/code/poolsnc.c +++ b/mps/code/poolsnc.c @@ -520,7 +520,7 @@ static Res SNCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) /* If the segment is buffered, only walk as far as the end */ /* of the initialized objects. */ - if (SegHasBuffer(seg)) { + if (SegBuffer(seg) != NULL) { limit = BufferScanLimit(SegBuffer(seg)); } else { limit = SegLimit(seg); @@ -648,7 +648,7 @@ static void SNCWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, /* If the segment is buffered, only walk as far as the end */ /* of the initialized objects. Cf. SNCScan. */ - if (SegHasBuffer(seg)) + if (SegBuffer(seg) != NULL) limit = BufferScanLimit(SegBuffer(seg)); else limit = SegLimit(seg); diff --git a/mps/code/segsmss.c b/mps/code/segsmss.c index b29ec3a8a74..49f75e681e2 100644 --- a/mps/code/segsmss.c +++ b/mps/code/segsmss.c @@ -334,7 +334,6 @@ static Res AMSTInit(Pool pool, ArgList args) Res res; unsigned gen = AMS_GEN_DEFAULT; ArgStruct arg; - unsigned gen = AMS_GEN_DEFAULT; AVERT(Pool, pool); AVERT(ArgList, args); @@ -556,7 +555,7 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, if (SegLimit(seg) == limit && SegBase(seg) == base) { if (amstseg->prev != NULL) { Seg segLo = AMSTSeg2Seg(amstseg->prev); - if (!SegHasBuffer(segLo) && SegGrey(segLo) == SegGrey(seg)) { + if (SegBuffer(segLo) == NULL && SegGrey(segLo) == SegGrey(seg)) { /* .merge */ Seg mergedSeg; Res mres; From a0e943d74e5a207f766a0a505210fb109d139997 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 30 Apr 2014 12:59:04 +0100 Subject: [PATCH 32/43] Revert a couple of whitespace changes to simplify the diffs. Copied from Perforce Change: 185891 ServerID: perforce.ravenbrook.com --- mps/code/poolawl.c | 2 +- mps/code/poollo.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mps/code/poolawl.c b/mps/code/poolawl.c index d57f70877c6..72a717fa7b9 100644 --- a/mps/code/poolawl.c +++ b/mps/code/poolawl.c @@ -1131,7 +1131,7 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) continue; } p = awlAddrOfIndex(base, awl, i); - if (SegBuffer(seg) != NULL) { + if(SegBuffer(seg) != NULL) { Buffer buffer = SegBuffer(seg); if(p == BufferScanLimit(buffer) diff --git a/mps/code/poollo.c b/mps/code/poollo.c index f53beeb3226..9d97f261f63 100644 --- a/mps/code/poollo.c +++ b/mps/code/poollo.c @@ -252,7 +252,7 @@ static Bool loSegFindFree(Addr *bReturn, Addr *lReturn, AVER(agrains <= loseg->freeGrains); AVER(size <= SegSize(seg)); - if (SegBuffer(seg) != NULL) + if(SegBuffer(seg) != NULL) /* Don't bother trying to allocate from a buffered segment */ return FALSE; From 32416cc272cf3119b75f8d36bcd3e05edec03286 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 30 Apr 2014 13:49:26 +0100 Subject: [PATCH 33/43] Better names for poolgen functions. Accounting for segment splitting and merging. Accounting for allocation and freeing in segsmss. Copied from Perforce Change: 185894 ServerID: perforce.ravenbrook.com --- mps/code/chain.h | 8 +++--- mps/code/locus.c | 67 +++++++++++++++++++++++++++++----------------- mps/code/mpm.h | 1 + mps/code/poolamc.c | 4 +-- mps/code/poolams.c | 6 +++-- mps/code/poolawl.c | 4 +-- mps/code/poollo.c | 4 +-- mps/code/seg.c | 4 +++ mps/code/segsmss.c | 18 ++++++++----- 9 files changed, 74 insertions(+), 42 deletions(-) diff --git a/mps/code/chain.h b/mps/code/chain.h index 889c0fc886f..9666eac943e 100644 --- a/mps/code/chain.h +++ b/mps/code/chain.h @@ -98,12 +98,14 @@ extern Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool); extern void PoolGenFinish(PoolGen pgen); extern Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class, Size size, Bool withReservoirPermit, ArgList args); -extern void PoolGenBufferFill(PoolGen pgen, Size size, Bool deferred); +extern void PoolGenFree(PoolGen pgen, Seg seg); +extern void PoolGenFill(PoolGen pgen, Size size, Bool deferred); +extern void PoolGenEmpty(PoolGen pgen, Size unused, Bool deferred); extern void PoolGenAge(PoolGen pgen, Size aged, Bool deferred); extern void PoolGenReclaim(PoolGen pgen, Size reclaimed, Bool deferred); -extern void PoolGenBufferEmpty(PoolGen pgen, Size unused, Bool deferred); extern void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize); -extern void PoolGenFree(PoolGen pgen, Seg seg); +extern void PoolGenSegSplit(PoolGen pgen); +extern void PoolGenSegMerge(PoolGen pgen); #endif /* chain_h */ diff --git a/mps/code/locus.c b/mps/code/locus.c index 8c547edd582..ec7ae24f1ea 100644 --- a/mps/code/locus.c +++ b/mps/code/locus.c @@ -465,12 +465,12 @@ Bool PoolGenCheck(PoolGen pgen) } -/* PoolGenBufferFill -- accounting for buffer fill +/* PoolGenFill -- accounting for allocation * - * The buffer was free, is now new (or newDeferred). + * The memory was free, is now new (or newDeferred). */ -void PoolGenBufferFill(PoolGen pgen, Size size, Bool ramping) +void PoolGenFill(PoolGen pgen, Size size, Bool ramping) { AVERT(PoolGen, pgen); AVERT(Bool, ramping); @@ -484,6 +484,27 @@ void PoolGenBufferFill(PoolGen pgen, Size size, Bool ramping) } +/* PoolGenEmpty -- accounting for emptying a buffer + * + * The unused part of the buffer was new (or newDeferred) and is now free. + */ + +void PoolGenEmpty(PoolGen pgen, Size unused, Bool ramping) +{ + AVERT(PoolGen, pgen); + AVERT(Bool, ramping); + + if (ramping) { + AVER(pgen->newDeferredSize >= unused); + pgen->newDeferredSize -= unused; + } else { + AVER(pgen->newSize >= unused); + pgen->newSize -= unused; + } + pgen->freeSize += unused; +} + + /* PoolGenAge -- accounting for condemning a segment * * The memory was new (or newDeferred), is now old (or oldDeferred) @@ -526,27 +547,6 @@ void PoolGenReclaim(PoolGen pgen, Size reclaimed, Bool ramping) } -/* PoolGenBufferEmpty -- accounting for emptying a buffer - * - * The unused part of the buffer was new (or newDeferred) and is now free. - */ - -void PoolGenBufferEmpty(PoolGen pgen, Size unused, Bool ramping) -{ - AVERT(PoolGen, pgen); - AVERT(Bool, ramping); - - if (ramping) { - AVER(pgen->newDeferredSize >= unused); - pgen->newDeferredSize -= unused; - } else { - AVER(pgen->newSize >= unused); - pgen->newSize -= unused; - } - pgen->freeSize += unused; -} - - /* PoolGenUndefer -- accounting for end of ramp mode * * The memory was oldDeferred or newDeferred, is now old or new. @@ -564,6 +564,25 @@ void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize) } +/* PoolGenSegSplit -- accounting for splitting a segment */ + +void PoolGenSegSplit(PoolGen pgen) +{ + AVERT(PoolGen, pgen); + ++ pgen->segs; +} + + +/* PoolGenSegMerge -- accounting for merging a segment */ + +void PoolGenSegMerge(PoolGen pgen) +{ + AVERT(PoolGen, pgen); + AVER(pgen->segs > 0); + -- pgen->segs; +} + + /* PoolGenFree -- free a segment and update accounting * * The segment is assumed to finish free. diff --git a/mps/code/mpm.h b/mps/code/mpm.h index e732ca8ee9f..0cbad2b0f1c 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -708,6 +708,7 @@ extern Addr (SegLimit)(Seg seg); #define SegOfPoolRing(node) (RING_ELT(Seg, poolRing, (node))) #define SegOfGreyRing(node) (&(RING_ELT(GCSeg, greyRing, (node)) \ ->segStruct)) + #define SegSummary(seg) (((GCSeg)(seg))->summary) #define SegSetPM(seg, mode) ((void)((seg)->pm = BS_BITFIELD(Access, (mode)))) diff --git a/mps/code/poolamc.c b/mps/code/poolamc.c index 72331270583..4baf8cb1789 100644 --- a/mps/code/poolamc.c +++ b/mps/code/poolamc.c @@ -1062,7 +1062,7 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, } } - PoolGenBufferFill(pgen, SegSize(seg), Seg2amcSeg(seg)->deferred); + PoolGenFill(pgen, SegSize(seg), Seg2amcSeg(seg)->deferred); *baseReturn = base; *limitReturn = limit; return ResOK; @@ -1107,7 +1107,7 @@ static void AMCBufferEmpty(Pool pool, Buffer buffer, ShieldCover(arena, seg); } - PoolGenBufferEmpty(&amcSegGen(seg)->pgen, 0, Seg2amcSeg(seg)->deferred); + PoolGenEmpty(&amcSegGen(seg)->pgen, 0, Seg2amcSeg(seg)->deferred); } diff --git a/mps/code/poolams.c b/mps/code/poolams.c index f822c9323d4..5ad40ed0153 100644 --- a/mps/code/poolams.c +++ b/mps/code/poolams.c @@ -399,6 +399,7 @@ static Res AMSSegMerge(Seg seg, Seg segHi, amssegHi->sig = SigInvalid; AVERT(AMSSeg, amsseg); + PoolGenSegMerge(&ams->pgen); return ResOK; failSuper: @@ -504,6 +505,7 @@ static Res AMSSegSplit(Seg seg, Seg segHi, amssegHi->sig = AMSSegSig; AVERT(AMSSeg, amsseg); AVERT(AMSSeg, amssegHi); + PoolGenSegSplit(&ams->pgen); return ResOK; failSuper: @@ -997,7 +999,7 @@ static Res AMSBufferFill(Addr *baseReturn, Addr *limitReturn, DebugPoolFreeCheck(pool, baseAddr, limitAddr); allocatedSize = AddrOffset(baseAddr, limitAddr); - PoolGenBufferFill(&ams->pgen, allocatedSize, FALSE); + PoolGenFill(&ams->pgen, allocatedSize, FALSE); *baseReturn = baseAddr; *limitReturn = limitAddr; return ResOK; @@ -1079,7 +1081,7 @@ static void AMSBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) AVER(amsseg->newGrains >= limitIndex - initIndex); amsseg->newGrains -= limitIndex - initIndex; size = AddrOffset(init, limit); - PoolGenBufferEmpty(&ams->pgen, size, FALSE); + PoolGenEmpty(&ams->pgen, size, FALSE); } diff --git a/mps/code/poolawl.c b/mps/code/poolawl.c index 72a717fa7b9..d0d5b75cd5a 100644 --- a/mps/code/poolawl.c +++ b/mps/code/poolawl.c @@ -685,7 +685,7 @@ static Res AWLBufferFill(Addr *baseReturn, Addr *limitReturn, AVER(awlseg->freeGrains >= j - i); awlseg->freeGrains -= j - i; awlseg->newGrains += j - i; - PoolGenBufferFill(&awl->pgen, AddrOffset(base, limit), FALSE); + PoolGenFill(&awl->pgen, AddrOffset(base, limit), FALSE); } *baseReturn = base; *limitReturn = limit; @@ -724,7 +724,7 @@ static void AWLBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) AVER(awlseg->newGrains >= j - i); awlseg->newGrains -= j - i; awlseg->freeGrains += j - i; - PoolGenBufferEmpty(&awl->pgen, AddrOffset(init, limit), FALSE); + PoolGenEmpty(&awl->pgen, AddrOffset(init, limit), FALSE); } } diff --git a/mps/code/poollo.c b/mps/code/poollo.c index 9d97f261f63..0bab8336998 100644 --- a/mps/code/poollo.c +++ b/mps/code/poollo.c @@ -610,7 +610,7 @@ static Res LOBufferFill(Addr *baseReturn, Addr *limitReturn, loseg->newGrains += limitIndex - baseIndex; } - PoolGenBufferFill(&lo->pgen, AddrOffset(base, limit), FALSE); + PoolGenFill(&lo->pgen, AddrOffset(base, limit), FALSE); *baseReturn = base; *limitReturn = limit; @@ -665,7 +665,7 @@ static void LOBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) AVER(loseg->newGrains >= limitIndex - initIndex); loseg->newGrains -= limitIndex - initIndex; loseg->freeGrains += limitIndex - initIndex; - PoolGenBufferEmpty(&lo->pgen, AddrOffset(init, limit), FALSE); + PoolGenEmpty(&lo->pgen, AddrOffset(init, limit), FALSE); } } diff --git a/mps/code/seg.c b/mps/code/seg.c index 7f0cd4bc907..fe965dbbda4 100644 --- a/mps/code/seg.c +++ b/mps/code/seg.c @@ -636,6 +636,10 @@ Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at, AVER(at < limit); AVERT(Bool, withReservoirPermit); + /* Can only split a buffered segment if the entire buffer is below + * the split point. */ + AVER(SegBuffer(seg) == NULL || BufferLimit(SegBuffer(seg)) <= at); + ShieldFlush(arena); /* see */ /* Allocate the new segment object from the control pool */ diff --git a/mps/code/segsmss.c b/mps/code/segsmss.c index 49f75e681e2..ea57a4475a2 100644 --- a/mps/code/segsmss.c +++ b/mps/code/segsmss.c @@ -434,7 +434,7 @@ static Bool AMSSegRegionIsFree(Seg seg, Addr base, Addr limit) * Used as a means of overriding the behaviour of AMSBufferFill. * The code is similar to AMSBufferEmpty. */ -static void AMSUnallocateRange(Seg seg, Addr base, Addr limit) +static void AMSUnallocateRange(AMS ams, Seg seg, Addr base, Addr limit) { AMSSeg amsseg; Index baseIndex, limitIndex; @@ -465,6 +465,7 @@ static void AMSUnallocateRange(Seg seg, Addr base, Addr limit) amsseg->freeGrains += limitIndex - baseIndex; AVER(amsseg->newGrains >= limitIndex - baseIndex); amsseg->newGrains -= limitIndex - baseIndex; + PoolGenEmpty(&ams->pgen, AddrOffset(base, limit), FALSE); } @@ -473,7 +474,7 @@ static void AMSUnallocateRange(Seg seg, Addr base, Addr limit) * Used as a means of overriding the behaviour of AMSBufferFill. * The code is similar to AMSUnallocateRange. */ -static void AMSAllocateRange(Seg seg, Addr base, Addr limit) +static void AMSAllocateRange(AMS ams, Seg seg, Addr base, Addr limit) { AMSSeg amsseg; Index baseIndex, limitIndex; @@ -504,6 +505,7 @@ static void AMSAllocateRange(Seg seg, Addr base, Addr limit) AVER(amsseg->freeGrains >= limitIndex - baseIndex); amsseg->freeGrains -= limitIndex - baseIndex; amsseg->newGrains += limitIndex - baseIndex; + PoolGenFill(&ams->pgen, AddrOffset(base, limit), FALSE); } @@ -528,6 +530,7 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, PoolClass super; Addr base, limit; Arena arena; + AMS ams; AMST amst; Bool b; Seg seg; @@ -539,6 +542,7 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, AVER(limitReturn != NULL); /* other parameters are checked by next method */ arena = PoolArena(pool); + ams = Pool2AMS(pool); amst = Pool2AMST(pool); /* call next method */ @@ -560,14 +564,14 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, Seg mergedSeg; Res mres; - AMSUnallocateRange(seg, base, limit); + AMSUnallocateRange(ams, seg, base, limit); mres = SegMerge(&mergedSeg, segLo, seg, withReservoirPermit); if (ResOK == mres) { /* successful merge */ - AMSAllocateRange(mergedSeg, base, limit); + AMSAllocateRange(ams, mergedSeg, base, limit); /* leave range as-is */ } else { /* failed to merge */ AVER(amst->failSegs); /* deliberate fails only */ - AMSAllocateRange(seg, base, limit); + AMSAllocateRange(ams, seg, base, limit); } } @@ -578,13 +582,13 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, Addr mid = AddrAdd(base, half); Seg segLo, segHi; Res sres; - AMSUnallocateRange(seg, mid, limit); + AMSUnallocateRange(ams, seg, mid, limit); sres = SegSplit(&segLo, &segHi, seg, mid, withReservoirPermit); if (ResOK == sres) { /* successful split */ limit = mid; /* range is lower segment */ } else { /* failed to split */ AVER(amst->failSegs); /* deliberate fails only */ - AMSAllocateRange(seg, mid, limit); + AMSAllocateRange(ams, seg, mid, limit); } } From 74debda2320fb2795139f19466b0baa8964b912d Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 30 Apr 2014 16:34:40 +0100 Subject: [PATCH 34/43] Awl now returns segments to the arena when they are free. Copied from Perforce Change: 185906 ServerID: perforce.ravenbrook.com --- mps/code/poolawl.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/mps/code/poolawl.c b/mps/code/poolawl.c index d0d5b75cd5a..42528eaa014 100644 --- a/mps/code/poolawl.c +++ b/mps/code/poolawl.c @@ -1100,6 +1100,7 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) Addr base; AWL awl; AWLSeg awlseg; + Buffer buffer; Index i; Count oldFree; Format format; @@ -1119,6 +1120,7 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) format = pool->format; base = SegBase(seg); + buffer = SegBuffer(seg); i = 0; oldFree = awlseg->freeGrains; @@ -1131,15 +1133,12 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) continue; } p = awlAddrOfIndex(base, awl, i); - if(SegBuffer(seg) != NULL) { - Buffer buffer = SegBuffer(seg); - - if(p == BufferScanLimit(buffer) - && BufferScanLimit(buffer) != BufferLimit(buffer)) - { - i = awlIndexOfAddr(base, awl, BufferLimit(buffer)); - continue; - } + if (buffer != NULL + && p == BufferScanLimit(buffer) + && BufferScanLimit(buffer) != BufferLimit(buffer)) + { + i = awlIndexOfAddr(base, awl, BufferLimit(buffer)); + continue; } q = format->skip(AddrAdd(p, format->headerSize)); q = AddrSub(q, format->headerSize); @@ -1172,7 +1171,10 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) trace->preservedInPlaceCount += preservedInPlaceCount; trace->preservedInPlaceSize += preservedInPlaceSize; SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); - /* @@@@ never frees a segment, see job001687. */ + + if (awlseg->freeGrains == awlseg->grains && buffer == NULL) + /* No survivors */ + PoolGenFree(&awl->pgen, seg); } From 4fa0ae0db9c4c54b35486b5ce10309d9a8a0c910 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 30 Apr 2014 17:39:37 +0100 Subject: [PATCH 35/43] Output all the pool generation accounting information. Copied from Perforce Change: 185911 ServerID: perforce.ravenbrook.com --- mps/code/eventdef.h | 7 ++++++- mps/code/trace.c | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/mps/code/eventdef.h b/mps/code/eventdef.h index c7e076a9251..4999be4ee69 100644 --- a/mps/code/eventdef.h +++ b/mps/code/eventdef.h @@ -720,7 +720,12 @@ PARAM(X, 3, W, zone) /* zone set of generation */ \ PARAM(X, 4, P, pool) /* pool */ \ PARAM(X, 5, W, totalSize) /* total size of pool gen */ \ - PARAM(X, 6, W, newSizeAtCreate) /* new size of pool gen at trace create */ + PARAM(X, 6, W, freeSize) /* free size of pool gen */ \ + PARAM(X, 7, W, oldSize) /* old size of pool gen */ \ + PARAM(X, 8, W, newSize) /* new size of pool gen */ \ + PARAM(X, 9, W, oldDeferredSize) /* old size (deferred) of pool gen */ \ + PARAM(X, 10, W, newDeferredSize) /* new size (deferred) of pool gen */ \ + PARAM(X, 11, W, newSizeAtCreate) /* new size of pool gen at trace create */ #define EVENT_TraceCondemnZones_PARAMS(PARAM, X) \ PARAM(X, 0, P, trace) /* the trace */ \ diff --git a/mps/code/trace.c b/mps/code/trace.c index 93dddda955d..74144774564 100644 --- a/mps/code/trace.c +++ b/mps/code/trace.c @@ -1593,8 +1593,10 @@ static void TraceStartPoolGen(GenDesc gen) Ring n, nn; RING_FOR(n, &gen->locusRing, nn) { PoolGen pgen = RING_ELT(PoolGen, genRing, n); - EVENT7(TraceStartPoolGen, gen, gen->capacity, gen->mortality, gen->zones, - pgen->pool, pgen->totalSize, pgen->newSizeAtCreate); + EVENT12(TraceStartPoolGen, gen, gen->capacity, gen->mortality, gen->zones, + pgen->pool, pgen->totalSize, pgen->freeSize, pgen->oldSize, + pgen->newSize, pgen->oldDeferredSize, pgen->newDeferredSize, + pgen->newSizeAtCreate); } } From d05197dccb0582f3444bc800344159fcfc5ad408 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 1 May 2014 19:00:08 +0100 Subject: [PATCH 36/43] Avoid the need for newsizeatcreate by emitting events for the pool generations in tracecreate instead of tracestart. Get rid of proflow member of GenDescStruct (it was unused). Use STATISTIC to avoid updating segs, freeSize, oldSize, and oldDeferredSize in non-checking varieties. Add design for pool generation accounting. Copied from Perforce Change: 185930 ServerID: perforce.ravenbrook.com --- mps/code/chain.h | 24 +++---- mps/code/eventdef.h | 13 ++-- mps/code/locus.c | 117 ++++++++++++++++++-------------- mps/code/trace.c | 96 +++++++++----------------- mps/design/strategy.txt | 147 +++++++++++++++++++++++++++++----------- 5 files changed, 222 insertions(+), 175 deletions(-) diff --git a/mps/code/chain.h b/mps/code/chain.h index 9666eac943e..706838610ec 100644 --- a/mps/code/chain.h +++ b/mps/code/chain.h @@ -31,7 +31,6 @@ typedef struct GenDescStruct { ZoneSet zones; /* zoneset for this generation */ Size capacity; /* capacity in kB */ double mortality; - double proflow; /* predicted proportion of survivors promoted */ RingStruct locusRing; /* Ring of all PoolGen's in this GenDesc (locus) */ } GenDescStruct; @@ -49,21 +48,14 @@ typedef struct PoolGenStruct { /* link in ring of all PoolGen's in this GenDesc (locus) */ RingStruct genRing; - /* Accounting of memory in this generation for this pool */ - Size segs; /* number of segments */ - Size totalSize; /* total (sum of segment sizes) */ - Size freeSize; /* unused (free or lost to fragmentation) */ - Size oldSize; /* allocated prior to last collection */ - Size newSize; /* allocated since last collection */ - Size oldDeferredSize; /* allocated prior to last collection (deferred) */ - Size newDeferredSize; /* allocated since last collection (deferred) */ - - /* newSize when TraceCreate was called. This is used in the - * TraceStartPoolGen event emitted at the start of a trace; at that - * time, newSize has already been diminished by Whiten so we can't - * use that value. TODO: This will not work well with multiple - * traces. */ - Size newSizeAtCreate; + /* Accounting of memory in this generation for this pool */ + STATISTIC_DECL(Size segs); /* number of segments */ + Size totalSize; /* total (sum of segment sizes) */ + STATISTIC_DECL(Size freeSize); /* unused (free or lost to fragmentation) */ + Size newSize; /* allocated since last collection */ + STATISTIC_DECL(Size oldSize); /* allocated prior to last collection */ + Size newDeferredSize; /* new (but deferred) */ + STATISTIC_DECL(Size oldDeferredSize); /* old (but deferred) */ } PoolGenStruct; diff --git a/mps/code/eventdef.h b/mps/code/eventdef.h index 4999be4ee69..d47e434b868 100644 --- a/mps/code/eventdef.h +++ b/mps/code/eventdef.h @@ -187,7 +187,7 @@ EVENT(X, VMCompact , 0x0079, TRUE, Arena) \ EVENT(X, amcScanNailed , 0x0080, TRUE, Seg) \ EVENT(X, AMCTraceEnd , 0x0081, TRUE, Trace) \ - EVENT(X, TraceStartPoolGen , 0x0082, TRUE, Trace) \ + EVENT(X, TraceCreatePoolGen , 0x0082, TRUE, Trace) \ /* new events for performance analysis of large heaps. */ \ EVENT(X, TraceCondemnZones , 0x0083, TRUE, Trace) \ EVENT(X, ArenaGenZoneAdd , 0x0084, TRUE, Arena) \ @@ -713,7 +713,7 @@ PARAM(X, 19, W, pRL) \ PARAM(X, 20, W, pRLr) -#define EVENT_TraceStartPoolGen_PARAMS(PARAM, X) \ +#define EVENT_TraceCreatePoolGen_PARAMS(PARAM, X) \ PARAM(X, 0, P, gendesc) /* generation description */ \ PARAM(X, 1, W, capacity) /* capacity of generation */ \ PARAM(X, 2, D, mortality) /* mortality of generation */ \ @@ -721,11 +721,10 @@ PARAM(X, 4, P, pool) /* pool */ \ PARAM(X, 5, W, totalSize) /* total size of pool gen */ \ PARAM(X, 6, W, freeSize) /* free size of pool gen */ \ - PARAM(X, 7, W, oldSize) /* old size of pool gen */ \ - PARAM(X, 8, W, newSize) /* new size of pool gen */ \ - PARAM(X, 9, W, oldDeferredSize) /* old size (deferred) of pool gen */ \ - PARAM(X, 10, W, newDeferredSize) /* new size (deferred) of pool gen */ \ - PARAM(X, 11, W, newSizeAtCreate) /* new size of pool gen at trace create */ + PARAM(X, 7, W, newSize) /* new size of pool gen */ \ + PARAM(X, 8, W, oldSize) /* old size of pool gen */ \ + PARAM(X, 9, W, newDeferredSize) /* new size (deferred) of pool gen */ \ + PARAM(X, 10, W, oldDeferredSize) /* old size (deferred) of pool gen */ #define EVENT_TraceCondemnZones_PARAMS(PARAM, X) \ PARAM(X, 0, P, trace) /* the trace */ \ diff --git a/mps/code/locus.c b/mps/code/locus.c index ec7ae24f1ea..eca1256fffd 100644 --- a/mps/code/locus.c +++ b/mps/code/locus.c @@ -6,7 +6,8 @@ * DESIGN * * See and for basic locus stuff. - * See for chains. + * See for chains. See for the + * collection strategy. */ #include "chain.h" @@ -87,8 +88,6 @@ static Bool GenDescCheck(GenDesc gen) /* nothing to check for capacity */ CHECKL(gen->mortality >= 0.0); CHECKL(gen->mortality <= 1.0); - CHECKL(gen->proflow >= 0.0); - CHECKL(gen->proflow <= 1.0); CHECKD_NOSIG(Ring, &gen->locusRing); return TRUE; } @@ -156,9 +155,9 @@ Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount, gens[i].zones = ZoneSetEMPTY; gens[i].capacity = params[i].capacity; gens[i].mortality = params[i].mortality; - gens[i].proflow = 1.0; /* @@@@ temporary */ RingInit(&gens[i].locusRing); gens[i].sig = GenDescSig; + AVERT(GenDesc, &gens[i]); } res = ControlAlloc(&p, arena, sizeof(ChainStruct), FALSE); @@ -291,10 +290,12 @@ Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class, Size size, EVENT3(ArenaGenZoneAdd, arena, gen, moreZones); } - ++ pgen->segs; size = SegSize(seg); pgen->totalSize += size; - pgen->freeSize += size; + STATISTIC_STAT ({ + ++ pgen->segs; + pgen->freeSize += size; + }); *segReturn = seg; return ResOK; } @@ -420,13 +421,13 @@ Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool) pgen->pool = pool; pgen->gen = gen; RingInit(&pgen->genRing); - pgen->segs = 0; + STATISTIC(pgen->segs = 0); pgen->totalSize = 0; - pgen->freeSize = 0; + STATISTIC(pgen->freeSize = 0); pgen->newSize = 0; - pgen->oldSize = 0; + STATISTIC(pgen->oldSize = 0); pgen->newDeferredSize = 0; - pgen->oldDeferredSize = 0; + STATISTIC(pgen->oldDeferredSize = 0); pgen->sig = PoolGenSig; AVERT(PoolGen, pgen); @@ -440,8 +441,15 @@ Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool) void PoolGenFinish(PoolGen pgen) { AVERT(PoolGen, pgen); - AVER(pgen->segs == 0); AVER(pgen->totalSize == 0); + AVER(pgen->newSize == 0); + AVER(pgen->newDeferredSize == 0); + STATISTIC_STAT ({ + AVER(pgen->segs == 0); + AVER(pgen->freeSize == 0); + AVER(pgen->oldSize == 0); + AVER(pgen->oldDeferredSize == 0); + }); pgen->sig = SigInvalid; RingRemove(&pgen->genRing); @@ -457,10 +465,12 @@ Bool PoolGenCheck(PoolGen pgen) 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 * ArenaAlign(PoolArena(pgen->pool))); - CHECKL(pgen->totalSize == pgen->freeSize + pgen->oldSize + pgen->newSize - + pgen->oldDeferredSize + pgen->newDeferredSize); + STATISTIC_STAT ({ + CHECKL((pgen->totalSize == 0) == (pgen->segs == 0)); + CHECKL(pgen->totalSize >= pgen->segs * ArenaAlign(PoolArena(pgen->pool))); + CHECKL(pgen->totalSize == pgen->freeSize + pgen->newSize + pgen->oldSize + + pgen->newDeferredSize + pgen->oldDeferredSize); + }); return TRUE; } @@ -470,14 +480,16 @@ Bool PoolGenCheck(PoolGen pgen) * The memory was free, is now new (or newDeferred). */ -void PoolGenFill(PoolGen pgen, Size size, Bool ramping) +void PoolGenFill(PoolGen pgen, Size size, Bool deferred) { AVERT(PoolGen, pgen); - AVERT(Bool, ramping); + AVERT(Bool, deferred); - AVER(pgen->freeSize >= size); - pgen->freeSize -= size; - if (ramping) + STATISTIC_STAT ({ + AVER(pgen->freeSize >= size); + pgen->freeSize -= size; + }); + if (deferred) pgen->newDeferredSize += size; else pgen->newSize += size; @@ -489,19 +501,19 @@ void PoolGenFill(PoolGen pgen, Size size, Bool ramping) * The unused part of the buffer was new (or newDeferred) and is now free. */ -void PoolGenEmpty(PoolGen pgen, Size unused, Bool ramping) +void PoolGenEmpty(PoolGen pgen, Size unused, Bool deferred) { AVERT(PoolGen, pgen); - AVERT(Bool, ramping); + AVERT(Bool, deferred); - if (ramping) { + if (deferred) { AVER(pgen->newDeferredSize >= unused); pgen->newDeferredSize -= unused; } else { AVER(pgen->newSize >= unused); pgen->newSize -= unused; } - pgen->freeSize += unused; + STATISTIC(pgen->freeSize += unused); } @@ -510,18 +522,18 @@ void PoolGenEmpty(PoolGen pgen, Size unused, Bool ramping) * The memory was new (or newDeferred), is now old (or oldDeferred) */ -void PoolGenAge(PoolGen pgen, Size size, Bool ramping) +void PoolGenAge(PoolGen pgen, Size size, Bool deferred) { AVERT(PoolGen, pgen); - if (ramping) { + if (deferred) { AVER(pgen->newDeferredSize >= size); pgen->newDeferredSize -= size; - pgen->oldDeferredSize += size; + STATISTIC(pgen->oldDeferredSize += size); } else { AVER(pgen->newSize >= size); pgen->newSize -= size; - pgen->oldSize += size; + STATISTIC(pgen->oldSize += size); } } @@ -531,19 +543,21 @@ void PoolGenAge(PoolGen pgen, Size size, Bool ramping) * The reclaimed memory was old, and is now free. */ -void PoolGenReclaim(PoolGen pgen, Size reclaimed, Bool ramping) +void PoolGenReclaim(PoolGen pgen, Size reclaimed, Bool deferred) { AVERT(PoolGen, pgen); - AVERT(Bool, ramping); + AVERT(Bool, deferred); - if (ramping) { - AVER(pgen->oldDeferredSize >= reclaimed); - pgen->oldDeferredSize -= reclaimed; - } else { - AVER(pgen->oldSize >= reclaimed); - pgen->oldSize -= reclaimed; - } - pgen->freeSize += reclaimed; + STATISTIC_STAT ({ + if (deferred) { + AVER(pgen->oldDeferredSize >= reclaimed); + pgen->oldDeferredSize -= reclaimed; + } else { + AVER(pgen->oldSize >= reclaimed); + pgen->oldSize -= reclaimed; + } + pgen->freeSize += reclaimed; + }); } @@ -555,9 +569,11 @@ void PoolGenReclaim(PoolGen pgen, Size reclaimed, Bool ramping) void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize) { AVERT(PoolGen, pgen); - AVER(pgen->oldDeferredSize >= oldSize); - pgen->oldDeferredSize -= oldSize; - pgen->oldSize += oldSize; + STATISTIC_STAT ({ + AVER(pgen->oldDeferredSize >= oldSize); + pgen->oldDeferredSize -= oldSize; + pgen->oldSize += oldSize; + }); AVER(pgen->newDeferredSize >= newSize); pgen->newDeferredSize -= newSize; pgen->newSize += newSize; @@ -569,7 +585,7 @@ void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize) void PoolGenSegSplit(PoolGen pgen) { AVERT(PoolGen, pgen); - ++ pgen->segs; + STATISTIC(++ pgen->segs); } @@ -578,8 +594,10 @@ void PoolGenSegSplit(PoolGen pgen) void PoolGenSegMerge(PoolGen pgen) { AVERT(PoolGen, pgen); - AVER(pgen->segs > 0); - -- pgen->segs; + STATISTIC_STAT ({ + AVER(pgen->segs > 0); + -- pgen->segs; + }); } @@ -595,13 +613,15 @@ void PoolGenFree(PoolGen pgen, Seg seg) AVERT(PoolGen, pgen); AVERT(Seg, seg); - AVER(pgen->segs > 0); - -- pgen->segs; size = SegSize(seg); AVER(pgen->totalSize >= size); pgen->totalSize -= size; - AVER(pgen->freeSize >= size); - pgen->freeSize -= size; + STATISTIC_STAT ({ + AVER(pgen->segs > 0); + -- pgen->segs; + AVER(pgen->freeSize >= size); + pgen->freeSize -= size; + }); SegFree(seg); } @@ -619,7 +639,6 @@ void LocusInit(Arena arena) gen->zones = ZoneSetEMPTY; gen->capacity = 0; /* unused */ gen->mortality = 0.51; - gen->proflow = 0.0; RingInit(&gen->locusRing); gen->sig = GenDescSig; } diff --git a/mps/code/trace.c b/mps/code/trace.c index 74144774564..1f5358bd1e9 100644 --- a/mps/code/trace.c +++ b/mps/code/trace.c @@ -634,33 +634,6 @@ static Res traceFlip(Trace trace) return res; } -/* traceCopySizes -- preserve size information for later use - * - * A PoolGen's newSize is important information that we want to emit in - * a diagnostic message at TraceStart. In order to do that we must copy - * the information before Whiten changes it. This function does that. - */ - -static void traceCopySizes(Trace trace) -{ - Ring node, nextNode; - Index i; - Arena arena = trace->arena; - - RING_FOR(node, &arena->chainRing, nextNode) { - Chain chain = RING_ELT(Chain, chainRing, node); - - for(i = 0; i < chain->genCount; ++i) { - Ring n, nn; - GenDesc desc = &chain->gens[i]; - RING_FOR(n, &desc->locusRing, nn) { - PoolGen gen = RING_ELT(PoolGen, genRing, n); - gen->newSizeAtCreate = gen->newSize; - } - } - } - return; -} /* TraceCreate -- create a Trace object * @@ -677,6 +650,17 @@ static void traceCopySizes(Trace trace) * This code is written to be adaptable to allocating Trace objects * dynamically. */ +static void TraceCreatePoolGen(GenDesc gen) +{ + Ring n, nn; + RING_FOR(n, &gen->locusRing, nn) { + PoolGen pgen = RING_ELT(PoolGen, genRing, n); + EVENT11(TraceCreatePoolGen, gen, gen->capacity, gen->mortality, gen->zones, + pgen->pool, pgen->totalSize, pgen->freeSize, pgen->newSize, + pgen->oldSize, pgen->newDeferredSize, pgen->oldDeferredSize); + } +} + Res TraceCreate(Trace *traceReturn, Arena arena, int why) { TraceId ti; @@ -747,7 +731,24 @@ Res TraceCreate(Trace *traceReturn, Arena arena, int why) /* .. _request.dylan.160098: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/160098 */ ShieldSuspend(arena); - traceCopySizes(trace); + STATISTIC_STAT ({ + /* Iterate over all chains, all GenDescs within a chain, and all + * PoolGens within a GenDesc. */ + Ring node; + Ring nextNode; + + RING_FOR(node, &arena->chainRing, nextNode) { + Chain chain = RING_ELT(Chain, chainRing, node); + Index i; + for (i = 0; i < chain->genCount; ++i) { + GenDesc gen = &chain->gens[i]; + TraceCreatePoolGen(gen); + } + } + + /* Now do topgen GenDesc, and all PoolGens within it. */ + TraceCreatePoolGen(&arena->topGen); + }); *traceReturn = trace; return ResOK; @@ -1564,9 +1565,9 @@ double TraceWorkFactor = 0.25; * * TraceStart should be passed a trace with state TraceINIT, i.e., * recently returned from TraceCreate, with some condemned segments - * added. mortality is the fraction of the condemned set expected to - * survive. finishingTime is relative to the current polling clock, see - * . + * added. mortality is the fraction of the condemned set expected not + * to survive. finishingTime is relative to the current polling clock, + * see . * * .start.black: All segments are black w.r.t. a newly allocated trace. * However, if TraceStart initialized segments to black when it @@ -1588,19 +1589,6 @@ static Res rootGrey(Root root, void *p) } -static void TraceStartPoolGen(GenDesc gen) -{ - Ring n, nn; - RING_FOR(n, &gen->locusRing, nn) { - PoolGen pgen = RING_ELT(PoolGen, genRing, n); - EVENT12(TraceStartPoolGen, gen, gen->capacity, gen->mortality, gen->zones, - pgen->pool, pgen->totalSize, pgen->freeSize, pgen->oldSize, - pgen->newSize, pgen->oldDeferredSize, pgen->newDeferredSize, - pgen->newSizeAtCreate); - } -} - - /* TraceStart -- start a trace whose white set has been established * * The main job of TraceStart is to set up the grey list for a trace. The @@ -1665,26 +1653,6 @@ Res TraceStart(Trace trace, double mortality, double finishingTime) } while (SegNext(&seg, arena, seg)); } - STATISTIC_STAT ({ - /* @@ */ - /* Iterate over all chains, all GenDescs within a chain, */ - /* (and all PoolGens within a GenDesc). */ - Ring node; - Ring nextNode; - Index i; - - RING_FOR(node, &arena->chainRing, nextNode) { - Chain chain = RING_ELT(Chain, chainRing, node); - for(i = 0; i < chain->genCount; ++i) { - GenDesc gen = &chain->gens[i]; - TraceStartPoolGen(gen); - } - } - - /* Now do topgen GenDesc (and all PoolGens within it). */ - TraceStartPoolGen(&arena->topGen); - }); - res = RootsIterate(ArenaGlobals(arena), rootGrey, (void *)trace); AVER(res == ResOK); diff --git a/mps/design/strategy.txt b/mps/design/strategy.txt index 4b09c3cc004..f4d3cdb6e49 100644 --- a/mps/design/strategy.txt +++ b/mps/design/strategy.txt @@ -175,46 +175,113 @@ new segment occupies. Note that this zoneset can never shrink. + +Parameters +.......... + +_`.param.intro`: A generation has two parameters, *capacity* and +*mortality*, specified by the client program. + +_`.param.capacity`: The *capacity* of a generation is the amount of +*new* allocation in that generation (that is, allocation since the +last time the generation was condemned) that will cause the generation +to be collected by ``TracePoll()``. + +_`.param.capacity.misnamed`: The name *capacity* is unfortunate since +it suggests that the total amount of memory in the generation will not +exceed this value. But that will only be the case for pool classes +that always promote survivors to another generation. When there is +*old* allocation in the generation (that is, prior to the last time +the generation was condemned), as there is in the case of non-moving +pool classes, the size of a generation is unrelated to its capacity. + +_`.param.mortality`: The *mortality* of a generation is the proportion +(between 0 and 1) of memory in the generation that is expected to be +dead when the generation is collected. It is used in ``TraceStart()`` +to estimate the amount of data that will have to be scanned in order +to complete the trace. + + Accounting .......... -- ``gen[N].mortality`` +_`.accounting.intro`: Pool generations maintain the sizes of various +categories of data allocated in that generation for that pool. This +accounting information is reported via the event system, but also used +in two places: - - Specified by the client. - - TODO: fill in how this is used. +_`.accounting.poll`: ``ChainDeferral()`` uses the *new size* of each +generation to determine which generations in the chain are over +capacity and so might need to be collected via ``TracePoll()``. -- ``gen[N].capacity`` +_`.accounting.condemn`: ``ChainCondemnAuto()`` uses the *new size* of +each generation to determine which generations in the chain will be +collected; it also uses the *total size* of the generation to compute +the mortality. - - Specified by the client. - - TODO: fill in how this is used. +_`.accounting.check`: Computing the new size for a pool generation is +far from straightforward: see job003772_ for some (former) errors in +this code. In order to assist with checking that this has been +computed correctly, the locus module uses a double-entry book-keeping +system to account for every byte in each pool generation. This uses +six accounts: -- ``amcSeg->new`` +.. _job003772: http://www.ravenbrook.com/project/mps/issue/job003772/ - - TODO: fill this in +_`.account.total`: Memory acquired from the arena. -- ``pgen->totalSize``: +_`.account.total.negated`: From the point of view of the double-entry +system, the *total* should be negative as it is owing to the arena, +but it is inconvenient to represent negative sizes, and so the +positive value is stored instead. - - incremented by ``AMCBufferFill()``; - - decremented by ``amcReclaimNailed()`` and ``AMCReclaim()``; - - added up by ``GenDescTotalSize(gen)``. +_`.account.total.negated.justification`: We don't have a type for +signed sizes; but if we represented it in two's complement using the +unsigned ``Size`` type then Clang's unsigned integer overflow detector +would complain. -- ``pgen->newSize``: +_`.account.free`: Memory that is not in use (free or lost to +fragmentation). - - incremented by ``AMCBufferFill()`` (*when not ramping*) and ``AMCRampEnd()``; - - decremented by ``AMCWhiten()``, - - added up by ``GenDescNewSize(gen)``. +_`.account.new`: Memory in use by the client program, allocated +since the last time the generation was condemned. -- ``gen[N].proflow``: +_`.account.old`: Memory in use by the client program, allocated +prior to the last time the generation was condemned. - - set to 1.0 by ``ChainCreate()``; - - ``arena->topGen.proflow`` set to 0.0 by ``LocusInit(arena)``; - - *The value of this field is never used*. +_`.account.newDeferred`: Memory in use by the client program, +allocated since the last time the generation was condemned, but which +should not cause collections via ``TracePoll()``. (Due to ramping; see +below.) +_`.account.oldDeferred`: Memory in use by the client program, +allocated prior to the last time the generation was condemned, but +which should not cause collections via ``TracePoll()``. (Due to +ramping; see below.) -- ``pgen->newSizeAtCreate``: +_`.accounting.op`: The following operations are provided: + +_`.accounting.op.alloc`: Allocate a segment in a pool generation. +Debit *total*, credit *free*. (But see `.account.total.negated`_.) + +_`.accounting.op.free`: Free a segment. Debit *free*, credit *total*. +(But see `.account.total.negated`_.) + +_`.accounting.op.fill`: Allocate memory, for example by filling a +buffer. Debit *free*, credit *new* or *newDeferred*. + +_`.accounting.op.empty`: Deallocate memory, for example by emptying +the unused portion of a buffer. Debit *new* or *newDeferred*, credit +*free*. + +_`.accounting.op.age`: Condemn memory. Debit *new* or *newDeferred*, +credit *old* or *oldDeferred*. + +_`.accounting.op.reclaim`: Reclaim dead memory. Debit *old* or +*oldDeferred*, credit *free*. + +_`.accounting.op.undefer`: Stop deferring the accounting of memory. Debit *oldDeferred*, credit *old*. Debit *newDeferred*, credit *new*. - - set by ``traceCopySizes()`` (that is its purpose); - - output in the ``TraceStartPoolGen`` telemetry event. Ramps ..... @@ -289,29 +356,31 @@ Reclaiming any AMC segment: Now, some deductions: #. When OUTSIDE, the count is always zero, because (a) it starts that -way, and the only ways to go OUTSIDE are (b) by leaving an outermost -ramp (count goes to zero) or (c) by reclaiming when the count is zero. + way, and the only ways to go OUTSIDE are (b) by leaving an + outermost ramp (count goes to zero) or (c) by reclaiming when the + count is zero. #. When BEGIN, the count is never zero (consider the transitions to -BEGIN and the transition to zero). + BEGIN and the transition to zero). -#. When RAMPING, the count is never zero (again consider transitions to -RAMPING and the transition to zero). +#. When RAMPING, the count is never zero (again consider transitions + to RAMPING and the transition to zero). -#. When FINISH, the count can be anything (the transition to FINISH has -zero count, but the Enter transition when FINISH can change that and -then it can increment to any value). +#. When FINISH, the count can be anything (the transition to FINISH + has zero count, but the Enter transition when FINISH can change + that and then it can increment to any value). #. When COLLECTING, the count can be anything (from the previous fact, -and the transition to COLLECTING). + and the transition to COLLECTING). -#. *This is a bug!!* The ramp generation is not always reset (to forward -to the after-ramp generation). If we get into FINISH and then see -another ramp before the next condemnation of the ramp generation, we -will Enter followed by Leave. The Enter will keep us in FINISH, and -the Leave will take us back to OUTSIDE, skipping the transition to the -COLLECTING state which is what resets the ramp generation forwarding -buffer. [TODO: check whether I made an issue and/or fixed it; NB 2013-06-04] +#. *This is a bug!!* The ramp generation is not always reset (to + forward to the after-ramp generation). If we get into FINISH and + then see another ramp before the next condemnation of the ramp + generation, we will Enter followed by Leave. The Enter will keep us + in FINISH, and the Leave will take us back to OUTSIDE, skipping the + transition to the COLLECTING state which is what resets the ramp + generation forwarding buffer. [TODO: check whether I made an issue + and/or fixed it; NB 2013-06-04] The simplest change to fix this is to change the behaviour of the Leave transition, which should only take us OUTSIDE if we are in BEGIN or From 6adaa6196670012e5c3add084e7e1d2ad5422564 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 1 May 2014 22:06:47 +0100 Subject: [PATCH 37/43] Store flags as bitfields to save space in segment structure. Copied from Perforce Change: 185932 ServerID: perforce.ravenbrook.com --- mps/code/poolamc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mps/code/poolamc.c b/mps/code/poolamc.c index 4baf8cb1789..35dabd62441 100644 --- a/mps/code/poolamc.c +++ b/mps/code/poolamc.c @@ -94,8 +94,8 @@ typedef struct amcSegStruct { GCSegStruct gcSegStruct; /* superclass fields must come first */ amcGen gen; /* generation this segment belongs to */ Nailboard board; /* nailboard for this segment or NULL if none */ - Bool old; /* .seg.old */ - Bool deferred; /* .seg.deferred */ + unsigned old : 1; /* .seg.old */ + unsigned deferred : 1; /* .seg.deferred */ Sig sig; /* */ } amcSegStruct; From b953ee2432df37064833666ad1690fa002f441fd Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 13 May 2014 17:21:25 +0100 Subject: [PATCH 38/43] Document mps_key_spare keyword argument. Copied from Perforce Change: 186085 ServerID: perforce.ravenbrook.com --- mps/manual/source/pool/mvff.rst | 6 ++++++ mps/manual/source/topic/keyword.rst | 1 + 2 files changed, 7 insertions(+) diff --git a/mps/manual/source/pool/mvff.rst b/mps/manual/source/pool/mvff.rst index e688a00d09d..149d533d2d0 100644 --- a/mps/manual/source/pool/mvff.rst +++ b/mps/manual/source/pool/mvff.rst @@ -132,6 +132,12 @@ MVFF interface The minimum alignment supported by pools of this class is ``sizeof(void *)``. + * :c:macro:`MPS_KEY_SPARE` (type :c:type:`double`, default 0.75) + is the maximum proportion of freed memory that the pool will + keep spare for future allocations. If the proportion of memory + that's free exceeds this, then the pool will return some of it + to the arena for use by other pools. + * :c:macro:`MPS_KEY_MVFF_ARENA_HIGH` (type :c:type:`mps_bool_t`, default false) determines whether new segments are acquired at high addresses (if true), or at low addresses (if false). diff --git a/mps/manual/source/topic/keyword.rst b/mps/manual/source/topic/keyword.rst index 3b3fbcb01ad..bca0d471121 100644 --- a/mps/manual/source/topic/keyword.rst +++ b/mps/manual/source/topic/keyword.rst @@ -115,6 +115,7 @@ now :c:macro:`MPS_KEY_ARGS_END`. :c:macro:`MPS_KEY_MVT_RESERVE_DEPTH` :c:type:`mps_count_t` ``count`` :c:func:`mps_class_mvt` :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` ``mps_pool_debug_options_s *`` ``pool_debug_options`` :c:func:`mps_class_ams_debug`, :c:func:`mps_class_mv_debug`, :c:func:`mps_class_mvff_debug` :c:macro:`MPS_KEY_RANK` :c:type:`mps_rank_t` ``rank`` :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_snc` + :c:macro:`MPS_KEY_SPARE` :c:type:`double` ``d`` :c:func:`mps_class_mvff` :c:macro:`MPS_KEY_VMW3_TOP_DOWN` :c:type:`mps_bool_t` ``b`` :c:func:`mps_arena_class_vm` ======================================== ====================================================== ========================================================== From 56179ec206828b1418b11c6c27a99957e3ed8770 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 13 May 2014 17:31:43 +0100 Subject: [PATCH 39/43] Update release notes. Copied from Perforce Change: 186087 ServerID: perforce.ravenbrook.com --- mps/manual/source/pool/mvff.rst | 8 ++++---- mps/manual/source/release.rst | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/mps/manual/source/pool/mvff.rst b/mps/manual/source/pool/mvff.rst index 149d533d2d0..64c220e9506 100644 --- a/mps/manual/source/pool/mvff.rst +++ b/mps/manual/source/pool/mvff.rst @@ -133,10 +133,10 @@ MVFF interface ``sizeof(void *)``. * :c:macro:`MPS_KEY_SPARE` (type :c:type:`double`, default 0.75) - is the maximum proportion of freed memory that the pool will - keep spare for future allocations. If the proportion of memory - that's free exceeds this, then the pool will return some of it - to the arena for use by other pools. + is the maximum proportion of memory that the pool will keep + spare for future allocations. If the proportion of memory that's + free exceeds this, then the pool will return some of it to the + arena for use by other pools. * :c:macro:`MPS_KEY_MVFF_ARENA_HIGH` (type :c:type:`mps_bool_t`, default false) determines whether new segments are acquired at high diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index 0ac53f9020a..21d6ca6c13e 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -54,6 +54,10 @@ Interface changes the value ``FALSE`` is appropriate only when you know that all references are exact. See :ref:`pool-ams`. +#. The :ref:`pool-mvff` pool class takes a new keyword argument + :c:macro:`MPS_KEY_SPARE`. This specifies the maximum proportion of + memory that the pool will keep spare for future allocations. + Other changes ............. @@ -90,6 +94,9 @@ Other changes .. _job003771: https://www.ravenbrook.com/project/mps/issue/job003771/ +#. The :ref:`pool-mvt` and :ref:`pool-mvff` pool classes are now + around 25% faster (in our benchmarks) than they were in release + 1.113.0. .. _release-notes-1.113: From af4cf5fb060c7f52ad5a6aef720f05eefa694f06 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 13 May 2014 17:37:48 +0100 Subject: [PATCH 40/43] Delete files accidentally branched from branch/2014-02-23/mvff-tune. Copied from Perforce Change: 186088 ServerID: perforce.ravenbrook.com --- mps/code/spw3i3mv.c | 66 --------------------------- mps/code/spw3i6mv.c | 67 --------------------------- mps/tool/gcovfmt.py | 109 -------------------------------------------- 3 files changed, 242 deletions(-) delete mode 100644 mps/code/spw3i3mv.c delete mode 100644 mps/code/spw3i6mv.c delete mode 100755 mps/tool/gcovfmt.py diff --git a/mps/code/spw3i3mv.c b/mps/code/spw3i3mv.c deleted file mode 100644 index 3bb8ce94d0f..00000000000 --- a/mps/code/spw3i3mv.c +++ /dev/null @@ -1,66 +0,0 @@ -/* spw3i3mv.c: STACK PROBE FOR 32-BIT WINDOWS - * - * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. - * Portions copyright (C) 2001 Global Graphics Software. - * - * This function reads a location that is depth words beyond the - * current stack pointer. On Intel platforms, the stack grows - * downwards, so this means reading from a location with a lesser - * address. - */ - - -#include "mpm.h" - - -void StackProbe(Size depth) -{ - __asm { - mov eax, depth - neg eax - mov eax, [esp+eax*4] /* do the actual probe */ - } -} - - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2001-2014 Ravenbrook Limited . - * 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. - */ diff --git a/mps/code/spw3i6mv.c b/mps/code/spw3i6mv.c deleted file mode 100644 index 751e9680e4b..00000000000 --- a/mps/code/spw3i6mv.c +++ /dev/null @@ -1,67 +0,0 @@ -/* spw3i6mv.c: STACK PROBE FOR 64-BIT WINDOWS - * - * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. - * - * The function StackProbe ensures that the stack has at least depth - * words available. It achieves this by exploiting an obscure but - * documented feature of Microsoft's function _alloca: "A stack - * overflow exception is generated if the space cannot be allocated." - * _alloca: http://msdn.microsoft.com/en-us/library/wb1s57t5.aspx - * - * The purpose of this function to ensure that the stack overflow - * exception is generated here (before taking the arena lock) where it - * can be handled safely rather than at some later point where the - * arena lock is held and so handling the exception may cause the MPS - * to be entered recursively. - */ - -#include "mpm.h" -#include - -void StackProbe(Size depth) -{ - _alloca(depth*sizeof(Word)); -} - - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2013 Ravenbrook Limited . - * 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. - */ diff --git a/mps/tool/gcovfmt.py b/mps/tool/gcovfmt.py deleted file mode 100755 index 0f17bd0a353..00000000000 --- a/mps/tool/gcovfmt.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/python -# -# $Id$ -# Copyright (c) 2013 Ravenbrook Limited. See end of file for license. -# -# This program takes the output of gcov on standard input and writes a -# human-readable table with a summary, to the file named on the -# command line (or standard output if none is given). The summary line -# is always written to standard output so that in the context of "make -# test" where the detailed test output is being directed to a test log -# file, the coverage summary can still be presented. -# -# gcov output looks like this: -# -# File '/project/mps/master/code/mpsi.c' -# Lines executed:85.12% of 921 -# /project/mps/master/code/mpsi.c:creating 'mpsi.c.gcov' -# -# Note that we select only the .c files (there may also be output for -# system files like signal.h with inline function definitions, and we -# are not interested in covering them). The MPS has no inline function -# definitions in headers. - -from sys import argv, stdin, stdout -from re import match - -def coverage(): - """For each .c file with coverage data, generate a triple (percent - coverage, file name, number of lines). - - """ - for line in stdin: - m1 = match(r"File '.*/([^/]+\.c)'$", line) - if not m1: - continue - m2 = match(r"Lines executed:(\d[0-9.]*)% of (\d+)$", next(stdin)) - if m2: - yield float(m2.group(1)), m1.group(1), int(m2.group(2)) - -def main(): - if len(argv) >= 2: - out = open(argv[1], 'a') - else: - out = stdout - fmt1 = "{0:<16s} {1:<7s} {2:<7s} {3:<7s}\n" - fmt2 = "{0:<16s} {1:7d} {2:7d} {3:6.2f}%\n" - underlines = "---------------- ------- ------- -------".split() - out.write(fmt1.format(*"File Lines Covered Percent".split())) - out.write(fmt1.format(*underlines)) - total_lines, total_covered = 0, 0 - for percent, file, lines in sorted(coverage()): - covered = int(round(lines * percent / 100)) - total_lines += lines - total_covered += covered - out.write(fmt2.format(file, lines, covered, percent)) - out.write(fmt1.format(*underlines)) - if total_lines == 0: - total_percent = 100.0 - else: - total_percent = 100.0 * total_covered / total_lines - summary = fmt2.format("COVERAGE TOTAL", total_lines, total_covered, - total_percent) - out.write(summary) - if out != stdout: - stdout.write(summary) - -if __name__ == '__main__': - main() - - -# C. COPYRIGHT AND LICENSE -# -# Copyright (C) 2013 Ravenbrook Limited . -# 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. From 9b98c26ff3c7bda4c6c426c8b11b5736d90fe034 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 13 May 2014 17:49:35 +0100 Subject: [PATCH 41/43] Describe the block pool. Copied from Perforce Change: 186091 ServerID: perforce.ravenbrook.com --- mps/code/poolmvff.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index b874fbec340..2363ea3a772 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -650,22 +650,21 @@ static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream) " firstFit $U\n", (WriteFU)mvff->firstFit, " slotHigh $U\n", (WriteFU)mvff->slotHigh, NULL); - if (res != ResOK) - return res; + if (res != ResOK) return res; /* TODO: SegPrefDescribe(MVFFSegPref(mvff), stream); */ + res = PoolDescribe(MVFFBlockPool(mvff), stream); + if (res != ResOK) return res; + res = LandDescribe(MVFFTotalCBS(mvff), stream); - if (res != ResOK) - return res; + if (res != ResOK) return res; res = LandDescribe(MVFFFreeCBS(mvff), stream); - if (res != ResOK) - return res; + if (res != ResOK) return res; res = LandDescribe(MVFFFreelist(mvff), stream); - if (res != ResOK) - return res; + if (res != ResOK) return res; res = WriteF(stream, "}\n", NULL); return res; From 6980f345f1705c38a219180d99e4ffdcd632c4ed Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 13 May 2014 18:04:59 +0100 Subject: [PATCH 42/43] Add note about potential for speed improvement, as requested by rb. Copied from Perforce Change: 186093 ServerID: perforce.ravenbrook.com --- mps/code/poolmvff.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index 2363ea3a772..c3e18df5aec 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -10,11 +10,16 @@ * * .design: * - * * TRANSGRESSIONS * * .trans.stat: mps_mvff_stat is a temporary hack for measurement purposes, * see .stat below. + * + * NOTE + * + * There's potential for up to 4% speed improvement by calling Land + * methods statically instead of indirectly via the Land abstraction + * (thus, cbsInsert instead of LandInsert, and so on). */ #include "cbs.h" From 32f87cbdc16a1ca99556918141f44f7dac0e21de Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 13 May 2014 22:10:04 +0100 Subject: [PATCH 43/43] Fix formatting of table. Copied from Perforce Change: 186097 ServerID: perforce.ravenbrook.com --- mps/manual/source/topic/keyword.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps/manual/source/topic/keyword.rst b/mps/manual/source/topic/keyword.rst index bca0d471121..459f703e35f 100644 --- a/mps/manual/source/topic/keyword.rst +++ b/mps/manual/source/topic/keyword.rst @@ -115,7 +115,7 @@ now :c:macro:`MPS_KEY_ARGS_END`. :c:macro:`MPS_KEY_MVT_RESERVE_DEPTH` :c:type:`mps_count_t` ``count`` :c:func:`mps_class_mvt` :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` ``mps_pool_debug_options_s *`` ``pool_debug_options`` :c:func:`mps_class_ams_debug`, :c:func:`mps_class_mv_debug`, :c:func:`mps_class_mvff_debug` :c:macro:`MPS_KEY_RANK` :c:type:`mps_rank_t` ``rank`` :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_snc` - :c:macro:`MPS_KEY_SPARE` :c:type:`double` ``d`` :c:func:`mps_class_mvff` + :c:macro:`MPS_KEY_SPARE` :c:type:`double` ``d`` :c:func:`mps_class_mvff` :c:macro:`MPS_KEY_VMW3_TOP_DOWN` :c:type:`mps_bool_t` ``b`` :c:func:`mps_arena_class_vm` ======================================== ====================================================== ==========================================================