From f3c8926e045956be1c3a55ff67cedcb0314163bb Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 30 Mar 2014 18:10:33 +0100 Subject: [PATCH 01/53] 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 02/53] 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 03/53] 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 04/53] 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 05/53] 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 06/53] 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 07/53] 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 08/53] 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 09/53] 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 10/53] 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 11/53] 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 12/53] 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 13/53] 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 14/53] 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 15/53] 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 16/53] 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 4deafee12538813b282f124e50b32ad487ce9a5b Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 15 Apr 2014 14:23:53 +0100 Subject: [PATCH 17/53] 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 18/53] 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 b3a3b0b8fc21d72ca4342c7befa8a9cfb7b4e3c1 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 16 Apr 2014 10:24:26 +0100 Subject: [PATCH 19/53] 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 20/53] 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 21/53] 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 22/53] 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 23/53] 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 bb214490973a4cff1c902fd41df3b78926f99292 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 18 May 2014 22:46:16 +0100 Subject: [PATCH 24/53] Landiterate now returns a bool indicating whether all visitor calls returned true. Copied from Perforce Change: 186165 ServerID: perforce.ravenbrook.com --- mps/code/cbs.c | 6 +++--- mps/code/failover.c | 6 +++--- mps/code/freelist.c | 5 +++-- mps/code/land.c | 10 ++++++---- mps/code/mpm.h | 2 +- mps/code/mpmtypes.h | 2 +- mps/design/land.txt | 6 ++++-- 7 files changed, 21 insertions(+), 16 deletions(-) diff --git a/mps/code/cbs.c b/mps/code/cbs.c index 383ac47c472..c7b4478ce4c 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -748,7 +748,7 @@ static Bool cbsIterateVisit(Tree tree, void *closureP, Size closureS) return TRUE; } -static void cbsIterate(Land land, LandVisitor visitor, +static Bool cbsIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) { CBS cbs; @@ -769,8 +769,8 @@ static void cbsIterate(Land land, LandVisitor visitor, closure.visitor = visitor; closure.closureP = closureP; closure.closureS = closureS; - (void)TreeTraverse(SplayTreeRoot(splay), splay->compare, splay->nodeKey, - cbsIterateVisit, &closure, 0); + return TreeTraverse(SplayTreeRoot(splay), splay->compare, splay->nodeKey, + cbsIterateVisit, &closure, 0); } diff --git a/mps/code/failover.c b/mps/code/failover.c index e8a0dbc7dae..80ecb0a6210 100644 --- a/mps/code/failover.c +++ b/mps/code/failover.c @@ -164,7 +164,7 @@ static Res failoverDelete(Range rangeReturn, Land land, Range range) } -static void failoverIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +static Bool failoverIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) { Failover fo; @@ -173,8 +173,8 @@ static void failoverIterate(Land land, LandVisitor visitor, void *closureP, Size AVERT(Failover, fo); AVER(visitor != NULL); - LandIterate(fo->primary, visitor, closureP, closureS); - LandIterate(fo->secondary, visitor, closureP, closureS); + return LandIterate(fo->primary, visitor, closureP, closureS) + && LandIterate(fo->secondary, visitor, closureP, closureS); } diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 1e071e06763..241f08ff190 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -438,7 +438,7 @@ static Res freelistDelete(Range rangeReturn, Land land, Range range) } -static void freelistIterate(Land land, LandVisitor visitor, +static Bool freelistIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) { Freelist fl; @@ -468,8 +468,9 @@ static void freelistIterate(Land land, LandVisitor visitor, } cur = next; if (!cont) - break; + return FALSE; } + return TRUE; } diff --git a/mps/code/land.c b/mps/code/land.c index 9ff8257151c..7221514fff3 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -233,15 +233,17 @@ Res LandDelete(Range rangeReturn, Land land, Range range) * See */ -void LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) { + Bool res; AVERT(Land, land); AVER(FUNCHECK(visitor)); landEnter(land); - (*land->class->iterate)(land, visitor, closureP, closureS); + res = (*land->class->iterate)(land, visitor, closureP, closureS); landLeave(land); + return res; } @@ -503,13 +505,13 @@ static Res landNoDelete(Range rangeReturn, Land land, Range range) return ResUNIMPL; } -static void landNoIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +static Bool landNoIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) { AVERT(Land, land); AVER(visitor != NULL); UNUSED(closureP); UNUSED(closureS); - NOOP; + return FALSE; } static Bool landNoFind(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 9d5854b8cf6..0a87b8fc81d 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -1014,7 +1014,7 @@ 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 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); diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index 04f73a7e42d..d81255c974d 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -272,7 +272,7 @@ 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); -typedef void (*LandIterateMethod)(Land land, LandVisitor visitor, void *closureP, Size closureS); +typedef Bool (*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); diff --git a/mps/design/land.txt b/mps/design/land.txt index 11f192fa6ff..3ed8b466b0d 100644 --- a/mps/design/land.txt +++ b/mps/design/land.txt @@ -164,13 +164,15 @@ strategy. _`.function.delete.alias`: It is acceptable for ``rangeReturn`` and ``range`` to share storage. -``void LandIterate(Land land, LandIterateMethod iterate, void *closureP, Size closureS)`` +``Bool 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. If the iterator method -returns ``FALSE``, then the iteration is terminated. +returns ``FALSE``, then the iteration is terminated and +``LandIterate()`` returns ``FALSE``. If all iterator method calls +return ``TRUE``, then ``LandIterate()`` returns ``TRUE`` ``Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)`` From 64f59a379708f1fefdb93e82515d39606f664d27 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 18 May 2014 22:50:22 +0100 Subject: [PATCH 25/53] Ignore or use the result of landiterate. Copied from Perforce Change: 186166 ServerID: perforce.ravenbrook.com --- mps/code/freelist.c | 2 +- mps/code/land.c | 4 ++-- mps/code/landtest.c | 2 +- mps/code/poolmv2.c | 9 ++------- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 241f08ff190..4040630b1c0 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -749,7 +749,7 @@ static Res freelistDescribe(Land land, mps_lib_FILE *stream) " listSize = $U\n", (WriteFU)fl->listSize, NULL); - LandIterate(land, freelistDescribeVisitor, stream, 0); + (void)LandIterate(land, freelistDescribeVisitor, stream, 0); res = WriteF(stream, "}\n", NULL); return res; diff --git a/mps/code/land.c b/mps/code/land.c index 7221514fff3..c0f5f2c1cbc 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -416,7 +416,7 @@ void LandFlush(Land dest, Land src) AVERT(Land, dest); AVERT(Land, src); - LandIterate(src, landFlushVisitor, dest, 0); + (void)LandIterate(src, landFlushVisitor, dest, 0); } @@ -485,7 +485,7 @@ static Bool landSizeVisitor(Bool *deleteReturn, Land land, Range range, Size LandSlowSize(Land land) { Size size = 0; - LandIterate(land, landSizeVisitor, &size, 0); + (void)LandIterate(land, landSizeVisitor, &size, 0); return size; } diff --git a/mps/code/landtest.c b/mps/code/landtest.c index ef13e196600..988b4be9391 100644 --- a/mps/code/landtest.c +++ b/mps/code/landtest.c @@ -115,7 +115,7 @@ static void check(TestState state) closure.limit = addrOfIndex(state, ArraySize); closure.oldLimit = state->block; - LandIterate(state->land, checkVisitor, (void *)&closure, 0); + (void)LandIterate(state->land, checkVisitor, (void *)&closure, 0); if (closure.oldLimit == state->block) Insist(BTIsSetRange(state->allocTable, 0, diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index 45a38592946..e8b38489253 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -1240,7 +1240,7 @@ static void MVTRefillABQIfEmpty(MVT mvt, Size size) if (mvt->abqOverflow && ABQIsEmpty(MVTABQ(mvt))) { mvt->abqOverflow = FALSE; METER_ACC(mvt->refills, size); - LandIterate(MVTFailover(mvt), &MVTRefillVisitor, mvt, 0); + (void)LandIterate(MVTFailover(mvt), &MVTRefillVisitor, mvt, 0); } } @@ -1250,7 +1250,6 @@ static void MVTRefillABQIfEmpty(MVT mvt, Size size) typedef struct MVTContigencyClosureStruct { MVT mvt; - Bool found; RangeStruct range; Arena arena; Size min; @@ -1287,7 +1286,6 @@ static Bool MVTContingencyVisitor(Bool *deleteReturn, Land land, Range range, /* verify that min will fit when seg-aligned */ if (size >= 2 * cl->min) { RangeInit(&cl->range, base, limit); - cl->found = TRUE; return FALSE; } @@ -1295,7 +1293,6 @@ static Bool MVTContingencyVisitor(Bool *deleteReturn, Land land, Range range, cl->hardSteps++; if (MVTCheckFit(base, limit, cl->min, cl->arena)) { RangeInit(&cl->range, base, limit); - cl->found = TRUE; return FALSE; } @@ -1309,14 +1306,12 @@ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, MVTContigencyClosureStruct cls; cls.mvt = mvt; - cls.found = FALSE; cls.arena = PoolArena(MVT2Pool(mvt)); cls.min = min; cls.steps = 0; cls.hardSteps = 0; - LandIterate(MVTFailover(mvt), MVTContingencyVisitor, (void *)&cls, 0); - if (!cls.found) + if (LandIterate(MVTFailover(mvt), MVTContingencyVisitor, (void *)&cls, 0)) return FALSE; AVER(RangeSize(&cls.range) >= min); From 22278bce9e566afe60940110fcc6ecd15918f5c9 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Mon, 19 May 2014 16:07:24 +0100 Subject: [PATCH 26/53] Fix typo. Copied from Perforce Change: 186192 ServerID: perforce.ravenbrook.com --- mps/design/land.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps/design/land.txt b/mps/design/land.txt index 3ed8b466b0d..b9f80b29d23 100644 --- a/mps/design/land.txt +++ b/mps/design/land.txt @@ -225,7 +225,7 @@ _`.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 ``ResOK``. (The first such block, if ``high`` is ``FALSE``, or the last, if ``high`` is ``TRUE``.) If -there is no such block, , return ``ResFAIL``. +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 From 1683196b8971226f2691acfe7eda019d4ad71163 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 23 May 2014 15:58:51 +0100 Subject: [PATCH 27/53] Memory management reference progress. Copied from Perforce Change: 186264 ServerID: perforce.ravenbrook.com --- mps/manual/source/index.rst | 6 + mps/manual/source/make-mmref.py | 168 +++++++++++++ mps/manual/source/mmref-copyright.rst | 26 ++ mps/manual/source/mmref/index.rst | 2 + mps/manual/source/themes/mmref/layout.html | 7 +- .../source/themes/mmref/static/metal.png | Bin 0 -> 43694 bytes .../source/themes/mmref/static/mmref.css_t | 34 ++- .../source/themes/mmref/static/watermark.png | Bin 0 -> 75141 bytes .../source/themes/mmref/static/watermark.svg | 237 ++++++++++++++++++ mps/manual/source/themes/mmref/theme.conf | 14 +- 10 files changed, 481 insertions(+), 13 deletions(-) create mode 100755 mps/manual/source/make-mmref.py create mode 100644 mps/manual/source/mmref-copyright.rst create mode 100644 mps/manual/source/themes/mmref/static/metal.png create mode 100644 mps/manual/source/themes/mmref/static/watermark.png create mode 100644 mps/manual/source/themes/mmref/static/watermark.svg diff --git a/mps/manual/source/index.rst b/mps/manual/source/index.rst index 161a13ad3b3..91d27f7a8a8 100644 --- a/mps/manual/source/index.rst +++ b/mps/manual/source/index.rst @@ -18,6 +18,12 @@ Memory Pool System Memory Management Reference ########################### +.. toctree:: + :hidden: + + mmref-index + mmref-copyright + .. toctree:: :maxdepth: 2 diff --git a/mps/manual/source/make-mmref.py b/mps/manual/source/make-mmref.py new file mode 100755 index 00000000000..92add8bbbea --- /dev/null +++ b/mps/manual/source/make-mmref.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# +# Ravenbrook +# +# +# MAKE THE MEMORY MANAGEMENT REFERENCE +# +# Gareth Rees, Ravenbrook Limited, 2014-05-23 +# +# +# 1. INTRODUCTION +# +# This script builds the Memory Management Reference website from the +# Memory Pool System manual. +# +# The whole build procedure is as follows: +# +# 1. Sync //info.ravenbrook.com/project/mps/master/manual/... +# 2. make html MMREF=1 +# 3. Run this script +# +# +# 2. DESIGN +# +# We build the Memory Management Reference out of the Memory Pool +# System manual because: +# +# 1. having a single set of sources makes it easier to work on; +# 2. the glossary is a vital tool in organizing the MPS manual; +# 3. cross-references from the MMRef to the MPS are an opportunity +# for advertising the latter to readers of the former. +# +# +# 3. DEPENDENCIES +# +# html5lib +# six + +import html5lib +import html5lib.serializer +import html5lib.treewalkers +from io import open +import os +import re +from shutil import copyfile +import sys +from six.moves.urllib.parse import urljoin + + +# 4. CONFIGURATION + +# Subdirectories of the MPS manual that belong in the MMRef. +mmref_dirs = ('glossary', 'mmref', '_images', '_static') + +# Top-level files that belong in the MMRef. +mmref_files = ('index', 'copyright') + +# Regular expression matching files to be included in the MMRef. +url_filter_re = re.compile(r'^/html/(?:(?:{})\.html)?(?:#.*)?$|/(?:{})/'.format( + '|'.join(mmref_files), '|'.join(mmref_dirs))) + +# Root URL for the MPS manual. +rewrite_url = 'http://www.ravenbrook.com/project/mps/master/manual/html/' + + +def rewrite_links(src, src_base, url_filter, rewrite_base, + url_attributes = (('a', 'href'),)): + """Rewrite URLs in src and return the result. + + First, src is parsed as HTML. Second, all URLs found in the + document are resolved relative to src_base and the result passed to + the functions url_filter. If this returns False, the URL is resolved + again, this time relative to rewrite_base, and the result stored + back to the document. Finally, the updated document is serialized + as HTML and returned. + + The keyword argument url_attributes is a sequence of (tag, + attribute) pairs that contain URLs to be rewritten. + + """ + tree_builder = html5lib.treebuilders.getTreeBuilder('dom') + parser = html5lib.html5parser.HTMLParser(tree = tree_builder) + dom = parser.parse(src) + + for tag, attr in url_attributes: + for e in dom.getElementsByTagName(tag): + u = e.getAttribute(attr) + if u and not url_filter(urljoin(src_base, u)): + rewritten = urljoin(rewrite_base, u) + if u != rewritten: + print(" {} -> {}".format(u, rewritten)) + e.setAttribute(attr, rewritten) + + tree_walker = html5lib.treewalkers.getTreeWalker('dom') + html_serializer = html5lib.serializer.htmlserializer.HTMLSerializer() + return u''.join(html_serializer.serialize(tree_walker(dom))) + +def rewrite_file(src_dir, src_filename, target_path, rewrite_url): + src_path = os.path.join(src_dir, src_filename) + if (os.path.exists(target_path) + and os.stat(src_path).st_mtime <= os.stat(target_path).st_mtime): + return + print("Converting {} -> {}".format(src_path, target_path)) + src = open(os.path.join(src_dir, src_filename), encoding='utf-8').read() + src_base = '/{}/'.format(src_dir) + url_filter = url_filter_re.search + rewrite_base = urljoin(rewrite_url, src_dir) + result = rewrite_links(src, src_base, url_filter, rewrite_base) + open(target_path, 'w', encoding='utf-8').write(result) + +def main(argv): + src_root = 'html' + target_root = 'mmref' + for d in mmref_dirs: + src_dir = os.path.join(src_root, d) + target_dir = os.path.join(target_root, d) + os.makedirs(target_dir, exist_ok=True) + for f in os.listdir(src_dir): + target_path = os.path.join(target_dir, f) + if os.path.splitext(f)[1] == '.html': + rewrite_file(src_dir, f, target_path, rewrite_url) + else: + copyfile(os.path.join(src_dir, f), target_path) + for f in mmref_files: + rewrite_file(src_root, 'mmref-{}.html'.format(f), + os.path.join(target_root, '{}.html'.format(f)), + rewrite_url) + + +if __name__ == '__main__': + main(sys.argv) + + +# B. DOCUMENT HISTORY +# +# 2014-05-23 GDR Created. +# +# +# C. COPYRIGHT AND LICENCE +# +# Copyright (c) 2014 Ravenbrook Ltd. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# +# $Id: //info.ravenbrook.com/project/mps/master/tool/branch#9 $ diff --git a/mps/manual/source/mmref-copyright.rst b/mps/manual/source/mmref-copyright.rst new file mode 100644 index 00000000000..b2e95ecd3a7 --- /dev/null +++ b/mps/manual/source/mmref-copyright.rst @@ -0,0 +1,26 @@ +Copyright +********* + + +Use subject to copyright restrictions +===================================== + +The copyright in The Memory Management Reference is owned by +`Ravenbrook Limited`_. + +.. _Ravenbrook Limited: http://www.ravenbrook.com/ + +Permission to copy part or all of The Memory Management Reference for +personal or classroom use is granted without fee, provided that copies +are not made or distributed for profit or commercial advantage; that +the copyright notice, the title of the publication, and its date +appear; and that notice is given that copying is by permission of +Ravenbrook Limited. To copy otherwise, to republish, to post on +servers, or to redistribute to lists requires prior specific +permission. + + +Warranty disclaimer +=================== + +The Memory Management Reference is provided "as is" without warranty of any kind, express or implied, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement. diff --git a/mps/manual/source/mmref/index.rst b/mps/manual/source/mmref/index.rst index eb6ef48257e..6ca51d1fb39 100644 --- a/mps/manual/source/mmref/index.rst +++ b/mps/manual/source/mmref/index.rst @@ -1,3 +1,5 @@ +.. _mmref-intro: + Introduction to memory management ################################# diff --git a/mps/manual/source/themes/mmref/layout.html b/mps/manual/source/themes/mmref/layout.html index 0597e1ca7ef..961baeda428 100644 --- a/mps/manual/source/themes/mmref/layout.html +++ b/mps/manual/source/themes/mmref/layout.html @@ -17,15 +17,14 @@ {% block content %}
- {%- if prev and '../' not in prev.link %} + {%- if prev and '/' not in prev.link and 'mmref-' not in prev.link %} « {{ prev.title }} | {%- endif %} {{ title }} - {%- if next and '../' not in next.link %} + {%- if next and '/' not in next.link and 'mmref-' not in next.link %} | {{ next.title }} » {%- endif %}
diff --git a/mps/manual/source/themes/mmref/static/metal.png b/mps/manual/source/themes/mmref/static/metal.png new file mode 100644 index 0000000000000000000000000000000000000000..2f9f1ad08473dcf728044895fd7809749e38eb9e GIT binary patch literal 43694 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
C!Jx` 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⪙&q=#tG>6de17c{BTQQMXN{V(0X&DK0cOGJ zEOs)g7nJR`wy4=pr>;5EV6y=F`}vkL<O7(<{Rth3!_Cm+aI~7=Lkt+mz|Fm-g1qO3K0=rK{Q@U)sqs%OfGkdk%8OikgXr|cSY37kQ&!SUWTRQw z&n;h?V!E$7joL041iJQ4j!Tg++Ej5d7+8S-E|2~EdcscpuE6lm;_@30Di8$AC$_jq zJwrit#k|G&r>^tav!e0%~fdfY`Cgzev&|x2UK2^+(&H zWd{M-q{X6=r^mQ0uHlo}FnR(idaUK-FAC~7EDY^4tB7^Hc?C#dxXx-Un3L(x%#_0HuLyTSQRIkSRCDR??yS`{xpisiwvj z73%#aMkEa^nj$9f3?5$g9dL3^{NWtlu@jqY7K1cMndJA!rRn(YVI2PgoP4RA;jXzA z$%*N&26D*5g;kt^E1S{Yg_G*$koJt75oM(Elf|0F^WB& znxR11>ow!!v0p8Yu=)E!@3fP@ZDpiXPC-xp$_eId4mTE%GnL&f*6G`hVS9e&L4B%Q zWYqgB|8_zw3tJ;SHGTft_tbfpM!&CV%8MLfF^5fS3A@ApR#Mz z*fG=BgQ`J_@&F^4qtdFghj|5$PK(Xm5%>bd*7X~~l4-Nf{2HlrquM+=f)ve7E(drZ z39N@Af$Z8qZDp)PI(rFB_TL5VYPJcG4GYQ^s4g4^17=L6Sk|en7`8^l zRGg3W<-m_hNB*zoRycE8ug4M1bQ`N;>MDefQNR@BEt?AKeyx9(g6latGPJkubQwrZ z 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 literal 0 HcmV?d00001 diff --git a/mps/manual/source/themes/mmref/static/watermark.svg b/mps/manual/source/themes/mmref/static/watermark.svg new file mode 100644 index 00000000000..ca9159234e1 --- /dev/null +++ b/mps/manual/source/themes/mmref/static/watermark.svg @@ -0,0 +1,237 @@ + + + + + + + + + + image/svg+xml + + + + + + + + 2e2f 6d70 7369 2e63 0073 697a 6520 3e203000 6d70 735f 6172 656e 615f 6f20 213d204e 554c 4c00 6d70 735f 706f 6f6c 5f6f2021 3d20 4e55 4c4c 006d 7073 5f66 6d745f6f 2021 3d20 4e55 4c4c 006d 7073 5f666d74 5f41 2021 3d20 4e55 4c4c 006d 70735f66 6d74 5f42 2021 3d20 4e55 4c4c 006d7073 5f66 6d74 2021 3d20 4e55 4c4c 006d7073 5f66 6d74 5f66 6978 6564 2021 3d204e55 4c4c 0054 4553 5454 2846 6f72 6d61742c 2066 6f72 6d61 7429 0054 4553 54542850 6f6f 6c2c 2070 6f6f 6c29 0070 5f6f2021 3d20 4e55 4c4c 006d 7073 5f61 705f6f20 213d 204e 554c 4c00 6d70 735f 61702021 3d20 4e55 4c4c 0054 4553 5454 28427566 6665 722c 2062 7566 2900 5445 53545428 4275 6666 6572 2c20 4275 6666 65724f66 4150 286d 7073 5f61 7029 2900 6d70735f 6170 2d3e 696e 6974 203d 3d20 6d70735f 6170 2d3e 616c 6c6f 6300 7020 213d204e 554c 4c00 7020 3d3d 206d 7073 5f61702d 3e69 6e69 7400 2876 6f69 6420 2a292828 6368 6172 202a 296d 7073 5f61 702d3e69 6e69 7420 2b20 7369 7a65 2920 3d3d206d 7073 5f61 702d 3e61 6c6c 6f63 00667261 6d65 5f6f 2021 3d20 4e55 4c4c 0053697a 6549 7341 6c69 676e 6564 2873 697a652c 2042 7566 6665 7250 6f6f 6c28 62756629 2d3e 616c 6967 6e6d 656e 7429 006d7073 5f73 6163 5f6f 2021 3d20 4e55 4c4c0054 4553 5454 2853 4143 2c20 7361 63290054 4553 5454 2853 4143 2c20 5341 434f6645 7874 6572 6e61 6c53 4143 286d 7073 + + diff --git a/mps/manual/source/themes/mmref/theme.conf b/mps/manual/source/themes/mmref/theme.conf index 0977e252b47..e0ef30ece1b 100644 --- a/mps/manual/source/themes/mmref/theme.conf +++ b/mps/manual/source/themes/mmref/theme.conf @@ -1,18 +1,18 @@ -# Colour scheme: +# Colour scheme: [theme] inherit = scrolls stylesheet = mmref.css - [options] -headerbg = #B38184 +headerbg = transparent +headerhover = #81A8B8 subheadlinecolor = #000000 -linkcolor = #73626E -visitedlinkcolor = #73626E -admonitioncolor = #aaa +linkcolor = #5D7985 +visitedlinkcolor = #5D7985 +admonitioncolor = #A4BCC2 textcolor = #000000 -underlinecolor = #aaa +underlinecolor = #A4BCC2 bodyfont = 'Optima', sans-serif headfont = 'Verdana', sans-serif From f5a0a18095c6fb57a71be99e359a8be289b6ae4f Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 23 May 2014 21:23:47 +0100 Subject: [PATCH 28/53] Don't copy diagrams unless they are newer. Copied from Perforce Change: 186266 ServerID: perforce.ravenbrook.com --- mps/manual/source/extensions/mps/designs.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/mps/manual/source/extensions/mps/designs.py b/mps/manual/source/extensions/mps/designs.py index 83eb78c7115..ceaee15a256 100644 --- a/mps/manual/source/extensions/mps/designs.py +++ b/mps/manual/source/extensions/mps/designs.py @@ -124,6 +124,15 @@ def convert_file(name, source, dest): with open(dest, 'wb') as out: out.write(s.encode('utf-8')) +def newer(src, target): + """Return True if src is newer (that is, modified more recently) than + target, False otherwise. + + """ + return (not os.path.isfile(target) + or os.path.getmtime(target) < os.path.getmtime(src) + or os.path.getmtime(target) < os.path.getmtime(__file__)) + # Mini-make def convert_updated(app): app.info(bold('converting MPS design documents')) @@ -131,11 +140,11 @@ def convert_updated(app): name = os.path.splitext(os.path.basename(design))[0] if name == 'index': continue converted = 'source/design/%s.rst' % name - if (not os.path.isfile(converted) - or os.path.getmtime(converted) < os.path.getmtime(design) - or os.path.getmtime(converted) < os.path.getmtime(__file__)): + if newer(design, converted): app.info('converting design %s' % name) convert_file(name, design, converted) for diagram in glob.iglob('../design/*.svg'): - shutil.copyfile(diagram, 'source/design/%s' % os.path.basename(diagram)) - + target = os.path.join('source/design/', os.path.basename(diagram)) + if newer(diagram, target): + shutil.copyfile(diagram, target) + From 8ea33dc2c76f5f3210d72cdd8bc5c7a961debfd8 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 23 May 2014 21:24:23 +0100 Subject: [PATCH 29/53] Big up the memory pool system. Copied from Perforce Change: 186267 ServerID: perforce.ravenbrook.com --- mps/manual/source/mmref/faq.rst | 17 +++++++++++------ mps/manual/source/mmref/lang.rst | 24 ++++++++++++++---------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/mps/manual/source/mmref/faq.rst b/mps/manual/source/mmref/faq.rst index c45ba810d64..594f1656b97 100644 --- a/mps/manual/source/mmref/faq.rst +++ b/mps/manual/source/mmref/faq.rst @@ -22,6 +22,7 @@ garbage collection>` for :term:`C` exist as add-on libraries. .. link:: + `Memory Pool System `_, `Boehm–Demers–Weiser collector `_. @@ -130,6 +131,7 @@ semi-conservative garbage collectors for C++. .. link:: + `Memory Pool System `_, `Boehm–Demers–Weiser collector `_. @@ -163,11 +165,11 @@ In :term:`C++`, it may be that class libraries expect you to call Failing this, if there is a genuine :term:`memory leak` in a class library for which you don't have the source, then the only thing you -can try is to add a :term:`garbage collector`. The Boehm–Demers–Weiser -collector will work with C++. +can try is to add a :term:`garbage collector`. .. link:: + `Memory Pool System `_, `Boehm–Demers–Weiser collector `_. @@ -400,7 +402,7 @@ Where can I find out more about garbage collection? Many modern languages have :term:`garbage collection` built in, and the language documentation should give details. For some other languages, garbage collection can be added, for example via the -Boehm–Demers–Weiser collector. +Memory Pool System, or the Boehm–Demers–Weiser collector. .. seealso:: :term:`garbage collection` @@ -408,6 +410,7 @@ Boehm–Demers–Weiser collector. .. link:: + `Memory Pool System `_, `Boehm–Demers–Weiser collector `_, `GC-LIST FAQ `_. @@ -415,14 +418,16 @@ Boehm–Demers–Weiser collector. Where can I get a garbage collector? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The Boehm–Demers–Weiser collector is suitable for C or C++. The best way to -get a garbage collector, however, is to program in a language that -provides garbage collection. +The Memory Pool System and the Boehm–Demers–Weiser collector are +suitable for C or C++. The best way to get a garbage collector, +however, is to program in a language that provides garbage collection +natively. .. seealso:: :term:`garbage collection` .. link:: + `Memory Pool System `_, `Boehm–Demers–Weiser collector `_. diff --git a/mps/manual/source/mmref/lang.rst b/mps/manual/source/mmref/lang.rst index 31a22cc0717..247eb7c6c25 100644 --- a/mps/manual/source/mmref/lang.rst +++ b/mps/manual/source/mmref/lang.rst @@ -53,8 +53,9 @@ Memory management in various languages library functions for :term:`memory (2)` management in C, :term:`malloc` and :term:`free (2)`, have become almost synonymous with :term:`manual memory management`), although - with the Boehm–Demers–Weiser :term:`collector (1)`, it is now - possible to use :term:`garbage collection`. + with the Memory Pool System, or the Boehm–Demers–Weiser + collector, it is now possible to use :term:`garbage + collection`. The language is notorious for fostering memory management bugs, including: @@ -86,6 +87,7 @@ Memory management in various languages .. link:: + `Memory Pool System `_, `Boehm–Demers–Weiser collector `_, `C standardization `_, `comp.lang.c Frequently Asked Questions `_. @@ -148,11 +150,11 @@ Memory management in various languages The :term:`garbage collector` in the .NET Framework is configurable to run in soft real time, or in batch mode. - The Mono runtime comes with two collectors: the Boehm–Weiser - :term:`conservative collector `, and a :term:`generational ` :term:`copying collector `. + The Mono runtime comes with two collectors: the + Boehm–Demers–Weiser :term:`conservative collector + `, and a :term:`generational + ` :term:`copying collector + `. .. link:: @@ -173,9 +175,9 @@ Memory management in various languages abstraction level of C++ makes the bookkeeping required for :term:`manual memory management` even harder. Although the standard library provides only manual memory management, with - the Boehm–Demers–Weiser :term:`collector (1)`, it is now possible to - use :term:`garbage collection`. :term:`Smart pointers` are - another popular solution. + the Memory Pool System, or the Boehm–Demers–Weiser collector, + it is now possible to use :term:`garbage collection`. + :term:`Smart pointers` are another popular solution. The language is notorious for fostering memory management bugs, including: @@ -222,6 +224,8 @@ Memory management in various languages .. link:: + `Memory Pool System `_, + `Boehm–Demers–Weiser collector `_, `comp.lang.c++ FAQ `_, `C++ standardization `_. From b87b5d51c7d77a9f9ec82b132aec1037945f34c8 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 23 May 2014 21:25:08 +0100 Subject: [PATCH 30/53] Add home page. Copied from Perforce Change: 186268 ServerID: perforce.ravenbrook.com --- mps/manual/source/make-mmref.py | 24 +++++--- mps/manual/source/mmref-index.rst | 56 +++++++++++++++++++ .../source/themes/mmref/static/mmref.css_t | 8 +++ 3 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 mps/manual/source/mmref-index.rst diff --git a/mps/manual/source/make-mmref.py b/mps/manual/source/make-mmref.py index 92add8bbbea..31c12b9a6ba 100755 --- a/mps/manual/source/make-mmref.py +++ b/mps/manual/source/make-mmref.py @@ -88,19 +88,25 @@ def rewrite_links(src, src_base, url_filter, rewrite_base, if u and not url_filter(urljoin(src_base, u)): rewritten = urljoin(rewrite_base, u) if u != rewritten: - print(" {} -> {}".format(u, rewritten)) e.setAttribute(attr, rewritten) tree_walker = html5lib.treewalkers.getTreeWalker('dom') html_serializer = html5lib.serializer.htmlserializer.HTMLSerializer() return u''.join(html_serializer.serialize(tree_walker(dom))) +def newer(src, target): + """Return True if src is newer (that is, modified more recently) than + target, False otherwise. + + """ + return (not os.path.isfile(target) + or os.path.getmtime(target) < os.path.getmtime(src)) + def rewrite_file(src_dir, src_filename, target_path, rewrite_url): src_path = os.path.join(src_dir, src_filename) - if (os.path.exists(target_path) - and os.stat(src_path).st_mtime <= os.stat(target_path).st_mtime): + if not newer(src_path, target_path): return - print("Converting {} -> {}".format(src_path, target_path)) + print("Rewriting links in {} -> {}".format(src_path, target_path)) src = open(os.path.join(src_dir, src_filename), encoding='utf-8').read() src_base = '/{}/'.format(src_dir) url_filter = url_filter_re.search @@ -108,19 +114,19 @@ def rewrite_file(src_dir, src_filename, target_path, rewrite_url): result = rewrite_links(src, src_base, url_filter, rewrite_base) open(target_path, 'w', encoding='utf-8').write(result) -def main(argv): +def main(target_root='mmref'): src_root = 'html' - target_root = 'mmref' for d in mmref_dirs: src_dir = os.path.join(src_root, d) target_dir = os.path.join(target_root, d) os.makedirs(target_dir, exist_ok=True) for f in os.listdir(src_dir): + src_path = os.path.join(src_dir, f) target_path = os.path.join(target_dir, f) if os.path.splitext(f)[1] == '.html': rewrite_file(src_dir, f, target_path, rewrite_url) - else: - copyfile(os.path.join(src_dir, f), target_path) + elif os.path.isfile(src_path): + copyfile(src_path, target_path) for f in mmref_files: rewrite_file(src_root, 'mmref-{}.html'.format(f), os.path.join(target_root, '{}.html'.format(f)), @@ -128,7 +134,7 @@ def main(argv): if __name__ == '__main__': - main(sys.argv) + main(*sys.argv[1:]) # B. DOCUMENT HISTORY diff --git a/mps/manual/source/mmref-index.rst b/mps/manual/source/mmref-index.rst new file mode 100644 index 00000000000..264fa5c3e4a --- /dev/null +++ b/mps/manual/source/mmref-index.rst @@ -0,0 +1,56 @@ +Home +**** + +Welcome to the **Memory Management Reference**! This is a resource for programmers and computer scientists interested in :term:`memory management` and :term:`garbage collection`. + + +.. admonition:: :ref:`glossary` + + A glossary of more than 500 memory management terms, from + :term:`absolute address` to :term:`zero count table`. + + .. image:: diagrams/treadmill.svg + :target: glossary_ + + .. _glossary: glossary/index.html#glossary + + +.. admonition:: :ref:`mmref-intro` + + Articles giving a beginner's overview of memory management. + + .. image:: diagrams/address.svg + :target: intro_ + + .. _intro: mmref/index.html#mmref-intro + + +.. admonition:: :ref:`bibliography` + + Books and research papers related to memory management. + + .. image:: diagrams/copying.svg + :target: bib_ + + .. _bib: mmref/bib.html#bibliography + + +.. admonition:: :ref:`mmref-faq` + + Frequently asked questions about memory management. + + .. image:: diagrams/snap-out.svg + :target: faq_ + + .. _faq: mmref/faq.html#mmref-faq + +The Memory Management Reference is maintained by `Ravenbrook +Limited`_. We also maintain the `Memory Pool System`_ (an open-source, +thread-safe, :term:`incremental ` +garbage collector), and we are happy to provide advanced memory +management solutions to language and application developers through +our `consulting service`_. + +.. _Ravenbrook Limited: http://www.ravenbrook.com/ +.. _consulting service: http://www.ravenbrook.com/services/mm/ +.. _Memory Pool System: http://www.ravenbrook.com/project/mps/ diff --git a/mps/manual/source/themes/mmref/static/mmref.css_t b/mps/manual/source/themes/mmref/static/mmref.css_t index df9d26b1cf0..c24e7eb67a1 100644 --- a/mps/manual/source/themes/mmref/static/mmref.css_t +++ b/mps/manual/source/themes/mmref/static/mmref.css_t @@ -104,6 +104,14 @@ div.admonition-ref-glossary, div.admonition-ref-bibliography, div.admonition-ref vertical-align: top; } +div.admonition-ref-glossary, div.admonition-ref-mmref-intro { + height:400px; +} + +div.admonition-ref-bibliography, div.admonition-ref-mmref-faq { + height:230px; +} + div.admonition-ref-glossary, div.admonition-ref-bibliography { margin-right: 1%; } From e33f0cc954f51a806a337fcb5fbb027e1b9a50ef Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 23 May 2014 22:06:12 +0100 Subject: [PATCH 31/53] New memory management reference, using the memory pool systsem manual sources. Copied from Perforce Change: 186271 ServerID: perforce.ravenbrook.com --- mps/manual/source/make-mmref.py | 174 -------------------------------- 1 file changed, 174 deletions(-) delete mode 100755 mps/manual/source/make-mmref.py diff --git a/mps/manual/source/make-mmref.py b/mps/manual/source/make-mmref.py deleted file mode 100755 index 31c12b9a6ba..00000000000 --- a/mps/manual/source/make-mmref.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python -# -# Ravenbrook -# -# -# MAKE THE MEMORY MANAGEMENT REFERENCE -# -# Gareth Rees, Ravenbrook Limited, 2014-05-23 -# -# -# 1. INTRODUCTION -# -# This script builds the Memory Management Reference website from the -# Memory Pool System manual. -# -# The whole build procedure is as follows: -# -# 1. Sync //info.ravenbrook.com/project/mps/master/manual/... -# 2. make html MMREF=1 -# 3. Run this script -# -# -# 2. DESIGN -# -# We build the Memory Management Reference out of the Memory Pool -# System manual because: -# -# 1. having a single set of sources makes it easier to work on; -# 2. the glossary is a vital tool in organizing the MPS manual; -# 3. cross-references from the MMRef to the MPS are an opportunity -# for advertising the latter to readers of the former. -# -# -# 3. DEPENDENCIES -# -# html5lib -# six - -import html5lib -import html5lib.serializer -import html5lib.treewalkers -from io import open -import os -import re -from shutil import copyfile -import sys -from six.moves.urllib.parse import urljoin - - -# 4. CONFIGURATION - -# Subdirectories of the MPS manual that belong in the MMRef. -mmref_dirs = ('glossary', 'mmref', '_images', '_static') - -# Top-level files that belong in the MMRef. -mmref_files = ('index', 'copyright') - -# Regular expression matching files to be included in the MMRef. -url_filter_re = re.compile(r'^/html/(?:(?:{})\.html)?(?:#.*)?$|/(?:{})/'.format( - '|'.join(mmref_files), '|'.join(mmref_dirs))) - -# Root URL for the MPS manual. -rewrite_url = 'http://www.ravenbrook.com/project/mps/master/manual/html/' - - -def rewrite_links(src, src_base, url_filter, rewrite_base, - url_attributes = (('a', 'href'),)): - """Rewrite URLs in src and return the result. - - First, src is parsed as HTML. Second, all URLs found in the - document are resolved relative to src_base and the result passed to - the functions url_filter. If this returns False, the URL is resolved - again, this time relative to rewrite_base, and the result stored - back to the document. Finally, the updated document is serialized - as HTML and returned. - - The keyword argument url_attributes is a sequence of (tag, - attribute) pairs that contain URLs to be rewritten. - - """ - tree_builder = html5lib.treebuilders.getTreeBuilder('dom') - parser = html5lib.html5parser.HTMLParser(tree = tree_builder) - dom = parser.parse(src) - - for tag, attr in url_attributes: - for e in dom.getElementsByTagName(tag): - u = e.getAttribute(attr) - if u and not url_filter(urljoin(src_base, u)): - rewritten = urljoin(rewrite_base, u) - if u != rewritten: - e.setAttribute(attr, rewritten) - - tree_walker = html5lib.treewalkers.getTreeWalker('dom') - html_serializer = html5lib.serializer.htmlserializer.HTMLSerializer() - return u''.join(html_serializer.serialize(tree_walker(dom))) - -def newer(src, target): - """Return True if src is newer (that is, modified more recently) than - target, False otherwise. - - """ - return (not os.path.isfile(target) - or os.path.getmtime(target) < os.path.getmtime(src)) - -def rewrite_file(src_dir, src_filename, target_path, rewrite_url): - src_path = os.path.join(src_dir, src_filename) - if not newer(src_path, target_path): - return - print("Rewriting links in {} -> {}".format(src_path, target_path)) - src = open(os.path.join(src_dir, src_filename), encoding='utf-8').read() - src_base = '/{}/'.format(src_dir) - url_filter = url_filter_re.search - rewrite_base = urljoin(rewrite_url, src_dir) - result = rewrite_links(src, src_base, url_filter, rewrite_base) - open(target_path, 'w', encoding='utf-8').write(result) - -def main(target_root='mmref'): - src_root = 'html' - for d in mmref_dirs: - src_dir = os.path.join(src_root, d) - target_dir = os.path.join(target_root, d) - os.makedirs(target_dir, exist_ok=True) - for f in os.listdir(src_dir): - src_path = os.path.join(src_dir, f) - target_path = os.path.join(target_dir, f) - if os.path.splitext(f)[1] == '.html': - rewrite_file(src_dir, f, target_path, rewrite_url) - elif os.path.isfile(src_path): - copyfile(src_path, target_path) - for f in mmref_files: - rewrite_file(src_root, 'mmref-{}.html'.format(f), - os.path.join(target_root, '{}.html'.format(f)), - rewrite_url) - - -if __name__ == '__main__': - main(*sys.argv[1:]) - - -# B. DOCUMENT HISTORY -# -# 2014-05-23 GDR Created. -# -# -# C. COPYRIGHT AND LICENCE -# -# Copyright (c) 2014 Ravenbrook Ltd. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the -# distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# -# $Id: //info.ravenbrook.com/project/mps/master/tool/branch#9 $ From eaedbeffd834a231655fe29fc3eadac733371381 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sat, 24 May 2014 10:15:45 +0100 Subject: [PATCH 32/53] Add $id$ and copyright lines to various scripts. Copied from Perforce Change: 186276 ServerID: perforce.ravenbrook.com --- mps/tool/branch | 7 +++---- mps/tool/release | 7 +++---- mps/tool/testcoverage | 7 +++---- mps/tool/testemscripten | 7 +++---- mps/tool/testopendylan | 7 +++---- 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/mps/tool/branch b/mps/tool/branch index bb54f0d4527..981735d4360 100755 --- a/mps/tool/branch +++ b/mps/tool/branch @@ -1,11 +1,10 @@ #!/usr/bin/env python # -# Ravenbrook -# -# # BRANCH -- CREATE VERSION OR TASK BRANCH -# # Gareth Rees, Ravenbrook Limited, 2014-03-18 +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. # # # 1. INTRODUCTION diff --git a/mps/tool/release b/mps/tool/release index 6daf82bed1c..d47fc36e788 100755 --- a/mps/tool/release +++ b/mps/tool/release @@ -1,11 +1,10 @@ #!/usr/bin/env python # -# Ravenbrook -# -# # RELEASE -- MAKE A RELEASE -# # Gareth Rees, Ravenbrook Limited, 2014-03-18 +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. # # # 1. INTRODUCTION diff --git a/mps/tool/testcoverage b/mps/tool/testcoverage index cf4c62ae703..56ee5789bbf 100755 --- a/mps/tool/testcoverage +++ b/mps/tool/testcoverage @@ -1,11 +1,10 @@ #!/bin/sh # -# Ravenbrook -# -# # TESTCOVERAGE -- TEST COVERAGE REPORT FOR THE MPS -# # Gareth Rees, Ravenbrook Limited, 2014-03-21 +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. # # # 1. INTRODUCTION diff --git a/mps/tool/testemscripten b/mps/tool/testemscripten index 4639e9e69a4..b4c565440db 100755 --- a/mps/tool/testemscripten +++ b/mps/tool/testemscripten @@ -1,11 +1,10 @@ #!/bin/sh # -# Ravenbrook -# -# # TESTEMSCRIPTEN -- TEST THE MPS WITH EMSCRIPTEN -# # Gareth Rees, Ravenbrook Limited, 2014-04-17 +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. # # # 1. INTRODUCTION diff --git a/mps/tool/testopendylan b/mps/tool/testopendylan index 0c185ff2fd0..4bd1488261c 100755 --- a/mps/tool/testopendylan +++ b/mps/tool/testopendylan @@ -1,11 +1,10 @@ #!/bin/sh # -# Ravenbrook -# -# # TESTOPENDYLAN -- TEST THE MPS WITH OPENDYLAN -# # Gareth Rees, Ravenbrook Limited, 2014-03-20 +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. # # # 1. INTRODUCTION From 8646adf72becbcb3b0a6f24374e0b3bf52285341 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sat, 24 May 2014 15:40:22 +0100 Subject: [PATCH 33/53] Move bibliography to top level. Add many missing abstracts and fix some links. Just one bibliography entry for the C90 standard. Copied from Perforce Change: 186278 ServerID: perforce.ravenbrook.com --- mps/manual/source/{mmref => }/bib.rst | 1268 +++++++++++++++++++++---- mps/manual/source/index.rst | 32 +- mps/manual/source/mmref/index.rst | 2 - mps/manual/source/topic/interface.rst | 6 +- mps/manual/source/topic/plinth.rst | 10 +- 5 files changed, 1091 insertions(+), 227 deletions(-) rename mps/manual/source/{mmref => }/bib.rst (72%) diff --git a/mps/manual/source/mmref/bib.rst b/mps/manual/source/bib.rst similarity index 72% rename from mps/manual/source/mmref/bib.rst rename to mps/manual/source/bib.rst index e330ad8a568..00ef030f527 100644 --- a/mps/manual/source/mmref/bib.rst +++ b/mps/manual/source/bib.rst @@ -7,7 +7,7 @@ Bibliography Ole Agesen, David L. Detlefs. 1997. "`Finding References in Java Stacks `_". Sun Labs. OOPSLA97 Workshop on Garbage Collection and Memory Management. - .. abstract: ad97.html + .. admonition:: Abstract Exact garbage collection for the strongly-typed Java language may seem straightforward. Unfortunately, a single pair of bytecodes in @@ -22,7 +22,7 @@ Bibliography Ole Agesen, David L. Detlefs, J. Eliot B. Moss. 1998. "`Garbage Collection and Local Variable Type-precision and Liveness in Java Virtual Machines `_". ACM. Proceedings of the ACM SIGPLAN '98 conference on Programming language design and implementation, pp. 269--279. - .. abstract: adm98.html + .. admonition:: Abstract Full precision in garbage collection implies retaining only those heap allocated objects that will actually be used in the future. @@ -55,7 +55,7 @@ Bibliography Andrew Appel, John R. Ellis, Kai Li. 1988. "`Real-time Concurrent Collection on Stock Multiprocessors `_". ACM, SIGPLAN. ACM PLDI 88, SIGPLAN Notices 23, 7 (July 88), pp. 11--20. - .. abstract: ael88.html + .. admonition:: Abstract We've designed and implemented a copying garbage-collection algorithm that is efficient, real-time, concurrent, runs on @@ -73,9 +73,9 @@ Bibliography Apple Computer, Inc. 1994. *Inside Macintosh: Memory*. Addison-Wesley. ISBN 0-201-63240-3. - .. abstract: apple94.html + .. admonition:: Abstract - Inside Macintosh: Memory describes the parts of the Macintosh® + Inside Macintosh: Memory describes the parts of the Macintosh® Operating System that allow you to directly allocate, release, or otherwise manipulate memory. Everyone who programs Macintosh computers should read this book. @@ -90,7 +90,7 @@ Bibliography Giuseppe Attardi & Tito Flagella. 1994. "`A Customisable Memory Management Framework `_". TR-94-010. - .. abstract: attardi94.html + .. admonition:: Abstract Memory management is a critical issue for many large object-oriented applications, but in C++ only explicit memory @@ -114,7 +114,7 @@ Bibliography Giuseppe Attardi, Tito Flagella, Pietro Iglio. 1998. "`A customisable memory management framework for C++ `_". Software -- Practice and Experience. 28(11), 1143--1183. - .. abstract: afi98.html + .. admonition:: Abstract Automatic garbage collection relieves programmers from the burden of managing memory themselves and several techniques have been @@ -150,7 +150,7 @@ Bibliography Alain Azagury, Elliot K. Kolodner, Erez Petrank, Zvi Yehudai. 1998. "`Combining Card Marking with Remembered Sets: How to Save Scanning Time `_". ACM. ISMM'98 pp. 10--19. - .. abstract: akpy98.html + .. admonition:: Abstract We consider the combination of card marking with remembered sets for generational garbage collection as suggested by Hosking and @@ -167,7 +167,7 @@ Bibliography Henry G. Baker, Carl Hewitt. 1977. "`The Incremental Garbage Collection of Processes `_". ACM. SIGPLAN Notices 12, 8 (August 1977), pp. 55--59. - .. abstract: baker77.html + .. admonition:: Abstract This paper investigates some problems associated with an argument evaluation order that we call "future" order, which is different @@ -201,7 +201,7 @@ Bibliography Henry G. Baker. 1978. "`List Processing in Real Time on a Serial Computer `_". ACM. Communications of the ACM 21, 4 (April 1978), pp. 280--294. - .. abstract: baker78.html + .. admonition:: Abstract A real-time list processing system is one in which the time required by the elementary list operations (e.g. CONS, CAR, CDR, @@ -227,7 +227,7 @@ Bibliography Henry G. Baker. 1979. "`Optimizing Allocation and Garbage Collection of Spaces `_". In Winston and Brown, eds. *Artificial Intelligence: An MIT Perspective.* MIT Press. - .. abstract: baker79.html + .. admonition:: Abstract MACLISP, unlike some other implementations of LISP, allocates storage for different types of objects in noncontiguous areas @@ -258,7 +258,7 @@ Bibliography Henry G. Baker. 1991. "`Cache-Conscious Copying Collectors `_". OOPSLA'91/GC'91 Workshop on Garbage Collection. - .. abstract: baker91.html + .. admonition:: Abstract Garbage collectors must minimize the scarce resources of cache space and off-chip communications bandwidth to optimize @@ -272,7 +272,7 @@ Bibliography Henry G. Baker. 1992. "`Lively Linear Lisp -- 'Look Ma, No Garbage!' `_". ACM. SIGPLAN Notices 27, 8 (August 1992), pp. 89--98. - .. abstract: baker92a.html + .. admonition:: Abstract Linear logic has been proposed as one solution to the problem of garbage collection and providing efficient "update-in-place" @@ -297,7 +297,7 @@ Bibliography Henry G. Baker. 1992. "`The Treadmill: Real-Time Garbage Collection Without Motion Sickness `_". ACM. SIGPLAN Notices 27, 3 (March 1992), pp. 66--70. - .. abstract: baker92c.html + .. admonition:: Abstract A simple real-time garbage collection algorithm is presented which does not copy, thereby avoiding some of the problems caused by the @@ -312,7 +312,7 @@ Bibliography Henry G. Baker. 1992. "`CONS Should not CONS its Arguments, or, a Lazy Alloc is a Smart Alloc `_". ACM. SIGPLAN Notices 27, 3 (March 1992), 24--34. - .. abstract: baker92.html + .. admonition:: Abstract "Lazy allocation" is a model for allocating objects on the execution stack of a high-level language which does not create @@ -359,7 +359,7 @@ Bibliography Henry G. Baker. 1992. "`NREVERSAL of Fortune -- The Thermodynamics of Garbage Collection `_". Springer-Verlag. LNCS Vol. 637. - .. abstract: baker92b.html + .. admonition:: Abstract The need to *reverse* a computation arises in many contexts -- debugging, editor undoing, optimistic concurrency undoing, @@ -407,7 +407,7 @@ Bibliography Henry G. Baker. 1993. "`'Infant Mortality' and Generational Garbage Collection `_". ACM. SIGPLAN Notices 28, 4 (April 1993), pp. 55--57. - .. abstract: baker93.html + .. admonition:: Abstract Generation-based garbage collection has been advocated by appealing to the intuitive but vague notion that "young objects @@ -427,7 +427,7 @@ Bibliography Henry G. Baker. 1993. "`Equal Rights for Functional Objects or, The More Things Change, The More They Are the Same `_". ACM. OOPS Messenger 4, 4 (October 1993), pp. 2--27. - .. abstract: baker93a.html + .. admonition:: Abstract We argue that intensional object identity in object-oriented programming languages and databases is best defined operationally @@ -450,7 +450,7 @@ Bibliography Henry G. Baker. 1994. "`Minimizing Reference Count Updating with Deferred and Anchored Pointers for Functional Data Structures `_". ACM. SIGPLAN Notices 29, 9 (September 1994), pp. 38--43. - .. abstract: baker94.html + .. admonition:: Abstract "Reference counting" can be an attractive form of dynamic storage management. It recovers storage promptly and (with a garbage stack @@ -481,7 +481,7 @@ Bibliography Henry G. Baker. 1994. "`Thermodynamics and Garbage Collection `_". ACM. SIGPLAN Notices 29, 4 (April 1994), pp. 58--63. - .. abstract: baker94a.html + .. admonition:: Abstract We discuss the principles of statistical thermodynamics and their application to storage management problems. We point out problems @@ -492,7 +492,7 @@ Bibliography Henry G. Baker. 1995. "`'Use-Once' Variables and Linear Objects -- Storage Management, Reflection and Multi-Threading `_". ACM. SIGPLAN Notices 30, 1 (January 1995), pp. 45--52. - .. abstract: baker95a.html + .. admonition:: Abstract Programming languages should have 'use-once' variables in addition to the usual 'multiple-use' variables. 'Use-once' variables are @@ -532,23 +532,44 @@ Bibliography Henry G. Baker. 1995. *Memory Management: International Workshop IWMM'95*. Springer-Verlag. ISBN 3-540-60368-9. - .. abstract: baker95.html + .. admonition:: From the Preface - [from the preface] The International Workshop on Memory Management - 1995 (IWMM'95) is a continuation of the excellent series started - by Yves Bekkers and Jacques Cohen with IWMM'92. The present volume - assembles the refereed and invited technical papers which were - presented during this year's workshop. + The International Workshop on Memory Management 1995 (IWMM'95) is + a continuation of the excellent series started by Yves Bekkers and + Jacques Cohen with IWMM'92. The present volume assembles the + refereed and invited technical papers which were presented during + this year's workshop. * .. _BBW97: - Nick Barnes, Richard Brooksby, David Jones, Gavin Matthews, Pekka P. Pirinen, Nick Dalton, P. Tucker Withington. 1997. "`A Proposal for a Standard Memory Management Interface `_". OOPSLA97 Workshop on Garbage Collection and Memory Management. + Nick Barnes, Richard Brooksby, David Jones, Gavin Matthews, Pekka P. Pirinen, Nick Dalton, P. Tucker Withington. 1997. "`A Proposal for a Standard Memory Management Interface `_". OOPSLA97 Workshop on Garbage Collection and Memory Management. + + .. admonition:: From the notes + + There is no well-defined memory-management library API which would + allow programmers to easily choose the best memory management + implementation for their application. + + Some languages allow replacement of their memory management + functions, but usually only the program API is specified, hence + replacement of the entire program interface is required. + + Few languages support multiple memory management policies within a + single program. Those that do use proprietary memory management + policies. + + We believe that the design of an abstract program API is a + prerequisite to the design of a “server” API and eventually an API + that would permit multiple cooperating memory “servers”. If the + interface is simple yet powerful enough to encompass most memory + management systems, it stands a good chance of being widely + adopted. * .. _ZORN93B: David A. Barrett, Benjamin Zorn. 1993. "`Using Lifetime Predictors to Improve Memory Allocation Performance `_". ACM. SIGPLAN'93 Conference on Programming Language Design and Implementation, pp. 187--196. - .. abstract: zorn93b.html + .. admonition:: Abstract Dynamic storage allocation is used heavily in many application areas including interpreters, simulators, optimizers, and @@ -556,9 +577,9 @@ Bibliography the performance of dynamic storage allocation by predicting the lifetimes of short-lived objects when they are allocated. Using five significant, allocation-intensive C programs, we show that a - great fraction of all bytes allocated are short-lived (> 90% in + great fraction of all bytes allocated are short-lived (> 90% in all cases). Furthermore, we describe an algorithm for lifetime - prediction that accurately predicts the lifetimes of 42-99% of all + prediction that accurately predicts the lifetimes of 42--99% of all objects allocated. We describe and simulate a storage allocator that takes advantage of lifetime prediction of short-lived objects and show that it can significantly improve a program's memory @@ -569,7 +590,7 @@ Bibliography David A. Barrett, Benjamin Zorn. 1995. "`Garbage Collection using a Dynamic Threatening Boundary `_". ACM. SIGPLAN'95 Conference on Programming Language Design and Implementation, pp. 301--314. - .. abstract: barrett93.html + .. admonition:: Abstract Generational techniques have been very successful in reducing the impact of garbage collection algorithms upon the performance of @@ -604,7 +625,7 @@ Bibliography Joel F. Bartlett. 1988. "`Compacting Garbage Collection with Ambiguous Roots `_". Digital Equipment Corporation. - .. abstract: bartlett88.html + .. admonition:: Abstract This paper introduces a copying garbage collection algorithm which is able to compact most of the accessible storage in the heap @@ -644,7 +665,7 @@ Bibliography Joel F. Bartlett. 1989. "`Mostly-Copying Garbage Collection Picks Up Generations and C++ `_". Digital Equipment Corporation. - .. abstract: bartlett89.html + .. admonition:: Abstract The "mostly-copying" garbage collection algorithm provides a way to perform compacting garbage collection in spite of the presence @@ -665,7 +686,7 @@ Bibliography Emery D. Berger, Robert D. Blumofe. 1999. "`Hoard: A Fast, Scalable, and Memory-Efficient Allocator for Shared-Memory Multiprocessors `_". University of Texas at Austin. UTCS TR99-22. - .. abstract: bb99.html + .. admonition:: Abstract In this paper, we present Hoard, a memory allocator for shared-memory multiprocessors. We prove that its worst-case memory @@ -677,11 +698,39 @@ Bibliography Emery D. Berger, Benjamin G. Zorn, Kathryn S. McKinley. 2001. "`Composing high-performance memory allocators `_" ACM SIGPLAN Conference on Programming Language Design and Implementation 2001, pp. 114--124. + .. admonition:: Abstract + + Current general-purpose memory allocators do not provide + sufficient speed or flexibility for modern high-performance + applications. Highly-tuned general purpose allocators have + per-operation costs around one hundred cycles, while the cost of + an operation in a custom memory allocator can be just a handful of + cycles. To achieve high performance, programmers often write + custom memory allocators from scratch -- a difficult and + error-prone process. + + In this paper, we present a flexible and efficient infrastructure + for building memory allocators that is based on C++ templates and + inheritance. This novel approach allows programmers to build + custom and general-purpose allocators as “heap layers” that can be + composed without incurring any additional runtime overhead or + additional programming cost. We show that this infrastructure + simplifies allocator construction and results in allocators that + either match or improve the performance of heavily-tuned + allocators written in C, including the Kingsley allocator and the + GNU obstack library. We further show this infrastructure can be + used to rapidly build a general-purpose allocator that has + performance comparable to the Lea allocator, one of the best + uniprocessor allocators available. We thus demonstrate a clean, + easy-to-use allocator interface that seamlessly combines the power + and efficiency of any number of general and custom allocators + within a single application. + * .. _BW88: Hans-J. Boehm, Mark Weiser. 1988. "`Garbage collection in an uncooperative environment `_". Software -- Practice and Experience. 18(9):807--820. - .. abstract: bw88.html + .. admonition:: Abstract We describe a technique for storage allocation and garbage collection in the absence of significant co-operation from the @@ -699,7 +748,7 @@ Bibliography Hans-J. Boehm, Alan J. Demers, Scott Shenker. 1991. "`Mostly Parallel Garbage Collection `_". Xerox PARC. ACM PLDI 91, SIGPLAN Notices 26, 6 (June 1991), pp. 157--164. - .. abstract: bds91.html + .. admonition:: Abstract We present a method for adapting garbage collectors designed to run sequentially with the client, so that they may run @@ -712,13 +761,26 @@ Bibliography * .. _BC92A: - Hans-J. Boehm, David Chase. 1992. "A Proposal for Garbage-Collector-Safe C Compilation". *Journal of C Language Translation.* vol. 4, 2 (December 1992), pp. 126--141. + Hans-J. Boehm, David Chase. 1992. "`A Proposal for Garbage-Collector-Safe C Compilation `_". *Journal of C Language Translation.* vol. 4, 2 (December 1992), pp. 126--141. + + .. admonition:: Abstract + + Conservative garbage collectors are commonly used in combination + with conventional C programs. Empirically, this usually works + well. However, there are no guarantees that this is safe in the + presence of "improved" compiler optimization. We propose that C + compilers provide a facility to suppress optimizations that are + unsafe in the presence of conservative garbage collection. Such a + facility can be added to an existing compiler at very minimal + cost, provided the additional analysis is done in a + machine-independent source-to-source prepass. Such a prepass may + also check the source code for garbage-collector-safety. * .. _BOEHM93: Hans-J. Boehm. 1993. "`Space Efficient Conservative Garbage Collection `_". ACM, SIGPLAN. Proceedings of the ACM SIGPLAN '91 Conference on Programming Language Design and Implementation, SIGPLAN Notices 28, 6, pp 197--206. - .. abstract: boehm93.html + .. admonition:: Abstract We call a garbage collector conservative if it has only partial information about the location of pointers, and is thus forced to @@ -742,7 +804,7 @@ Bibliography Hans-J. Boehm. 2000. "`Reducing Garbage Collector Cache Misses `_". ACM. ISMM'00 pp. 59--64. - .. abstract: boehm00.html + .. admonition:: Abstract Cache misses are currently a major factor in the cost of garbage collection, and we expect them to dominate in the future. @@ -769,10 +831,57 @@ Bibliography Hans-J. Boehm. 2002. "`Destructors, Finalizers, and Synchronization `_". HP Labs technical report HPL-2002-335. + .. admonition:: Abstract + + We compare two different facilities for running cleanup actions + for objects that are about to reach the end of their life. + Destructors, such as we find in C++, are invoked synchronously + when an object goes out of scope. They make it easier to implement + cleanup actions for objects of well-known lifetime, especially in + the presence of exceptions. Languages like Java, Modula-3, and C# + provide a different kind of "finalization" facility: Cleanup + methods may be run when the garbage collector discovers a heap + object to be otherwise inaccessible. Unlike C++ destructors, such + methods run in a separate thread at some much less well-defined + time. We argue that these are fundamentally different, and + potentially complementary, language facilities. We also try to + resolve some common misunderstandings about finalization in the + process. In particular: 1. The asynchronous nature of finalizers + is not just an accident of implementation or a shortcoming of + tracing collectors; it is necessary for correctness of client + code, fundamentally affects how finalizers must be written, and + how finalization facilities should be presented to the user. 2. An + object may legitimately be finalized while one of its methods are + still running. This should and can be addressed by the language + specification and client code. + * .. _BM77: Robert S. Boyer and J. Strother Moore. 1977. "`A Fast String Searching Algorithm `_". *Communications of the ACM* 20(10):762--772. + .. admonition:: Abstract + + An algorithm is presented that searches for the location, "*i*," + of the first occurrence of a character string, "*pat*," in another + string, "*string*." During the search operation, the characters of + *pat* are matched starting with the last character of *pat*. The + information gained by starting the match at the end of the pattern + often allows the algorithm to proceed in large jumps through the + text being searched. Thus the algorithm has the unusual property + that, in most cases, not all of the first *i* characters of + *string* are inspected. The number of characters actually + inspected (on the average) decreases as a function of the length + of *pat*. For a random English pattern of length 5, the algorithm + will typically inspect *i*/4 characters of string before finding a + match at *i*. Furthermore, the algorithm has been implemented so + that (on the average) fewer than *i* + *patlen* machine + instructions are executed. These conclusions are supported with + empirical evidence and a theoretical analysis of the average + behavior of the algorithm. The worst case behavior of the + algorithm is linear in *i* + *patlen*, assuming the availability + of array space for tables linear in *patlen* plus the size of the + alphabet. + * .. _BL72: P. Branquart, J. Lewi. 1972. "A scheme of storage allocation and garbage collection for ALGOL 68". Elsevier/North-Holland. ALGOL 68 Implementation -- Proceedings of the IFIP Working Conference on ALGOL 68 Implementation, July 1970. @@ -781,6 +890,28 @@ Bibliography Richard Brooksby. 2002. "`The Memory Pool System: Thirty person-years of memory management development goes Open Source `_". ISMM'02. + .. admonition:: Abstract + + The Memory Pool System (MPS) is a very general, adaptable, + flexible, reliable, and efficient memory management system. It + permits the flexible combination of memory management techniques, + supporting manual and automatic memory management, in-line + allocation, finalization, weakness, and multiple simultaneous + co-operating incremental generational garbage collections. It also + includes a library of memory pool classes implementing specialized + memory management policies. + + Between 1994 and 2001, Harlequin (now part of Global Graphics) + invested about thirty person-years of effort developing the MPS. + The system contained many innovative techniques and abstractions + which were kept secret. In 1997 Richard Brooksby, the manager and + chief architect of the project, and Nicholas Barnes, a senior + developer, left Harlequin to form their own consultancy company, + Ravenbrook, and in 2001, Ravenbrook acquired the MPS technology + from Global Graphics. We are happy to announce that we are + publishing the source code and documentation under an open source + licence. This paper gives an overview of the system. + * .. _C1990: International Standard ISO/IEC 9899:1990. "Programming languages — C". @@ -793,7 +924,7 @@ Bibliography Brad Calder, Dirk Grunwald, Benjamin Zorn. 1994. "`Quantifying Behavioral Differences Between C and C++ Programs `_". *Journal of Programming Languages.* 2(4):313--351. - .. abstract: cgz94.html + .. admonition:: Abstract Improving the performance of C programs has been a topic of great interest for many years. Both hardware technology and compiler @@ -816,23 +947,89 @@ Bibliography Dante J. Cannarozzi, Michael P. Plezbert, Ron K. Cytron. 2000. "`Contaminated garbage collection `_". ACM. Proceedings of the ACM SIGPLAN '00 conference on on Programming language design and implementation, pp. 264--273. + .. admonition:: Abstract + + We describe a new method for determining when an objct can be + garbage collected. The method does not require marking live + objects. Instead, each object *X* is *dynamically* associated with + a stack frame *M*, such that *X* is collectable when *M* pops. + Because *X* could have been dead earlier, our method is + conservative. Our results demonstrate that the methos nonetheless + idenitifies a large percentage of collectable objects. The method + has been implemented in Sun's Java™ Virtual Machine interpreter, + and results are presented based on this implementation. + * .. _CW86: Patrick J. Caudill, Allen Wirfs-Brock. 1986. "A Third-Generation Smalltalk-80 Implementation". ACM. SIGPLAN Notices. 21(11), OOPSLA'86 ACM Conference on Object-Oriented Systems, Languages and Applications. + .. admonition:: Abstract + + A new, high performance Smalltalk-80™ implementation is described + which builds directly upon two previous implementation efforts. + This implementation supports a large object space while retaining + compatibility with previous Smalltalk-80™ images. The + implementation utilizes a interpreter which incorporates a + generation based garbage collector and which does not have an + object table. This paper describes the design decisions which lead + to this implementation and reports preliminary performance + results. + * .. _CHENEY70: - C. J. Cheney. 1970. "A non-recursive list compacting algorithm". CACM. 13-11 pp. 677--678. + C. J. Cheney. 1970. "`A non-recursive list compacting algorithm `_". CACM. 13-11 pp. 677--678. + + .. admonition:: Abstract + + A simple nonrecursive list structure compacting scheme or garbage + collector suitable for both compact and LISP-like list structures + is presented. The algorithm avoids the need for recursion by using + the partial structure as it is built up to keep track of those + lists that have been copied. * .. _CHL98: Perry Cheng, Robert Harper, Peter Lee. 1998. "`Generational stack collection and profile-driven pretenuring `_". ACM. Proceedings of SIGPLAN'98 Conference on Programming Language Design and Implementation, pp. 162--173. + .. admonition:: Abstract + + This paper presents two techniques for improving garbage + collection performance: generational stack collection and + profile-driven pretenuring. The first is applicable to stack-based + implementations of functional languages while the second is useful + for any generational collector. We have implemented both + techniques in a generational collector used by the TIL compiler, + and have observed decreases in garbage collection times of as much + as 70% and 30%, respectively. + + Functional languages encourage the use of recursion which can lead + to a long chain of activation records. When a collection occurs, + these activation records must be scanned for roots. We show that + scanning many activation records can take so long as to become the + dominant cost of garbage collection. However, most deep stacks + unwind very infrequently, so most of the root information obtained + from the stack remains unchanged across successive garbage + collections. *Generational stack collection* greatly reduces the + stack scan cost by reusing information from previous scans. + + Generational techniques have been successful in reducing the cost + of garbage collection. Various complex heap arrangements and + tenuring policies have been proposed to increase the effectiveness + of generational techniques by reducing the cost and frequency of + scanning and copying. In contrast, we show that by using profile + information to make lifetime predictions, *pretenuring* can avoid + copying data altogether. In essence, this technique uses a + refinement of the generational hypothesis (most data die young) + with a locality principle concerning the age of data: most + allocations sites produce data that immediately dies, while a few + allocation sites consistently produce data that survives many + collections. + * .. _CL98: Trishul M. Chilimbi, James R. Larus. 1998. "`Using Generational Garbage Collection To Implement Cache-Conscious Data Placement `_". ACM. ISMM'98 pp. 37--48. - .. abstract: cl98.html + .. admonition:: Abstract Processor and memory technology trends show a continual increase in the cost of accessing main memory. Machine designers have tried @@ -858,7 +1055,7 @@ Bibliography William D Clinger & Lars T Hansen. 1997. "`Generational Garbage Collection and the Radioactive Decay Model `_". ACM. Proceedings of PLDI 1997. - .. abstract: ch97.html + .. admonition:: Abstract If a fixed exponentially decreasing probability distribution function is used to model every object's lifetime, then the age of @@ -882,7 +1079,7 @@ Bibliography Jacques Cohen. 1981. "Garbage collection of linked data structures". Computing Surveys. Vol. 13, no. 3. - .. abstract: cohen81.html + .. admonition:: Abstract A concise and unified view of the numerous existing algorithms for performing garbage collection of linked data structures is @@ -904,7 +1101,7 @@ Bibliography Dominique Colnet, Philippe Coucaud, Olivier Zendra. 1998. "`Compiler Support to Customize the Mark and Sweep Algorithm `_". ACM. ISMM'98 pp. 154--165. - .. abstract: ccz98.html + .. admonition:: Abstract Mark and sweep garbage collectors (GC) are classical but still very efficient automatic memory management systems. Although @@ -930,7 +1127,7 @@ Bibliography Jonathan E. Cook, Alexander L. Wolf, Benjamin Zorn. 1994. "`Partition Selection Policies in Object Database Garbage Collection `_". ACM. SIGMOD. International Conference on the Management of Data (SIGMOD'94), pp. 371--382. - .. abstract: cwz93.html + .. admonition:: Abstract The automatic reclamation of storage for unreferenced objects is very important in object databases. Existing language system @@ -954,11 +1151,31 @@ Bibliography Jonathan E. Cook, Artur Klauser, Alexander L. Wolf, Benjamin Zorn. 1996. "`Semi-automatic, Self-adaptive Control of Garbage Collection Rates in Object Databases `_". ACM, SIGMOD. International Conference on the Management of Data (SIGMOD'96), pp. 377--388. + .. admonition:: Abstract + + A fundamental problem in automating object database storage + reclamation is determining how often to perform garbage + collection. We show that the choice of collection rate can have a + significant impact on application performance and that the "best" + rate depends on the dynamic behavior of the application, tempered + by the particular performance goals of the user. We describe two + semi-automatic, self-adaptive policies for controlling collection + rate that we have developed to address the problem. Using + trace-driven simulations, we evaluate the performance of the + policies on a test database application that demonstrates two + distinct reclustering behaviors. Our results show that the + policies are effective at achieving user-specified levels of I/O + operations and database garbage percentage. We also investigate + the sensitivity of the policies over a range of object + connectivities. The evaluation demonstrates that semi-automatic, + self-adaptive policies are a practical means for flexibly + controlling garbage collection rate. + * .. _CNS92: Eric Cooper, Scott Nettles, Indira Subramanian. 1992. "Improving the Performance of SML Garbage Collection using Application-Specific Virtual Memory Management". ACM Conference on LISP and Functional Programming, pp. 43--52. - .. abstract: cns92.html + .. admonition:: Abstract We improved the performance of garbage collection in the Standard ML of New Jersey system by using the virtual memory facilities provided by @@ -974,19 +1191,19 @@ Bibliography Michael C. Daconta. 1995. *C++ Pointers and Dynamic Memory Management.* Wiley. ISBN 0-471-04998-0. - .. abstract: daconta95.html + .. admonition:: From the back cover - [from the back cover] Using techniques developed in the classroom - at America Online's Programmer's University, Michael Daconta - deftly pilots programmers through the intricacies of the two most - difficult aspects of C++ programming: pointers and dynamic memory - management. Written by a programmer for programmers, this - no-nonsense, nuts-and-bolts guide shows you how to fully exploit - advanced C++ programming features, such as creating class-specific - allocators, understanding references versus pointers, manipulating - multidimensional arrays with pointers, and how pointers and - dynamic memory are the core of object-oriented constructs like - inheritance, name-mangling, and virtual functions. + Using techniques developed in the classroom at America Online's + Programmer's University, Michael Daconta deftly pilots programmers + through the intricacies of the two most difficult aspects of C++ + programming: pointers and dynamic memory management. Written by a + programmer for programmers, this no-nonsense, nuts-and-bolts guide + shows you how to fully exploit advanced C++ programming features, + such as creating class-specific allocators, understanding + references versus pointers, manipulating multidimensional arrays + with pointers, and how pointers and dynamic memory are the core of + object-oriented constructs like inheritance, name-mangling, and + virtual functions. * .. _DAHL63: @@ -994,25 +1211,91 @@ Bibliography * .. _DENNING68: - P. J. Denning. 1968. "Thrashing: Its Causes and Prevention". Proceedings AFIPS,1968 Fall Joint Computer Conference, vol. 33, pp. 915--922. + P. J. Denning. 1968. "`Thrashing: Its Causes and Prevention `_". Proceedings AFIPS,1968 Fall Joint Computer Conference, vol. 33, pp. 915--922. + + .. admonition:: From the introduction + + A particularly troublesome phenomenon, thrashing, may seriously + interfere with the performance of paged memory systems, reducing + computing giants (Multics, IBM System 360, and others not + necessarily excepted) to computing dwarfs. The term thrashing + denotes excessive overhead and severe performance degradation or + collapse caused by too much paging. Thrashing inevitably turns a + shortage of memory space into a surplus of processor time. * .. _DENNING70: P. J. Denning. 1970. "`Virtual Memory `_". ACM. ACM Computing Surveys, vol. 2, no. 3, pp. 153--190, Sept. 1970. + .. admonition:: Abstract + + The need for automatic storage allocation arises from desires for + program modularity, machine independence, and resource sharing. + Virtual memory is an elegant way of achieving these objectives. In + a virtual memory, the addresses a program may use to identify + information are distinguished from the addresses the memory system + uses to identify physical storage sites, and program-generated + addresses are translated automatically to the corresponding + machine addresses. Two principal methods for implementing virtual + memory, segmentation and paging, are compared and contrasted. Many + contemporary implementations have experienced one or more of these + problems: poor utilization of storage, thrashing, and high costs + associated with loading information into memory. These and + subsidiary problems are studied from a theoretic view, and are + shown to be controllable by a proper combination of hardware and + memory management policies. + * .. _DS72: P. J. Denning, S. C. Schwartz. 1972. "`Properties of the Working-set Model `_". CACM. vol. 15, no. 3, pp. 191--198. + .. admonition:: Abstract + + A program's working set *W*\ (*t*, *T*) at time *t* is the set of + distinct pages among the *T* most recently referenced pages. + Relations between the average working-set size, the missing-page + rate, and the interreference-interval distribution may be derived + both from time-average definitions and from ensemble-average + (statistical) definitions. An efficient algorithm for estimating + these quantities is given. The relation to LRU (least recently + used) paging is characterized. The independent-reference model, in + which page references are statistically independent, is used to + assess the effects of interpage dependencies on working-set size + observations. Under general assumptions, working-set size is shown + to be normally distributed. + * .. _DETLEFS92: David L. Detlefs. 1992. "`Garbage collection and runtime typing as a C++ library `_". USENIX C++ Conference. + .. admonition:: From the introduction + + Automatic storage management, or *garbage collection*, is a + feature that can ease program development and enhance program + reliability. Many high-level languages other than C++ provide + garbage collection. This paper proposes the use of "smart pointer" + template classes as an interface for the use of garbage collection + in C++. Template classes and operator overloading are techniques + allowing language extension at the level of user code; I claim + that using these techniques to create smart pointer classes + provdes a syntax for manipulating garbage-collected storage safely + and conveniently. Further, the use of a smart-pointer template + class offers the possibility of implementing the collector at the + user-level, without requiring support from the compiler. If such a + compiler-independent implementation is possible with adequate + performance, then programmers can start to write code using + garbage collection without waiting for language and compiler + modifications. If the use of such a garbage collection interface + becomes widespread, then C++ compilation systems can be built to + specially support tht garbage collection interface, thereby + allowing the use of collection algorithms with enhanced + performance. + * .. _ZORN93: David L. Detlefs, Al Dosser, Benjamin Zorn. 1994. "`Memory Allocation Costs in Large C and C++ Programs `_". Software -- Practice and Experience. 24(6):527--542. - .. abstract: zorn93.html + .. admonition:: Abstract Dynamic storage allocation is an important part of a large class of computer programs written in C and C++. High-performance @@ -1033,15 +1316,44 @@ Bibliography L. Peter Deutsch, Daniel G. Bobrow. 1976. "`An Efficient, Incremental, Automatic Garbage Collector `_". CACM. vol. 19, no. 9, pp. 522--526. + .. admonition:: Abstract + + This paper describes a new way of solving the storage reclamation + problem for a system such as Lisp that allocates storage + automatically from a heap, and does not require the programmer to + give any indication that particular items are no longer useful or + accessible. A reference count scheme for reclaiming + non-self-referential structures, and a linearizing, compacting, + copying scheme to reorganize all storage at the users discretion + are proposed. The algorithms are designed to work well in systems + which use multiple levels of storage, and large virtual address + space. They depend on the fact that most cells are referenced + exactly once, and that reference counts need only be accurate when + storage is about to be reclaimed. A transaction file stores + changes to reference counts, and a multiple reference table stores + the count for items which are referenced more than once. + * .. _DLMSS76: E. W. Dijkstra, Leslie Lamport, A. J. Martin, C. S. Scholten, E. F. M. Steffens. 1976. "`On-the-fly Garbage Collection: An Exercise in Cooperation `_". Springer-Verlag. Lecture Notes in Computer Science, Vol. 46. + .. admonition:: Abstract + + As an example of cooperation between sequential processes with + very little mutual interference despite frequent manipulations of + a large shared data space, a technique is developed which allows + nearly all of the activity needed for garbage detection and + collection to be performed by an additional processor operating + con- currently with the processor devoted to the computation + proper. Exclusion and synchronization constraints have been kept + as weak as could be achieved; the severe complexities engendered + by doing so are illustrated. + * .. _DMH92: Amer Diwan, Richard L. Hudson, J. Eliot B. Moss. 1992. "`Compiler Support for Garbage Collection in a Statically Typed Language `_". ACM. Proceedings of the 5th ACM SIGPLAN conference on Programming language design and implementation, pp. 273--282. - .. abstract: dmh92.html + .. admonition:: Abstract We consider the problem of supporting compacting garbage collection in the presence of modern compiler optimizations. Since @@ -1064,7 +1376,7 @@ Bibliography Amer Diwan, David Tarditi, J. Eliot B. Moss. 1993. "`Memory Subsystem Performance of Programs with Intensive Heap Allocation `_". Carnegie Mellon University. CMU-CS-93-227. - .. abstract: dtm93.html + .. admonition:: Abstract Heap allocation with copying garbage collection is a general storage management technique for modern programming languages. It @@ -1089,7 +1401,7 @@ Bibliography Amer Diwan, David Tarditi, J. Eliot B. Moss. 1994. "`Memory Subsystem Performance of Programs Using Copying Garbage Collection `_". ACM. CMU-CS-93-210, also in POPL '94. - .. abstract: dtm93a.html + .. admonition:: Abstract Heap allocation with copying garbage collection is believed to have poor memory subsystem performance. We conducted a study of @@ -1103,7 +1415,7 @@ Bibliography Damien Doligez & Xavier Leroy. 1993. "`A concurrent, generational garbage collector for a multithreaded implementation of ML `_". ACM. POPL '93, 113--123. - .. abstract: doligez93.html + .. admonition:: Abstract This paper presents the design and implementation of a "quasi real-time" garbage collector for Concurrent Caml Light, an @@ -1117,7 +1429,7 @@ Bibliography Damien Doligez & Georges Gonthier. 1994. "`Portable, unobtrusive garbage collection for multiprocessor systems `_". ACM. POPL '94, 70--83. - .. abstract: doligez94.html + .. admonition:: Abstract We describe and prove the correctness of a new concurrent mark-and-sweep garbage collection algorithm. This algorithm @@ -1138,7 +1450,7 @@ Bibliography R. Kent Dybvig, Carl Bruggeman, David Eby. 1993. "`Guardians in a Generation-Based Garbage Collector `_". SIGPLAN. Proceedings of the ACM SIGPLAN '93 Conference on Programming Language Design and Implementation, June 1993. - .. abstract: dbe93.html + .. admonition:: Abstract This paper describes a new language feature that allows dynamically allocated objects to be saved from deallocation by an @@ -1155,15 +1467,49 @@ Bibliography Daniel R. Edelson. 1992. "`Smart pointers: They're smart, but they're not pointers `_". USENIX C++ Conference. + .. admonition:: From the introduction + + This paper shows hhow the behaviour of smart pointers diverges + from that of pointers in certain common C++ constructs. Given + this, we conclude that the C++ programming language does not + support seamless smart pointers: smart pointers cannot + transparently replace raw pointers in all ways except declaration + syntax. We show that this conclusion also applies to *accessors*. + * .. _EDELSON92: - Daniel R. Edelson. 1992. "Comparing Two Garbage Collectors for C++". University of California at Santa Cruz. Technical Report UCSC-CRL-93-20. + Daniel R. Edelson. 1992. "`Comparing Two Garbage Collectors for C++ `_". University of California at Santa Cruz. Technical Report UCSC-CRL-93-20. + + .. admonition:: Abstract + + Our research is concerned with compiler- independent, tag-free + garbage collection for the C++ programming language. This paper + presents a mark-and-sweep collector, and explains how it + ameliorates shortcomings of a previous copy collector. The new + collector, like the old, uses C++'s facilities for creating + abstract data types to define a *tracked reference* type, called + *roots*, at the level of the application program. A programmer + wishing to utilize the garbage collection service uses these roots + in place of normal, raw pointers. We present a detailed study of + the cost of using roots, as compared to both normal pointers and + reference counted pointers, in terms of instruction counts. We + examine the efficiency of a small C++ application using roots, + reference counting, manual reclamation, and conservative + collection. Coding the application to use garbage collection, and + analyzing the resulting efficiency, helped us identify a number of + memory leaks and inefficiencies in the original, manually + reclaimed version. We find that for this program, garbage + collection using roots is much more efficient than reference + counting, though less efficient than manual reclamation. It is + hard to directly compare our collector to the conservative + collector because of the differing efficiencies of their + respective memory allocators. * .. _EDWARDS: Daniel J. Edwards. n.d. "`Lisp II Garbage Collector `_". MIT. AI Memo 19 (AIM-19). - .. abstract: edwards.html + .. admonition:: Our summary (This short memo doesn't have an abstract. Basically, it describes the plan for the LISP II Relocating Garbage Collector. It has four @@ -1179,7 +1525,7 @@ Bibliography John R. Ellis, David L. Detlefs. 1993. "`Safe, Efficient Garbage Collection for C++ `_". Xerox PARC. - .. abstract: ellis93.html + .. admonition:: Abstract We propose adding safe, efficient garbage collection to C++, eliminating the possibility of storage-management bugs and making @@ -1192,7 +1538,7 @@ Bibliography Paulo Ferreira. 1996. "`Larchant: garbage collection in a cached distributed shared store with persistence by reachability `_". UniversitĂ© Paris VI. ThĂ©se de doctorat. - .. abstract: ferreira96.html + .. admonition:: Abstract The model of Larchant is that of a *Shared Address Space* (spanning every site in a network including secondary storage) @@ -1229,15 +1575,53 @@ Bibliography Paulo Ferreira & Marc Shapiro. 1998. "`Modelling a Distributed Cached Store for Garbage Collection `_". Springer-Verlag. Proceedings of 12th European Conference on Object-Oriented Programming, ECOOP98, LNCS 1445. + .. admonition:: Abstract + + Caching and persistence support efficient, convenient and + transparent distributed data sharing. The most natural model of + persistence is persistence by reachability, managed automatically + by a garbage collector (GC). We propose a very general model of + such a system (based on distributed shared memory) and a scalable, + asynchronous distributed GC algorithm. Within this model, we show + sufficient and widely applicable correctness conditions for the + interactions between applications, store, memory, coherence, and + GC. + + The GC runs as a set of processes (local to each participating + machine) communicating by asynchronous messages. Collection does + not interfere with applications by setting locks, polluting + caches, or causing I/O; this requirement raised some novel and + interesting challenges which we address in this article. The + algorithm is safe and live; it is not complete, i.e. it collects + some distributed cycles of garbage but not necessarily all. + * .. _FW76: Daniel P Friedman, David S. Wise. 1976. "`Garbage collecting a heap which includes a scatter table `_". *Information Processing Letters.* 5, 6 (December 1976): 161--164. + .. admonition:: Abstract + + A new algorithm is introduced for garbage collecting a heap which + contains shared data structures accessed from a scatter table. The + scheme provides for the purging of useless entries from the + scatter table with no traverslas beyond the two required by + classic collection schemes. For languages which use scatter tables + to sustain unique existence of complex structures, like natural + variables of SNOBOL, it indirectly allows liberal use of a single + scatter table by ensuring efficient deletion of useless entries. + Since the scatter table is completely restructured during the + course of execution, the hashing scheme itself is easily altered + during garbage collection whenever skewed loading of the scatter + table warrants abandonment of the old hashing. This procedure is + applicable to the maintenance of dynamic structures such as those + in information retrieval schemes or in languages like LISP and + SNOBOL. + * .. _FW77: Daniel P Friedman, David S. Wise. 1977. "`The One Bit Reference Count `_". *BIT.* (17)3: 351--359. - .. abstract: fw77.html + .. admonition:: Abstract Deutsch and Bobrow propose a storage reclamation scheme for a heap which is a hybrid of garbage collection and reference counting. @@ -1258,11 +1642,23 @@ Bibliography Daniel P Friedman, David S. Wise. 1979. "`Reference counting can manage the circular environments of mutual recursion `_". *Information Processing Letters.* 8, 1 (January 1979): 41--45. + .. admonition:: From the introduction + + In this note we advance reference counting as a storage management + technique viable for implementing recursive languages like ISWIM + or pure LISP with the ``labels`` construct for implementing mutual + recursion from SCHEME. ``Labels`` is derived from ``letrec`` and + displaces the ``label`` operator, a version of the paradoxical + Y-combinator. The key observation is that the requisite circular + structure (which ordinarily cripples reference counts) occurs only + within the language--rather than the user--structure, and that the + references into this structure are well-controlled. + * .. _GZH93: Dirk Grunwald, Benjamin Zorn, R. Henderson. 1993. "`Improving the Cache Locality of Memory Allocation `_". SIGPLAN. SIGPLAN '93, Conference on PLDI, June 1993, Albuquerque, New Mexico. - .. abstract: gzh93.html + .. admonition:: Abstract The allocation and disposal of memory is a ubiquitous operation in most programs. Rarely do programmers concern themselves with @@ -1289,7 +1685,7 @@ Bibliography Dirk Grunwald & Benjamin Zorn. 1993. "`CustoMalloc: Efficient Synthesized Memory Allocators `_". Software -- Practice and Experience. 23(8):851--869. - .. abstract: grun92.html + .. admonition:: Abstract The allocation and disposal of memory is a ubiquitous operation in most programs. Rarely do programmers concern themselves with @@ -1312,7 +1708,7 @@ Bibliography David Gudeman. 1993. "`Representing Type Information in Dynamically Typed Languages `_". University of Arizona at Tucson. Technical Report TR 93-27. - .. abstract: gudeman93.html + .. admonition:: Abstract This report is a discussion of various techniques for representing type information in dynamically typed languages, as implemented on @@ -1333,7 +1729,7 @@ Bibliography Timothy Harris. 1999. "`Early storage reclamation in a tracing garbage collector `_". ACM. ACM SIG-PLAN Notices 34:4, pp. 46--53. - .. abstract: harris99.html + .. admonition:: Abstract This article presents a technique for allowing the early recovery of storage space occupied by garbage data. The idea is similar to @@ -1347,7 +1743,7 @@ Bibliography Roger Henriksson. 1994. "Scheduling Real Time Garbage Collection". Department of Computer Science at Lund University. LU-CS-TR:94-129. - .. abstract: henrik94.html + .. admonition:: Abstract This paper presents a new model for scheduling the work of an incremental garbage collector in a system with hard real time @@ -1366,7 +1762,7 @@ Bibliography Roger Henriksson. 1996. "`Adaptive Scheduling of Incremental Copying Garbage Collection for Interactive Applications `_". NWPER96. - .. abstract: henrik96.html + .. admonition:: Abstract Incremental algorithms are often used to interleave the work of a garbage collector with the execution of an application program, @@ -1382,7 +1778,7 @@ Bibliography Roger Henriksson. 1998. "`Scheduling Garbage Collection in Embedded Systems `_". Department of Computer Science at Lund University. Ph.D. thesis. - .. abstract: henriksson98.html + .. admonition:: Abstract The complexity of systems for automatic control and other safety-critical applications grows rapidly. Computer software @@ -1431,23 +1827,78 @@ Bibliography Antony L. Hosking. 1991. "`Main memory management for persistence `_". ACM. Proceedings of the ACM OOPSLA'91 Workshop on Garbage Collection. + .. admonition:: Abstract + + Reachability-based persistence imposes new requirements for main + memory management in general, and garbage collection in + particular. After a brief introduction to the characteristics and + requirements of reachability-based persistence, we present the + design of a run-time storage manager for Persistent Smalltalk and + Persistent Modula-3, which allows the reclamation of storage from + both temporary objects and buffered persistent objects. + * .. _HMS92: Antony L. Hosking, J. Eliot B. Moss, Darko Stefanovic. 1992. "`A comparative performance evaluation of write barrier implementations `_". ACM. OOPSLA'92 Conference Proceedings, ACM SIGPLAN Notices 27(10), pp 92--109. + .. admonition:: Abstract + + Generational garbage collectors are able to achieve very small + pause times by concentrating on the youngest (most recently + allocated) objects when collecting, since objects have been + observed to die young in many systems. Generational collectors + must keep track of all pointers from older to younger generations, + by “monitoring” all stores into the heap. This *write barrier* has + been implemented in a number of ways, varying essentially in the + granularity of the information observed and stored. Here we + examine a range of write barrier implementations and evaluate + their relative performance within a generation scavenging garbage + collector for Smalltalk. + * .. _HH93: Antony L. Hosking, Richard L. Hudson. 1993. "`Remembered sets can also play cards `_". ACM. Proceedings of the ACM OOPSLA'93 Workshop on Memory Management and Garbage Collection. + .. admonition:: Abstract + + Remembered sets and dirty bits have been proposed as alternative + implementations of the write barrier for garbage collection. There + are advantages to both approaches. Dirty bits can be efficiently + maintained with minimal, bounded overhead per store operation, + while remembered sets concisely, and accurately record the + necessary information. Here we present evidence to show that + hybrids can combine the virtues of both schemes and offer + competitive performance. Moreover, we argue that a hybrid can + better avoid the devils that are the downfall of the separate + alternatives. + * .. _HM93: Antony L. Hosking, J. Eliot B. Moss. 1993. "`Protection traps and alternatives for memory management of an object-oriented language `_". ACM. Proceedings of the Fourteenth ACM Symposium on Operating Systems Principles, ACM Operating Systems Review 27(5), pp 106--119. + .. admonition:: Abstract + + Many operating systems allow user programs to specify the + protection level (inaccessible, read-only, read-write) of pages in + their virtual memory address space, and to handle any protection + violations that may occur. Such page-protection techniques have + been exploited by several user-level algorithms for applications + including generational garbage collection and persistent stores. + Unfortunately, modern hardware has made efficient handling of page + protection faults more difficult. Moreover, page-sized granularity + may not match the natural granularity of a given application. In + light of these problems, we reevaluate the usefulness of + page-protection primitives in such applications, by comparing the + performance of implementations that make use of the primitives + with others that do not. Our results show that for certain + applications software solutions outperform solutions that rely on + page-protection or other related virtual memory primitives. + * .. _HMDW91: Richard L. Hudson, J. Eliot B. Moss, Amer Diwan, Christopher F. Weight. 1991. "`A Language-Independent Garbage Collector Toolkit `_". University of Massachusetts at Amherst. COINS Technical Report 91--47. - .. abstract: hmdw91.html + .. admonition:: Abstract We describe a memory management toolkit for language implementors. It offers efficient and flexible generation scavenging garbage @@ -1465,7 +1916,7 @@ Bibliography Richard L. Hudson, J. Eliot B. Moss. 1992. "`Incremental Collection of Mature Objects `_". Springer-Verlag. LNCS #637 International Workshop on Memory Management, St. Malo, France, Sept. 1992, pp. 388--403. - .. abstract: hm92.html + .. admonition:: Abstract We present a garbage collection algorithm that extends generational scavenging to collect large older generations (mature @@ -1481,7 +1932,7 @@ Bibliography Richard L. Hudson, Ron Morrison, J. Eliot B. Moss, David S. Munro. 1997. "`Garbage Collecting the World: One Car at a Time `_". ACM. Proc. OOPSLA 97, pp. 162--175. - .. abstract: hmmm97.html + .. admonition:: Abstract A new garbage collection algorithm for distributed object systems, called DMOS (Distributed Mature Object Space), is presented. It is @@ -1493,15 +1944,11 @@ Bibliography incrementality, and scalability. Furthermore, the DMOS collector is non-blocking and does not use global tracing. -* .. _ISO90: - - "International Standard ISO/IEC 9899:1990 Programming languages — C". - * .. _JOHNSTONE97: Mark S. Johnstone. 1997. "`Non-Compacting Memory Allocation and Real-Time Garbage Collection `_". University of Texas at Austin. - .. abstract: johnstone97.html + .. admonition:: Abstract Dynamic memory use has been widely recognized to have profound effects on program performance, and has been the topic of many @@ -1548,7 +1995,7 @@ Bibliography Mark S. Johnstone, Paul R. Wilson. 1998. "`The Memory Fragmentation Problem: Solved? `_". ACM. ISMM'98 pp. 26--36. - .. abstract: jw98.html + .. admonition:: Abstract We show that for 8 real and varied C and C++ programs, several conventional dynamic storage allocators provide near-zero @@ -1569,11 +2016,26 @@ Bibliography Richard E. Jones. 1992. "`Tail recursion without space leaks `_". *Journal of Functional Programming.* 2(1):73--79. + .. admonition:: Abstract + + The G-machine is a compiled graph reduction machine for lazy + functional languages. The G-machine compiler contains many + optimisations to improve performance. One set of such + optimisations is designed to improve the performance of tail + recursive functions. Unfortunately the abstract machine is subject + to a space leak--objects are unnecessarily preserved by the + garbage collector. + + This paper analyses why a particular form of space leak occurs in + the G-machine, and presents some ideas for fixing this problem. + This phenomena in other abstract machines is also examined + briefly. + * .. _JL92: Richard E. Jones, Rafael Lins. 1992. "`Cyclic weighted reference counting without delay `_". Computing Laboratory, The University of Kent at Canterbury. Technical Report 28-92. - .. abstract: jl92.html + .. admonition:: Abstract Weighted Reference Counting is a low-communication distributed storage reclamation scheme for loosely-coupled multiprocessors. @@ -1590,23 +2052,22 @@ Bibliography Richard E. Jones, Rafael Lins. 1996. "`Garbage Collection: Algorithms for Automatic Dynamic Memory Management `_". Wiley. ISBN 0-471-94148-4. - .. abstract: jones96.html + .. admonition:: From the back cover - [from the back cover] The memory storage requirements of complex - programs are extremely difficult to manage correctly by hand. A - single error may lead to indeterminate and inexplicable program - crashes. Worse still, failures are often unrepeatable and may - surface only long after the program has been delivered to the - customer. The eradication of memory errors typically consumes a - substantial amount of development time. And yet the answer is - relatively easy -- garbage collection; removing the clutter of - memory management from module interfaces, which then frees the - programmer to concentrate on the problem at hand rather than - low-level book-keeping details. For this reason, most modern - object-oriented languages such as Smalltalk, Eiffel, Java and - Dylan, are supported by garbage collection. Garbage collecting - libraries are even available for such uncooperative languages as C - and C++. + The memory storage requirements of complex programs are extremely + difficult to manage correctly by hand. A single error may lead to + indeterminate and inexplicable program crashes. Worse still, + failures are often unrepeatable and may surface only long after + the program has been delivered to the customer. The eradication of + memory errors typically consumes a substantial amount of + development time. And yet the answer is relatively easy -- garbage + collection; removing the clutter of memory management from module + interfaces, which then frees the programmer to concentrate on the + problem at hand rather than low-level book-keeping details. For + this reason, most modern object-oriented languages such as + Smalltalk, Eiffel, Java and Dylan, are supported by garbage + collection. Garbage collecting libraries are even available for + such uncooperative languages as C and C++. This book considers how dynamic memory can be recycled automatically to guarantee error-free memory management. There is @@ -1631,20 +2092,19 @@ Bibliography Richard E. Jones. 1998. "`ISMM'98 International Symposium on Memory Management `_". ACM. ISBN 1-58113-114-3. - .. abstract: acm98.html + .. admonition:: From the Preface - (From the preface:) The International Symposium on Memory - Management is a forum for research in several related areas of - memory management, especially garbage collectors and dynamic - storage allocators. [...] The nineteen papers selected for - publication in this volume cover a remarkably broad range of - memory management topics from explicit malloc-style allocation to - automatic memory management, from cache-conscious data layout to - efficient management of distributed references, from conservative - to type-accurate garbage collection, for applications ranging from - user application to long-running servers, supporting languages as - different as C, C++, Modula-3, Java, Eiffel, Erlang, Scheme, ML, - Haskell and Prolog. + The International Symposium on Memory Management is a forum for + research in several related areas of memory management, especially + garbage collectors and dynamic storage allocators. [...] The + nineteen papers selected for publication in this volume cover a + remarkably broad range of memory management topics from explicit + malloc-style allocation to automatic memory management, from + cache-conscious data layout to efficient management of distributed + references, from conservative to type-accurate garbage collection, + for applications ranging from user application to long-running + servers, supporting languages as different as C, C++, Modula-3, + Java, Eiffel, Erlang, Scheme, ML, Haskell and Prolog. * .. _JONES12: @@ -1654,6 +2114,32 @@ Bibliography Ian Joyner. 1996. "`C++??: A Critique of C++ `_.". + .. admonition:: Abstract + + The C++?? Critique is an analysis of some of the flaws of C++. It + is by no means exhaustive, nor does it attempt to document every + little niggle with C++, rather concentrating on main themes. The + critique uses Java and Eiffel as comparisons to C++ to give a more + concrete feel to the criticisms, viewing conceptual differences + rather than syntactic ones as being more important. Some C++ + authors realising there are glaring deficiencies in C++ have + chosen to defend C++ by also being critical within their own work. + Most notable are Bjarne Stroustup's "Design and Evolution of C++," + and Scott Meyers' "Effective" and "More Effective C++." These warn + of many traps and pitfalls, but reach the curious conclusion that + since "good" C++ programmers are aware of these problems and know + how to avoid them, C++ is alright. + + The C++ critique makes many of the same criticisms, but comes to + the different conclusion that these pitfalls are not acceptable, + and should not be in a language used for modern large scale + software engineering. Clean design is more important than after + the fact warnings, and it is inconceivable that purchasers of end + user software would tolerate this tactic on the part of vendors. + The critique also takes a look at C, and concludes that many of + the features of C should be left out of modern languages, and that + C is a flawed base for a language. + * .. _KANEFSKY89: Bob Kanefsky. 1989. "`Recursive Memory Allocation `_". Bob Kanefsky. Songworm 3, p.?. @@ -1662,7 +2148,7 @@ Bibliography Jin-Soo Kim, Xiaohan Qin, Yarsun Hsu. 1998. "`Memory Characterization of a Parallel Data Mining Workload `_". IEEE. Proc. Workload Characterization: Methodology and Case Studies, pp. . - .. abstract: kqh98.html + .. admonition:: Abstract This paper studies a representative of an important class of emerging applications, a parallel data mining workload. The @@ -1691,11 +2177,34 @@ Bibliography Jin-Soo Kim & Yarsun Hsu. 2000. "Memory system behavior of Java programs: methodology and analysis". ACM. Proc. International conference on measurements and modeling of computer systems, pp. 264--274. + .. admonition:: Abstract + + This paper studies the memory system behavior of Java programs by + analyzing memory reference traces of several SPECjvm98 + applications running with a Just-In-Time (JIT) compiler. Trace + information is collected by an exception-based tracing tool called + JTRACE, without any instrumentation to the Java programs or the + JIT compiler.First, we find that the overall cache miss ratio is + increased due to garbage collection, which suffers from higher + cache misses compared to the application. We also note that going + beyond 2-way cache associativity improves the cache miss ratio + marginally. Second, we observe that Java programs generate a + substantial amount of short-lived objects. However, the size of + frequently-referenced long-lived objects is more important to the + cache performance, because it tends to determine the application's + working set size. Finally, we note that the default heap + configuration which starts from a small initial heap size is very + inefficient since it invokes a garbage collector frequently. + Although the direct costs of garbage collection decrease as we + increase the available heap size, there exists an optimal heap + size which minimizes the total execution time due to the + interaction with the virtual memory performance. + * .. _KOLODNER92: Elliot K. Kolodner. 1992. "Atomic Incremental Garbage Collection and Recovery for a Large Stable Heap". Laboratory for Computer Science at MIT. MIT-LCS-TR-534. - .. abstract: kolodner92.html + .. admonition:: Abstract A stable heap is a storage that is managed automatically using garbage collection, manipulated using atomic transactions, and @@ -1723,7 +2232,7 @@ Bibliography Per-Ă…ke Larson & Murali Krishnan. 1998. "`Memory Allocation for Long-Running Server Applications `_". ACM. ISMM'98 pp. 176--185. - .. abstract: lk98.html + .. admonition:: Abstract Prior work on dynamic memory allocation has largely neglected long-running server applications, for example, web servers and @@ -1748,46 +2257,69 @@ Bibliography Henry Lieberman & Carl Hewitt. 1983. "`A real-time garbage collector based on the lifetimes of objects `_". ACM. 26(6):419--429. + .. admonition:: Abstract + + In previous heap storage systems, the cost of creating objects and + garbage collection is independent of the lifetime of the object. + Since objects with short lifetimes account for a large portion of + storage use, it is worth optimizing a garbage collector to reclaim + storage for these objects more quickly. The garbage collector + should spend proportionately less effort reclaiming objects with + longer lifetimes. We present a garbage collection algorithm that + (1) makes storage for short-lived objects cheaper than storage for + long-lived objects, (2) that operates in real-time--object + creation and access times are bounded, (3) increases locality of + reference, for better virtual memory performance, (4) works well + with multiple processors and a large address space. + * .. _MM59: - J. McCarthy, M. L. Minsky. 1959. "Artificial Intelligence, Quarterly Progress Report no. 53". Research Laboratory of Electronics at MIT. + J. McCarthy, M. L. Minsky. 1959. "`Artificial Intelligence, Quarterly Progress Report no. 53 `_". Research Laboratory of Electronics at MIT. * .. _MCCARTHY60: J. McCarthy. 1960. "`Recursive Functions of Symbolic Expressions and Their Computation by Machine `_". CACM. - .. abstract: mccarthy60.html + .. admonition:: Abstract - A programming system called LISP (for LISt Processor) has been - developed for the IBM 704 computer by the Artificial Intelligence - group at M.I.T. The system was designed to facilitate experiments - with a proposed system called the Advice Taker, whereby a machine - could be instructed to handle declarative as well as imperative - sentences and could exhibit "common sense" in carrying out its - instructions. The original proposal for the Advice Taker was made - in November 1958. The main requirement was a programming system - for manipulating expressions representing formalized declarative - and imperative sentences so that the Advice Taker could make - deductions. + A programming system called LISP (for LISt Processor) has been + developed for the IBM 704 computer by the Artificial Intelligence + group at M.I.T. The system was designed to facilitate experiments + with a proposed system called the Advice Taker, whereby a machine + could be instructed to handle declarative as well as imperative + sentences and could exhibit "common sense" in carrying out its + instructions. The original proposal for the Advice Taker was made + in November 1958. The main requirement was a programming system + for manipulating expressions representing formalized declarative + and imperative sentences so that the Advice Taker could make + deductions. + + In the course of its development the LISP system went through + several stages of simplification and eventually came to be based + on a scheme for representing the partial recursive functions of a + certain class of symbolic expressions. This representation is + independent of the IBM 704 computer, or of any other electronic + computer, and it now seems expedient to expound the system by + starting with the class of expressions called S-expressions and + the functions called S-functions. * .. _MCCARTHY79: - John McCarthy. 1979. "`History of Lisp `_". In *History of programming languages I*, pp. 173–185. ACM. + John McCarthy. 1979. "`History of Lisp `_". In *History of programming languages I*, pp. 173--185. ACM. * .. _PTM98: Veljko Milutinovic, Jelica Protic, Milo Tomasevic. 1997. "`Distributed shared memory: concepts and systems `_". IEEE Computer Society Press. ISBN 0-8186-7737-6. - .. abstract: ptm98.html + .. admonition:: From the publisher's catalog - [introduction from the catalog] Presents a survey of both - distributed shared memory (DSM) efforts and commercial DSM - systems. The book discusses relevant issues that make the concept - of DSM one of the most attractive approaches for building - large-scale, high-performance multiprocessor systems. Its text - provides a general introduction to the DSM field as well as a - broad survey of the basic DSM concepts, mechanisms, design issues, - and systems. + Presents a survey of both distributed shared memory (DSM) efforts + and commercial DSM systems. The book discusses relevant issues + that make the concept of DSM one of the most attractive approaches + for building large-scale, high-performance multiprocessor systems. + Its text provides a general introduction to the DSM field as well + as a broad survey of the basic DSM concepts, mechanisms, design + issues, and systems. Distributed Shared Memory concentrates on basic DSM algorithms, their enhancements, and their performance evaluation. In addition, @@ -1799,12 +2331,45 @@ Bibliography * .. _MINSKY63: - M. L. Minsky. 1963. "A LISP Garbage Collector Algorithm Using Serial Secondary Storage". MIT. Memorandum MAC-M-129, Artificial Intelligence Project, Memo 58 (revised). + M. L. Minsky. 1963. "`A LISP Garbage Collector Algorithm Using Serial Secondary Storage `_". MIT. Memorandum MAC-M-129, Artificial Intelligence Project, Memo 58 (revised). + + .. admonition:: Abstract + + This paper presents an algorithm for reclaiming unused free + storage memory cells is LISP. It depends on availability of a fast + secondary storage device, or a large block of available temporary + storage. For this price, we get 1. Packing of free-storage into a + solidly packed block. 2. Smooth packing of arbitrary linear blocks + and arrays. 3. The collector will handle arbitrarily complex + re-entrant list structure with no introduction of spurious copies. + 4. The algorithm is quite efficient; the marking pass visits words + at most twice and usually once, and the loading pass is linear. + 5. The system is easily modified to allow for increase in size of + already fixed consecutive blocks, provide one can afford to + initiate a collection pass or use a modified array while waiting + for such a pass to occur. * .. _MOON84: David Moon. 1984. "`Garbage Collection in a Large Lisp System `_". ACM. Symposium on Lisp and Functional Programming, August 1984. + .. admonition:: Abstract + + This paper discusses garbage collection techniques used in a + high-performance Lisp implementation with a large virtual memory, + the Symbolics 3600. Particular attention is paid to practical + issues and experience. In a large system problems of scale appear + and the most straightforward garbage-collection techniques do not + work well. Many of these problems involve the interaction of the + garbage collector with demand-paged virtual memory. Some of the + solutions adopted in the 3600 are presented, including incremental + copying garbage collection, approximately depth-first copying, + ephemeral objects, tagged architecture, and hardware assists. We + discuss techniques for improving the efficiency of garbage + collection by recognizing that objects in the Lisp world have a + variety of lifetimes. The importance of designing the architecture + and the hardware to facilitate garbage collection is stressed. + * .. _MOON85: David Moon. 1985. "Architecture of the Symbolics 3600". IEEE. 12th International Symposium on Computer Architecture, pp. 76--83. @@ -1825,11 +2390,30 @@ Bibliography Luc Moreau. 1998. "`Hierarchical Distributed Reference Counting `_". ACM. ISMM'98 pp. 57--67. + .. admonition:: Abstract + + Massively distributed computing is a challenging problem for + garbage collection algorithm designers as it raises the issue of + scalability. The high number of hosts involved in a computation + can require large tables for reference listing, whereas the lack + of information sharing between hosts in a same locality can entail + redundant GC traffic. In this paper, we argue that a conceptual + hierarchical organisation of massive distributed computations can + solve this problem. By conceptual hierarchical organisation, we + mean that processors are still able to communicate in a peer to + peer manner using their usual communication mechanism, but GC + messages will be routed as if processors were organised in + hierarchy. We present an extension of a distributed reference + counting algorithm that uses such a hierarchical organisation. It + allows us to bound table sizes by the number of hosts in a domain, + and it allows us to share GC information between hosts in a same + locality in order to reduce cross-network GC traffic. + * .. _MFH95: Greg Morrisett, Matthias Felleisen, Robert Harper. 1995. "`Abstract Models of Memory Management `_". Carnegie Mellon University. CMU-CS-FOX-95-01. - .. abstract: mfh95.html + .. admonition:: Abstract Most specifications of garbage collectors concentrate on the low-level algorithmic details of how to find and preserve @@ -1864,7 +2448,7 @@ Bibliography David S. Munro, Alfred Brown, Ron Morrison, J. Eliot B. Moss. 1999. "`Incremental Garbage Collection of a Persistent Object Store using PMOS `_". Morgan Kaufmann. in Advances in Persistent Object Systems, pp. 78--91. - .. abstract: mbmm99.html + .. admonition:: Abstract PMOS is an incremental garbage collector designed specifically to reclaim space in a persistent object store. It is complete in that @@ -1889,7 +2473,7 @@ Bibliography Scott Nettles, James O'Toole, David Pierce, Nickolas Haines. 1992. "`Replication-Based Incremental Copying Collection `_". IWMM'92. - .. abstract: noph92.html + .. admonition:: Abstract We introduce a new replication-based copying garbage collection technique. We have implemented one simple variation of this method @@ -1916,7 +2500,7 @@ Bibliography Scott Nettles. 1992. "`A Larch Specification of Copying Garbage Collection `_". Carnegie Mellon University. CMU-CS-92-219. - .. abstract: nettles92.html + .. admonition:: Abstract Garbage collection (GC) is an important part of many language implementations. One of the most important garbage collection @@ -1934,7 +2518,7 @@ Bibliography Scott Nettles & James O'Toole. 1993. "Implementing Orthogonal Persistence: A Simple Optimization Using Replicating Collection". USENIX. IWOOOS'93. - .. abstract: no93a.html + .. admonition:: Abstract Orthogonal persistence provides a safe and convenient model of object persistence. We have implemented a transaction system which @@ -1958,7 +2542,7 @@ Bibliography Scott Nettles & James O'Toole. 1993. "`Real-Time Replication Garbage Collection `_". ACM. PLDI'93. - .. abstract: no93.html + .. admonition:: Abstract We have implemented the first copying garbage collector that permits continuous unimpeded mutator access to the original @@ -1973,7 +2557,7 @@ Bibliography Norman R. Nielsen. 1977. "Dynamic Memory Allocation in Computer Simulation". ACM. CACM 20:11. - .. abstract: nielsen77.html + .. admonition:: Abstract This paper investigates the performance of 35 dynamic memory allocation algorithms when used to service simulation programs as @@ -1992,7 +2576,7 @@ Bibliography James O'Toole. 1990. "Garbage Collecting Locally". - .. abstract: otoole90.html + .. admonition:: Abstract Generational garbage collection is a simple technique for automatic partial memory reclamation. In this paper, I present the @@ -2013,7 +2597,7 @@ Bibliography James O'Toole & Scott Nettles. 1994. "`Concurrent Replicating Garbage Collection `_". ACM. LFP'94. - .. abstract: on94.html + .. admonition:: Abstract We have implemented a concurrent copying garbage collector that uses replicating garbage collection. In our design, the client can @@ -2031,7 +2615,7 @@ Bibliography Simon Peyton Jones, Norman Ramsey, Fermin Reig. 1999. "`C--: a portable assembly language that supports garbage collection `_". Springer-Verlag. International Conference on Principles and Practice of Declarative Programming 1999, LNCS 1702, pp. 1--28. - .. abstract: jrr99.html + .. admonition:: Abstract For a compiler writer, generating good machine code for a variety of platforms is hard work. One might try to reuse a retargetable @@ -2054,7 +2638,7 @@ Bibliography John S. Pieper. 1993. "Compiler Techniques for Managing Data Motion". Carnegie Mellon University. Technical report number CMU-CS-93-217. - .. abstract: pieper93.html + .. admonition:: Abstract Software caching, automatic algorithm blocking, and data overlays are different names for the same problem: compiler management of @@ -2109,7 +2693,7 @@ Bibliography Pekka P. Pirinen. 1998. "Barrier techniques for incremental tracing". ACM. ISMM'98 pp. 20--25. - .. abstract: pirinen98.html + .. admonition:: Abstract This paper presents a classification of barrier techniques for interleaving tracing with mutator operation during an incremental @@ -2126,7 +2710,7 @@ Bibliography Tony Printezis. 1996. "Disk Garbage Collection Strategies for Persistent Java". Proceedings of the First International Workshop on Persistence and Java. - .. abstract: printezis96.html + .. admonition:: Abstract This paper presents work currently in progress on Disk Garbage Collection issues for PJava, an orthogonally persistent version of @@ -2151,7 +2735,7 @@ Bibliography M. B. Reinhold. 1993. "`Cache Performance of Garbage Collected Programming Languages `_". Laboratory for Computer Science at MIT. MIT/LCS/TR-581. - .. abstract: reinhold93.html + .. admonition:: Abstract As processor speeds continue to improve relative to main-memory access times, cache performance is becoming an increasingly @@ -2195,7 +2779,7 @@ Bibliography Gustavo Rodriguez-Rivera & Vince Russo. 1997. "Non-intrusive Cloning Garbage Collection with Stock Operating System Support". Software -- Practice and Experience. 27:8. - .. abstract: rr97.html + .. admonition:: Abstract It is well accepted that automatic garbage collection simplifies programming, promotes modularity, and reduces development effort. @@ -2227,7 +2811,7 @@ Bibliography Niklas Röjemo. 1995. "Highlights from nhc -- a space-efficient Haskell compiler". Chalmers University of Technology. - .. abstract: rojemo95.html + .. admonition:: Abstract Self-compiling implementations of Haskell, i.e., those written in Haskell, have been and, except one, are still space consuming @@ -2261,11 +2845,28 @@ Bibliography Niklas Röjemo. 1995. "Generational garbage collection for lazy functional languages without temporary space leaks". Chalmers University of Technology. + .. admonition:: Abstract + + Generational garbage collection is an established method for + creating efficient garbage collectors. Even a simple + implementation where all nodes that survive one garbage collection + are *tenured*, i.e., moved to an old generation, works well in + strict languages. In lazy languages, however, such an + implementation can create severe *temporary space leaks*. The + temporary space leaks appear in programs that traverse large + lazily built data structures, e.g., a lazy list representing a + large file, where only a small part is needed at any time. A + simple generational garbage collector cannot reclaim the memory, + used by the lazily built list, at minor collections. The reason is + that at least one of the nodes in the list belongs to the old + generation, after the first minor collection, and will hold on to + the rest of the nodes in the list until the next major collection. + * .. _RR96: Niklas Röjemo & Colin Runciman. 1996. "Lag, drag, void and use -- heap profiling and space-efficient compilation revisited". ACM, SIGPLAN. ICFP'96, ACM SIGPLAN Notices 31:6, ISBN 0-89791-770-7, pp. 34--41. - .. abstract: rr96.html + .. admonition:: Abstract The context for this paper is functional computation by graph reduction. Our overall aim is more efficient use of memory. The @@ -2283,7 +2884,7 @@ Bibliography David J. Roth, David S. Wise. 1999. "`One-bit counts between unique and sticky `_". ACM. ISMM'98, pp. 49--56. - .. abstract: rw99.html + .. admonition:: Abstract Stoye's one-bit reference tagging scheme can be extended to local counts of two or more via two strategies. The first, suited to @@ -2303,13 +2904,37 @@ Bibliography * .. _ROVNER85: - Paul Rovner. 1985. "`On Adding Garbage Collection and Runtime Types to a Strongly-Typed, Statically-Checked, Concurrent Language `_". Xerox PARC. TR CSL-84-7. + Paul Rovner. 1985. "`On Adding Garbage Collection and Runtime Types to a Strongly-Typed, Statically-Checked, Concurrent Language `_". Xerox PARC. TR CSL-84-7. + + .. admonition:: Abstract + + Enough is known now about garbage collection, runtime types, + strong-typing, static-checking and concurrency that it is possible + to explore what happens when they are combined in a real + programming system. + + Storage management is one of a few central issues through which + one can get a good view of the design of an entire system. + Tensions between ease of integration and the need for protection; + between generality, simplicity, flexibility, extensibility and + efficiency are all manifest when assumptions and attitudes about + managing storage are studied. And deep understanding follows best + from the analysis of systems that people use to get real work + done. + + This paper is not for those who seek arguments pro or con about + the need for these features in programming systems; such issues + are for other papers. This one assumes these features to be good + and describes how they combine and interact in Cedar, a + programming language and environment designed to help programmers + build moderate-sized experimental systems for moderate numbers of + people to test and use. * .. _RUNCIMAN92: Colin Runciman & David Wakeling. 1992. "`Heap Profiling of Lazy Functional Programs `_". University of York. - .. abstract: runciman92.html + .. admonition:: Abstract We describe the design, implementation, and use of a new kind of profiling tool that yields valuable information about the memory @@ -2327,7 +2952,7 @@ Bibliography Colin Runciman & Niklas Röjemo. 1994. "`New dimensions in heap profiling `_". University of York. - .. abstract: rr94.html + .. admonition:: Abstract First-generation heap profilers for lazy functional languages have proved to be effective tools for locating some kinds of space @@ -2349,11 +2974,45 @@ Bibliography Colin Runciman & Niklas Röjemo. 1996. "Two-pass heap profiling: a matter of life and death". Department of Computer Science, University of York. + .. admonition:: Abstract + + A heap profile is a chart showing the contents of heap memory + throughout a computation. Contents are depicted abstractly by + showing how much space is occupied by memory cells in each of + several classes. A good heap profiler can use a variety of + attributes of memory cells to de-fine a classification. Effective + profiling usually involves a combination of attributes. The ideal + profiler gives full support for combination in two ways. First, a + section of the heap of interest to the programmer can be specified + by constraining the values of any combination of cell attributes. + Secondly, no matter what attributes are used to specify such a + section, a heap profile can be obtained for that section only, and + any other attribute can be used to define the classification. + + Achieving this ideal is not simple For some combinations of + attributes. A heap profile is derived by interpolation of a series + of censuses of heap contents at different stages. The obvious way + to obtain census data is to traverse the live heap at intervals + throughout the computation. This is fine for static attributes + (e.g. What type of value does this memory cell represent?), and + for dynamic attributes that can be determined for each cell by + examining the heap at any given moment (e.g. From which function + closures can this cell be reached?). But some attributes of cells + can only be determined retrospectively by post-mortem inspection + asa cell is overwritten or garbage-collected (e.g. Is this cell + ever used again?). Now we see the problem: if a profiler supports + both live and pose-mortem attributes, how can we implement the + ideal of unrestricted combinations? That is the problem me solve + in this paper. We give techniques for profiling a. heap section + specified in terms of both live and post-mortem attributes. We + show how to generate live-attribute profiles of a section of the + heal, specified using post-mortem attributes, and vice versa. + * .. _SG95: Jacob Seligmann & Steffen Grarup. 1995. "`Incremental Mature Garbage Collection Using the Train Algorithm `_". Springer-Verlag. ECOOP'95, Lecture Notes in Computer Science, Vol. 952, pp. 235--252, ISBN 3-540-60160-0. - .. abstract: sg95.html + .. admonition:: Abstract We present an implementation of the Train Algorithm, an incremental collection scheme for reclamation of mature garbage in @@ -2369,11 +3028,32 @@ Bibliography Manuel Serrano, Hans-J. Boehm. 2000. "`Understanding memory allocation of Scheme programs `_". ACM. Proceedings of International Conference on Functional Programming 2000. + .. admonition:: Abstract + + Memory is the performance bottleneck of modern architectures. + Keeping memory consumption as low as possible enables fast and + unobtrusive applications. But it is not easy to estimate the + memory use of programs implemented in functional languages, due to + both the complex translations of some high level constructs, and + the use of automatic memory managers. To help understand memory + allocation behavior of Scheme programs, we have designed two + complementary tools. The first one reports on frequency of + allocation, heap configurations and on memory reclamation. The + second tracks down memory leaks. We have applied these tools to + our Scheme compiler, the largest Scheme program we have been + developing. This has allowed us to drastically reduce the amount + of memory consumed during its bootstrap process, without requiring + much development time. Development tools will be neglected unless + they are both conveniently accessible and easy to use. In order to + avoid this pitfall, we have carefully designed the user interface + of these two tools. Their integration into a real programming + environment for Scheme is detailed in the paper. + * .. _SHAPIRO94: Marc Shapiro & Paulo Ferreira. 1994. "`Larchant-RDOSS: a distributed shared persistent memory and its garbage collector `_". INRIA. INRIA Rapport de Recherche no. 2399; Cornell Computer Science TR94-1466. - .. abstract: shapiro94.html + .. admonition:: Abstract Larchant-RDOSS is a distributed shared memory that persists on reliable storage across process lifetimes. Memory management is @@ -2403,7 +3083,7 @@ Bibliography Vivek Singhal, Sheetal V. Kakkad, Paul R. Wilson. 1992. "`Texas: An Efficient, Portable Persistent Store `_". University of Texas at Austin. - .. abstract: singhal92.html + .. admonition:: Abstract Texas is a persistent storage system for C++, providing high performance while emphasizing simplicity, modularity and @@ -2446,13 +3126,30 @@ Bibliography P. G. Sobalvarro. 1988. "`A Lifetime-based Garbage Collector for LISP Systems on General-Purpose Computers `_". MIT. AITR-1417. - .. abstract: sobalvarro88.html + .. admonition:: Abstract Garbage collector performance in LISP systems on custom hardware has been substantially improved by the adoption of lifetime-based garbage collection techniques. To date, however, successful lifetime-based garbage collectors have required special-purpose hardware, or at least privileged access to data structures maintained by the virtual memory system. I present here a lifetime-based garbage collector requiring no special-purpose hardware or virtual memory system support, and discuss its performance. * .. _STEELE75: - Guy L. Steele. 1975. "`Multiprocessing Compactifying Garbage Collection `_". CACM. 18:9 pp. 495--508. + Guy L. Steele. 1975. "Multiprocessing Compactifying Garbage Collection". CACM. 18:9 pp. 495--508. + + .. admonition:: Abstract + + Algorithms for a multiprocessing compactifying garbage collector + are presented and discussed. The simple case of two processors, + one performing LISP-like list operations and the other performing + garbage collection continuously, is thoroughly examined. The + necessary capabilities of each processor are defined, as well as + interprocessor communication and interlocks. Complete procedures + for garbage collection and for standard list processing primitives + are presented and thoroughly explained. Particular attention is + given to the problems of marking and relocating list cells while + another processor may be operating on them. The primary aim + throughout is to allow the list processor to run unimpeded while + the other processor reclaims list storage The more complex case + involving several list processors and one or more garbage + collection processors are also briefly discussed. * .. _STEELE76: @@ -2460,21 +3157,68 @@ Bibliography * .. _STEELE77: - Guy L. Steele. 1977. "Data Representation in PDP-10 MACLISP". MIT. AI Memo 421. + Guy L. Steele. 1977. "`Data Representation in PDP-10 MACLISP `_". MIT. AI Memo 420. + + .. admonition:: Abstract + + The internal representations of the various MacLISP data types are + presented and discussed. Certain implementation tradeoffs are + considered. The ultimate decisions on these tradeoffs are + discussed in the light of MacLISP's prime objective of being an + efficient high-level language for the implementation of large + systems such as MACSYMA. The basic strategy of garbage collection + is outlined, with reference to the specific representations + involved. Certain "clever tricks" are explained and justified. The + "address space crunch" is explained and some alternative solutions + explored. * .. _SLC99: - James M. Stichnoth, Guei-Yuan Lueh, Michal Cierniak. 1999. "`Support for Garbage Collection at Every Instruction in a Java Compiler `_". SIGPLAN. Proceedings of the 1999 ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI). SIGPLAN Notices 34(5). pp. 118--127. + James M. Stichnoth, Guei-Yuan Lueh, Michal Cierniak. 1999. "`Support for Garbage Collection at Every Instruction in a Java Compiler `_". SIGPLAN. Proceedings of the 1999 ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI). SIGPLAN Notices 34(5). pp. 118--127. + + .. admonition:: Abstract + + A high-performance implementation of a Java Virtual Machine + requires a compiler to translate Java bytecodes into native + instructions, as well as an advanced garbage collector (e.g., + copying or generational). When the Java heap is exhausted and the + garbage collector executes, the compiler must report to the + garbage collector all live object references contained in physical + registers and stack locations. Typical compilers only allow + certain instructions (e.g., call instructions and backward + branches) to be GC-safe; if GC happens at some other instruction, + the compiler may need to advance execution to the next GC-safe + point. Until now, no one has ever attempted to make every + compiler-generated instruction GC-safe, due to the perception that + recording this information would require too much space. This kind + of support could improve the GC performance in multithreaded + applications. We show how to use simple compression techniques to + reduce the size of the GC map to about 20% of the generated code + size, a result that is competitive with the best previously + published results. In addition, we extend the work of Agesen, + Detlefs, and Moss, regarding the so-called “JSR Problem” (the + single exception to Java’s type safety property), in a way that + eliminates the need for extra runtime overhead in the generated + code. * .. _SCN84: Will R Stoye, T J W Clarke, Arthur C Norman. 1984. "Some Practical Methods for Rapid Combinator Reduction". In LFP 1984, 159--166. + .. admonition:: Abstract + + The SKIM II processor is a microcoded hardware machine for the + rapid evaluation of functional languages. This paper gives details + of some of the more novel methods employed by SKIM II, and + resulting performance measurements. The authors conclude that + combinator reduction can still form the basis for the efficient + implementation of a functional language. + * .. _TD95: David Tarditi & Amer Diwan. 1995. "`Measuring the Cost of Storage Management `_". Carnegie Mellon University. CMU-CS-94-201. - .. abstract: td95.html + .. admonition:: Abstract We study the cost of storage management for garbage-collected programs compiled with the Standard ML of New Jersey compiler. We @@ -2487,7 +3231,7 @@ Bibliography Stephen Thomas, Richard E. Jones. 1994. "Garbage Collection for Shared Environment Closure Reducers". Computing Laboratory, The University of Kent at Canterbury. Technical Report 31-94. - .. abstract: tj94.html + .. admonition:: Abstract Shared environment closure reducers such as Fairbairn and Wray's TIM incur a comparatively low cost when creating a suspension, and @@ -2514,11 +3258,22 @@ Bibliography Stephen Thomas. 1995. "Garbage Collection in Shared-Environment Closure Reducers: Space-Efficient Depth First Copying using a Tailored Approach". *Information Processing Letters.* 56:1, pp. 1--7. + .. admonition:: Abstract + + Implementations of abstract machines such as the OP-TIM and the + PG-TIM need to use a tailored garbage collector which seems to + require an auxiliary stack,with a potential maximum size that is + directly proportional to the amount of live data in the heap. + However, it turns out that it is possible to build a recursive + copying collector that does not require additional space by + reusing already-scavenged space. This paper is a description of + this technique. + * .. _TT97: Mads Tofte & Jean-Pierre Talpin. 1997. "`Region-Based Memory Management `_". Information and Computation 132(2), pp. 109--176. - .. abstract: tt97.html + .. admonition:: Abstract This paper describes a memory management discipline for programs that perform dynamic memory allocation and de-allocation. At @@ -2547,11 +3302,22 @@ Bibliography Dave Ungar. 1984. "`Generation Scavenging: A Non-disruptive High Performance Storage Reclamation Algorithm `_". ACM, SIGSOFT, SIGPLAN. Practical Programming Environments Conference. + .. admonition:: Abstract + + Many interactive computing environments provide automatic storage + reclamation and virtual memory to ease the burden of managing + storage. Unfortunately, many storage reclamation algorithms impede + interaction with distracting pauses. *Generation Scavenging* is a + reclamation algorithm that has no noticeable pauses, eliminates + page faults for transient objects, compacts objects without + resorting to indirection, and reclaims circular structures, in one + third the time of traditional approaches. + * .. _UNGAR88: Dave Ungar & Frank Jackson. 1988. "`Tenuring Policies for Generation-Based Storage Reclamation `_". SIGPLAN. OOPSLA '88 Conference Proceedings, ACM SIGPLAN Notices, Vol. 23, No. 11, pp. 1--17. - .. abstract: ungar88.html + .. admonition:: Abstract One of the most promising automatic storage reclamation techniques, generation-based storage reclamation, suffers poor @@ -2571,7 +3337,7 @@ Bibliography Kiem-Phong Vo. 1996. "Vmalloc: A General and Efficient Memory Allocator". Software -- Practice and Experience. 26(3): 357--374 (1996). - .. abstract: vo96.html + .. admonition:: Abstract On C/Unix systems, the malloc interface is standard for dynamic memory allocation. Despite its popularity, malloc's shortcomings @@ -2598,11 +3364,24 @@ Bibliography Daniel C. Watson, David S. Wise. 1976. "Tuning Garwick's algorithm for repacking sequential storage". *BIT.* 16, 4 (December 1976): 442--450. + .. admonition:: Abstract + + Garwick's algorithm, for repacking LIFO lists stored in a + contiguous block of memory, bases the allocation of remaining + space upon both sharing and previous stack growth. A system + whereby the weight applied to each method can be adjusted + according to the current behaviour of the stacks is discussed. + + We also investigate the problem of determining during memory + repacking that the memory is used to saturation and the driving + program should therefore be aborted. The tuning parameters studied + here seem to offer no new grasp on this problem. + * .. _WLM92: Paul R. Wilson, Michael S. Lam, Thomas G. Moher. 1992. "Caching Considerations for Generational Garbage Collection". ACM. L&FP 92. - .. abstract: wlm92.html + .. admonition:: Abstract GC systems allocate and reuse memory cyclically; this imposes a cyclic pattern on memory accesses that has its own distinctive @@ -2633,7 +3412,7 @@ Bibliography Paul R. Wilson, Sheetal V. Kakkad. 1992. "`Pointer Swizzling at Page Fault Time `_". University of Texas at Austin. - .. abstract: wil92a.html + .. admonition:: Abstract Pointer swizzling at page fault time is a novel address translation mechanism that exploits conventional address @@ -2660,7 +3439,7 @@ Bibliography Paul R. Wilson. 1994. "`Uniprocessor Garbage Collection Techniques `_". University of Texas. - .. abstract: wil94.html + .. admonition:: Abstract We survey basic garbage collection algorithms, and variations such as incremental and generational collection; we then discuss @@ -2676,7 +3455,7 @@ Bibliography Paul R. Wilson, Mark S. Johnstone, Michael Neely, David Boles. 1995. "`Dynamic Storage Allocation: A Survey and Critical Review `_". University of Texas at Austin. - .. abstract: wil95.html + .. admonition:: Abstract Dynamic memory allocation has been a fundamental part of most computer systems since roughly 1960, and memory allocation is @@ -2700,23 +3479,69 @@ Bibliography * .. _WISE78: - David S. Wise. 1978. "`The double buddy system `_". Department of Computer Science at Indiana University. Technical Report 79. + David S. Wise. 1978. "`The double buddy system `_". Department of Computer Science at Indiana University. Technical Report 79. + + .. admonition:: Abstract + + A new buddy system is described in which the region of storage + being managed is partitioned into two sub-regions, each managed by + a fairly standard "binary" buddy system. Like the weighted buddy + systems of Shen and Peterson, the block sizes are of sizes 2\ + :superscript:`n+1` or 3·2\ :superscript:`n`, but unlike theirs + there is no extra overhead for typing information or for buddy + calculation, and an allocation which requires splitting an extant + available block only rarely creates a block smaller than the one + being allocated. Such smaller blocks are carved out only when the + boundary between the two subregions floats; the most interesting + property of this system is that the procedures for allocation and + deallocation are designed to keep blocks immediately adjacent to + the subregion boundary free, so that the boundary may be moved + within a range of unused space without disturbing blocks in use. + This option is attained with a minimum of extra computation beyond + that of a binary buddy system, and provides this scheme with a new + approach to the problem of external fragmentation. * .. _WISE79: - David S. Wise. 1979. "`Morris's garbage compaction algorithm restores reference counts `_". TOPLAS. 1, 1 (July l979): 115--120. + David S. Wise. 1979. "`Morris's garbage compaction algorithm restores reference counts `_". TOPLAS. 1, 1 (July 1979): 115--120. + + .. admonition:: Abstract + + The two-pass compaction algorithm of F.L. Morris, which follows + upon the mark phase in a garbage collector, may be modified to + recover reference counts for a hybrid storage management system. + By counting the executions of two loops in that algorithm where + upward and downward references, respectively, are forwarded to the + relocation address of one node, we can initialize a count of + active references and then update it but once. The reference count + may share space with the mark bit in each node, but it may not + share the additional space required in each pointer by Morris's + algorithm, space which remains unused outside the garbage + collector. * .. _WISE85: David S. Wise. 1985. "`Design for a multiprocessing heap with on-board reference counting `_". Springer-Verlag. In J.-P. Jouannaud (ed.), Functional Programming Languages and Computer Architecture, Lecture Notes in Computer Science 201: 289--304. + .. admonition:: Abstract + + A project to design a pair of memory chips with a modicum of + intelligence is described. Together, the two allow simple + fabrication of a small memory bank, a heap of binary (LISP-like) + nodes that offers the following features: 64-bit nodes; two + pointer fields per node up to 29 bits each; reference counts + implicitly maintained on writes; 2 bits per node for marking + (uncounted) circular references; 4 bits per node for + conditional-store testing at the memory; provision for + processor-driven, recounting garbage collection. + * .. _WISE92: .. _WISE93: David S. Wise. 1993. "`Stop-and-copy and one-bit reference counting `_". *Information Processing Letters.* 46, 5 (July 1993): 243--249. - .. abstract: wise92.html + .. admonition:: Abstract A stop-and-copy garbage collector updates one-bit reference counting with essentially no extra space and minimal memory cycles @@ -2734,15 +3559,50 @@ Bibliography David S. Wise, Joshua Walgenbach. 1996. "`Static and Dynamic Partitioning of Pointers as Links and Threads `_". SIGPLAN. Proc. 1996 ACM SIGPLAN Intl. Conf. on Functional Programming, SIGPLAN Not. 31, 6 (June 1996), pp. 42--49. + .. admonition:: Abstract + + Identifying some pointers as invisible threads, for the purposes + of storage management, is a generalization from several widely + used programming conventions, like threaded trees. The necessary + invariant is that nodes that are accessible (without threads) emit + threads only to other accessible nodes. Dynamic tagging or static + typing of threads ameliorates storage recycling both in functional + and imperative languages. + + We have seen the distinction between threads and links sharpen + both hardware- and software-supported storage management in + SCHEME, and also in C. Certainly, therefore, implementations of + languages that already have abstract management and concrete + typing, should detect and use this as a new static type. + * .. _WHHHO94: David S. Wise, Brian Heck, Caleb Hess, Willie Hunt, Eric Ost. 1997. "`Uniprocessor Performance of a Reference-Counting Hardware Heap `_". *LISP and Symbolic Computation.* 10, 2 (July 1997), pp. 159--181. + .. admonition:: Abstract + + A hardware self-managing heap memory (RCM) for languages like + LISP, SMALLTALK, and JAVA has been designed, built, tested and + benchmarked. On every pointer write from the processor, + reference-counting transactions are performed in real time within + this memory, and garbage cells are reused without processor + cycles. A processor allocates new nodes simply by reading from a + distinguished location in its address space. The memory hardware + also incorporates support for off-line, multiprocessing, + mark-sweep garbage collection. + + Performance statistics are presented from a partial implementation + of SCHEME over five different memory models and two garbage + collection strategies, from main memory (no access to RCM) to a + fully operational RCM installed on an external bus. The + performance of the RCM memory is more than competitive with main + memory. + * .. _WITHINGTON91: P. Tucker Withington. 1991. "`How Real is 'Real-Time' Garbage Collection? `_". ACM. OOPSLA/ECOOP '91 Workshop on Garbage Collection in Object-Oriented Systems. - .. abstract: withington91.html + .. admonition:: Abstract A group at Symbolics is developing a Lisp runtime kernel, derived from its Genera operating system, to support real-time control @@ -2765,7 +3625,7 @@ Bibliography G. May Yip. 1991. "`Incremental, Generational Mostly-Copying Garbage Collection in Uncooperative Environments `_". Digital Equipment Corporation. - .. abstract: yip91.html + .. admonition:: Abstract The thesis of this project is that incremental collection can be done feasibly and efficiently in an architecture and compiler @@ -2793,11 +3653,27 @@ Bibliography Taiichi Yuasa. 1990. "Real-Time Garbage Collection on General-Purpose Machines". Journal of Software and Systems. 11:3 pp. 181--198. + .. admonition:: Abstract + + An algorithm for real-time garbage collection is presented, proved + correct, and evaluated. This algorithm is intended for + list-processing systems on general-purpose machines, i.e., Von + Neumann style serial computers with a single processor. On these + machines, real-time garbage collection inevitably causes some + overhead on the overall execution of the list-processing system, + because some of the primitive list-processing operations must + check the status of garbage collection. By removing such overhead + from frequently used primitives such as pointer references (e.g., + Lisp car and cdr) and stack manipulations, the presented algorithm + reduces the execution overhead to a great extent. Although the + algorithm does not support compaction of the whole data space, it + efficiently supports partial compaction such as array relocation. + * .. _ZORN88: Benjamin Zorn & Paul Hilfinger. 1988. "`A Memory Allocation Profiler for C and Lisp Programs `_". USENIX. Proceedings for the Summer 1988 USENIX Conference, pp. 223--237. - .. abstract: zorn88.html + .. admonition:: Abstract This paper describes inprof, a tool used to study the memory allocation behavior of programs. mprof records the amount of @@ -2815,7 +3691,7 @@ Bibliography Benjamin Zorn. 1989. "`Comparative Performance Evaluation of Garbage Collection Algorithms `_". Computer Science Division (EECS) of University of California at Berkeley. Technical Report UCB/CSD 89/544 and PhD thesis. - .. abstract: zorn89.html + .. admonition:: Abstract This thesis shows that object-level, trace-driven simulation can facilitate evaluation of language runtime systems and reaches new @@ -2855,7 +3731,7 @@ Bibliography Benjamin Zorn. 1990. "Comparing Mark-and-sweep and Stop-and-copy Garbage Collection". ACM. Conference on Lisp and Functional Programming, pp. 87--98. - .. abstract: zorn90b.html + .. admonition:: Abstract Stop-and-copy garbage collection has been preferred to mark-and-sweep collection in the last decade because its @@ -2873,7 +3749,7 @@ Bibliography Benjamin Zorn. 1990. "`Barrier Methods for Garbage Collection `_". University of Colorado at Boulder. Technical Report CU-CS-494-90. - .. abstract: zorn90.html + .. admonition:: Abstract Garbage collection algorithms have been enhanced in recent years with two methods: generation-based collection and Baker @@ -2900,7 +3776,7 @@ Bibliography Benjamin Zorn. 1991. "`The Effect of Garbage Collection on Cache Performance `_". University of Colorado at Boulder. Technical Report CU-CS-528-91. - .. abstract: zorn91.html + .. admonition:: Abstract Cache performance is an important part of total performance in modern computer systems. This paper describes the use of @@ -2926,7 +3802,7 @@ Bibliography Benjamin Zorn & Dirk Grunwald. 1992. "`Empirical Measurements of Six Allocation-intensive C Programs `_". ACM, SIGPLAN. SIGPLAN notices, 27(12):71--80. - .. abstract: zorn92b.html + .. admonition:: Abstract Dynamic memory management is an important part of a large class of computer programs and high-performance algorithms for dynamic @@ -2950,7 +3826,7 @@ Bibliography Benjamin Zorn. 1993. "`The Measured Cost of Conservative Garbage Collection `_". Software -- Practice and Experience. 23(7):733--756. - .. abstract: zorn92.html + .. admonition:: Abstract Because dynamic memory management is an important part of a large class of computer programs, high-performance algorithms for @@ -2979,7 +3855,7 @@ Bibliography Benjamin Zorn & Dirk Grunwald. 1994. "`Evaluating Models of Memory Allocation `_". ACM. Transactions on Modeling and Computer Simulation 4(1):107--131. - .. abstract: zorn92a.html + .. admonition:: Abstract Because dynamic memory management is an important part of a large class of computer programs, high-performance algorithms for diff --git a/mps/manual/source/index.rst b/mps/manual/source/index.rst index 91d27f7a8a8..4edc69bbf60 100644 --- a/mps/manual/source/index.rst +++ b/mps/manual/source/index.rst @@ -1,7 +1,3 @@ -.. Memory Pool System documentation master file, created by - sphinx-quickstart on Tue Oct 9 11:21:17 2012. - - Memory Pool System ################## @@ -15,32 +11,26 @@ Memory Pool System design/old -Memory Management Reference -########################### - -.. toctree:: - :hidden: - - mmref-index - mmref-copyright - -.. toctree:: - :maxdepth: 2 - - mmref/index - mmref/bib - mmref/credit - - Appendices ########## .. toctree:: :maxdepth: 1 + bib glossary/index copyright contact release * :ref:`genindex` + + +.. toctree:: + :hidden: + + mmref/index + mmref-index + mmref/faq + mmref-copyright + mmref/credit diff --git a/mps/manual/source/mmref/index.rst b/mps/manual/source/mmref/index.rst index 6ca51d1fb39..d87db74339d 100644 --- a/mps/manual/source/mmref/index.rst +++ b/mps/manual/source/mmref/index.rst @@ -11,5 +11,3 @@ Introduction to memory management alloc recycle lang - faq - diff --git a/mps/manual/source/topic/interface.rst b/mps/manual/source/topic/interface.rst index a939ddf302b..54637557f60 100644 --- a/mps/manual/source/topic/interface.rst +++ b/mps/manual/source/topic/interface.rst @@ -194,7 +194,7 @@ out parameter, like this:: res = mps_alloc((mps_addr_t *)&fp, pool, sizeof(struct foo)); This is known as :term:`type punning`, and its behaviour is not -defined in ANSI/ISO Standard C. See :ref:`ISO/IEC 9899:1990 ` +defined in ANSI/ISO Standard C. See :ref:`ISO/IEC 9899:1990 ` §6.3.2.3, which defines the conversion of a pointer from one type to another: the behaviour of this cast is not covered by any of the cases in the standard. @@ -209,7 +209,7 @@ Instead, we recommend this approach:: This has defined behaviour because conversion from ``void *`` to any other :term:`object pointer` type is defined by :ref:`ISO/IEC -9899:1990 ` §6.3.2.3.1. +9899:1990 ` §6.3.2.3.1. .. index:: @@ -219,7 +219,7 @@ Macros ------ #. For function-like macros, the MPS follows the same convention as - the Standard C library. To quote :ref:`ISO/IEC 9899:1990 ` + the Standard C library. To quote :ref:`ISO/IEC 9899:1990 ` §7.1.7: Any function declared in a header may additionally be diff --git a/mps/manual/source/topic/plinth.rst b/mps/manual/source/topic/plinth.rst index a7e94d21a0b..daba2ad62de 100644 --- a/mps/manual/source/topic/plinth.rst +++ b/mps/manual/source/topic/plinth.rst @@ -296,7 +296,7 @@ Library module This function is intended to have the same semantics as the :c:func:`fputc` function of the ANSI C Standard (:ref:`ISO/IEC - 9899:1990 ` §7.11.7.3). + 9899:1990 ` §7.11.7.3). .. note:: @@ -314,7 +314,7 @@ Library module This function is intended to have the same semantics as the :c:func:`fputs` function of the ANSI C Standard (:ref:`ISO/IEC - 9899:1990 ` §7.11.7.4). + 9899:1990 ` §7.11.7.4). Return a non-negative integer if successful, or :c:func:`mps_lib_get_EOF` if not. @@ -383,7 +383,7 @@ Library module This function is intended to have the same semantics as the :c:func:`memcmp` function of the ANSI C Standard (:ref:`ISO/IEC - 9899:1990 ` §7.11.4.1). + 9899:1990 ` §7.11.4.1). .. note:: @@ -406,7 +406,7 @@ Library module This function is intended to have the same semantics as the :c:func:`memcpy` function of the ANSI C Standard (:ref:`ISO/IEC - 9899:1990 ` §7.11.2.1). + 9899:1990 ` §7.11.2.1). The MPS never passes overlapping blocks to :c:func:`mps_lib_memcpy`. @@ -432,7 +432,7 @@ Library module This function is intended to have the same semantics as the :c:func:`memset` function of the ANSI C Standard (:ref:`ISO/IEC - 9899:1990 ` §7.11.6.1). + 9899:1990 ` §7.11.6.1). .. note:: From b7667e3bbe67f36ae727968549a520a65299c234 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sat, 24 May 2014 15:48:57 +0100 Subject: [PATCH 34/53] Bibliography was moved to top level. Copied from Perforce Change: 186279 ServerID: perforce.ravenbrook.com --- mps/manual/source/mmref-index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps/manual/source/mmref-index.rst b/mps/manual/source/mmref-index.rst index 264fa5c3e4a..c645899bb49 100644 --- a/mps/manual/source/mmref-index.rst +++ b/mps/manual/source/mmref-index.rst @@ -32,7 +32,7 @@ Welcome to the **Memory Management Reference**! This is a resource for programm .. image:: diagrams/copying.svg :target: bib_ - .. _bib: mmref/bib.html#bibliography + .. _bib: bib.html#bibliography .. admonition:: :ref:`mmref-faq` From 4958cf81f38f94056ce6e2528c1997789e560e09 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sat, 24 May 2014 16:55:02 +0100 Subject: [PATCH 35/53] List all the glossary entries. Copied from Perforce Change: 186282 ServerID: perforce.ravenbrook.com --- mps/manual/source/glossary/index.rst | 587 ++++++++++++++++++ .../source/themes/mmref/static/mmref.css_t | 5 + 2 files changed, 592 insertions(+) diff --git a/mps/manual/source/glossary/index.rst b/mps/manual/source/glossary/index.rst index 91e022df5ad..f3f3ed154c0 100644 --- a/mps/manual/source/glossary/index.rst +++ b/mps/manual/source/glossary/index.rst @@ -32,3 +32,590 @@ Memory Management Glossary v w z + +:term:`absolute address ` +:term:`activation frame ` +:term:`activation record` +:term:`activation stack ` +:term:`active ` +:term:`address` +:term:`address space` +:term:`address space layout randomization` +:term:`address translation cache ` +:term:`address-ordered first fit` +:term:`aging space` +:term:`algebraic data type` +:term:`alignment` +:term:`alive ` +:term:`allocate` +:term:`allocation frame` +:term:`allocation mechanism` +:term:`allocation pattern` +:term:`allocation point` +:term:`allocation point protocol` +:term:`allocation policy` +:term:`allocation strategy` +:term:`allocator` +:term:`ambiguous reference` +:term:`ambiguous root` +:term:`arena` +:term:`arena class` +:term:`ASLR
` +:term:`assertion` +:term:`asynchronous garbage collector` +:term:`ATC ` +:term:`atomic object ` +:term:`automatic memory management` +:term:`automatic storage duration` + +:term:`backing store` +:term:`barrier (1)` +:term:`barrier (2)` +:term:`barrier hit ` +:term:`base pointer` +:term:`best fit` +:term:`BIBOP` +:term:`big bag of pages ` +:term:`binary buddies` +:term:`bit array ` +:term:`bit table ` +:term:`bit vector ` +:term:`bitmap` +:term:`bitmapped fit` +:term:`bitmask` +:term:`bitset ` +:term:`black` +:term:`blacklisting` +:term:`black-listing` +:term:`block` +:term:`bounds error ` +:term:`boxed` +:term:`break-table` +:term:`brk` +:term:`broken heart` +:term:`bucket` +:term:`buddy system` +:term:`buffer` +:term:`bus error` +:term:`byte (1)` +:term:`byte (2)` +:term:`byte (3)` +:term:`byte (4)` + +:term:`C89 ` +:term:`C90` +:term:`C99` +:term:`cache (1)` +:term:`cache (2)` +:term:`cache memory ` +:term:`cache policy` +:term:`caching (3)` +:term:`cactus stack` +:term:`card` +:term:`card marking` +:term:`cell ` +:term:`Cheney collector` +:term:`Cheney scan ` +:term:`clamped state` +:term:`client arena` +:term:`client object` +:term:`client pointer` +:term:`client program ` +:term:`closure` +:term:`coalesce` +:term:`collect` +:term:`collection ` +:term:`collection cycle` +:term:`collector (1) ` +:term:`collector (2)` +:term:`color` +:term:`colour` +:term:`commit limit` +:term:`committed (1) ` +:term:`committed (2)` +:term:`compactifying ` +:term:`compaction` +:term:`composite object` +:term:`comprehensive` +:term:`concurrent garbage collection ` +:term:`condemned set` +:term:`connected` +:term:`cons (1)` +:term:`cons (2) ` +:term:`conservative garbage collection` +:term:`constant root` +:term:`constructor (1)` +:term:`constructor (2)` +:term:`continuation` +:term:`control stack` +:term:`cool` +:term:`copy method` +:term:`copying garbage collection` +:term:`core` +:term:`creation space` +:term:`critical path` +:term:`crossing map` +:term:`cyclic data structure` + +:term:`dangling pointer` +:term:`data stack` +:term:`dead` +:term:`deallocate ` +:term:`debugging pool` +:term:`deferred coalescing` +:term:`deferred reference counting` +:term:`dependent object` +:term:`derived pointer ` +:term:`derived type` +:term:`destructor (1)` +:term:`destructor (2)` +:term:`DGC ` +:term:`direct method` +:term:`dirty bit` +:term:`distributed garbage collection` +:term:`double buddies` +:term:`double free` +:term:`doubleword` +:term:`doubly weak hash table` +:term:`DRAM ` +:term:`dynamic allocation ` +:term:`dynamic extent` +:term:`dynamic memory` +:term:`dynamic RAM ` + +:term:`ecru ` +:term:`edge` +:term:`entry table (1)` +:term:`entry table (2)` +:term:`exact garbage collection` +:term:`exact reference` +:term:`exact root` +:term:`exact segregated fit` +:term:`execution stack ` +:term:`exit table` +:term:`extent ` +:term:`external fragmentation` + +:term:`fencepost` +:term:`fence post` +:term:`fencepost error` +:term:`fence post error` +:term:`Fibonacci buddies` +:term:`FIFO-ordered first fit` +:term:`file mapping ` +:term:`finalization` +:term:`finalized block` +:term:`first fit` +:term:`fix` +:term:`flip` +:term:`floating garbage` +:term:`foreign code` +:term:`format` +:term:`format method` +:term:`formatted object` +:term:`forward method` +:term:`forwarding marker` +:term:`forwarding object` +:term:`forwarding pointer` +:term:`fragmentation` +:term:`frame ` +:term:`free (1)` +:term:`free (2)` +:term:`free (3)` +:term:`free (4) ` +:term:`free block` +:term:`free block chain` +:term:`free list` +:term:`free store ` +:term:`freestore ` +:term:`from space` +:term:`fromspace` +:term:`function pointer` +:term:`function record ` + +:term:`garbage` +:term:`garbage collection` +:term:`garbage collector` +:term:`GB ` +:term:`GC ` +:term:`General Protection Fault` +:term:`generation` +:term:`generation chain` +:term:`generation scavenging ` +:term:`generational garbage collection` +:term:`generational hypothesis` +:term:`gigabyte` +:term:`good fit` +:term:`GPF ` +:term:`grain` +:term:`graph` +:term:`gray` +:term:`grey` +:term:`gray list` +:term:`grey list` + +:term:`handle` +:term:`header ` +:term:`heap` +:term:`heap allocation` +:term:`hit` +:term:`hit rate` +:term:`hot` +:term:`huge page` + +:term:`immediate data` +:term:`immune set` +:term:`immutable` +:term:`immutable object ` +:term:`in-band header` +:term:`in parameter` +:term:`in/out parameter` +:term:`incremental garbage collection` +:term:`incremental update` +:term:`indefinite extent` +:term:`indexed fit` +:term:`indirect method` +:term:`infant mortality ` +:term:`inline allocation (1)` +:term:`inline allocation (2)` +:term:`inter-generational pointer` +:term:`interior pointer` +:term:`internal fragmentation` +:term:`invalid page fault` +:term:`inverted page table` +:term:`inverted page-table` +:term:`is-forwarded method` + +:term:`kB ` +:term:`keyword argument` +:term:`kilobyte` + +:term:`large object area` +:term:`large page ` +:term:`leaf object` +:term:`leak ` +:term:`life ` +:term:`lifetime` +:term:`LIFO-ordered first fit` +:term:`limited-field reference count` +:term:`linear addressing` +:term:`live` +:term:`load` +:term:`locality of reference` +:term:`location ` +:term:`location dependency` +:term:`lock free` +:term:`logical address ` +:term:`longword ` + +:term:`machine word ` +:term:`main memory` +:term:`malloc` +:term:`manual memory management` +:term:`mapped` +:term:`mapping` +:term:`mark-compact` +:term:`mark-sweep` +:term:`mark-and-sweep` +:term:`marking` +:term:`MB ` +:term:`megabyte` +:term:`memoization ` +:term:`memory (1)` +:term:`memory (2)` +:term:`memory (3)
` +:term:`memory (4)` +:term:`memory bandwidth` +:term:`memory cache ` +:term:`memory hierarchy ` +:term:`memory leak` +:term:`memory location` +:term:`memory management` +:term:`Memory Management Unit ` +:term:`memory manager` +:term:`memory mapping` +:term:`memory protection ` +:term:`message` +:term:`message queue` +:term:`message type` +:term:`misaligned ` +:term:`miss` +:term:`miss rate` +:term:`mmap` +:term:`MMU` +:term:`mostly-copying garbage collection` +:term:`mostly-exact garbage collection ` +:term:`mostly-precise garbage collection ` +:term:`moving garbage collector` +:term:`moving memory manager` +:term:`mutable` +:term:`mutator` + +:term:`nailing ` +:term:`natural alignment` +:term:`nepotism` +:term:`next fit` +:term:`new space` +:term:`newspace ` +:term:`node` +:term:`non-moving garbage collector` +:term:`non-moving memory manager` +:term:`nursery generation ` +:term:`nursery space` + +:term:`object` +:term:`object format` +:term:`object pointer` +:term:`off-white` +:term:`old space ` +:term:`oldspace ` +:term:`one-bit reference count` +:term:`opaque type` +:term:`out parameter` +:term:`out-of-band header` +:term:`overcommit` +:term:`overwriting error` + +:term:`padding` +:term:`padding method` +:term:`padding object` +:term:`page` +:term:`page fault` +:term:`page marking` +:term:`page protection ` +:term:`page table` +:term:`paged in` +:term:`paged out` +:term:`paging` +:term:`palimpsest` +:term:`parallel garbage collection` +:term:`parked state` +:term:`perfect fit` +:term:`phantom reachable` +:term:`phantomly reachable` +:term:`phantom reference` +:term:`physical address` +:term:`physical address space` +:term:`physical memory (1)` +:term:`physical memory (2)` +:term:`physical storage ` +:term:`pig in the python` +:term:`pig in the snake ` +:term:`pinning` +:term:`placement policy ` +:term:`platform` +:term:`plinth` +:term:`pointer` +:term:`pool` +:term:`pool class` +:term:`precise garbage collection ` +:term:`precise reference ` +:term:`precise root ` +:term:`premature free` +:term:`premature promotion ` +:term:`premature tenuring` +:term:`primary storage
` +:term:`promotion` +:term:`protectable root` +:term:`protection` +:term:`protection exception ` +:term:`protection fault` +:term:`protection violation ` + +:term:`quadword` + +:term:`RAM` +:term:`random access memory ` +:term:`ramp allocation` +:term:`rank` +:term:`rash` +:term:`raw ` +:term:`reachable` +:term:`read barrier` +:term:`read fault` +:term:`read-only memory ` +:term:`real memory (1)` +:term:`real memory (2) ` +:term:`reclaim` +:term:`recycle` +:term:`reference` +:term:`reference counting` +:term:`reference object` +:term:`region inference` +:term:`register` +:term:`register set partitioning` +:term:`relocation` +:term:`remembered set` +:term:`remote reference` +:term:`replicating garbage collector` +:term:`reserved` +:term:`resident` +:term:`resident set` +:term:`result code` +:term:`resurrection` +:term:`ROM` +:term:`root` +:term:`root description` +:term:`root mode` +:term:`root set` + +:term:`sbrk` +:term:`scalar data type` +:term:`scan` +:term:`scan method` +:term:`scan state` +:term:`scavenging garbage collection ` +:term:`SDRAM` +:term:`segmentation violation` +:term:`segmented addressing` +:term:`segregated allocation cache` +:term:`segregated fit` +:term:`segregated free list` +:term:`segregated free-list` +:term:`semi-conservative garbage collection` +:term:`semi-space` +:term:`semi-space collector ` +:term:`sequential fit` +:term:`sequential store buffer` +:term:`shared memory` +:term:`simple object` +:term:`simple segregated storage` +:term:`size` +:term:`size class` +:term:`skip method` +:term:`smart pointer` +:term:`snap-out` +:term:`snapshot at the beginning` +:term:`soft reference` +:term:`softly reachable` +:term:`space leak ` +:term:`spare commit limit` +:term:`spare committed memory` +:term:`spaghetti stack ` +:term:`splat` +:term:`split` +:term:`SRAM ` +:term:`SSB ` +:term:`stack` +:term:`stack allocation` +:term:`stack frame` +:term:`stack record ` +:term:`static allocation` +:term:`static memory (1)` +:term:`static memory (2)` +:term:`static object` +:term:`static RAM ` +:term:`static storage duration` +:term:`stepper function` +:term:`sticky reference count ` +:term:`stop-and-copy collection` +:term:`storage ` +:term:`storage hierarchy` +:term:`storage level` +:term:`storage management ` +:term:`store (1)` +:term:`store (2) ` +:term:`strict segregated fit` +:term:`strong reference` +:term:`strong root` +:term:`strong tri-color invariant` +:term:`strong tri-colour invariant` +:term:`strong tricolor invariant` +:term:`strong tricolour invariant` +:term:`strongly reachable` +:term:`suballocator` +:term:`subgraph` +:term:`superpage ` +:term:`sure reference ` +:term:`swap space` +:term:`swapped in` +:term:`swapped out` +:term:`swapping` +:term:`sweeping` +:term:`synchronous garbage collector` + +:term:`tabling ` +:term:`tag` +:term:`tagged architecture` +:term:`tagged reference` +:term:`TB (1) ` +:term:`TB (2) ` +:term:`telemetry filter` +:term:`telemetry label` +:term:`telemetry stream` +:term:`tenuring ` +:term:`terabyte` +:term:`termination ` +:term:`thrash` +:term:`thread` +:term:`threatened set ` +:term:`TLB ` +:term:`to space` +:term:`tospace` +:term:`trace` +:term:`tracing garbage collection` +:term:`translation buffer` +:term:`translation lookaside buffer` +:term:`transparent alias` +:term:`transparent type` +:term:`transport` +:term:`transport snap-out ` +:term:`treadmill` +:term:`tri-color invariant` +:term:`tri-colour invariant` +:term:`tricolor invariant` +:term:`tricolour invariant` +:term:`tri-color marking` +:term:`tri-colour marking` +:term:`tricolor marking` +:term:`tricolour marking` +:term:`two-space collector` +:term:`two space collector` +:term:`type-accurate garbage collection ` +:term:`type punning` + +:term:`unaligned` +:term:`unboxed` +:term:`unclamped state` +:term:`undead` +:term:`unmapped` +:term:`unreachable` +:term:`unsure reference ` +:term:`unwrapped` +:term:`use after free ` + +:term:`value object` +:term:`variety` +:term:`vector data type` +:term:`virtual address` +:term:`virtual address space` +:term:`virtual memory` +:term:`virtual memory arena` +:term:`visitor function ` +:term:`VM (1) ` +:term:`VM (2)` + +:term:`weak-key hash table` +:term:`weak-value hash table` +:term:`weak hash table` +:term:`weak reference (1)` +:term:`weak reference (2)` +:term:`weak root` +:term:`weak tri-color invariant` +:term:`weak tri-colour invariant` +:term:`weak tricolor invariant` +:term:`weak tricolour invariant` +:term:`weakly reachable` +:term:`weighted buddies` +:term:`weighted reference counting` +:term:`white` +:term:`word` +:term:`working set` +:term:`worst fit` +:term:`wrapped` +:term:`wrapper` +:term:`write barrier` +:term:`write fault` + +:term:`ZCT ` +:term:`zero count table` diff --git a/mps/manual/source/themes/mmref/static/mmref.css_t b/mps/manual/source/themes/mmref/static/mmref.css_t index c24e7eb67a1..54523e04e54 100644 --- a/mps/manual/source/themes/mmref/static/mmref.css_t +++ b/mps/manual/source/themes/mmref/static/mmref.css_t @@ -134,3 +134,8 @@ div#home h1 + p { margin-top: 0; padding-top: 15px; } + +div#memory-management-glossary em.xref.std-term { + white-space: no-wrap; + margin-right: 1em; +} From 1ea419e09ec3ccb46ff7c1ebd1fb4b755bb059c3 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sat, 24 May 2014 16:56:14 +0100 Subject: [PATCH 36/53] Correct spelling of "nowrap". Copied from Perforce Change: 186283 ServerID: perforce.ravenbrook.com --- mps/manual/source/themes/mmref/static/mmref.css_t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps/manual/source/themes/mmref/static/mmref.css_t b/mps/manual/source/themes/mmref/static/mmref.css_t index 54523e04e54..ceab4faddef 100644 --- a/mps/manual/source/themes/mmref/static/mmref.css_t +++ b/mps/manual/source/themes/mmref/static/mmref.css_t @@ -136,6 +136,6 @@ div#home h1 + p { } div#memory-management-glossary em.xref.std-term { - white-space: no-wrap; + white-space: nowrap; margin-right: 1em; } From deba5a00748ec37a99ee54b0c2fae6a39776c4f4 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 25 May 2014 16:49:52 +0100 Subject: [PATCH 37/53] Fix typo. Copied from Perforce Change: 186293 ServerID: perforce.ravenbrook.com --- mps/manual/source/bib.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mps/manual/source/bib.rst b/mps/manual/source/bib.rst index 00ef030f527..f32b6d33422 100644 --- a/mps/manual/source/bib.rst +++ b/mps/manual/source/bib.rst @@ -949,7 +949,7 @@ Bibliography .. admonition:: Abstract - We describe a new method for determining when an objct can be + We describe a new method for determining when an object can be garbage collected. The method does not require marking live objects. Instead, each object *X* is *dynamically* associated with a stack frame *M*, such that *X* is collectable when *M* pops. @@ -2155,10 +2155,10 @@ Bibliography application, extracted from the IBM Intelligent Miner, identifies groups of records that are mathematically similar based on a neural network model called self-organizing map. We examine and - compare in details two implementations of the application: (1) - temporal locality or working set sizes; (2) spatial locality and - memory block utilization; (3) communication characteristics and - scalability; and (4) TLB performance. + compare in details two implementations of the application: + (1) temporal locality or working set sizes; (2) spatial locality + and memory block utilization; (3) communication characteristics + and scalability; and (4) TLB performance. First, we find that the working set hierarchy of the application is governed by two parameters, namely the size of an input record From 1c59113e2f5ec4095d605d97aa8194b3c335c73b Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 25 May 2014 16:50:21 +0100 Subject: [PATCH 38/53] Fix typo, clarify cautions. Copied from Perforce Change: 186294 ServerID: perforce.ravenbrook.com --- mps/manual/source/topic/finalization.rst | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/mps/manual/source/topic/finalization.rst b/mps/manual/source/topic/finalization.rst index 745feefed16..5b65d9945dd 100644 --- a/mps/manual/source/topic/finalization.rst +++ b/mps/manual/source/topic/finalization.rst @@ -51,7 +51,7 @@ the block was allocated. to do the finalization. In such an implementation, the client program's finalization code may end up running concurrently with other code that accesses the underlying resource, and so access to - the resource need to be guarded with a lock, but then an unlucky + the resource needs to be guarded with a lock, but then an unlucky scheduling of finalization can result in deadlock. See :ref:`Boehm (2002) ` for a detailed discussion of this issue. @@ -170,8 +170,9 @@ Cautions #. The MPS does not finalize objects in the context of :c:func:`mps_arena_destroy` or :c:func:`mps_pool_destroy`. - :c:func:`mps_pool_destroy` should therefore not be invoked on pools - containing objects registered for finalization. + Moreover, if you have pools containing objects registered for + finalization, you must destroy these pools by following the “safe + tear-down” procedure described under :c:func:`mps_pool_destroy`. .. note:: @@ -189,11 +190,6 @@ Cautions .. note:: - You can safely destroy pools containing objects registered for - finalization if you follow the "safe tear-down" procedure - described under :c:func:`mps_pool_destroy`, but the objects do - not get finalized. - The only reliable way to ensure that all finalizable objects are finalized is to maintain a table of :term:`weak references (1)` to all such objects. The weak references don't From 181767e4dbe3bbc16b0b7cac25318b3a48fcc0dd Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 25 May 2014 19:26:48 +0100 Subject: [PATCH 39/53] Split land iteration into two functions, one which deletes ranges, the other which does not. Copied from Perforce Change: 186298 ServerID: perforce.ravenbrook.com --- mps/code/cbs.c | 14 +------------- mps/code/freelist.c | 29 +++++++++++++++++++++++++++-- mps/code/land.c | 41 +++++++++++++++++++++++++++++++++++------ mps/code/landtest.c | 4 +--- mps/code/mpm.h | 1 + mps/code/mpmst.h | 1 + mps/code/mpmtypes.h | 4 +++- mps/code/poolmv2.c | 6 ++---- mps/design/cbs.txt | 12 +++++++++--- mps/design/land.txt | 25 +++++++++++++++++++------ 10 files changed, 99 insertions(+), 38 deletions(-) diff --git a/mps/code/cbs.c b/mps/code/cbs.c index c7b4478ce4c..de6c25ff08c 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -705,16 +705,6 @@ static Res cbsZonedSplayNodeDescribe(Tree tree, mps_lib_FILE *stream) /* 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 - * visitor function, and an visitor function to invoke on every range - * in address order. If the visitor returns ``FALSE``, then the iteration - * is terminated. - * - * The visitor function may not modify the CBS during the iteration. - * This is because CBSIterate uses TreeTraverse, which does not permit - * modification, for speed and to avoid perturbing the splay tree balance. * * See . */ @@ -733,15 +723,13 @@ 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)); - cont = (*closure->visitor)(&delete, land, &range, closure->closureP, closure->closureS); - AVER(!delete); /* */ + cont = (*closure->visitor)(land, &range, closure->closureP, closure->closureS); if (!cont) return FALSE; METER_ACC(cbs->treeSearch, cbs->treeSize); diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 4040630b1c0..0abf766a9f3 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -440,6 +440,30 @@ static Res freelistDelete(Range rangeReturn, Land land, Range range) static Bool freelistIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +{ + Freelist fl; + FreelistBlock cur; + + AVERT(Land, land); + fl = freelistOfLand(land); + AVERT(Freelist, fl); + AVER(FUNCHECK(visitor)); + /* closureP and closureS are arbitrary */ + + for (cur = fl->list; cur != freelistEND; cur = FreelistBlockNext(cur)) { + RangeStruct range; + Bool cont; + RangeInit(&range, FreelistBlockBase(cur), FreelistBlockLimit(fl, cur)); + cont = (*visitor)(land, &range, closureP, closureS); + if (!cont) + return FALSE; + } + return TRUE; +} + + +static Bool freelistIterateAndDelete(Land land, LandDeleteVisitor visitor, + void *closureP, Size closureS) { Freelist fl; FreelistBlock prev, cur, next; @@ -448,6 +472,7 @@ static Bool freelistIterate(Land land, LandVisitor visitor, fl = freelistOfLand(land); AVERT(Freelist, fl); AVER(FUNCHECK(visitor)); + /* closureP and closureS are arbitrary */ prev = freelistEND; cur = fl->list; @@ -712,13 +737,12 @@ static Res freelistFindInZones(Range rangeReturn, Range oldRangeReturn, * closureP. */ -static Bool freelistDescribeVisitor(Bool *deleteReturn, Land land, Range range, +static Bool freelistDescribeVisitor(Land land, Range range, void *closureP, Size closureS) { Res res; mps_lib_FILE *stream = closureP; - if (deleteReturn == NULL) return FALSE; if (!TESTT(Land, land)) return FALSE; if (!RangeCheck(range)) return FALSE; if (stream == NULL) return FALSE; @@ -767,6 +791,7 @@ DEFINE_LAND_CLASS(FreelistLandClass, class) class->insert = freelistInsert; class->delete = freelistDelete; class->iterate = freelistIterate; + class->iterateAndDelete = freelistIterateAndDelete; class->findFirst = freelistFindFirst; class->findLast = freelistFindLast; class->findLargest = freelistFindLargest; diff --git a/mps/code/land.c b/mps/code/land.c index c0f5f2c1cbc..b7f4ebf8386 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -28,8 +28,9 @@ 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: The visitor functions passed to LandIterate and + * LandIterateAndDelete are 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. @@ -247,6 +248,26 @@ Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) } +/* LandIterateAndDelete -- iterate over isolated ranges of addresses + * in land, deleting some of them + * + * See + */ + +Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS) +{ + Bool res; + AVERT(Land, land); + AVER(FUNCHECK(visitor)); + landEnter(land); + + res = (*land->class->iterateAndDelete)(land, visitor, closureP, closureS); + + landLeave(land); + return res; +} + + /* LandFindFirst -- find first range of given size * * See @@ -416,7 +437,7 @@ void LandFlush(Land dest, Land src) AVERT(Land, dest); AVERT(Land, src); - (void)LandIterate(src, landFlushVisitor, dest, 0); + (void)LandIterateAndDelete(src, landFlushVisitor, dest, 0); } @@ -464,12 +485,11 @@ static Size landNoSize(Land land) /* LandSlowSize -- generic size method but slow */ -static Bool landSizeVisitor(Bool *deleteReturn, Land land, Range range, +static Bool landSizeVisitor(Land land, Range range, void *closureP, Size closureS) { Size *size; - AVER(deleteReturn != NULL); AVERT(Land, land); AVERT(Range, range); AVER(closureP != NULL); @@ -477,7 +497,6 @@ static Bool landSizeVisitor(Bool *deleteReturn, Land land, Range range, size = closureP; *size += RangeSize(range); - *deleteReturn = FALSE; return TRUE; } @@ -514,6 +533,15 @@ static Bool landNoIterate(Land land, LandVisitor visitor, void *closureP, Size c return FALSE; } +static Bool landNoIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS) +{ + AVERT(Land, land); + AVER(visitor != NULL); + UNUSED(closureP); + UNUSED(closureS); + return FALSE; +} + static Bool landNoFind(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { AVER(rangeReturn != NULL); @@ -556,6 +584,7 @@ DEFINE_CLASS(LandClass, class) class->insert = landNoInsert; class->delete = landNoDelete; class->iterate = landNoIterate; + class->iterateAndDelete = landNoIterateAndDelete; class->findFirst = landNoFind; class->findLast = landNoFind; class->findLargest = landNoFind; diff --git a/mps/code/landtest.c b/mps/code/landtest.c index 988b4be9391..195f9758656 100644 --- a/mps/code/landtest.c +++ b/mps/code/landtest.c @@ -75,13 +75,11 @@ static void describe(TestState state) { } -static Bool checkVisitor(Bool *deleteReturn, Land land, Range range, - void *closureP, Size closureS) +static Bool checkVisitor(Land land, Range range, void *closureP, Size closureS) { Addr base, limit; CheckTestClosure cl = closureP; - Insist(deleteReturn != NULL); testlib_unused(land); testlib_unused(closureS); Insist(cl != NULL); diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 0a87b8fc81d..28373eadc69 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -1015,6 +1015,7 @@ extern void LandFinish(Land land); extern Res LandInsert(Range rangeReturn, Land land, Range range); extern Res LandDelete(Range rangeReturn, Land land, Range range); extern Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS); +extern Bool LandIterateAndDelete(Land land, LandDeleteVisitor 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); diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 8ce4d14f01a..86087a31102 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -621,6 +621,7 @@ typedef struct LandClassStruct { LandInsertMethod insert; /* insert a range into the land */ LandDeleteMethod delete; /* delete a range from the land */ LandIterateMethod iterate; /* iterate over ranges in the land */ + LandIterateAndDeleteMethod iterateAndDelete; /* iterate and maybe delete */ LandFindMethod findFirst; /* find first range of given size */ LandFindMethod findLast; /* find last range of given size */ LandFindMethod findLargest; /* find largest range */ diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index d81255c974d..c79c049ca5f 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -271,8 +271,10 @@ 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); +typedef Bool (*LandVisitor)(Land land, Range range, void *closureP, Size closureS); +typedef Bool (*LandDeleteVisitor)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS); typedef Bool (*LandIterateMethod)(Land land, LandVisitor visitor, void *closureP, Size closureS); +typedef Bool (*LandIterateAndDeleteMethod)(Land land, LandDeleteVisitor 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); diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index e8b38489253..4f36884ab79 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -1209,12 +1209,11 @@ static Bool MVTReturnSegs(MVT mvt, Range range, Arena arena) * empty. */ -static Bool MVTRefillVisitor(Bool *deleteReturn, Land land, Range range, +static Bool MVTRefillVisitor(Land land, Range range, void *closureP, Size closureS) { MVT mvt; - AVER(deleteReturn != NULL); AVERT(Land, land); mvt = closureP; AVERT(MVT, mvt); @@ -1258,7 +1257,7 @@ typedef struct MVTContigencyClosureStruct Count hardSteps; } MVTContigencyClosureStruct, *MVTContigencyClosure; -static Bool MVTContingencyVisitor(Bool *deleteReturn, Land land, Range range, +static Bool MVTContingencyVisitor(Land land, Range range, void *closureP, Size closureS) { MVT mvt; @@ -1266,7 +1265,6 @@ static Bool MVTContingencyVisitor(Bool *deleteReturn, Land land, Range range, Addr base, limit; MVTContigencyClosure cl; - AVER(deleteReturn != NULL); AVERT(Land, land); AVERT(Range, range); AVER(closureP != NULL); diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt index c0661f2f792..496cc5ea246 100644 --- a/mps/design/cbs.txt +++ b/mps/design/cbs.txt @@ -124,9 +124,10 @@ _`.limit.zones`: ``CBSLandClass`` and ``CBSFastLandClass`` do not support the ``LandFindInZones()`` generic function (the subclass ``CBSZonedLandClass`` does support this operation). -_`.limit.iterate`: CBS does not support visitors setting -``deleteReturn`` to ``TRUE`` when iterating over ranges with -``LandIterate()``. +_`.limit.iterate`: CBS does not provide an implementation for the +``LandIterateAndDelete()`` generic function. This is because +``TreeTraverse()`` does not permit modification, for speed and to +avoid perturbing the splay tree balance. _`.limit.flush`: CBS cannot be used as the source in a call to ``LandFlush()``. (Because of `.limit.iterate`_.) @@ -221,6 +222,11 @@ converting them when they reach all free in the bit set. Note that this would make coalescence slightly less eager, by up to ``(word-width - 1)``. +_`.future.iterate.and.delete`: It would be possible to provide an +implementation for the ``LandIterateAndDelete()`` generic function by +calling ``TreeToVine()`` first, and then iterating over the vine +(where deletion is straightforward). + Risks ----- diff --git a/mps/design/land.txt b/mps/design/land.txt index b9f80b29d23..a4b79dec937 100644 --- a/mps/design/land.txt +++ b/mps/design/land.txt @@ -77,15 +77,22 @@ Types _`.type.land`: The type of a generic land instance. -``typedef Bool (*LandVisitor)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS);`` +``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. + +``typedef Bool (*LandDeleteVisitor)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS);`` + +_`.type.visitor`: Type ``LandDeleteVisitor`` is a callback function that may +be passed to ``LandIterateAndDelete()``. 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.) +keep the range.) Generic functions @@ -164,16 +171,22 @@ strategy. _`.function.delete.alias`: It is acceptable for ``rangeReturn`` and ``range`` to share storage. -``Bool LandIterate(Land land, LandIterateMethod iterate, void *closureP, Size closureS)`` +``Bool LandIterate(Land land, LandVisitor visitor, 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. If the iterator method -returns ``FALSE``, then the iteration is terminated and +visitor function to invoke on every range, and a pointer, ``Size`` +closure pair to pass on to the visitor function. If the visitor +function returns ``FALSE``, then iteration is terminated and ``LandIterate()`` returns ``FALSE``. If all iterator method calls return ``TRUE``, then ``LandIterate()`` returns ``TRUE`` +``Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS)`` + +_`.function.iterate.and.delete`: As ``LandIterate()``, but the visitor +function additionally returns a Boolean indicating whether the range +should be deleted from the land. + ``Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)`` _`.function.find.first`: Locate the first block (in address order) From fd57486106b5bdbbdba29f9c9327632d3aab4f63 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 25 May 2014 20:49:22 +0100 Subject: [PATCH 40/53] Fix problems identified by dl in review . Copied from Perforce Change: 186300 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 16 +++++------ mps/code/cbs.c | 64 +++++++++++++++++++++++--------------------- mps/code/failover.c | 39 ++++++++++++++++++--------- mps/code/freelist.c | 27 +++++++++++-------- mps/code/land.c | 53 +++++++++++++++++++----------------- mps/code/landtest.c | 4 ++- mps/code/mpm.h | 4 +-- mps/code/mpmtypes.h | 2 +- mps/code/poolmv2.c | 3 ++- mps/design/index.txt | 6 ++--- mps/design/land.txt | 11 ++++---- 11 files changed, 129 insertions(+), 100 deletions(-) diff --git a/mps/code/arena.c b/mps/code/arena.c index bd631e2ac49..be3137fd9bd 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -843,7 +843,7 @@ static Res arenaAllocFromLand(Tract *tractReturn, ZoneSet zones, Bool high, Arena arena; RangeStruct range, oldRange; Chunk chunk; - Bool b; + Bool found, b; Index baseIndex; Count pages; Res res; @@ -860,8 +860,8 @@ static Res arenaAllocFromLand(Tract *tractReturn, ZoneSet zones, Bool high, /* Step 1. Find a range of address space. */ - res = LandFindInZones(&range, &oldRange, ArenaFreeLand(arena), - size, zones, high); + res = LandFindInZones(&found, &range, &oldRange, ArenaFreeLand(arena), + size, zones, high); if (res == ResLIMIT) { /* found block, but couldn't store info */ RangeStruct pageRange; @@ -869,17 +869,17 @@ static Res arenaAllocFromLand(Tract *tractReturn, ZoneSet zones, Bool high, if (res != ResOK) /* disastrously short on memory */ return res; arenaExcludePage(arena, &pageRange); - res = LandFindInZones(&range, &oldRange, ArenaFreeLand(arena), - size, zones, high); + res = LandFindInZones(&found, &range, &oldRange, ArenaFreeLand(arena), + size, zones, high); AVER(res != ResLIMIT); } - if (res == ResFAIL) /* out of address space */ - return ResRESOURCE; - AVER(res == ResOK); /* unexpected error from ZoneCBS */ if (res != ResOK) /* defensive return */ return res; + + if (!found) /* out of address space */ + return ResRESOURCE; /* Step 2. Make memory available in the address space range. */ diff --git a/mps/code/cbs.c b/mps/code/cbs.c index de6c25ff08c..caad67aba71 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -989,17 +989,20 @@ static Bool cbsFindLargest(Range rangeReturn, Range oldRangeReturn, } -static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, - Land land, Size size, +static Res cbsFindInZones(Bool *foundReturn, Range rangeReturn, + Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) { CBS cbs; + CBSBlock block; Tree tree; cbsTestNodeInZonesClosureStruct closure; Res res; LandFindMethod landFind; SplayFindMethod splayFind; + RangeStruct rangeStruct, oldRangeStruct; + AVER(foundReturn != NULL); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVERT(Land, land); @@ -1013,16 +1016,14 @@ static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, splayFind = high ? SplayFindLast : SplayFindFirst; if (zoneSet == ZoneSetEMPTY) - return ResFAIL; + goto fail; if (zoneSet == ZoneSetUNIV) { FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; - if ((*landFind)(rangeReturn, oldRangeReturn, land, size, fd)) - return ResOK; - else - return ResFAIL; + *foundReturn = (*landFind)(rangeReturn, oldRangeReturn, land, size, fd); + return ResOK; } if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) - return ResFAIL; + goto fail; /* It would be nice if there were a neat way to eliminate all runs of zones in zoneSet too small for size.*/ @@ -1031,31 +1032,34 @@ static Res cbsFindInZones(Range rangeReturn, Range oldRangeReturn, 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; + if (!(*splayFind)(&tree, cbsSplay(cbs), + cbsTestNodeInZones, cbsTestTreeInZones, + &closure, sizeof(closure))) + goto fail; - 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)); + block = cbsBlockOfTree(tree); - 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; + 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)); - return res; + 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) + /* not enough memory to split block */ + return res; + RangeCopy(rangeReturn, &rangeStruct); + RangeCopy(oldRangeReturn, &oldRangeStruct); + *foundReturn = TRUE; + return ResOK; + +fail: + *foundReturn = FALSE; + return ResOK; } diff --git a/mps/code/failover.c b/mps/code/failover.c index 80ecb0a6210..8032cfe55d0 100644 --- a/mps/code/failover.c +++ b/mps/code/failover.c @@ -96,7 +96,7 @@ static Res failoverInsert(Range rangeReturn, Land land, Range range) /* Provide more opportunities for coalescence. See * . */ - LandFlush(fo->primary, fo->secondary); + (void)LandFlush(fo->primary, fo->secondary); res = LandInsert(rangeReturn, fo->primary, range); if (res != ResOK && res != ResFAIL) @@ -121,7 +121,7 @@ static Res failoverDelete(Range rangeReturn, Land land, Range range) /* Prefer efficient search in the primary. See * . */ - LandFlush(fo->primary, fo->secondary); + (void)LandFlush(fo->primary, fo->secondary); res = LandDelete(&oldRange, fo->primary, range); @@ -144,16 +144,22 @@ static Res failoverDelete(Range rangeReturn, Land land, Range range) /* 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) + if (res != ResOK) { + /* The range was successful deleted from the primary above. */ + AVER(res != ResFAIL); res = LandInsert(&dummyRange, fo->secondary, &left); - AVER(res == ResOK); + AVER(res == ResOK); + } } RangeInit(&right, RangeLimit(range), RangeLimit(&oldRange)); if (!RangeIsEmpty(&right)) { res = LandInsert(&dummyRange, fo->primary, &right); - if (res != ResOK && res != ResFAIL) + if (res != ResOK) { + /* The range was successful deleted from the primary above. */ + AVER(res != ResFAIL); res = LandInsert(&dummyRange, fo->secondary, &right); - AVER(res == ResOK); + AVER(res == ResOK); + } } } if (res == ResOK) { @@ -190,7 +196,7 @@ static Bool failoverFindFirst(Range rangeReturn, Range oldRangeReturn, Land land AVERT(FindDelete, findDelete); /* See . */ - LandFlush(fo->primary, fo->secondary); + (void)LandFlush(fo->primary, fo->secondary); return LandFindFirst(rangeReturn, oldRangeReturn, fo->primary, size, findDelete) || LandFindFirst(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete); @@ -209,7 +215,7 @@ static Bool failoverFindLast(Range rangeReturn, Range oldRangeReturn, Land land, AVERT(FindDelete, findDelete); /* See . */ - LandFlush(fo->primary, fo->secondary); + (void)LandFlush(fo->primary, fo->secondary); return LandFindLast(rangeReturn, oldRangeReturn, fo->primary, size, findDelete) || LandFindLast(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete); @@ -228,17 +234,20 @@ static Bool failoverFindLargest(Range rangeReturn, Range oldRangeReturn, Land la AVERT(FindDelete, findDelete); /* See . */ - LandFlush(fo->primary, fo->secondary); + (void)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) +static Bool failoverFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) { Failover fo; + Bool found = FALSE; + Res res; + AVER(foundReturn != NULL); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVERT(Land, land); @@ -248,10 +257,14 @@ static Bool failoverFindInZones(Range rangeReturn, Range oldRangeReturn, Land la AVERT(Bool, high); /* See . */ - LandFlush(fo->primary, fo->secondary); + (void)LandFlush(fo->primary, fo->secondary); - return LandFindInZones(rangeReturn, oldRangeReturn, fo->primary, size, zoneSet, high) - || LandFindInZones(rangeReturn, oldRangeReturn, fo->secondary, size, zoneSet, high); + res = LandFindInZones(&found, rangeReturn, oldRangeReturn, fo->primary, size, zoneSet, high); + if (res != ResOK || !found) + res = LandFindInZones(&found, rangeReturn, oldRangeReturn, fo->secondary, size, zoneSet, high); + + *foundReturn = found; + return res; } diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 0abf766a9f3..a1f4803906e 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -32,7 +32,7 @@ typedef union 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 + * ambiguous. However, freelistEND is 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). @@ -666,8 +666,8 @@ static Bool freelistFindLargest(Range rangeReturn, Range oldRangeReturn, } -static Res freelistFindInZones(Range rangeReturn, Range oldRangeReturn, - Land land, Size size, +static Res freelistFindInZones(Bool *foundReturn, Range rangeReturn, + Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) { Freelist fl; @@ -691,16 +691,14 @@ static Res freelistFindInZones(Range rangeReturn, Range oldRangeReturn, search = high ? RangeInZoneSetLast : RangeInZoneSetFirst; if (zoneSet == ZoneSetEMPTY) - return ResFAIL; + goto fail; if (zoneSet == ZoneSetUNIV) { FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; - if ((*landFind)(rangeReturn, oldRangeReturn, land, size, fd)) - return ResOK; - else - return ResFAIL; + *foundReturn = (*landFind)(rangeReturn, oldRangeReturn, land, size, fd); + return ResOK; } if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) - return ResFAIL; + goto fail; prev = freelistEND; cur = fl->list; @@ -723,10 +721,15 @@ static Res freelistFindInZones(Range rangeReturn, Range oldRangeReturn, } if (!found) - return ResFAIL; + goto fail; freelistDeleteFromBlock(oldRangeReturn, fl, &foundRange, foundPrev, foundCur); RangeCopy(rangeReturn, &foundRange); + *foundReturn = TRUE; + return ResOK; + +fail: + *foundReturn = FALSE; return ResOK; } @@ -762,6 +765,7 @@ static Res freelistDescribe(Land land, mps_lib_FILE *stream) { Freelist fl; Res res; + Bool b; if (!TESTT(Land, land)) return ResFAIL; fl = freelistOfLand(land); @@ -773,7 +777,8 @@ static Res freelistDescribe(Land land, mps_lib_FILE *stream) " listSize = $U\n", (WriteFU)fl->listSize, NULL); - (void)LandIterate(land, freelistDescribeVisitor, stream, 0); + b = LandIterate(land, freelistDescribeVisitor, stream, 0); + if (!b) return ResFAIL; res = WriteF(stream, "}\n", NULL); return res; diff --git a/mps/code/land.c b/mps/code/land.c index b7f4ebf8386..78dc7f0b324 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -236,15 +236,15 @@ Res LandDelete(Range rangeReturn, Land land, Range range) Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) { - Bool res; + Bool b; AVERT(Land, land); AVER(FUNCHECK(visitor)); landEnter(land); - res = (*land->class->iterate)(land, visitor, closureP, closureS); + b = (*land->class->iterate)(land, visitor, closureP, closureS); landLeave(land); - return res; + return b; } @@ -256,15 +256,15 @@ Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS) { - Bool res; + Bool b; AVERT(Land, land); AVER(FUNCHECK(visitor)); landEnter(land); - res = (*land->class->iterateAndDelete)(land, visitor, closureP, closureS); + b = (*land->class->iterateAndDelete)(land, visitor, closureP, closureS); landLeave(land); - return res; + return b; } @@ -275,7 +275,7 @@ Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - Bool res; + Bool b; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); @@ -284,11 +284,11 @@ Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size AVER(FindDeleteCheck(findDelete)); landEnter(land); - res = (*land->class->findFirst)(rangeReturn, oldRangeReturn, land, size, - findDelete); + b = (*land->class->findFirst)(rangeReturn, oldRangeReturn, land, size, + findDelete); landLeave(land); - return res; + return b; } @@ -299,7 +299,7 @@ Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - Bool res; + Bool b; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); @@ -308,11 +308,11 @@ Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, AVER(FindDeleteCheck(findDelete)); landEnter(land); - res = (*land->class->findLast)(rangeReturn, oldRangeReturn, land, size, - findDelete); + b = (*land->class->findLast)(rangeReturn, oldRangeReturn, land, size, + findDelete); landLeave(land); - return res; + return b; } @@ -323,7 +323,7 @@ Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - Bool res; + Bool b; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); @@ -332,11 +332,11 @@ Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size si AVER(FindDeleteCheck(findDelete)); landEnter(land); - res = (*land->class->findLargest)(rangeReturn, oldRangeReturn, land, size, - findDelete); + b = (*land->class->findLargest)(rangeReturn, oldRangeReturn, land, size, + findDelete); landLeave(land); - return res; + return b; } @@ -345,10 +345,11 @@ Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size si * See */ -Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) +Res LandFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) { Res res; + AVER(foundReturn != NULL); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVERT(Land, land); @@ -357,8 +358,8 @@ Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size siz AVERT(Bool, high); landEnter(land); - res = (*land->class->findInZones)(rangeReturn, oldRangeReturn, land, size, - zoneSet, high); + res = (*land->class->findInZones)(foundReturn, rangeReturn, oldRangeReturn, + land, size, zoneSet, high); landLeave(land); return res; @@ -432,12 +433,12 @@ static Bool landFlushVisitor(Bool *deleteReturn, Land land, Range range, * See */ -void LandFlush(Land dest, Land src) +Bool LandFlush(Land dest, Land src) { AVERT(Land, dest); AVERT(Land, src); - (void)LandIterateAndDelete(src, landFlushVisitor, dest, 0); + return LandIterateAndDelete(src, landFlushVisitor, dest, 0); } @@ -504,7 +505,8 @@ static Bool landSizeVisitor(Land land, Range range, Size LandSlowSize(Land land) { Size size = 0; - (void)LandIterate(land, landSizeVisitor, &size, 0); + Bool b = LandIterate(land, landSizeVisitor, &size, 0); + AVER(b); return size; } @@ -552,8 +554,9 @@ static Bool landNoFind(Range rangeReturn, Range oldRangeReturn, Land land, Size return ResUNIMPL; } -static Res landNoFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) +static Res landNoFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) { + AVER(foundReturn != NULL); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVERT(Land, land); diff --git a/mps/code/landtest.c b/mps/code/landtest.c index 195f9758656..a4eb9b369e1 100644 --- a/mps/code/landtest.c +++ b/mps/code/landtest.c @@ -108,12 +108,14 @@ static Bool checkVisitor(Land land, Range range, void *closureP, Size closureS) static void check(TestState state) { CheckTestClosureStruct closure; + Bool b; closure.state = state; closure.limit = addrOfIndex(state, ArraySize); closure.oldLimit = state->block; - (void)LandIterate(state->land, checkVisitor, (void *)&closure, 0); + b = LandIterate(state->land, checkVisitor, (void *)&closure, 0); + Insist(b); if (closure.oldLimit == state->block) Insist(BTIsSetRange(state->allocTable, 0, diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 28373eadc69..43110467be2 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -1019,9 +1019,9 @@ extern Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *clo 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 LandFindInZones(Bool *foundReturn, 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 LandFlush(Land dest, Land src); extern Size LandSlowSize(Land land); extern Bool LandClassCheck(LandClass class); diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index c79c049ca5f..c9c5b029c4a 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -276,7 +276,7 @@ typedef Bool (*LandDeleteVisitor)(Bool *deleteReturn, Land land, Range range, vo typedef Bool (*LandIterateMethod)(Land land, LandVisitor visitor, void *closureP, Size closureS); typedef Bool (*LandIterateAndDeleteMethod)(Land land, LandDeleteVisitor 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 (*LandFindInZonesMethod)(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high); typedef Res (*LandDescribeMethod)(Land land, mps_lib_FILE *stream); diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index 4f36884ab79..b0636a3d467 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -1239,7 +1239,8 @@ static void MVTRefillABQIfEmpty(MVT mvt, Size size) if (mvt->abqOverflow && ABQIsEmpty(MVTABQ(mvt))) { mvt->abqOverflow = FALSE; METER_ACC(mvt->refills, size); - (void)LandIterate(MVTFailover(mvt), &MVTRefillVisitor, mvt, 0); + /* The iteration stops if the ABQ overflows, so may finish or not. */ + (void)LandIterate(MVTFailover(mvt), MVTRefillVisitor, mvt, 0); } } diff --git a/mps/design/index.txt b/mps/design/index.txt index d5fe0a7658f..39594723074 100644 --- a/mps/design/index.txt +++ b/mps/design/index.txt @@ -45,17 +45,17 @@ arena_ The design of the MPS arena arenavm_ Virtual memory arena bt_ Bit tables buffer_ Allocation buffers and allocation points -cbs_ Coalescing Block Structure allocator +cbs_ Coalescing Block Structure land implementation 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 +failover_ Fail-over land implementation finalize_ Finalization fix_ The Design of the Generic Fix Function -freelist_ Free list allocator +freelist_ Free list land implementation guide.hex.trans_ Guide to transliterating the alphabet into hexadecimal guide.impl.c.format_ Coding standard: conventions for the general format of C source code in the MPS interface-c_ The design of the Memory Pool System interface to C diff --git a/mps/design/land.txt b/mps/design/land.txt index a4b79dec937..b4b8bd212a1 100644 --- a/mps/design/land.txt +++ b/mps/design/land.txt @@ -232,13 +232,14 @@ Like ``LandFindFirst()``, optionally delete the range (specifying 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)`` +``Res LandFindInZones(Bool *foundReturn, 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 ``ResOK``. (The first such block, -if ``high`` is ``FALSE``, or the last, if ``high`` is ``TRUE``.) If -there is no such block, return ``ResFAIL``. +``rangeReturn`` argument, set ``*foundReturn`` to ``TRUE``, and return +``ResOK``. (The first such block, if ``high`` is ``FALSE``, or the +last, if ``high`` is ``TRUE``.) If there is no such block, set +``*foundReturn`` to ``TRUE``, and return ``ResOK``. Delete the range as for ``LandFindFirst()`` and ``LastFindLast()`` (with the effect of ``FindDeleteLOW`` if ``high`` is ``FALSE`` and the @@ -248,7 +249,7 @@ 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. +case the result code indicates the cause of the failure. ``Res LandDescribe(Land land, mps_lib_FILE *stream)`` From da382f485939102a420c5b8d1319530c345afc19 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Mon, 26 May 2014 12:39:38 +0100 Subject: [PATCH 41/53] New chapter of the guide discusses the "stretchy vector" problem. Copied from Perforce Change: 186305 ServerID: perforce.ravenbrook.com --- mps/manual/source/glossary/index.rst | 1 + mps/manual/source/glossary/s.rst | 24 ++++- mps/manual/source/guide/index.rst | 1 + mps/manual/source/guide/vector.rst | 152 +++++++++++++++++++++++++++ 4 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 mps/manual/source/guide/vector.rst diff --git a/mps/manual/source/glossary/index.rst b/mps/manual/source/glossary/index.rst index f3f3ed154c0..9a426a5e742 100644 --- a/mps/manual/source/glossary/index.rst +++ b/mps/manual/source/glossary/index.rst @@ -515,6 +515,7 @@ Memory Management Glossary :term:`storage management ` :term:`store (1)` :term:`store (2) ` +:term:`stretchy vector` :term:`strict segregated fit` :term:`strong reference` :term:`strong root` diff --git a/mps/manual/source/glossary/s.rst b/mps/manual/source/glossary/s.rst index 41389cb2103..f244ad79b62 100644 --- a/mps/manual/source/glossary/s.rst +++ b/mps/manual/source/glossary/s.rst @@ -785,6 +785,26 @@ Memory Management Glossary: S .. see:: :term:`memory (1)`. + stretchy vector + + A data structure consisting of a vector that may grow or + shrink to accommodate adding or removing elements. Named after + the ```` abstract class in Dylan). + + .. relevance:: + + In the presence of an :term:`asynchronous garbage + collector`, the vector and its size may need to be updated + atomically. + + .. link:: + + `Dylan Reference Manual: Collections `_. + + .. mps:specific:: + + See :ref:`guide-stretchy-vector`. + strict segregated fit A :term:`segregated fit` :term:`allocation mechanism` which @@ -806,7 +826,7 @@ Memory Management Glossary: S collection>`, a strong reference is a :term:`reference` that keeps the :term:`object` it refers to :term:`alive `. - A strong reference is the usual sort of reference; The term is + A strong reference is the usual sort of reference: the term is usually used to draw a contrast with :term:`weak reference (1)`. @@ -819,7 +839,7 @@ Memory Management Glossary: S A strong root is a :term:`root` such that all :term:`references` in it are :term:`strong references`. - A strong root is the usual sort of root. The term is usually + A strong root is the usual sort of root: the term is usually used to draw a contrast with :term:`weak root`. .. opposite:: :term:`weak root`. diff --git a/mps/manual/source/guide/index.rst b/mps/manual/source/guide/index.rst index abd9ef242ca..a7218dd2ba5 100644 --- a/mps/manual/source/guide/index.rst +++ b/mps/manual/source/guide/index.rst @@ -9,6 +9,7 @@ Guide overview build lang + vector debug perf advanced diff --git a/mps/manual/source/guide/vector.rst b/mps/manual/source/guide/vector.rst new file mode 100644 index 00000000000..8b9452ac1f6 --- /dev/null +++ b/mps/manual/source/guide/vector.rst @@ -0,0 +1,152 @@ +.. index:: + single: stretchy vectors + single: atomic updates + +.. _guide-stretchy-vector: + +The stretchy vector problem +============================ + +The :ref:`previous chapter ` pointed out that: + + Because the MPS is :term:`asynchronous `, it might be scanning, moving, or collecting, at any + point in time. + +The consequences of this can take a while to sink in, so this chapter +discusses a particular instance that catches people out: the *stretchy +vector* problem (named after the |stretchy-vector|_ abstract class in +Dylan). + +.. |stretchy-vector| replace:: ```` +.. _stretchy-vector: http://opendylan.org/books/drm/Collection_Classes#stretchy-vector + +A *stretchy vector* is a vector that can change length dynamically. +Such a vector is often implemented using two objects: an array, and a +header object that stores the length and a pointer to an array. +Stretching (or shrinking) such a vector involves five steps: + +1. allocate a new array; +2. copy elements from the old array to the new array; +3. clear unused elements in the new array (if stretching); +4. update the pointer to the array in the header; +5. update the length in the header. + +For example: + +.. code-block:: c + + typedef struct vector_s { + type_t type; /* TYPE_VECTOR */ + size_t length; /* number of elements */ + obj_t *array; /* array of elements */ + } vector_s, *vector_t; + + void resize_vector(vector_t vector, size_t new_length) { + obj_t *new_array = realloc(vector->array, new_length * sizeof(obj_t)); + if (new_array == NULL) + error("out of memory in resize_vector"); + if (vector->length < new_length) { + memset(&vector->array[vector->length], 0, + (new_length - vector->length) * sizeof(obj_t)); + } + vector->array = new_array; + vector->length = new_length; + } + +When adapting this code to the MPS, the following problems must be +solved: + +1. During step 2, the new array must be :term:`reachable` from the + roots, and :term:`scannable `. (If it's not reachable, then + it may be collected; if it's not scannable, then references it + contains will not be updated when they are moved by the collector.) + + This can solved by storing the new array in a :term:`root` until + the header has been updated. If the thread's stack has been + registered as a root by calling :c:func:`mps_root_create_reg` then + any local variable will do. + +2. References in the new array must not be scanned until they have been + copied or cleared. (Otherwise they will be invalid.) + + This can be solved by clearing the new array before calling + :c:func:`mps_commit`. + +3. The old array must be scanned at the old length (otherwise the scan + may run off the end of the old array when the vector grows), and + the new array must be scanned at the new length (otherwise the scan + may run off the end of the old array when the vector shrinks). + +4. The array object must be scannable without referring to the header + object. (Because the header object may have been protected by the + MPS: see :ref:`topic-format-cautions`.) + +Problems 3 and 4 can be solved by storing the length in the array. The +revised data structures and resizing code might look like this: + +.. code-block:: c + + typedef struct vector_s { + type_t type; /* TYPE_VECTOR */ + obj_t array; /* TYPE_ARRAY object */ + } vector_s, *vector_t; + + typedef struct array_s { + type_t type; /* TYPE_ARRAY */ + size_t length; /* number of elements */ + obj_t array[0]; /* array of elements */ + } array_s, *array_t; + + void resize_vector(vector_t vector, size_t new_length) { + size_t size = ALIGN_OBJ(offsetof(array_s, array) + new_length * sizeof(obj_t)); + mps_addr_t addr; + array_t array; + + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in resize_vector"); + array = addr; + array->type = TYPE_ARRAY; + array->length = new_length; + memset(array->array, 0, new_length * sizeof(obj_t)); + /* Now the new array is scannable, and it is reachable via the + * local variable 'array', so it is safe to commit it. */ + } while(!mps_commit(ap, addr, size)); + + /* Copy elements after committing, so that the collector will + * update them if they move. */ + memcpy(array->array, vector->array->array, + min(vector->array->length, new_length) * sizeof(obj_t)); + vector->array = array; + } + +Similar difficulties can arise even when adapting code written for +other garbage collectors. For example, here's the function +|setarrayvector|_ from Lua_: + +.. |setarrayvector| replace:: ``setarrayvector()`` +.. _setarrayvector: http://www.lua.org/source/5.2/ltable.c.html#setarrayvector +.. _Lua: http://www.lua.org + +.. code-block:: c + + static void setarrayvector (lua_State *L, Table *t, int size) { + int i; + luaM_reallocvector(L, t->array, t->sizearray, size, TValue); + for (i=t->sizearray; iarray[i]); + t->sizearray = size; + } + +Lua's garbage collector is :term:`synchronous `, so it can be assumed that there cannot be a garbage +collection between the assignment to ``t->array`` (resulting from the +expansion of the |luaM_reallocvector|_ macro) and the assignment to +``t->sizearray``, and so the collector will always consistently see +either the old array or the new array, with the correct size. This +assumption will no longer be correct if this code is adapted to the +MPS. + +.. |luaM_reallocvector| replace:: ``luaM_reallocvector()`` +.. _luaM_reallocvector: http://www.lua.org/source/5.2/lmem.h.html#luaM_reallocvector From d350df4f52b676de7cd41068053dce82b24b3a8d Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Mon, 26 May 2014 13:46:10 +0100 Subject: [PATCH 42/53] The java reference pages have moved to oracle.com. Add more cross-references to the MPS documentation. Copied from Perforce Change: 186307 ServerID: perforce.ravenbrook.com --- mps/manual/source/glossary/a.rst | 6 +++++- mps/manual/source/glossary/c.rst | 8 +++++++- mps/manual/source/glossary/f.rst | 5 +++++ mps/manual/source/glossary/g.rst | 15 ++++++++++----- mps/manual/source/glossary/i.rst | 6 ++++++ mps/manual/source/glossary/l.rst | 7 ++++++- mps/manual/source/glossary/m.rst | 5 +++++ mps/manual/source/glossary/n.rst | 6 ++++++ mps/manual/source/glossary/p.rst | 12 ++++++++++-- mps/manual/source/glossary/r.rst | 9 +++++++-- mps/manual/source/glossary/s.rst | 12 ++++++------ mps/manual/source/glossary/w.rst | 4 ++-- 12 files changed, 75 insertions(+), 20 deletions(-) diff --git a/mps/manual/source/glossary/a.rst b/mps/manual/source/glossary/a.rst index e7c6afe4d4f..e5c2693a71c 100644 --- a/mps/manual/source/glossary/a.rst +++ b/mps/manual/source/glossary/a.rst @@ -202,7 +202,11 @@ Memory Management Glossary: A .. mps:specific:: An alignment is represented by the unsigned integral type - :c:type:`mps_align_t`. It must be a positive power of 2. + :c:type:`mps_align_t`. It must be a power of 2. The + alignment of objects allocated in a :term:`pool` may be + specified by passing the :c:macro:`MPS_KEY_ALIGN` + :term:`keyword argument` when calling + :c:func:`mps_pool_create`. alive diff --git a/mps/manual/source/glossary/c.rst b/mps/manual/source/glossary/c.rst index b2d8f2b1400..2449ce9f82a 100644 --- a/mps/manual/source/glossary/c.rst +++ b/mps/manual/source/glossary/c.rst @@ -131,7 +131,7 @@ Memory Management Glossary: C A cactus stack is a :term:`stack` with branches. When diagrammed, its shape resembles that of a `saguaro cactus - `_. + `_. In languages that support :term:`continuations`, :term:`activation records` can have :term:`indefinite extent`. @@ -615,6 +615,12 @@ Memory Management Glossary: C .. seealso:: :term:`broken heart`, :term:`forwarding pointer`, :term:`two-space collector`. + .. mps:specific:: + + The :ref:`pool-amc` pool class implements copying garbage + collection (more precisely, :term:`mostly-copying garbage + collection`). + core A historical synonym for :term:`main memory`, deriving from diff --git a/mps/manual/source/glossary/f.rst b/mps/manual/source/glossary/f.rst index 540cc048d46..eedbd00a9da 100644 --- a/mps/manual/source/glossary/f.rst +++ b/mps/manual/source/glossary/f.rst @@ -26,6 +26,11 @@ Memory Management Glossary: F .. similar:: :term:`in-band header`. + .. mps:specific:: + + :term:`Debugging pools` use fenceposts. See + :ref:`topic-debugging`. + fencepost error fence post error diff --git a/mps/manual/source/glossary/g.rst b/mps/manual/source/glossary/g.rst index 25feb894906..4dc74e66221 100644 --- a/mps/manual/source/glossary/g.rst +++ b/mps/manual/source/glossary/g.rst @@ -89,7 +89,7 @@ Memory Management Glossary: G This term is often used when referring to particular implementations or algorithms, for example, "the - Boehm-Demers-Weiser *collector*". + Boehm--Demers--Weiser *collector*". GB @@ -132,16 +132,16 @@ Memory Management Glossary: G .. mps:specific:: The :term:`client program` specifies the generational - structure of a :term:`pool` using a :term:`generation - chain`. See :ref:`topic-collection`. + structure of a :term:`pool` (or group of pools) using a + :term:`generation chain`. See :ref:`topic-collection`. generation chain .. mps:specific:: A data structure that specifies the structure of the - :term:`generations` in a :term:`pool`. See - :ref:`topic-collection`. + :term:`generations` in a :term:`pool` (or group of pools). + See :ref:`topic-collection`. generation scavenging @@ -174,6 +174,11 @@ Memory Management Glossary: G .. seealso:: :term:`remembered set`. + .. mps:specific:: + + The :ref:`pool-amc` and :ref:`pool-amcz` pool classes + support generational garbage collection. + generational hypothesis .. aka:: *infant mortality*. diff --git a/mps/manual/source/glossary/i.rst b/mps/manual/source/glossary/i.rst index 9ce2a3f9cea..b174241959c 100644 --- a/mps/manual/source/glossary/i.rst +++ b/mps/manual/source/glossary/i.rst @@ -133,6 +133,12 @@ Memory Management Glossary: I .. bibref:: :ref:`Appel et al. (1988) `, :ref:`Boehm et al. (1991) `. + .. mps:specific:: + + The MPS uses incremental collection, except for + collections started by calling + :c:func:`mps_arena_collect`. + incremental update Incremental-update algorithms for :term:`tracing `, diff --git a/mps/manual/source/glossary/l.rst b/mps/manual/source/glossary/l.rst index 5a143e55f69..3e4f0db4270 100644 --- a/mps/manual/source/glossary/l.rst +++ b/mps/manual/source/glossary/l.rst @@ -81,9 +81,14 @@ Memory Management Glossary: L If leaf objects can be identified, a :term:`garbage collector` can make certain optimizations: leaf objects do not have to be :term:`scanned ` for references nor - are :term:`barrier (1)` needed to detect + are :term:`barriers (1)` needed to detect and maintain references in the object. + .. mps:specific:: + + The :ref:`pool-amcz` and :ref:`pool-lo` pool classes are + designed for the storage of leaf objects. + leak .. see:: :term:`memory leak`. diff --git a/mps/manual/source/glossary/m.rst b/mps/manual/source/glossary/m.rst index 4562d30d7b6..db4d8b88a59 100644 --- a/mps/manual/source/glossary/m.rst +++ b/mps/manual/source/glossary/m.rst @@ -535,6 +535,11 @@ Memory Management Glossary: M .. bibref:: :ref:`Bartlett (1989) `, :ref:`Yip (1991) `. + .. mps:specific:: + + The :ref:`pool-amc` pool class implements mostly-copying + garbage collection. + mostly-exact garbage collection .. see:: :term:`semi-conservative garbage collection`. diff --git a/mps/manual/source/glossary/n.rst b/mps/manual/source/glossary/n.rst index 24ddcf66cea..792ad32b251 100644 --- a/mps/manual/source/glossary/n.rst +++ b/mps/manual/source/glossary/n.rst @@ -117,4 +117,10 @@ Memory Management Glossary: N The size of the nursery space must be chosen carefully. Often it is related to the size of :term:`physical memory (1)`. + .. mps:specific:: + By default, a garbage-collected :term:`pool` allocates + into the first :term:`generation` in its :term:`generation + chain`, but this can be altered by setting the + :c:macro:`MPS_KEY_GEN` :term:`keyword argument` when + calling :c:func:`mps_pool_create`. diff --git a/mps/manual/source/glossary/p.rst b/mps/manual/source/glossary/p.rst index fc08d9c2abb..e0259978d8b 100644 --- a/mps/manual/source/glossary/p.rst +++ b/mps/manual/source/glossary/p.rst @@ -222,7 +222,7 @@ Memory Management Glossary: P .. link:: - `Class java.lang.ref.PhantomReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.PhantomReference `_, `Reference Objects and Garbage Collection `_. phantom reference @@ -239,7 +239,7 @@ Memory Management Glossary: P .. link:: - `Class java.lang.ref.PhantomReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.PhantomReference `_, `Reference Objects and Garbage Collection `_. physical address @@ -321,6 +321,14 @@ Memory Management Glossary: P .. seealso:: :term:`generational garbage collection`. + .. mps:specific:: + + A :term:`pool` can be configured to allocate into a + specific :term:`generation` in its :term:`generation + chain` by setting the :c:macro:`MPS_KEY_GEN` + :term:`keyword argument` when calling + :c:func:`mps_pool_create`. + pig in the snake .. see:: :term:`pig in the python`. diff --git a/mps/manual/source/glossary/r.rst b/mps/manual/source/glossary/r.rst index a521c1d7bd4..2c8a9ef70ad 100644 --- a/mps/manual/source/glossary/r.rst +++ b/mps/manual/source/glossary/r.rst @@ -93,7 +93,7 @@ Memory Management Glossary: R .. link:: - `Package java.lang.ref `_, `Reference Objects and Garbage Collection `_. + `Package java.lang.ref `_, `Reference Objects and Garbage Collection `_. read barrier @@ -317,7 +317,7 @@ Memory Management Glossary: R .. link:: - `Package java.lang.ref `_, `Reference Objects and Garbage Collection `_. + `Package java.lang.ref `_, `Reference Objects and Garbage Collection `_. .. bibref:: :ref:`Dybvig et al. (1993) `. @@ -471,6 +471,11 @@ Memory Management Glossary: R .. seealso:: :term:`mapping`, :term:`mmap`. + .. mps:specific:: + + The function :c:func:`mps_arena_reserved` returns the + total address space reserved by an arena. + resident In a :term:`cache (2)` system, that part of the cached storage diff --git a/mps/manual/source/glossary/s.rst b/mps/manual/source/glossary/s.rst index f244ad79b62..dc9355f2a0f 100644 --- a/mps/manual/source/glossary/s.rst +++ b/mps/manual/source/glossary/s.rst @@ -333,7 +333,7 @@ Memory Management Glossary: S By overloading certain operators it is possible for the class to present the illusion of being a pointer, so that - ``operator\*``, ``operator-\>``, etc. can be used as normal. + ``operator*``, ``operator->``, etc. can be used as normal. Reference counting allows the objects that are referred to using the smart pointer class to have their :term:`memory (1)` automatically :term:`reclaimed` when they are no longer @@ -429,7 +429,7 @@ Memory Management Glossary: S .. link:: - `Class java.lang.ref.SoftReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.SoftReference `_, `Reference Objects and Garbage Collection `_. softly reachable @@ -453,7 +453,7 @@ Memory Management Glossary: S .. link:: - `Class java.lang.ref.SoftReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.SoftReference `_, `Reference Objects and Garbage Collection `_. space leak @@ -787,9 +787,9 @@ Memory Management Glossary: S stretchy vector - A data structure consisting of a vector that may grow or - shrink to accommodate adding or removing elements. Named after - the ```` abstract class in Dylan). + A :term:`vector ` that may grow or shrink to + accommodate adding or removing elements. Named after the + ```` abstract class in Dylan. .. relevance:: diff --git a/mps/manual/source/glossary/w.rst b/mps/manual/source/glossary/w.rst index 7d061294ce6..46d8d9edd06 100644 --- a/mps/manual/source/glossary/w.rst +++ b/mps/manual/source/glossary/w.rst @@ -70,7 +70,7 @@ Memory Management Glossary: W .. link:: - `Class java.lang.ref.WeakReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.WeakReference `_, `Reference Objects and Garbage Collection `_. weak root @@ -134,7 +134,7 @@ Memory Management Glossary: W .. link:: - `Class java.lang.ref.WeakReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.WeakReference `_, `Reference Objects and Garbage Collection `_. weighted buddies From f5719876cccb09ddeb87704db73beb5a7e405d2d Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Mon, 26 May 2014 14:10:37 +0100 Subject: [PATCH 43/53] Reference mps_pool_create_k, not the deprecated function mps_pool_create. Copied from Perforce Change: 186309 ServerID: perforce.ravenbrook.com --- mps/manual/source/glossary/a.rst | 2 +- mps/manual/source/glossary/n.rst | 2 +- mps/manual/source/glossary/p.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mps/manual/source/glossary/a.rst b/mps/manual/source/glossary/a.rst index e5c2693a71c..746b5c0cf49 100644 --- a/mps/manual/source/glossary/a.rst +++ b/mps/manual/source/glossary/a.rst @@ -206,7 +206,7 @@ Memory Management Glossary: A alignment of objects allocated in a :term:`pool` may be specified by passing the :c:macro:`MPS_KEY_ALIGN` :term:`keyword argument` when calling - :c:func:`mps_pool_create`. + :c:func:`mps_pool_create_k`. alive diff --git a/mps/manual/source/glossary/n.rst b/mps/manual/source/glossary/n.rst index 792ad32b251..67fbd65a371 100644 --- a/mps/manual/source/glossary/n.rst +++ b/mps/manual/source/glossary/n.rst @@ -123,4 +123,4 @@ Memory Management Glossary: N into the first :term:`generation` in its :term:`generation chain`, but this can be altered by setting the :c:macro:`MPS_KEY_GEN` :term:`keyword argument` when - calling :c:func:`mps_pool_create`. + calling :c:func:`mps_pool_create_k`. diff --git a/mps/manual/source/glossary/p.rst b/mps/manual/source/glossary/p.rst index e0259978d8b..8909d6fb964 100644 --- a/mps/manual/source/glossary/p.rst +++ b/mps/manual/source/glossary/p.rst @@ -327,7 +327,7 @@ Memory Management Glossary: P specific :term:`generation` in its :term:`generation chain` by setting the :c:macro:`MPS_KEY_GEN` :term:`keyword argument` when calling - :c:func:`mps_pool_create`. + :c:func:`mps_pool_create_k`. pig in the snake From ad7d16fe422eacc844554500946964337920c615 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 27 May 2014 15:53:55 +0100 Subject: [PATCH 44/53] Fix problems in design/splay: "Node" structure now called "Tree", so fix cross-reference. .future.reverse was removed, so remove cross-reference to it. Copied from Perforce Change: 186315 ServerID: perforce.ravenbrook.com --- mps/design/splay.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mps/design/splay.txt b/mps/design/splay.txt index 4290b703ac1..fefb40fe8a9 100644 --- a/mps/design/splay.txt +++ b/mps/design/splay.txt @@ -51,7 +51,7 @@ _`.def.splay-tree`: A *splay tree* is a self-adjusting binary tree as described in [ST85]_ and [Sleator96]_. _`.def.node`: A *node* is used in the typical data structure sense to -mean an element of a tree (see also `.type.splay.node`_). +mean an element of a tree (see also `.type.tree`_). _`.def.key`: A *key* is a value associated with each node; the keys are totally ordered by a client provided comparator. @@ -442,8 +442,7 @@ right trees. For the left tree, we traverse the right child line, reversing pointers, until we reach the node that was the last node prior to the transplantation of the root's children. Then we update from that node back to the left tree's root, restoring pointers. -Updating the right tree is the same, mutatis mutandis. (See -`.future.reverse`_ for an alternative approach). +Updating the right tree is the same, mutatis mutandis. Usage From 14146c9a952bfc23bdecb22a6a4d3e3efcebf589 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 27 May 2014 19:34:02 +0100 Subject: [PATCH 45/53] Test "make install" as well as "make test". Copied from Perforce Change: 186317 ServerID: perforce.ravenbrook.com --- mps/.travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mps/.travis.yml b/mps/.travis.yml index 07a0dd66528..bc1dfd5bfb1 100644 --- a/mps/.travis.yml +++ b/mps/.travis.yml @@ -9,3 +9,5 @@ notifications: email: - mps-travis@ravenbrook.com irc: "irc.freenode.net#memorypoolsystem" +script: + - ./configure --prefix=$PWD/prefix && make install && make test From 5b70aff365d5c47030a467f93e2c14a2707c950f Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 27 May 2014 20:26:25 +0100 Subject: [PATCH 46/53] Fix typo; add cross-ref from "automatic memory management". Copied from Perforce Change: 186319 ServerID: perforce.ravenbrook.com --- mps/manual/source/glossary/a.rst | 6 ++++++ mps/manual/source/glossary/p.rst | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mps/manual/source/glossary/a.rst b/mps/manual/source/glossary/a.rst index 746b5c0cf49..ec4da5f86d2 100644 --- a/mps/manual/source/glossary/a.rst +++ b/mps/manual/source/glossary/a.rst @@ -480,6 +480,12 @@ Memory Management Glossary: A .. opposite:: :term:`manual memory management`. + .. mps:specific:: + + The MPS provides automatic memory management through + :term:`pool classes` such as :ref:`pool-amc`, + :ref:`pool-ams`, and :ref:`pool-awl`. + automatic storage duration In :term:`C`, :term:`objects` that are declared with diff --git a/mps/manual/source/glossary/p.rst b/mps/manual/source/glossary/p.rst index 8909d6fb964..5fdba129d77 100644 --- a/mps/manual/source/glossary/p.rst +++ b/mps/manual/source/glossary/p.rst @@ -167,7 +167,7 @@ Memory Management Glossary: P mutator changing :term:`objects` while collection occurs. The problem is similar to that of :term:`incremental GC `, but harder. The solution - typically involves :term:`barrier (1)`. + typically involves :term:`barriers (1)`. .. similar:: :term:`incremental `. From 00b9ba04b2514a391e97819d2f83737c0d6c52a1 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 27 May 2014 21:52:25 +0100 Subject: [PATCH 47/53] Format the glossary index in two colummns. Copied from Perforce Change: 186321 ServerID: perforce.ravenbrook.com --- mps/manual/source/glossary/index.rst | 3 +++ .../source/themes/mmref/static/mmref.css_t | 26 ++++++++++++++++--- mps/manual/source/themes/mps/static/mps.css_t | 21 ++++++++++++++- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/mps/manual/source/glossary/index.rst b/mps/manual/source/glossary/index.rst index 9a426a5e742..40656261350 100644 --- a/mps/manual/source/glossary/index.rst +++ b/mps/manual/source/glossary/index.rst @@ -33,6 +33,9 @@ Memory Management Glossary w z +All +=== + :term:`absolute address ` :term:`activation frame ` :term:`activation record` diff --git a/mps/manual/source/themes/mmref/static/mmref.css_t b/mps/manual/source/themes/mmref/static/mmref.css_t index ceab4faddef..f16758a7391 100644 --- a/mps/manual/source/themes/mmref/static/mmref.css_t +++ b/mps/manual/source/themes/mmref/static/mmref.css_t @@ -2,6 +2,11 @@ @import url('scrolls.css'); +sup { + vertical-align: top; + font-size: 80%; +} + dl.glossary dt { font-family: {{ theme_headfont }}; } @@ -135,7 +140,22 @@ div#home h1 + p { padding-top: 15px; } -div#memory-management-glossary em.xref.std-term { - white-space: nowrap; - margin-right: 1em; +/* Format the glossary index in two columns. */ + +div#memory-management-glossary div#all { + -webkit-columns: 2; + -moz-columns: 2; + -o-columns: 2; + -ms-columns: 2; + columns: 2; + padding-top: 1em; +} + +div#memory-management-glossary div#all h2 { + display: none; +} + +div#memory-management-glossary div#all a.reference.internal:after { + content: "\A"; + white-space: pre; } diff --git a/mps/manual/source/themes/mps/static/mps.css_t b/mps/manual/source/themes/mps/static/mps.css_t index 6b0e20d0eb4..5904c165580 100644 --- a/mps/manual/source/themes/mps/static/mps.css_t +++ b/mps/manual/source/themes/mps/static/mps.css_t @@ -188,7 +188,7 @@ p.glossary-alphabet { } sup { - vertical-align: 20%; + vertical-align: top; font-size: 80%; } @@ -220,3 +220,22 @@ li.toctree-l1, li.toctree-l2, li.toctree-l3 { padding-top: 0 !important; } +/* Format the glossary index in two columns. */ + +div#memory-management-glossary div#all { + -webkit-columns: 2; + -moz-columns: 2; + -o-columns: 2; + -ms-columns: 2; + columns: 2; + padding-top: 1em; +} + +div#memory-management-glossary div#all h2 { + display: none; +} + +div#memory-management-glossary div#all a.reference.internal:after { + content: "\A"; + white-space: pre; +} From 2d9aa0b7839d9906ca2a638948135b973b52a0c6 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 28 May 2014 11:09:14 +0100 Subject: [PATCH 48/53] Failoverfindinzones is untested. Copied from Perforce Change: 186327 ServerID: perforce.ravenbrook.com --- mps/code/failover.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mps/code/failover.c b/mps/code/failover.c index 8032cfe55d0..1129c627590 100644 --- a/mps/code/failover.c +++ b/mps/code/failover.c @@ -247,6 +247,7 @@ static Bool failoverFindInZones(Bool *foundReturn, Range rangeReturn, Range oldR Bool found = FALSE; Res res; + AVER(FALSE); /* TODO: this code is completely untested! */ AVER(foundReturn != NULL); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); From 13ba0ef139ec72be06dea1f9d9a14bcff831aea9 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 28 May 2014 17:24:46 +0100 Subject: [PATCH 49/53] Add comment explaining purpose of attribute_unused. Copied from Perforce Change: 186335 ServerID: perforce.ravenbrook.com --- mps/code/config.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mps/code/config.h b/mps/code/config.h index 081b3ffaeeb..e4db1be6423 100644 --- a/mps/code/config.h +++ b/mps/code/config.h @@ -292,6 +292,11 @@ /* Attribute for functions that may be unused in some build configurations. * GCC: + * + * This attribute must be applied to all Check functions, otherwise + * the RASH variety fails to compile with -Wunused-function. (It + * should not be applied to functions that are unused in all build + * configurations: these functions should not be compiled.) */ #if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL) #define ATTRIBUTE_UNUSED __attribute__((__unused__)) From f1b01ff0c16eafc6258e8664fae62d9635b6f251 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Wed, 28 May 2014 17:42:11 +0100 Subject: [PATCH 50/53] Clarifying a couple of comments most likely messed up by search-and-replace edits. Copied from Perforce Change: 186344 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mps/code/arena.c b/mps/code/arena.c index be3137fd9bd..ecfcb029e05 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -386,7 +386,7 @@ void ArenaDestroy(Arena arena) LandFinish(ArenaFreeLand(arena)); /* The CBS block pool can't free its own memory via ArenaFree because - that would use the CBSZoned. */ + that would use the freeLand. */ MFSFinishTracts(ArenaCBSBlockPool(arena), arenaMFSPageFreeVisitor, NULL, 0); PoolFinish(ArenaCBSBlockPool(arena)); @@ -687,7 +687,7 @@ static Res arenaExtendCBSBlockPool(Range pageRangeReturn, Arena arena) return ResOK; } -/* arenaExcludePage -- exclude CBS block pool's page from Land +/* arenaExcludePage -- exclude CBS block pool's page from free land * * Exclude the page we specially allocated for the CBS block pool * so that it doesn't get reallocated. From 331306ee0e24d58bc2935456298798bf327e7170 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Thu, 29 May 2014 14:07:24 +0100 Subject: [PATCH 51/53] Fixing unbracketed macro parameter. Copied from Perforce Change: 186345 ServerID: perforce.ravenbrook.com --- mps/code/freelist.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mps/code/freelist.c b/mps/code/freelist.c index a1f4803906e..87f34384f27 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -14,7 +14,7 @@ SRCID(freelist, "$Id$"); #define freelistOfLand(land) PARENT(FreelistStruct, landStruct, land) -#define freelistAlignment(fl) LandAlignment(&fl->landStruct) +#define freelistAlignment(fl) LandAlignment(&(fl)->landStruct) typedef union FreelistBlockUnion { @@ -246,6 +246,9 @@ static Size freelistSize(Land land) * 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'. + * + * TODO: Consider using a sentinel FreelistBlockUnion in the FreeListStruct + * as the end marker and see if that simplifies. */ static void freelistBlockSetPrevNext(Freelist fl, FreelistBlock prev, From 8e4721db4ccd20b08bd8293d4711f34a633d1fdd Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 29 May 2014 14:50:36 +0100 Subject: [PATCH 52/53] Fix problems identified by rb in review . Copied from Perforce Change: 186347 ServerID: perforce.ravenbrook.com --- mps/code/cbs.c | 6 +----- mps/code/failover.c | 15 +++++++++++---- mps/code/fotest.c | 4 +--- mps/code/freelist.c | 9 ++++++--- mps/code/poolmvff.c | 5 ++--- mps/code/splay.c | 4 ++-- mps/code/splay.h | 2 +- 7 files changed, 24 insertions(+), 21 deletions(-) diff --git a/mps/code/cbs.c b/mps/code/cbs.c index caad67aba71..6e30d77daa8 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -132,12 +132,8 @@ static Bool cbsTestTree(SplayTree splay, Tree tree, AVERT(SplayTree, splay); AVERT(Tree, tree); -#if 0 AVER(closureP == NULL); AVER(size > 0); -#endif - UNUSED(closureP); - UNUSED(size); AVER(IsLandSubclass(cbsLand(cbsOfSplay(splay)), CBSFastLandClass)); block = cbsFastBlockOfTree(tree); @@ -397,7 +393,7 @@ static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range) block->base = RangeBase(range); block->limit = RangeLimit(range); - SplayNodeUpdate(cbsSplay(cbs), cbsBlockTree(block)); + SplayNodeInit(cbsSplay(cbs), cbsBlockTree(block)); AVERT(CBSBlock, block); *blockReturn = block; diff --git a/mps/code/failover.c b/mps/code/failover.c index 1129c627590..7e9f07d7b8c 100644 --- a/mps/code/failover.c +++ b/mps/code/failover.c @@ -129,10 +129,17 @@ static Res failoverDelete(Range rangeReturn, Land land, Range range) /* Range not found in primary: try secondary. */ return LandDelete(rangeReturn, fo->secondary, range); } 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 . + /* Range was found in primary, but couldn't be deleted. The only + * case we expect to encounter here is the case where the primary + * is out of memory. (In particular, we don't handle the case of a + * CBS returning ResLIMIT because its block pool has been + * configured not to automatically extend itself.) + */ + AVER(ResIsAllocFailure(res)); + + /* 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 2729395c0df..788253b570d 100644 --- a/mps/code/fotest.c +++ b/mps/code/fotest.c @@ -52,13 +52,11 @@ static Res oomAlloc(Addr *pReturn, Pool pool, Size size, UNUSED(pool); UNUSED(size); UNUSED(withReservoirPermit); - switch (rnd() % 4) { + switch (rnd() % 3) { case 0: return ResRESOURCE; case 1: return ResMEMORY; - case 2: - return ResLIMIT; default: return ResCOMMIT_LIMIT; } diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 87f34384f27..ca66a0b273d 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -246,9 +246,12 @@ static Size freelistSize(Land land) * 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'. - * - * TODO: Consider using a sentinel FreelistBlockUnion in the FreeListStruct - * as the end marker and see if that simplifies. + + * It is tempting to try to simplify this code by putting a + * FreelistBlockUnion into the FreelistStruct and so avoiding the + * special case on prev. But the problem with that idea is that we + * can't guarantee that such a sentinel would respect the isolated + * range invariant, and so it would still have to be special-cases. */ static void freelistBlockSetPrevNext(Freelist fl, FreelistBlock prev, diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index 8eb177434b1..7b1f435944c 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -82,9 +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). + * Updates rangeIO to be maximally coalesced range containing given + * range. Does not attempt to free segments (see MVFFFreeSegs). */ static Res MVFFInsert(Range rangeIO, MVFF mvff) { AVERT(Range, rangeIO); diff --git a/mps/code/splay.c b/mps/code/splay.c index 278f038b9b5..1b0e8afb8fd 100644 --- a/mps/code/splay.c +++ b/mps/code/splay.c @@ -1317,9 +1317,9 @@ void SplayNodeRefresh(SplayTree splay, Tree node) } -/* SplayNodeUpdate -- update the client property without splaying */ +/* SplayNodeInit -- initialize client property without splaying */ -void SplayNodeUpdate(SplayTree splay, Tree node) +void SplayNodeInit(SplayTree splay, Tree node) { AVERT(SplayTree, splay); AVERT(Tree, node); diff --git a/mps/code/splay.h b/mps/code/splay.h index d6496ff9977..24b97c4b055 100644 --- a/mps/code/splay.h +++ b/mps/code/splay.h @@ -69,7 +69,7 @@ extern Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, void *closureP, Size closureS); extern void SplayNodeRefresh(SplayTree splay, Tree node); -extern void SplayNodeUpdate(SplayTree splay, Tree node); +extern void SplayNodeInit(SplayTree splay, Tree node); extern Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, TreeDescribeMethod nodeDescribe); From 51e39dc08736c8300f70927c3f8c5d23387e5bd6 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 29 May 2014 15:28:33 +0100 Subject: [PATCH 53/53] Pass and check special closure values unused_pointer, unused_size instead of null, 0. Copied from Perforce Change: 186352 ServerID: perforce.ravenbrook.com --- mps/code/abqtest.c | 3 ++- mps/code/arena.c | 6 ++++-- mps/code/cbs.c | 17 +++++++++-------- mps/code/fbmtest.c | 5 +++-- mps/code/freelist.c | 4 ++-- mps/code/land.c | 6 ++++-- mps/code/landtest.c | 4 ++-- mps/code/misc.h | 9 +++++++++ mps/code/poolmfs.c | 4 +++- mps/code/poolmv2.c | 11 +++++++---- 10 files changed, 45 insertions(+), 24 deletions(-) diff --git a/mps/code/abqtest.c b/mps/code/abqtest.c index 367bafe730b..9aad3351cb6 100644 --- a/mps/code/abqtest.c +++ b/mps/code/abqtest.c @@ -96,6 +96,7 @@ static Bool TestDeleteCallback(Bool *deleteReturn, void *element, { TestBlock *a = (TestBlock *)element; TestClosure cl = (TestClosure)closureP; + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); if (*a == cl->b) { *deleteReturn = TRUE; @@ -144,7 +145,7 @@ static void step(void) cdie(b != NULL, "found to delete"); cl.b = b; cl.res = ResFAIL; - ABQIterate(&abq, TestDeleteCallback, &cl, 0); + ABQIterate(&abq, TestDeleteCallback, &cl, UNUSED_SIZE); cdie(cl.res == ResOK, "ABQIterate"); } } diff --git a/mps/code/arena.c b/mps/code/arena.c index ecfcb029e05..d270adf7499 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -360,6 +360,8 @@ static void arenaMFSPageFreeVisitor(Pool pool, Addr base, Size size, void *closureP, Size closureS) { AVERT(Pool, pool); + AVER(closureP == UNUSED_POINTER); + AVER(closureS == UNUSED_SIZE); UNUSED(closureP); UNUSED(closureS); UNUSED(size); @@ -387,8 +389,8 @@ void ArenaDestroy(Arena arena) /* The CBS block pool can't free its own memory via ArenaFree because that would use the freeLand. */ - MFSFinishTracts(ArenaCBSBlockPool(arena), - arenaMFSPageFreeVisitor, NULL, 0); + MFSFinishTracts(ArenaCBSBlockPool(arena), arenaMFSPageFreeVisitor, + UNUSED_POINTER, UNUSED_SIZE); PoolFinish(ArenaCBSBlockPool(arena)); /* Call class-specific finishing. This will call ArenaFinish. */ diff --git a/mps/code/cbs.c b/mps/code/cbs.c index 6e30d77daa8..6f0350d0519 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -721,6 +721,7 @@ static Bool cbsIterateVisit(Tree tree, void *closureP, Size closureS) CBS cbs = cbsOfLand(land); Bool cont = TRUE; + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); cbsBlock = cbsBlockOfTree(tree); @@ -754,7 +755,7 @@ static Bool cbsIterate(Land land, LandVisitor visitor, closure.closureP = closureP; closure.closureS = closureS; return TreeTraverse(SplayTreeRoot(splay), splay->compare, splay->nodeKey, - cbsIterateVisit, &closure, 0); + cbsIterateVisit, &closure, UNUSED_SIZE); } @@ -871,15 +872,15 @@ typedef struct cbsTestNodeInZonesClosureStruct { } cbsTestNodeInZonesClosureStruct, *cbsTestNodeInZonesClosure; static Bool cbsTestNodeInZones(SplayTree splay, Tree tree, - void *closureP, Size closureSize) + void *closureP, Size closureS) { CBSBlock block = cbsBlockOfTree(tree); cbsTestNodeInZonesClosure closure = closureP; RangeInZoneSet search; UNUSED(splay); - AVER(closureSize == sizeof(cbsTestNodeInZonesClosureStruct)); - UNUSED(closureSize); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); search = closure->high ? RangeInZoneSetLast : RangeInZoneSetFirst; @@ -889,15 +890,15 @@ static Bool cbsTestNodeInZones(SplayTree splay, Tree tree, } static Bool cbsTestTreeInZones(SplayTree splay, Tree tree, - void *closureP, Size closureSize) + void *closureP, Size closureS) { CBSFastBlock fastBlock = cbsFastBlockOfTree(tree); CBSZonedBlock zonedBlock = cbsZonedBlockOfTree(tree); cbsTestNodeInZonesClosure closure = closureP; UNUSED(splay); - AVER(closureSize == sizeof(cbsTestNodeInZonesClosureStruct)); - UNUSED(closureSize); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); return fastBlock->maxSize >= closure->size && ZoneSetInter(zonedBlock->zones, closure->zoneSet) != ZoneSetEMPTY; @@ -1030,7 +1031,7 @@ static Res cbsFindInZones(Bool *foundReturn, Range rangeReturn, closure.high = high; if (!(*splayFind)(&tree, cbsSplay(cbs), cbsTestNodeInZones, cbsTestTreeInZones, - &closure, sizeof(closure))) + &closure, UNUSED_SIZE)) goto fail; block = cbsBlockOfTree(tree); diff --git a/mps/code/fbmtest.c b/mps/code/fbmtest.c index 231d02482a7..e24fcc564ee 100644 --- a/mps/code/fbmtest.c +++ b/mps/code/fbmtest.c @@ -98,6 +98,7 @@ static Bool checkCallback(Range range, void *closureP, Size closureS) Addr base, limit; CheckFBMClosure cl = (CheckFBMClosure)closureP; + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); Insist(cl != NULL); @@ -149,10 +150,10 @@ static void check(FBMState state) switch (state->type) { case FBMTypeCBS: - CBSIterate(state->the.cbs, checkCBSCallback, (void *)&closure, 0); + CBSIterate(state->the.cbs, checkCBSCallback, &closure, UNUSED_SIZE); break; case FBMTypeFreelist: - FreelistIterate(state->the.fl, checkFLCallback, (void *)&closure, 0); + FreelistIterate(state->the.fl, checkFLCallback, &closure, UNUSED_SIZE); break; default: cdie(0, "invalid state->type"); diff --git a/mps/code/freelist.c b/mps/code/freelist.c index ca66a0b273d..2be00189aa8 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -755,7 +755,7 @@ static Bool freelistDescribeVisitor(Land land, Range range, if (!TESTT(Land, land)) return FALSE; if (!RangeCheck(range)) return FALSE; if (stream == NULL) return FALSE; - UNUSED(closureS); + if (closureS != UNUSED_SIZE) return FALSE; res = WriteF(stream, " [$P,", (WriteFP)RangeBase(range), @@ -783,7 +783,7 @@ static Res freelistDescribe(Land land, mps_lib_FILE *stream) " listSize = $U\n", (WriteFU)fl->listSize, NULL); - b = LandIterate(land, freelistDescribeVisitor, stream, 0); + b = LandIterate(land, freelistDescribeVisitor, stream, UNUSED_SIZE); if (!b) return ResFAIL; res = WriteF(stream, "}\n", NULL); diff --git a/mps/code/land.c b/mps/code/land.c index 78dc7f0b324..19f26057623 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -414,6 +414,7 @@ static Bool landFlushVisitor(Bool *deleteReturn, Land land, Range range, AVERT(Land, land); AVERT(Range, range); AVER(closureP != NULL); + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); dest = closureP; @@ -438,7 +439,7 @@ Bool LandFlush(Land dest, Land src) AVERT(Land, dest); AVERT(Land, src); - return LandIterateAndDelete(src, landFlushVisitor, dest, 0); + return LandIterateAndDelete(src, landFlushVisitor, dest, UNUSED_SIZE); } @@ -494,6 +495,7 @@ static Bool landSizeVisitor(Land land, Range range, AVERT(Land, land); AVERT(Range, range); AVER(closureP != NULL); + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); size = closureP; @@ -505,7 +507,7 @@ static Bool landSizeVisitor(Land land, Range range, Size LandSlowSize(Land land) { Size size = 0; - Bool b = LandIterate(land, landSizeVisitor, &size, 0); + Bool b = LandIterate(land, landSizeVisitor, &size, UNUSED_SIZE); AVER(b); return size; } diff --git a/mps/code/landtest.c b/mps/code/landtest.c index a4eb9b369e1..af6beff29ac 100644 --- a/mps/code/landtest.c +++ b/mps/code/landtest.c @@ -81,7 +81,7 @@ static Bool checkVisitor(Land land, Range range, void *closureP, Size closureS) CheckTestClosure cl = closureP; testlib_unused(land); - testlib_unused(closureS); + Insist(closureS == UNUSED_SIZE); Insist(cl != NULL); base = RangeBase(range); @@ -114,7 +114,7 @@ static void check(TestState state) closure.limit = addrOfIndex(state, ArraySize); closure.oldLimit = state->block; - b = LandIterate(state->land, checkVisitor, (void *)&closure, 0); + b = LandIterate(state->land, checkVisitor, &closure, UNUSED_SIZE); Insist(b); if (closure.oldLimit == state->block) diff --git a/mps/code/misc.h b/mps/code/misc.h index 7380421d5c5..853903b58fa 100644 --- a/mps/code/misc.h +++ b/mps/code/misc.h @@ -152,6 +152,15 @@ typedef const struct SrcIdStruct { #define UNUSED(param) ((void)param) +/* UNUSED_POINTER, UNUSED_SIZE -- values for unused arguments + * + * Use these values for unused pointer, size closure arguments and + * check them in the callback or visitor. + */ +#define UNUSED_POINTER ((Pointer)0xB60405ED) /* PointeR UNUSED */ +#define UNUSED_SIZE ((Size)0x520405ED) /* SiZe UNUSED */ + + /* PARENT -- parent structure * * Given a pointer to a field of a structure this returns a pointer to diff --git a/mps/code/poolmfs.c b/mps/code/poolmfs.c index b40094d839c..c203c5697b6 100644 --- a/mps/code/poolmfs.c +++ b/mps/code/poolmfs.c @@ -151,6 +151,8 @@ void MFSFinishTracts(Pool pool, MFSTractVisitor visitor, static void MFSTractFreeVisitor(Pool pool, Addr base, Size size, void *closureP, Size closureS) { + AVER(closureP == UNUSED_POINTER); + AVER(closureS == UNUSED_SIZE); UNUSED(closureP); UNUSED(closureS); ArenaFree(base, size, pool); @@ -165,7 +167,7 @@ static void MFSFinish(Pool pool) mfs = PoolPoolMFS(pool); AVERT(MFS, mfs); - MFSFinishTracts(pool, MFSTractFreeVisitor, NULL, 0); + MFSFinishTracts(pool, MFSTractFreeVisitor, UNUSED_POINTER, UNUSED_SIZE); mfs->sig = SigInvalid; } diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index fcffea6f129..4dd85c184e5 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -754,6 +754,7 @@ static Bool MVTDeleteOverlapping(Bool *deleteReturn, void *element, AVER(deleteReturn != NULL); AVER(element != NULL); AVER(closureP != NULL); + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); oldRange = element; @@ -820,7 +821,7 @@ static Res MVTInsert(MVT mvt, Addr base, Addr limit) * with ranges on the ABQ, so ensure that the corresponding ranges * are coalesced on the ABQ. */ - ABQIterate(MVTABQ(mvt), MVTDeleteOverlapping, &newRange, 0); + ABQIterate(MVTABQ(mvt), MVTDeleteOverlapping, &newRange, UNUSED_SIZE); (void)MVTReserve(mvt, &newRange); } @@ -849,7 +850,7 @@ static Res MVTDelete(MVT mvt, Addr base, Addr limit) * might be on the ABQ, so ensure it is removed. */ if (RangeSize(&rangeOld) >= mvt->reuseSize) - ABQIterate(MVTABQ(mvt), MVTDeleteOverlapping, &rangeOld, 0); + ABQIterate(MVTABQ(mvt), MVTDeleteOverlapping, &rangeOld, UNUSED_SIZE); /* There might be fragments at the left or the right of the deleted * range, and either might be big enough to go back on the ABQ. @@ -1211,6 +1212,7 @@ static Bool MVTRefillVisitor(Land land, Range range, AVERT(Land, land); mvt = closureP; AVERT(MVT, mvt); + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); if (RangeSize(range) < mvt->reuseSize) @@ -1234,7 +1236,7 @@ static void MVTRefillABQIfEmpty(MVT mvt, Size size) mvt->abqOverflow = FALSE; METER_ACC(mvt->refills, size); /* The iteration stops if the ABQ overflows, so may finish or not. */ - (void)LandIterate(MVTFailover(mvt), MVTRefillVisitor, mvt, 0); + (void)LandIterate(MVTFailover(mvt), MVTRefillVisitor, mvt, UNUSED_SIZE); } } @@ -1266,6 +1268,7 @@ static Bool MVTContingencyVisitor(Land land, Range range, cl = closureP; mvt = cl->mvt; AVERT(MVT, mvt); + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); base = RangeBase(range); @@ -1304,7 +1307,7 @@ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, cls.steps = 0; cls.hardSteps = 0; - if (LandIterate(MVTFailover(mvt), MVTContingencyVisitor, (void *)&cls, 0)) + if (LandIterate(MVTFailover(mvt), MVTContingencyVisitor, &cls, UNUSED_SIZE)) return FALSE; AVER(RangeSize(&cls.range) >= min);