Catch-up merge from master sources @186481 to branch/2014-05-17/chunk-tree.

Copied from Perforce
 Change: 186491
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Gareth Rees 2014-06-11 13:52:31 +01:00
commit de2a30d207
141 changed files with 13283 additions and 3783 deletions

View file

@ -9,3 +9,5 @@ notifications:
email:
- mps-travis@ravenbrook.com
irc: "irc.freenode.net#memorypoolsystem"
script:
- ./configure --prefix=$PWD/prefix && make install && make test

View file

@ -37,12 +37,12 @@ install-make-build: make-install-dirs build-via-make
$(INSTALL_PROGRAM) $(addprefix code/$(MPS_TARGET_NAME)/hot/,$(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/
@ -76,7 +76,7 @@ test-make-build:
$(MAKE) -C code -f anan$(MPS_BUILD_NAME).gmk VARIETY=cool CFLAGS="-DCONFIG_POLL_NONE" clean testpoll
test-xcode-build:
$(XCODEBUILD) -config Release -target testci
$(XCODEBUILD) -config Debug -target testci
$(XCODEBUILD) -config Release -target testci
test: @TEST_TARGET@

View file

@ -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");
}
}

View file

@ -105,7 +105,7 @@ static mps_addr_t make(void)
/* test -- the actual stress test */
static mps_pool_debug_option_s freecheckOptions =
{ NULL, 0, (const void *)"Dead", 4 };
{ NULL, 0, "Dead", 4 };
static void test_pool(mps_class_t pool_class, mps_arg_s args[],
mps_bool_t haveAmbiguous)

View file

@ -43,28 +43,25 @@ static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size)
/* stress -- create a pool of the requested type and allocate in it */
static mps_res_t stress(mps_class_t class, size_t (*size)(size_t i),
mps_arena_t arena, ...)
static mps_res_t stress(mps_arena_t arena, mps_align_t align,
size_t (*size)(size_t i, mps_align_t align),
const char *name, mps_class_t class, mps_arg_s args[])
{
mps_res_t res = MPS_RES_OK;
mps_pool_t pool;
mps_ap_t ap;
va_list arg;
size_t i, k;
int *ps[testSetSIZE];
size_t ss[testSetSIZE];
va_start(arg, arena);
res = mps_pool_create_v(&pool, arena, class, arg);
va_end(arg);
if (res != MPS_RES_OK)
return res;
printf("stress %s\n", name);
die(mps_pool_create_k(&pool, arena, class, args), "pool_create");
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
/* allocate a load of objects */
for (i=0; i<testSetSIZE; ++i) {
ss[i] = (*size)(i);
ss[i] = (*size)(i, align);
res = make((mps_addr_t *)&ps[i], ap, ss[i]);
if (res != MPS_RES_OK)
@ -96,7 +93,7 @@ static mps_res_t stress(mps_class_t class, size_t (*size)(size_t i),
}
/* allocate some new objects */
for (i=testSetSIZE/2; i<testSetSIZE; ++i) {
ss[i] = (*size)(i);
ss[i] = (*size)(i, align);
res = make((mps_addr_t *)&ps[i], ap, ss[i]);
if (res != MPS_RES_OK)
goto allocFail;
@ -111,63 +108,72 @@ static mps_res_t stress(mps_class_t class, size_t (*size)(size_t i),
}
/* randomSizeAligned -- produce sizes both large and small,
* aligned by platform alignment */
/* randomSizeAligned -- produce sizes both large and small, aligned to
* align.
*/
static size_t randomSizeAligned(size_t i)
static size_t randomSizeAligned(size_t i, mps_align_t align)
{
size_t maxSize = 2 * 160 * 0x2000;
/* Reduce by a factor of 2 every 10 cycles. Total allocation about 40 MB. */
return alignUp(rnd() % max((maxSize >> (i / 10)), 2) + 1, MPS_PF_ALIGN);
return alignUp(rnd() % max((maxSize >> (i / 10)), 2) + 1, align);
}
static mps_pool_debug_option_s bothOptions = {
/* .fence_template = */ (const void *)"postpostpostpost",
/* .fence_size = */ MPS_PF_ALIGN,
/* .free_template = */ (const void *)"DEAD",
/* .fence_template = */ "post",
/* .fence_size = */ 4,
/* .free_template = */ "DEAD",
/* .free_size = */ 4
};
static mps_pool_debug_option_s fenceOptions = {
/* .fence_template = */ (const void *)"\0XXX ''\"\"'' XXX\0",
/* .fence_size = */ 16,
/* .fence_template = */ "123456789abcdef",
/* .fence_size = */ 15,
/* .free_template = */ NULL,
/* .free_size = */ 0
};
/* testInArena -- test all the pool classes in the given arena */
static void testInArena(mps_arena_t arena, mps_pool_debug_option_s *options)
{
mps_res_t res;
MPS_ARGS_BEGIN(args) {
mps_align_t align = sizeof(void *) << (rnd() % 4);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE);
die(stress(arena, align, randomSizeAligned, "MVFF", mps_class_mvff(), args),
"stress MVFF");
} MPS_ARGS_END(args);
/* IWBN to test MVFFDebug, but the MPS doesn't support debugging APs, */
/* yet (MV Debug works here, because it fakes it through PoolAlloc). */
printf("MVFF\n");
res = stress(mps_class_mvff(), randomSizeAligned, arena,
(size_t)65536, (size_t)32, (mps_align_t)MPS_PF_ALIGN, TRUE, TRUE, TRUE);
if (res == MPS_RES_COMMIT_LIMIT) return;
die(res, "stress MVFF");
printf("MV debug\n");
res = stress(mps_class_mv_debug(), randomSizeAligned, arena,
options, (size_t)65536, (size_t)32, (size_t)65536);
if (res == MPS_RES_COMMIT_LIMIT) return;
die(res, "stress MV debug");
MPS_ARGS_BEGIN(args) {
mps_align_t align = (mps_align_t)1 << (rnd() % 6);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
die(stress(arena, align, randomSizeAligned, "MV", mps_class_mv(), args),
"stress MV");
} MPS_ARGS_END(args);
printf("MV\n");
res = stress(mps_class_mv(), randomSizeAligned, arena,
(size_t)65536, (size_t)32, (size_t)65536);
if (res == MPS_RES_COMMIT_LIMIT) return;
die(res, "stress MV");
MPS_ARGS_BEGIN(args) {
mps_align_t align = (mps_align_t)1 << (rnd() % 6);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, options);
die(stress(arena, align, randomSizeAligned, "MV debug",
mps_class_mv_debug(), args),
"stress MV debug");
} MPS_ARGS_END(args);
printf("MVT\n");
res = stress(mps_class_mvt(), randomSizeAligned, arena,
(size_t)8, (size_t)32, (size_t)65536, (mps_word_t)4,
(mps_word_t)50);
if (res == MPS_RES_COMMIT_LIMIT) return;
die(res, "stress MVT");
MPS_ARGS_BEGIN(args) {
mps_align_t align = sizeof(void *) << (rnd() % 4);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
die(stress(arena, align, randomSizeAligned, "MVT", mps_class_mvt(), args),
"stress MVT");
} MPS_ARGS_END(args);
}

View file

@ -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) (&(arena)->freeLandStruct.landStruct)
/* Forward declarations */
@ -153,9 +153,9 @@ Bool ArenaCheck(Arena arena)
CHECKL(LocusCheck(arena));
CHECKL(BoolCheck(arena->hasFreeCBS));
if (arena->hasFreeCBS)
CHECKD(CBS, ArenaFreeCBS(arena));
CHECKL(BoolCheck(arena->hasFreeLand));
if (arena->hasFreeLand)
CHECKD(Land, ArenaFreeLand(arena));
CHECKL(BoolCheck(arena->zoned));
@ -200,7 +200,7 @@ Res ArenaInit(Arena arena, ArenaClass class, Align alignment, ArgList args)
arena->poolReady = FALSE; /* <design/arena/#pool.ready> */
arena->lastTract = NULL;
arena->lastTractBase = NULL;
arena->hasFreeCBS = FALSE;
arena->hasFreeLand = FALSE;
arena->freeZones = ZoneSetUNIV;
arena->zoned = zoned;
@ -215,14 +215,15 @@ 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));
MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSZonedBlockStruct));
MPS_ARGS_ADD(piArgs, MPS_KEY_EXTEND_BY, arena->alignment);
MPS_ARGS_ADD(piArgs, MFSExtendSelf, FALSE);
res = PoolInit(ArenaCBSBlockPool(arena), arena, PoolClassMFS(), piArgs);
@ -231,17 +232,17 @@ 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));
res = CBSInit(ArenaFreeCBS(arena), arena, arena, alignment,
/* fastFind */ TRUE, arena->zoned, cbsiArgs);
} MPS_ARGS_END(cbsiArgs);
/* Initialise the freeLand. */
MPS_ARGS_BEGIN(liArgs) {
MPS_ARGS_ADD(liArgs, CBSBlockPool, ArenaCBSBlockPool(arena));
res = LandInit(ArenaFreeLand(arena), CBSZonedLandClassGet(), arena,
alignment, arena, liArgs);
} MPS_ARGS_END(liArgs);
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, <design/reservoir/> */
res = ReservoirInit(&arena->reservoirStruct, arena);
@ -252,8 +253,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));
@ -303,15 +304,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. */
res = ArenaFreeCBSInsert(arena,
PageIndexBase(arena->primary,
arena->primary->allocBase),
arena->primary->limit);
res = ArenaFreeLandInsert(arena,
PageIndexBase(arena->primary,
arena->primary->allocBase),
arena->primary->limit);
if (res != ResOK)
goto failPrimaryCBS;
arena->hasFreeCBS = TRUE;
goto failPrimaryLand;
arena->hasFreeLand = TRUE;
res = ControlInit(arena);
if (res != ResOK)
@ -328,7 +329,7 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args)
failGlobalsCompleteCreate:
ControlFinish(arena);
failControlInit:
failPrimaryCBS:
failPrimaryLand:
failStripeSize:
(*class->finish)(arena);
failInit:
@ -359,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);
@ -378,16 +381,15 @@ 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. */
MFSFinishTracts(ArenaCBSBlockPool(arena),
arenaMFSPageFreeVisitor, NULL, 0);
MFSFinishTracts(ArenaCBSBlockPool(arena), arenaMFSPageFreeVisitor,
UNUSED_POINTER, UNUSED_SIZE);
/* Call class-specific finishing. This will call ArenaFinish. */
(*arena->class->finish)(arena);
@ -628,9 +630,10 @@ void ArenaChunkInsert(Arena arena, Tree tree) {
/* 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.
*/
typedef struct ArenaAllocPageClosureStruct {
@ -746,7 +749,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 free land
*
* Exclude the page we specially allocated for the CBS block pool
* so that it doesn't get reallocated.
@ -757,20 +760,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 range to arena's land, maybe extending block pool
*
* The arena's CBSs 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.
*/
static Res arenaCBSInsert(Range rangeReturn, Arena arena, Range range)
static Res arenaLandInsert(Range rangeReturn, Arena arena, Range range)
{
Res res;
@ -778,17 +781,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) { /* CBS block 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 CBS block pool */
arenaExcludePage(arena, &pageRange);
}
@ -796,16 +799,16 @@ static Res arenaCBSInsert(Range rangeReturn, Arena arena, Range range)
}
/* ArenaFreeCBSInsert -- add a block to arena CBS, maybe stealing memory
/* ArenaFreeLandInsert -- add range to arena's 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
* 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.
*/
static void arenaCBSInsertSteal(Range rangeReturn, Arena arena, Range rangeIO)
static void arenaLandInsertSteal(Range rangeReturn, Arena arena, Range rangeIO)
{
Res res;
@ -813,7 +816,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;
@ -834,22 +837,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 CBS block pool */
}
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 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
* 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;
@ -857,7 +860,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;
@ -870,7 +873,7 @@ Res ArenaFreeCBSInsert(Arena arena, Addr base, Addr limit)
}
/* ArenaFreeCBSDelete -- remove a block from free CBS, 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.
@ -881,13 +884,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
@ -896,13 +899,13 @@ 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;
RangeStruct range, oldRange;
Chunk chunk;
Bool b;
Bool found, b;
Index baseIndex;
Count pages;
Res res;
@ -919,8 +922,8 @@ static Res arenaAllocFromCBS(Tract *tractReturn, ZoneSet zones, Bool high,
/* Step 1. Find a range of address space. */
res = CBSFindInZones(&range, &oldRange, ArenaFreeCBS(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;
@ -928,17 +931,17 @@ 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),
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. */
@ -962,7 +965,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. */
}
@ -1003,10 +1006,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;
}
@ -1018,7 +1021,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;
}
@ -1029,13 +1032,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;
}
@ -1047,7 +1050,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;
}
@ -1056,7 +1059,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;
@ -1174,7 +1177,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);

View file

@ -204,8 +204,6 @@ static Res BufferInit(Buffer buffer, BufferClass class,
AVER(buffer != NULL);
AVERT(BufferClass, class);
AVERT(Pool, pool);
/* The PoolClass should support buffer protocols */
AVER(PoolHasAttr(pool, AttrBUF));
arena = PoolArena(pool);
/* Initialize the buffer. See <code/mpmst.h> for a definition of */
@ -382,8 +380,6 @@ void BufferFinish(Buffer buffer)
pool = BufferPool(buffer);
/* The PoolClass should support buffer protocols */
AVER(PoolHasAttr(pool, AttrBUF));
AVER(BufferIsReady(buffer));
/* <design/alloc-frame/#lw-frame.sync.trip> */

File diff suppressed because it is too large Load diff

View file

@ -15,55 +15,37 @@
#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. */
typedef struct CBSBlockStruct *CBSBlock;
typedef struct CBSBlockStruct {
TreeStruct treeStruct;
Addr base;
Addr limit;
Size maxSize; /* accurate maximum block size of sub-tree */
ZoneSet zones; /* union zone set of all ranges in sub-tree */
} CBSBlockStruct;
typedef struct CBSFastBlockStruct *CBSFastBlock;
typedef struct CBSFastBlockStruct {
struct CBSBlockStruct cbsBlockStruct;
Size maxSize; /* accurate maximum block size of sub-tree */
} CBSFastBlockStruct;
typedef struct CBSZonedBlockStruct *CBSZonedBlock;
typedef struct CBSZonedBlockStruct {
struct CBSFastBlockStruct cbsFastBlockStruct;
ZoneSet zones; /* union zone set of all ranges in sub-tree */
} CBSZonedBlockStruct;
typedef struct CBSStruct *CBS;
typedef Bool (*CBSVisitor)(CBS cbs, Range range,
void *closureP, Size closureS);
extern Bool CBSCheck(CBS cbs);
extern LandClass CBSLandClassGet(void);
extern LandClass CBSFastLandClassGet(void);
extern LandClass CBSZonedLandClassGet(void);
extern const struct mps_key_s _mps_key_cbs_block_pool;
#define CBSBlockPool (&_mps_key_cbs_block_pool)
#define CBSBlockPool_FIELD pool
/* 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 */

View file

@ -1,7 +1,7 @@
/* chain.h: GENERATION CHAINS
*
* $Id$
* Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
* Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*/
#ifndef chain_h
@ -31,7 +31,6 @@ typedef struct GenDescStruct {
ZoneSet zones; /* zoneset for this generation */
Size capacity; /* capacity in kB */
double mortality;
double proflow; /* predicted proportion of survivors promoted */
RingStruct locusRing; /* Ring of all PoolGen's in this GenDesc (locus) */
} GenDescStruct;
@ -48,14 +47,15 @@ typedef struct PoolGenStruct {
GenDesc gen; /* generation this belongs to */
/* link in ring of all PoolGen's in this GenDesc (locus) */
RingStruct genRing;
Size totalSize; /* total size of segs in gen in this pool */
Size newSize; /* size allocated since last GC */
/* newSize when TraceCreate was called. This is used in the
* TraceStartPoolGen event emitted at the start of a trace; at that
* time, newSize has already been diminished by Whiten so we can't
* use that value. TODO: This will not work well with multiple
* traces. */
Size newSizeAtCreate;
/* Accounting of memory in this generation for this pool */
STATISTIC_DECL(Size segs); /* number of segments */
Size totalSize; /* total (sum of segment sizes) */
STATISTIC_DECL(Size freeSize); /* unused (free or lost to fragmentation) */
Size newSize; /* allocated since last collection */
STATISTIC_DECL(Size oldSize); /* allocated prior to last collection */
Size newDeferredSize; /* new (but deferred) */
STATISTIC_DECL(Size oldDeferredSize); /* old (but deferred) */
} PoolGenStruct;
@ -90,13 +90,22 @@ extern Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool);
extern void PoolGenFinish(PoolGen pgen);
extern Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class,
Size size, Bool withReservoirPermit, ArgList args);
extern void PoolGenFree(PoolGen pgen, Seg seg, Size freeSize, Size oldSize,
Size newSize, Bool deferred);
extern void PoolGenAccountForFill(PoolGen pgen, Size size, Bool deferred);
extern void PoolGenAccountForEmpty(PoolGen pgen, Size unused, Bool deferred);
extern void PoolGenAccountForAge(PoolGen pgen, Size aged, Bool deferred);
extern void PoolGenAccountForReclaim(PoolGen pgen, Size reclaimed, Bool deferred);
extern void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize);
extern void PoolGenAccountForSegSplit(PoolGen pgen);
extern void PoolGenAccountForSegMerge(PoolGen pgen);
#endif /* chain_h */
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2002 Ravenbrook Limited <http://www.ravenbrook.com/>.
* Copyright (C) 2001-2014 Ravenbrook Limited <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*

View file

@ -163,12 +163,54 @@ FMTDYTST = fmtdy.c fmtno.c fmtdytst.c
FMTHETST = fmthe.c fmtdy.c fmtno.c fmtdytst.c
FMTSCM = fmtscheme.c
PLINTH = mpsliban.c mpsioan.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 nailboard.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 \
failover.c \
format.c \
freelist.c \
global.c \
land.c \
ld.c \
locus.c \
message.c \
meter.c \
mpm.c \
mpsi.c \
nailboard.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) $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(PLINTH)
@ -221,11 +263,11 @@ TEST_TARGETS=\
djbench \
exposet0 \
expt825 \
fbmtest \
finalcv \
finaltest \
fotest \
gcbench \
landtest \
locbwcss \
lockcov \
lockut \
@ -292,17 +334,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
@ -403,9 +453,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
@ -418,6 +465,9 @@ $(PFM)/$(VARIETY)/fotest: $(PFM)/$(VARIETY)/fotest.o \
$(PFM)/$(VARIETY)/gcbench: $(PFM)/$(VARIETY)/gcbench.o \
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ)
$(PFM)/$(VARIETY)/landtest: $(PFM)/$(VARIETY)/landtest.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
$(PFM)/$(VARIETY)/locbwcss: $(PFM)/$(VARIETY)/locbwcss.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a

View file

@ -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 testci testall testansi testpoll: $(TEST_TARGETS)
!IFDEF VARIETY
..\tool\testrun.bat $(PFM) $(VARIETY) $@
!ELSE
$(MAKE) /nologo /f $(PFM).nmk VARIETY=hot $@
$(MAKE) /nologo /f $(PFM).nmk VARIETY=cool $@
$(MAKE) /nologo /f $(PFM).nmk VARIETY=hot $@
!ENDIF
@ -157,7 +157,7 @@ $(PFM)\$(VARIETY)\cvmicv.exe: $(PFM)\$(VARIETY)\cvmicv.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
$(PFM)\$(VARIETY)\djbench.exe: $(PFM)\$(VARIETY)\djbench.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ)
$(TESTLIBOBJ) $(TESTTHROBJ)
$(PFM)\$(VARIETY)\exposet0.exe: $(PFM)\$(VARIETY)\exposet0.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
@ -165,9 +165,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)
@ -178,7 +175,10 @@ $(PFM)\$(VARIETY)\fotest.exe: $(PFM)\$(VARIETY)\fotest.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
$(PFM)\$(VARIETY)\gcbench.exe: $(PFM)\$(VARIETY)\gcbench.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ)
$(FMTTESTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ)
$(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)

View file

@ -72,11 +72,11 @@ TEST_TARGETS=\
djbench.exe \
exposet0.exe \
expt825.exe \
fbmtest.exe \
finalcv.exe \
finaltest.exe \
fotest.exe \
gcbench.exe \
landtest.exe \
locbwcss.exe \
lockcov.exe \
lockut.exe \
@ -131,9 +131,11 @@ MPMCOMMON=\
<dbgpool> \
<dbgpooli> \
<event> \
<failover> \
<format> \
<freelist> \
<global> \
<land> \
<ld> \
<locus> \
<message> \

View file

@ -292,6 +292,11 @@
/* Attribute for functions that may be unused in some build configurations.
* GCC: <http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>
*
* 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__))
@ -354,6 +359,7 @@
/* Pool MV Configuration -- see <code/poolmv.c> */
#define MV_ALIGN_DEFAULT MPS_PF_ALIGN
#define MV_EXTEND_BY_DEFAULT ((Size)65536)
#define MV_AVG_SIZE_DEFAULT ((Size)32)
#define MV_MAX_SIZE_DEFAULT ((Size)65536)
@ -419,7 +425,7 @@
pool to be very heavily used. */
#define CONTROL_EXTEND_BY 4096
#define VM_ARENA_SIZE_DEFAULT ((Size)1 << 20)
#define VM_ARENA_SIZE_DEFAULT ((Size)1 << 28)
/* Stack configuration */

View file

@ -123,10 +123,14 @@ Bool PoolDebugOptionsCheck(PoolDebugOptions opt)
ARG_DEFINE_KEY(pool_debug_options, PoolDebugOptions);
static PoolDebugOptionsStruct debugPoolOptionsDefault = {
"POST", 4, "DEAD", 4,
};
static Res DebugPoolInit(Pool pool, ArgList args)
{
Res res;
PoolDebugOptions options;
PoolDebugOptions options = &debugPoolOptionsDefault;
PoolDebugMixin debug;
TagInitMethod tagInit;
Size tagSize;
@ -134,10 +138,8 @@ static Res DebugPoolInit(Pool pool, ArgList args)
AVERT(Pool, pool);
/* TODO: Split this structure into separate keyword arguments,
now that we can support them. */
ArgRequire(&arg, args, MPS_KEY_POOL_DEBUG_OPTIONS);
options = (PoolDebugOptions)arg.val.pool_debug_options;
if (ArgPick(&arg, args, MPS_KEY_POOL_DEBUG_OPTIONS))
options = (PoolDebugOptions)arg.val.pool_debug_options;
AVERT(PoolDebugOptions, options);
@ -158,10 +160,6 @@ static Res DebugPoolInit(Pool pool, ArgList args)
/* into Addr memory, to avoid breaking <design/type/#addr.use>. */
debug->fenceSize = options->fenceSize;
if (debug->fenceSize != 0) {
if (debug->fenceSize % PoolAlignment(pool) != 0) {
res = ResPARAM;
goto alignFail;
}
/* Fenceposting turns on tagging */
if (tagInit == NULL) {
tagSize = 0;
@ -176,10 +174,6 @@ static Res DebugPoolInit(Pool pool, ArgList args)
/* into Addr memory, to avoid breaking <design/type#addr.use>. */
debug->freeSize = options->freeSize;
if (debug->freeSize != 0) {
if (PoolAlignment(pool) % debug->freeSize != 0) {
res = ResPARAM;
goto alignFail;
}
debug->freeTemplate = options->freeTemplate;
}
@ -208,7 +202,6 @@ static Res DebugPoolInit(Pool pool, ArgList args)
return ResOK;
tagFail:
alignFail:
SuperclassOfPool(pool)->finish(pool);
AVER(res != ResOK);
return res;
@ -234,39 +227,150 @@ static void DebugPoolFinish(Pool pool)
}
/* freeSplat -- splat free block with splat pattern
/* patternIterate -- call visitor for occurrences of pattern between
* base and limit
*
* If base is in a segment, the whole block has to be in it.
* pattern is an arbitrary pattern that's size bytes long.
*
* Imagine that the entirety of memory were covered by contiguous
* copies of pattern starting at address 0. Then call visitor for each
* copy (or part) of pattern that lies between base and limit. In each
* call, target is the address of the copy or part (where base <=
* target < limit); source is the corresponding byte of the pattern
* (where pattern <= source < pattern + size); and size is the length
* of the copy or part.
*/
typedef Bool (*patternVisitor)(Addr target, ReadonlyAddr source, Size size);
static Bool patternIterate(ReadonlyAddr pattern, Size size,
Addr base, Addr limit, patternVisitor visitor)
{
Addr p;
AVER(pattern != NULL);
AVER(0 < size);
AVER(base != NULL);
AVER(base <= limit);
p = base;
while (p < limit) {
Addr end = AddrAdd(p, size);
Addr rounded = AddrRoundUp(p, size);
Size offset = (Word)p % size;
if (end < p || rounded < p) {
/* Address range overflow */
break;
} else if (p == rounded && end <= limit) {
/* Room for a whole copy */
if (!(*visitor)(p, pattern, size))
return FALSE;
p = end;
} else if (p < rounded && rounded <= end && rounded <= limit) {
/* Copy up to rounded */
if (!(*visitor)(p, ReadonlyAddrAdd(pattern, offset),
AddrOffset(p, rounded)))
return FALSE;
p = rounded;
} else {
/* Copy up to limit */
AVER(limit <= end && (p == rounded || limit <= rounded));
if (!(*visitor)(p, ReadonlyAddrAdd(pattern, offset),
AddrOffset(p, limit)))
return FALSE;
p = limit;
}
}
return TRUE;
}
/* patternCopy -- copy pattern to fill a range
*
* Fill the range of addresses from base (inclusive) to limit
* (exclusive) with copies of pattern (which is size bytes long).
*/
static Bool patternCopyVisitor(Addr target, ReadonlyAddr source, Size size)
{
(void)AddrCopy(target, source, size);
return TRUE;
}
static void patternCopy(ReadonlyAddr pattern, Size size, Addr base, Addr limit)
{
(void)patternIterate(pattern, size, base, limit, patternCopyVisitor);
}
/* patternCheck -- check pattern against a range
*
* Compare the range of addresses from base (inclusive) to limit
* (exclusive) with copies of pattern (which is size bytes long). The
* copies of pattern must be arranged so that fresh copies start at
* aligned addresses wherever possible.
*/
static Bool patternCheckVisitor(Addr target, ReadonlyAddr source, Size size)
{
return AddrComp(target, source, size) == 0;
}
static Bool patternCheck(ReadonlyAddr pattern, Size size, Addr base, Addr limit)
{
return patternIterate(pattern, size, base, limit, patternCheckVisitor);
}
/* debugPoolSegIterate -- iterate over a range of segments in an arena
*
* Expects to be called on a range corresponding to objects withing a
* single pool.
*
* NOTE: This relies on pools consistently using segments
* contiguously.
*/
static void debugPoolSegIterate(Arena arena, Addr base, Addr limit,
void (*visitor)(Arena, Seg))
{
Seg seg;
if (SegOfAddr(&seg, arena, base)) {
do {
base = SegLimit(seg);
(*visitor)(arena, seg);
} while (base < limit && SegOfAddr(&seg, arena, base));
AVER(base >= limit); /* shouldn't run out of segments */
}
}
static void debugPoolShieldExpose(Arena arena, Seg seg)
{
ShieldExpose(arena, seg);
}
static void debugPoolShieldCover(Arena arena, Seg seg)
{
ShieldCover(arena, seg);
}
/* freeSplat -- splat free block with splat pattern */
static void freeSplat(PoolDebugMixin debug, Pool pool, Addr base, Addr limit)
{
Addr p, next;
Size freeSize = debug->freeSize;
Arena arena;
Seg seg = NULL; /* suppress "may be used uninitialized" */
Bool inSeg;
AVER(base < limit);
/* If the block is in a segment, make sure any shield is up. */
/* If the block is in one or more segments, make sure the segments
are exposed so that we can overwrite the block with the pattern. */
arena = PoolArena(pool);
inSeg = SegOfAddr(&seg, arena, base);
if (inSeg) {
AVER(limit <= SegLimit(seg));
ShieldExpose(arena, seg);
}
/* Write as many copies of the template as fit in the block. */
for (p = base, next = AddrAdd(p, freeSize);
next <= limit && p < next /* watch out for overflow in next */;
p = next, next = AddrAdd(next, freeSize))
(void)AddrCopy(p, debug->freeTemplate, freeSize);
/* Fill the tail of the block with a partial copy of the template. */
if (next > limit || next < p)
(void)AddrCopy(p, debug->freeTemplate, AddrOffset(p, limit));
if (inSeg) {
ShieldCover(arena, seg);
}
debugPoolSegIterate(arena, base, limit, debugPoolShieldExpose);
patternCopy(debug->freeTemplate, debug->freeSize, base, limit);
debugPoolSegIterate(arena, base, limit, debugPoolShieldCover);
}
@ -274,41 +378,17 @@ static void freeSplat(PoolDebugMixin debug, Pool pool, Addr base, Addr limit)
static Bool freeCheck(PoolDebugMixin debug, Pool pool, Addr base, Addr limit)
{
Addr p, next;
Size freeSize = debug->freeSize;
Res res;
Bool res;
Arena arena;
Seg seg = NULL; /* suppress "may be used uninitialized" */
Bool inSeg;
AVER(base < limit);
/* If the block is in a segment, make sure any shield is up. */
/* If the block is in one or more segments, make sure the segments
are exposed so we can read the pattern. */
arena = PoolArena(pool);
inSeg = SegOfAddr(&seg, arena, base);
if (inSeg) {
AVER(limit <= SegLimit(seg));
ShieldExpose(arena, seg);
}
/* Compare this to the AddrCopys in freeSplat. */
/* Check the complete copies of the template in the block. */
for (p = base, next = AddrAdd(p, freeSize);
next <= limit && p < next /* watch out for overflow in next */;
p = next, next = AddrAdd(next, freeSize))
if (AddrComp(p, debug->freeTemplate, freeSize) != 0) {
res = FALSE; goto done;
}
/* Check the partial copy of the template at the tail of the block. */
if (next > limit || next < p)
if (AddrComp(p, debug->freeTemplate, AddrOffset(p, limit)) != 0) {
res = FALSE; goto done;
}
res = TRUE;
done:
if (inSeg) {
ShieldCover(arena, seg);
}
debugPoolSegIterate(arena, base, limit, debugPoolShieldExpose);
res = patternCheck(debug->freeTemplate, debug->freeSize, base, limit);
debugPoolSegIterate(arena, base, limit, debugPoolShieldCover);
return res;
}
@ -354,63 +434,75 @@ static void freeCheckFree(PoolDebugMixin debug,
* start fp client object slop end fp
*
* slop is the extra allocation from rounding up the client request to
* the pool's alignment. The fenceposting code does this, so there's a
* better chance of the end fencepost being flush with the next object
* (can't be guaranteed, since the underlying pool could have allocated
* an even larger block). The alignment slop is filled from the
* fencepost template as well (as much as fits, .fence.size guarantees
* the template is larger).
* the pool's alignment. The fenceposting code adds this slop so that
* there's a better chance of the end fencepost being flush with the
* next object (though it can't be guaranteed, since the underlying
* pool could have allocated an even larger block). The alignment slop
* is filled from the fencepost template as well.
*
* Keep in sync with fenceCheck.
*/
static Res fenceAlloc(Addr *aReturn, PoolDebugMixin debug, Pool pool,
Size size, Bool withReservoir)
{
Res res;
Addr new, clientNew;
Size alignedSize;
Addr obj, startFence, clientNew, clientLimit, limit;
Size alignedFenceSize, alignedSize;
AVER(aReturn != NULL);
AVERT(PoolDebugMixin, debug);
AVERT(Pool, pool);
alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool));
alignedSize = SizeAlignUp(size, PoolAlignment(pool));
res = freeCheckAlloc(&new, debug, pool, alignedSize + 2*debug->fenceSize,
res = freeCheckAlloc(&obj, debug, pool,
alignedSize + 2 * alignedFenceSize,
withReservoir);
if (res != ResOK)
return res;
clientNew = AddrAdd(new, debug->fenceSize);
startFence = obj;
clientNew = AddrAdd(startFence, alignedFenceSize);
clientLimit = AddrAdd(clientNew, size);
limit = AddrAdd(clientNew, alignedSize + alignedFenceSize);
/* @@@@ shields? */
/* start fencepost */
(void)AddrCopy(new, debug->fenceTemplate, debug->fenceSize);
/* alignment slop */
(void)AddrCopy(AddrAdd(clientNew, size),
debug->fenceTemplate, alignedSize - size);
/* end fencepost */
(void)AddrCopy(AddrAdd(clientNew, alignedSize),
debug->fenceTemplate, debug->fenceSize);
patternCopy(debug->fenceTemplate, debug->fenceSize, startFence, clientNew);
patternCopy(debug->fenceTemplate, debug->fenceSize, clientLimit, limit);
*aReturn = clientNew;
return res;
return ResOK;
}
/* fenceCheck -- check fences of an object */
/* fenceCheck -- check fences of an object
*
* Keep in sync with fenceAlloc.
*/
static Bool fenceCheck(PoolDebugMixin debug, Pool pool, Addr obj, Size size)
{
Size alignedSize;
Addr startFence, clientNew, clientLimit, limit;
Size alignedFenceSize, alignedSize;
AVERT_CRITICAL(PoolDebugMixin, debug);
AVERT_CRITICAL(Pool, pool);
/* Can't check obj */
alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool));
alignedSize = SizeAlignUp(size, PoolAlignment(pool));
startFence = AddrSub(obj, alignedFenceSize);
clientNew = obj;
clientLimit = AddrAdd(clientNew, size);
limit = AddrAdd(clientNew, alignedSize + alignedFenceSize);
/* @@@@ shields? */
/* Compare this to the AddrCopys in fenceAlloc */
return (AddrComp(AddrSub(obj, debug->fenceSize), debug->fenceTemplate,
debug->fenceSize) == 0
&& AddrComp(AddrAdd(obj, size), debug->fenceTemplate,
alignedSize - size) == 0
&& AddrComp(AddrAdd(obj, alignedSize), debug->fenceTemplate,
debug->fenceSize) == 0);
return patternCheck(debug->fenceTemplate, debug->fenceSize,
startFence, clientNew)
&& patternCheck(debug->fenceTemplate, debug->fenceSize,
clientLimit, limit);
}
@ -419,13 +511,14 @@ static Bool fenceCheck(PoolDebugMixin debug, Pool pool, Addr obj, Size size)
static void fenceFree(PoolDebugMixin debug,
Pool pool, Addr old, Size size)
{
Size alignedSize;
Size alignedFenceSize, alignedSize;
ASSERT(fenceCheck(debug, pool, old, size), "fencepost check on free");
alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool));
alignedSize = SizeAlignUp(size, PoolAlignment(pool));
freeCheckFree(debug, pool, AddrSub(old, debug->fenceSize),
alignedSize + 2*debug->fenceSize);
freeCheckFree(debug, pool, AddrSub(old, alignedFenceSize),
alignedSize + 2 * alignedFenceSize);
}

View file

@ -26,9 +26,9 @@ typedef void (*TagInitMethod)(void* tag, va_list args);
*/
typedef struct PoolDebugOptionsStruct {
void* fenceTemplate;
const void *fenceTemplate;
Size fenceSize;
void* freeTemplate;
const void *freeTemplate;
Size freeSize;
/* TagInitMethod tagInit; */
/* Size tagSize; */
@ -43,9 +43,9 @@ typedef PoolDebugOptionsStruct *PoolDebugOptions;
typedef struct PoolDebugMixinStruct {
Sig sig;
Addr fenceTemplate;
const struct AddrStruct *fenceTemplate;
Size fenceSize;
Addr freeTemplate;
const struct AddrStruct *freeTemplate;
Size freeSize;
TagInitMethod tagInit;
Size tagSize;

View file

@ -48,6 +48,7 @@ static double pact = 0.2; /* probability per pass of acting */
static unsigned rinter = 75; /* pass interval for recursion */
static unsigned rmax = 10; /* maximum recursion depth */
static mps_bool_t zoned = TRUE; /* arena allocates using zones */
static size_t arenasize = 256ul * 1024 * 1024; /* arena size */
#define DJRUN(fname, alloc, free) \
static unsigned fname##_inner(mps_ap_t ap, unsigned depth, unsigned r) { \
@ -177,7 +178,7 @@ static void wrap(dj_t dj, mps_class_t dummy, const char *name)
static void arena_wrap(dj_t dj, mps_class_t pool_class, const char *name)
{
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 256ul * 1024 * 1024); /* FIXME: Why is there no default? */
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arenasize);
MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, zoned);
DJMUST(mps_arena_create_k(&arena, mps_arena_class_vm(), args));
} MPS_ARGS_END(args);
@ -201,6 +202,7 @@ static struct option longopts[] = {
{"rinter", required_argument, NULL, 'r'},
{"rmax", required_argument, NULL, 'd'},
{"seed", required_argument, NULL, 'x'},
{"arena-size", required_argument, NULL, 'm'},
{"arena-unzoned", no_argument, NULL, 'z'},
{NULL, 0, NULL, 0}
};
@ -235,7 +237,7 @@ int main(int argc, char *argv[]) {
seed = rnd_seed();
while ((ch = getopt_long(argc, argv, "ht:i:p:b:s:a:r:d:x:z", longopts, NULL)) != -1)
while ((ch = getopt_long(argc, argv, "ht:i:p:b:s:a:r:d:m:x:z", longopts, NULL)) != -1)
switch (ch) {
case 't':
nthreads = (unsigned)strtoul(optarg, NULL, 10);
@ -267,6 +269,20 @@ int main(int argc, char *argv[]) {
case 'z':
zoned = FALSE;
break;
case 'm': {
char *p;
arenasize = (unsigned)strtoul(optarg, &p, 10);
switch(toupper(*p)) {
case 'G': arenasize <<= 30; break;
case 'M': arenasize <<= 20; break;
case 'K': arenasize <<= 10; break;
case '\0': break;
default:
fprintf(stderr, "Bad arena size %s\n", optarg);
return EXIT_FAILURE;
}
}
break;
default:
fprintf(stderr,
"Usage: %s [option...] [test...]\n"

View file

@ -95,7 +95,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) \
@ -187,7 +187,7 @@
EVENT(X, VMCompact , 0x0079, TRUE, Arena) \
EVENT(X, amcScanNailed , 0x0080, TRUE, Seg) \
EVENT(X, AMCTraceEnd , 0x0081, TRUE, Trace) \
EVENT(X, TraceStartPoolGen , 0x0082, TRUE, Trace) \
EVENT(X, TraceCreatePoolGen , 0x0082, TRUE, Trace) \
/* new events for performance analysis of large heaps. */ \
EVENT(X, TraceCondemnZones , 0x0083, TRUE, Trace) \
EVENT(X, ArenaGenZoneAdd , 0x0084, TRUE, Arena) \
@ -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) \
@ -713,17 +713,18 @@
PARAM(X, 19, W, pRL) \
PARAM(X, 20, W, pRLr)
#define EVENT_TraceStartPoolGen_PARAMS(PARAM, X) \
PARAM(X, 0, P, chain) /* chain (or NULL for topGen) */ \
PARAM(X, 1, B, top) /* 1 for topGen, 0 otherwise */ \
PARAM(X, 2, W, index) /* index of generation in the chain */ \
PARAM(X, 3, P, gendesc) /* generation description */ \
PARAM(X, 4, W, capacity) /* capacity of generation */ \
PARAM(X, 5, D, mortality) /* mortality of generation */ \
PARAM(X, 6, W, zone) /* zone set of generation */ \
PARAM(X, 7, P, pool) /* pool */ \
PARAM(X, 8, W, totalSize) /* total size of pool gen */ \
PARAM(X, 9, W, newSizeAtCreate) /* new size of pool gen at trace create */
#define EVENT_TraceCreatePoolGen_PARAMS(PARAM, X) \
PARAM(X, 0, P, gendesc) /* generation description */ \
PARAM(X, 1, W, capacity) /* capacity of generation */ \
PARAM(X, 2, D, mortality) /* mortality of generation */ \
PARAM(X, 3, W, zone) /* zone set of generation */ \
PARAM(X, 4, P, pool) /* pool */ \
PARAM(X, 5, W, totalSize) /* total size of pool gen */ \
PARAM(X, 6, W, freeSize) /* free size of pool gen */ \
PARAM(X, 7, W, newSize) /* new size of pool gen */ \
PARAM(X, 8, W, oldSize) /* old size of pool gen */ \
PARAM(X, 9, W, newDeferredSize) /* new size (deferred) of pool gen */ \
PARAM(X, 10, W, oldDeferredSize) /* old size (deferred) of pool gen */
#define EVENT_TraceCondemnZones_PARAMS(PARAM, X) \
PARAM(X, 0, P, trace) /* the trace */ \

360
mps/code/failover.c Normal file
View file

@ -0,0 +1,360 @@
/* failover.c: FAILOVER IMPLEMENTATION
*
* $Id$
* Copyright (c) 2014 Ravenbrook Limited. See end of file for license.
*
* .design: <design/failover/>
*/
#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 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;
Res res;
AVER(rangeReturn != NULL);
AVERT(Land, land);
fo = failoverOfLand(land);
AVERT(Failover, fo);
AVERT(Range, range);
/* Provide more opportunities for coalescence. See
* <design/failover/#impl.assume.flush>.
*/
(void)LandFlush(fo->primary, fo->secondary);
res = LandInsert(rangeReturn, fo->primary, range);
if (res != ResOK && res != ResFAIL)
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
* <design/failover/#impl.assume.flush>.
*/
(void)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 (res != ResOK) {
/* 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
* <design/failover/#impl.assume.delete>.
*/
res = LandDelete(&dummyRange, fo->primary, &oldRange);
if (res != ResOK)
return res;
AVER(RangesEqual(&oldRange, &dummyRange));
RangeInit(&left, RangeBase(&oldRange), RangeBase(range));
if (!RangeIsEmpty(&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) {
/* The range was successful deleted from the primary above. */
AVER(res != ResFAIL);
res = LandInsert(&dummyRange, fo->secondary, &left);
AVER(res == ResOK);
}
}
RangeInit(&right, RangeLimit(range), RangeLimit(&oldRange));
if (!RangeIsEmpty(&right)) {
res = LandInsert(&dummyRange, fo->primary, &right);
if (res != ResOK) {
/* The range was successful deleted from the primary above. */
AVER(res != ResFAIL);
res = LandInsert(&dummyRange, fo->secondary, &right);
AVER(res == ResOK);
}
}
}
if (res == ResOK) {
AVER(RangesNest(&oldRange, range));
RangeCopy(rangeReturn, &oldRange);
}
return res;
}
static Bool failoverIterate(Land land, LandVisitor visitor, void *closureP, Size closureS)
{
Failover fo;
AVERT(Land, land);
fo = failoverOfLand(land);
AVERT(Failover, fo);
AVER(visitor != NULL);
return 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 <design/failover/#impl.assume.flush>. */
(void)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 <design/failover/#impl.assume.flush>. */
(void)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 <design/failover/#impl.assume.flush>. */
(void)LandFlush(fo->primary, fo->secondary);
return LandFindLargest(rangeReturn, oldRangeReturn, fo->primary, size, findDelete)
|| LandFindLargest(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete);
}
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(FALSE); /* TODO: this code is completely untested! */
AVER(foundReturn != NULL);
AVER(rangeReturn != NULL);
AVER(oldRangeReturn != NULL);
AVERT(Land, land);
fo = failoverOfLand(land);
AVERT(Failover, fo);
/* AVERT(ZoneSet, zoneSet); */
AVERT(Bool, high);
/* See <design/failover/#impl.assume.flush>. */
(void)LandFlush(fo->primary, fo->secondary);
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;
}
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->sizeMethod = failoverSize;
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 <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Redistributions in any form must be accompanied by information on how
* to obtain complete source code for this software and any accompanying
* software that uses this software. The source code must either be
* included in the distribution or be available for no more than the cost
* of distribution plus a nominal fee, and must be freely redistributable
* under reasonable conditions. For an executable file, complete source
* code means the source code for all modules it contains. It does not
* include source code for modules or files that typically accompany the
* major components of the operating system on which the executable file
* runs.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

69
mps/code/failover.h Normal file
View file

@ -0,0 +1,69 @@
/* failover.h: FAILOVER ALLOCATOR INTERFACE
*
* $Id$
* Copyright (c) 2014 Ravenbrook Limited. See end of file for license.
*
* .source: <design/failover/>.
*/
#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 <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Redistributions in any form must be accompanied by information on how
* to obtain complete source code for this software and any accompanying
* software that uses this software. The source code must either be
* included in the distribution or be available for no more than the cost
* of distribution plus a nominal fee, and must be freely redistributable
* under reasonable conditions. For an executable file, complete source
* code means the source code for all modules it contains. It does not
* include source code for modules or files that typically accompany the
* major components of the operating system on which the executable file
* runs.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

View file

@ -21,7 +21,6 @@
#include "mpm.h"
#include "mps.h"
#include "mpsavm.h"
#include "mpstd.h"
#include "testlib.h"
#include <stdio.h> /* printf */
@ -98,6 +97,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 +149,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");

View file

@ -255,8 +255,8 @@ static void test_mode(int mode, mps_arena_t arena, mps_chain_t chain)
test_pool(mode, arena, chain, mps_class_amc());
test_pool(mode, arena, chain, mps_class_amcz());
test_pool(mode, arena, chain, mps_class_ams());
/* test_pool(mode, arena, chain, mps_class_lo()); TODO: job003773 */
/* test_pool(mode, arena, chain, mps_class_awl()); TODO: job003772 */
test_pool(mode, arena, chain, mps_class_awl());
test_pool(mode, arena, chain, mps_class_lo());
}

View file

@ -38,28 +38,36 @@
/* 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(Pool);
extern Land _mps_mvt_cbs(Pool);
/* "OOM" pool class -- dummy alloc/free pool class whose alloc()
* method always returns ResMEMORY */
* method always fails and whose free method does nothing. */
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() % 3) {
case 0:
return ResRESOURCE;
case 1:
return ResMEMORY;
default:
return ResCOMMIT_LIMIT;
}
}
extern PoolClass PoolClassOOM(void);
extern PoolClass OOMPoolClassGet(void);
DEFINE_POOL_CLASS(OOMPoolClass, this)
{
INHERIT_CLASS(this, AbstractAllocFreePoolClass);
this->alloc = OOMAlloc;
INHERIT_CLASS(this, AbstractPoolClass);
this->alloc = oomAlloc;
this->free = PoolTrivFree;
this->size = sizeof(PoolStruct);
AVERT(PoolClass, this);
}
@ -83,16 +91,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;
@ -182,8 +191,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 = _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);
@ -201,8 +210,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 = _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);

View file

@ -6,32 +6,58 @@
* .sources: <design/freelist/>.
*/
#include "cbs.h"
#include "freelist.h"
#include "mpm.h"
#include "range.h"
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;
FreelistBlock next; /* not tagged (low bit 0) */
Addr limit;
} large;
} FreelistBlockUnion;
/* See <design/freelist/#impl.grain.align> */
#define freelistMinimumAlignment ((Align)sizeof(FreelistBlock))
/* freelistEND -- the end of a list
*
* The end of a list should not be represented with NULL, as this is
* 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).
*/
#define freelistEND ((FreelistBlock)0)
/* 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))))
@ -51,7 +77,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;
}
@ -65,7 +91,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);
@ -73,8 +99,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)
{
@ -106,7 +132,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);
@ -129,12 +155,12 @@ 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);
block->small.next = FreelistTagSet(freelistEND);
FreelistBlockSetLimit(fl, block, limit);
AVERT(FreelistBlock, block);
return block;
@ -143,23 +169,39 @@ static FreelistBlock FreelistBlockInit(Freelist fl, Addr base, Addr limit)
Bool FreelistCheck(Freelist fl)
{
Land land;
CHECKS(Freelist, fl);
land = &fl->landStruct;
CHECKD(Land, land);
/* See <design/freelist/#impl.grain.align> */
CHECKL(AlignIsAligned(fl->alignment, freelistMinimumAlignment));
CHECKL((fl->list == NULL) == (fl->listSize == 0));
CHECKL(AlignIsAligned(freelistAlignment(fl), FreelistMinimumAlignment));
CHECKL((fl->list == freelistEND) == (fl->listSize == 0));
CHECKL((fl->list == freelistEND) == (fl->size == 0));
CHECKL(SizeIsAligned(fl->size, freelistAlignment(fl)));
return TRUE;
}
Res FreelistInit(Freelist fl, Align alignment)
static Res freelistInit(Land land, ArgList args)
{
/* See <design/freelist/#impl.grain> */
if (!AlignIsAligned(alignment, freelistMinimumAlignment))
return ResPARAM;
Freelist fl;
LandClass super;
Res res;
fl->alignment = alignment;
fl->list = NULL;
AVERT(Land, land);
super = LAND_SUPERCLASS(FreelistLandClass);
res = (*super->init)(land, args);
if (res != ResOK)
return res;
/* See <design/freelist/#impl.grain> */
AVER(AlignIsAligned(LandAlignment(land), FreelistMinimumAlignment));
fl = freelistOfLand(land);
fl->list = freelistEND;
fl->listSize = 0;
fl->size = 0;
fl->sig = FreelistSig;
AVERT(Freelist, fl);
@ -167,31 +209,56 @@ 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;
fl->list = freelistEND;
}
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.
* 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'.
* 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,
FreelistBlock next, int delta)
{
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);
@ -202,29 +269,32 @@ 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);
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;
@ -235,8 +305,8 @@ Res FreelistInsert(Range rangeReturn, Freelist fl, 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);
@ -262,17 +332,20 @@ Res FreelistInsert(Range rangeReturn, Freelist fl, Range range)
freelistBlockSetPrevNext(fl, prev, new, +1);
}
fl->size += RangeSize(range);
RangeInit(rangeReturn, base, limit);
return ResOK;
}
/* 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 freelistEND if block is the
* first block on the list.
*/
static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl,
Range range, FreelistBlock prev,
FreelistBlock block)
@ -283,8 +356,8 @@ static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl,
AVER(rangeReturn != NULL);
AVERT(Freelist, fl);
AVERT(Range, range);
AVER(RangeIsAligned(range, fl->alignment));
AVER(prev == NULL || FreelistBlockNext(prev) == block);
AVER(RangeIsAligned(range, freelistAlignment(fl)));
AVER(prev == freelistEND || FreelistBlockNext(prev) == block);
AVERT(FreelistBlock, block);
AVER(FreelistBlockBase(block) <= RangeBase(range));
AVER(RangeLimit(range) <= FreelistBlockLimit(fl, block));
@ -317,25 +390,30 @@ 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);
}
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);
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);
@ -359,43 +437,82 @@ Res FreelistDelete(Range rangeReturn, Freelist fl, Range range)
}
void FreelistIterate(Freelist fl, FreelistIterateMethod iterate,
void *closureP, Size closureS)
static Bool freelistIterate(Land land, LandVisitor visitor,
void *closureP, Size closureS)
{
FreelistBlock prev, cur, next;
Freelist fl;
FreelistBlock cur, next;
AVERT(Land, land);
fl = freelistOfLand(land);
AVERT(Freelist, fl);
AVER(FUNCHECK(iterate));
AVER(FUNCHECK(visitor));
/* closureP and closureS are arbitrary */
prev = NULL;
cur = fl->list;
while (cur) {
Bool delete = FALSE;
for (cur = fl->list; cur != freelistEND; cur = next) {
RangeStruct range;
Bool cont;
RangeInit(&range, FreelistBlockBase(cur), FreelistBlockLimit(fl, cur));
cont = (*iterate)(&delete, &range, closureP, closureS);
/* .next.first: Take next before calling the visitor, in case the
* visitor touches the block. */
next = FreelistBlockNext(cur);
if (delete) {
freelistBlockSetPrevNext(fl, prev, next, -1);
} else {
prev = cur;
}
cur = next;
RangeInit(&range, FreelistBlockBase(cur), FreelistBlockLimit(fl, cur));
cont = (*visitor)(land, &range, closureP, closureS);
if (!cont)
break;
return FALSE;
}
return TRUE;
}
/* 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.
static Bool freelistIterateAndDelete(Land land, LandDeleteVisitor visitor,
void *closureP, Size closureS)
{
Freelist fl;
FreelistBlock prev, cur, next;
AVERT(Land, land);
fl = freelistOfLand(land);
AVERT(Freelist, fl);
AVER(FUNCHECK(visitor));
/* closureP and closureS are arbitrary */
prev = freelistEND;
cur = fl->list;
while (cur != freelistEND) {
Bool delete = FALSE;
RangeStruct range;
Bool cont;
Size size;
next = FreelistBlockNext(cur); /* See .next.first. */
size = FreelistBlockSize(fl, cur);
RangeInit(&range, FreelistBlockBase(cur), FreelistBlockLimit(fl, cur));
cont = (*visitor)(&delete, land, &range, closureP, closureS);
if (delete) {
freelistBlockSetPrevNext(fl, prev, next, -1);
AVER(fl->size >= size);
fl->size -= size;
} else {
prev = cur;
}
if (!cont)
return FALSE;
cur = next;
}
return TRUE;
}
/* 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 freelistEND if block is the first
* block in the list.
*/
static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn,
Freelist fl, Size size,
FindDelete findDelete,
@ -407,9 +524,9 @@ 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);
AVER(prev == freelistEND || FreelistBlockNext(prev) == block);
AVERT(FreelistBlock, block);
AVER(FreelistBlockSize(fl, block) >= size);
@ -447,20 +564,23 @@ 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;
prev = freelistEND;
cur = fl->list;
while (cur) {
while (cur != freelistEND) {
if (FreelistBlockSize(fl, cur) >= size) {
freelistFindDeleteFromBlock(rangeReturn, oldRangeReturn, fl, size,
findDelete, prev, cur);
@ -475,22 +595,25 @@ 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;
FreelistBlock foundPrev = freelistEND, foundCur = freelistEND;
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;
prev = freelistEND;
cur = fl->list;
while (cur) {
while (cur != freelistEND) {
if (FreelistBlockSize(fl, cur) >= size) {
found = TRUE;
foundPrev = prev;
@ -509,21 +632,24 @@ 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;
FreelistBlock bestPrev = freelistEND, bestCur = freelistEND;
AVER(rangeReturn != NULL);
AVER(oldRangeReturn != NULL);
AVERT(Land, land);
fl = freelistOfLand(land);
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);
@ -543,20 +669,90 @@ Bool FreelistFindLargest(Range rangeReturn, Range oldRangeReturn,
}
/* freelistDescribeIterateMethod -- Iterate method for
* FreelistDescribe. Writes a decription of the range into the stream
* pointed to by 'closureP'.
static Res freelistFindInZones(Bool *foundReturn, 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 = freelistEND, foundCur = freelistEND;
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 ? freelistFindLast : freelistFindFirst;
search = high ? RangeInZoneSetLast : RangeInZoneSetFirst;
if (zoneSet == ZoneSetEMPTY)
goto fail;
if (zoneSet == ZoneSetUNIV) {
FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW;
*foundReturn = (*landFind)(rangeReturn, oldRangeReturn, land, size, fd);
return ResOK;
}
if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land)))
goto fail;
prev = freelistEND;
cur = fl->list;
while (cur != freelistEND) {
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)
goto fail;
freelistDeleteFromBlock(oldRangeReturn, fl, &foundRange, foundPrev, foundCur);
RangeCopy(rangeReturn, &foundRange);
*foundReturn = TRUE;
return ResOK;
fail:
*foundReturn = FALSE;
return ResOK;
}
/* 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(Land land, Range range,
void *closureP, Size closureS)
{
Res res;
mps_lib_FILE *stream = closureP;
AVER(deleteReturn != NULL);
AVERT(Range, range);
AVER(stream != NULL);
UNUSED(closureS);
if (!TESTT(Land, land)) return FALSE;
if (!RangeCheck(range)) return FALSE;
if (stream == NULL) return FALSE;
if (closureS != UNUSED_SIZE) return FALSE;
res = WriteF(stream,
" [$P,", (WriteFP)RangeBase(range),
@ -564,64 +760,52 @@ 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;
Bool b;
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);
b = LandIterate(land, freelistDescribeVisitor, stream, UNUSED_SIZE);
if (!b) return ResFAIL;
res = WriteF(stream, "}\n", NULL);
return res;
}
/* freelistFlushIterateMethod -- Iterate method for
* FreelistFlushToCBS. Attempst to insert the range into the CBS.
*/
static Bool freelistFlushIterateMethod(Bool *deleteReturn, Range range,
void *closureP, Size closureS)
DEFINE_LAND_CLASS(FreelistLandClass, class)
{
Res res;
RangeStruct newRange;
CBS cbs;
AVER(deleteReturn != NULL);
AVERT(Range, range);
AVER(closureP != NULL);
UNUSED(closureS);
cbs = closureP;
res = CBSInsert(&newRange, cbs, range);
if (res == ResOK) {
*deleteReturn = TRUE;
return TRUE;
} else {
*deleteReturn = FALSE;
return FALSE;
}
}
void FreelistFlushToCBS(Freelist fl, CBS cbs)
{
AVERT(Freelist, fl);
AVERT(CBS, cbs);
FreelistIterate(fl, freelistFlushIterateMethod, cbs, 0);
INHERIT_CLASS(class, LandClass);
class->name = "FREELIST";
class->size = sizeof(FreelistStruct);
class->init = freelistInit;
class->finish = freelistFinish;
class->sizeMethod = freelistSize;
class->insert = freelistInsert;
class->delete = freelistDelete;
class->iterate = freelistIterate;
class->iterateAndDelete = freelistIterateAndDelete;
class->findFirst = freelistFindFirst;
class->findLast = freelistFindLast;
class->findLargest = freelistFindLargest;
class->findInZones = freelistFindInZones;
class->describe = freelistDescribe;
AVERT(LandClass, class);
}

View file

@ -1,7 +1,7 @@
/* freelist.h: FREE LIST ALLOCATOR INTERFACE
*
* $Id$
* Copyright (c) 2013 Ravenbrook Limited. See end of file for license.
* Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license.
*
* .source: <design/freelist/>.
*/
@ -9,51 +9,23 @@
#ifndef freelist_h
#define freelist_h
#include "cbs.h"
#include "mpmtypes.h"
#include "range.h"
#define FreelistSig ((Sig)0x519F6331) /* SIGnature FREEL */
typedef struct FreelistStruct *Freelist;
typedef union FreelistBlockUnion *FreelistBlock;
typedef Bool (*FreelistIterateMethod)(Bool *deleteReturn, Range range,
void *closureP, Size closureS);
extern Bool FreelistCheck(Freelist freelist);
typedef struct FreelistStruct {
Sig sig;
Align alignment;
FreelistBlock list;
Count listSize;
} FreelistStruct;
/* See <design/freelist/#impl.grain.align> */
#define FreelistMinimumAlignment ((Align)sizeof(FreelistBlock))
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 FreelistFlushToCBS(Freelist fl, CBS cbs);
extern LandClass FreelistLandClassGet(void);
#endif /* freelist.h */
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2013 Ravenbrook Limited <http://www.ravenbrook.com/>.
* Copyright (C) 2013-2014 Ravenbrook Limited <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*

642
mps/code/land.c Normal file
View file

@ -0,0 +1,642 @@
/* 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: <design/land/>
*/
#include "mpm.h"
#include "range.h"
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); /* <code/mpm.c#check.unused> */
return TRUE;
}
/* landEnter, landLeave -- Avoid re-entrance
*
* .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.
*/
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));
return TRUE;
}
/* LandInit -- initialize land
*
* See <design/land/#function.init>
*/
Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args)
{
Res res;
AVER(land != NULL);
AVERT(LandClass, class);
AVERT(Align, alignment);
land->inLand = TRUE;
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);
landLeave(land);
return ResOK;
failInit:
land->sig = SigInvalid;
return res;
}
/* LandCreate -- allocate and initialize land
*
* See <design/land/#function.create>
*/
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;
}
/* LandDestroy -- finish and deallocate land
*
* See <design/land/#function.destroy>
*/
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);
}
/* LandFinish -- finish land
*
* See <design/land/#function.finish>
*/
void LandFinish(Land land)
{
AVERT(Land, land);
landEnter(land);
(*land->class->finish)(land);
land->sig = SigInvalid;
}
/* LandSize -- return the total size of ranges in land
*
* See <design/land/#function.size>
*/
Size LandSize(Land land)
{
/* .enter-leave.simple */
AVERT(Land, land);
return (*land->class->sizeMethod)(land);
}
/* LandInsert -- insert range of addresses into land
*
* See <design/land/#function.insert>
*/
Res LandInsert(Range rangeReturn, Land land, Range range)
{
Res res;
AVER(rangeReturn != NULL);
AVERT(Land, land);
AVERT(Range, range);
AVER(RangeIsAligned(range, land->alignment));
landEnter(land);
res = (*land->class->insert)(rangeReturn, land, range);
landLeave(land);
return res;
}
/* LandDelete -- delete range of addresses from land
*
* See <design/land/#function.delete>
*/
Res LandDelete(Range rangeReturn, Land land, Range range)
{
Res res;
AVER(rangeReturn != NULL);
AVERT(Land, land);
AVERT(Range, range);
AVER(RangeIsAligned(range, land->alignment));
landEnter(land);
res = (*land->class->delete)(rangeReturn, land, range);
landLeave(land);
return res;
}
/* LandIterate -- iterate over isolated ranges of addresses in land
*
* See <design/land/#function.iterate>
*/
Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS)
{
Bool b;
AVERT(Land, land);
AVER(FUNCHECK(visitor));
landEnter(land);
b = (*land->class->iterate)(land, visitor, closureP, closureS);
landLeave(land);
return b;
}
/* LandIterateAndDelete -- iterate over isolated ranges of addresses
* in land, deleting some of them
*
* See <design/land/#function.iterate.and.delete>
*/
Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS)
{
Bool b;
AVERT(Land, land);
AVER(FUNCHECK(visitor));
landEnter(land);
b = (*land->class->iterateAndDelete)(land, visitor, closureP, closureS);
landLeave(land);
return b;
}
/* LandFindFirst -- find first range of given size
*
* See <design/land/#function.find.first>
*/
Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
{
Bool b;
AVER(rangeReturn != NULL);
AVER(oldRangeReturn != NULL);
AVERT(Land, land);
AVER(SizeIsAligned(size, land->alignment));
AVER(FindDeleteCheck(findDelete));
landEnter(land);
b = (*land->class->findFirst)(rangeReturn, oldRangeReturn, land, size,
findDelete);
landLeave(land);
return b;
}
/* LandFindLast -- find last range of given size
*
* See <design/land/#function.find.last>
*/
Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
{
Bool b;
AVER(rangeReturn != NULL);
AVER(oldRangeReturn != NULL);
AVERT(Land, land);
AVER(SizeIsAligned(size, land->alignment));
AVER(FindDeleteCheck(findDelete));
landEnter(land);
b = (*land->class->findLast)(rangeReturn, oldRangeReturn, land, size,
findDelete);
landLeave(land);
return b;
}
/* LandFindLargest -- find largest range of at least given size
*
* See <design/land/#function.find.largest>
*/
Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
{
Bool b;
AVER(rangeReturn != NULL);
AVER(oldRangeReturn != NULL);
AVERT(Land, land);
AVER(SizeIsAligned(size, land->alignment));
AVER(FindDeleteCheck(findDelete));
landEnter(land);
b = (*land->class->findLargest)(rangeReturn, oldRangeReturn, land, size,
findDelete);
landLeave(land);
return b;
}
/* LandFindInSize -- find range of given size in set of zones
*
* See <design/land/#function.find.zones>
*/
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);
AVER(SizeIsAligned(size, land->alignment));
/* AVER(ZoneSet, zoneSet); */
AVERT(Bool, high);
landEnter(land);
res = (*land->class->findInZones)(foundReturn, rangeReturn, oldRangeReturn,
land, size, zoneSet, high);
landLeave(land);
return res;
}
/* LandDescribe -- describe land for debugging
*
* See <design/land/#function.describe>
*/
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,
" inLand: $U\n", (WriteFU)land->inLand,
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;
}
/* 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(Land, land);
AVERT(Range, range);
AVER(closureP != NULL);
AVER(closureS == UNUSED_SIZE);
UNUSED(closureS);
dest = closureP;
res = LandInsert(&newRange, dest, range);
if (res == ResOK) {
*deleteReturn = TRUE;
return TRUE;
} else {
*deleteReturn = FALSE;
return FALSE;
}
}
/* LandFlush -- move ranges from src to dest
*
* See <design/land/#function.flush>
*/
Bool LandFlush(Land dest, Land src)
{
AVERT(Land, dest);
AVERT(Land, src);
return LandIterateAndDelete(src, landFlushVisitor, dest, UNUSED_SIZE);
}
/* LandClassCheck -- check land class */
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 Size landNoSize(Land land)
{
UNUSED(land);
NOTREACHED;
return 0;
}
/* LandSlowSize -- generic size method but slow */
static Bool landSizeVisitor(Land land, Range range,
void *closureP, Size closureS)
{
Size *size;
AVERT(Land, land);
AVERT(Range, range);
AVER(closureP != NULL);
AVER(closureS == UNUSED_SIZE);
UNUSED(closureS);
size = closureP;
*size += RangeSize(range);
return TRUE;
}
Size LandSlowSize(Land land)
{
Size size = 0;
Bool b = LandIterate(land, landSizeVisitor, &size, UNUSED_SIZE);
AVER(b);
return size;
}
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 Bool landNoIterate(Land land, LandVisitor visitor, void *closureP, Size closureS)
{
AVERT(Land, land);
AVER(visitor != NULL);
UNUSED(closureP);
UNUSED(closureS);
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);
AVER(oldRangeReturn != NULL);
AVERT(Land, land);
UNUSED(size);
AVER(FindDeleteCheck(findDelete));
return ResUNIMPL;
}
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);
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->sizeMethod = landNoSize;
class->finish = landTrivFinish;
class->insert = landNoInsert;
class->delete = landNoDelete;
class->iterate = landNoIterate;
class->iterateAndDelete = landNoIterateAndDelete;
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 <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Redistributions in any form must be accompanied by information on how
* to obtain complete source code for this software and any accompanying
* software that uses this software. The source code must either be
* included in the distribution or be available for no more than the cost
* of distribution plus a nominal fee, and must be freely redistributable
* under reasonable conditions. For an executable file, complete source
* code means the source code for all modules it contains. It does not
* include source code for modules or files that typically accompany the
* major components of the operating system on which the executable file
* runs.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

637
mps/code/landtest.c Normal file
View file

@ -0,0 +1,637 @@
/* landtest.c: LAND TEST
*
* $Id$
* Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* 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;
*
* 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"
#include "failover.h"
#include "freelist.h"
#include "mpm.h"
#include "mps.h"
#include "mpsavm.h"
#include "mpstd.h"
#include "poolmfs.h"
#include "testlib.h"
#include <stdio.h> /* printf */
SRCID(landtest, "$Id$");
#define ArraySize ((Size)123456)
/* CBS is much faster than Freelist, so we apply more operations to
* the former. */
#define nCBSOperations ((Size)125000)
#define nFLOperations ((Size)12500)
#define nFOOperations ((Size)12500)
static Count NAllocateTried, NAllocateSucceeded, NDeallocateTried,
NDeallocateSucceeded;
static int verbose = 0;
typedef struct TestStateStruct {
Align align;
BT allocTable;
Addr block;
Land land;
} TestStateStruct, *TestState;
typedef struct CheckTestClosureStruct {
TestState state;
Addr limit;
Addr oldLimit;
} CheckTestClosureStruct, *CheckTestClosure;
static Addr (addrOfIndex)(TestState state, Index i)
{
return AddrAdd(state->block, (i * state->align));
}
static Index (indexOfAddr)(TestState state, Addr a)
{
return (Index)(AddrOffset(state->block, a) / state->align);
}
static void describe(TestState state) {
die(LandDescribe(state->land, mps_lib_get_stdout()), "LandDescribe");
}
static Bool checkVisitor(Land land, Range range, void *closureP, Size closureS)
{
Addr base, limit;
CheckTestClosure cl = closureP;
testlib_unused(land);
Insist(closureS == UNUSED_SIZE);
Insist(cl != NULL);
base = RangeBase(range);
limit = RangeLimit(range);
if (base > cl->oldLimit) {
Insist(BTIsSetRange(cl->state->allocTable,
indexOfAddr(cl->state, cl->oldLimit),
indexOfAddr(cl->state, base)));
} else { /* must be at start of table */
Insist(base == cl->oldLimit);
Insist(cl->oldLimit == cl->state->block);
}
Insist(BTIsResRange(cl->state->allocTable,
indexOfAddr(cl->state, base),
indexOfAddr(cl->state, limit)));
cl->oldLimit = limit;
return TRUE;
}
static void check(TestState state)
{
CheckTestClosureStruct closure;
Bool b;
closure.state = state;
closure.limit = addrOfIndex(state, ArraySize);
closure.oldLimit = state->block;
b = LandIterate(state->land, checkVisitor, &closure, UNUSED_SIZE);
Insist(b);
if (closure.oldLimit == state->block)
Insist(BTIsSetRange(state->allocTable, 0,
indexOfAddr(state, closure.limit)));
else if (closure.limit > closure.oldLimit)
Insist(BTIsSetRange(state->allocTable,
indexOfAddr(state, closure.oldLimit),
indexOfAddr(state, closure.limit)));
else
Insist(closure.oldLimit == closure.limit);
}
static Word fbmRnd(Word limit)
{
/* Not very uniform, but never mind. */
return (Word)rnd() % limit;
}
/* nextEdge -- Finds the next transition in the bit table
*
* Returns the index greater than <base> such that the
* range [<base>, <return>) has the same value in the bit table,
* and <return> has a different value or does not exist.
*/
static Index nextEdge(BT bt, Size size, Index base)
{
Index end;
Bool baseValue;
Insist(bt != NULL);
Insist(base < size);
baseValue = BTGet(bt, base);
for(end = base + 1; end < size && BTGet(bt, end) == baseValue; end++)
NOOP;
return end;
}
/* lastEdge -- Finds the previous transition in the bit table
*
* Returns the index less than <base> such that the range
* [<return>, <base>] has the same value in the bit table,
* and <return>-1 has a different value or does not exist.
*/
static Index lastEdge(BT bt, Size size, Index base)
{
Index end;
Bool baseValue;
Insist(bt != NULL);
Insist(base < size);
baseValue = BTGet(bt, base);
for(end = base; end > (Index)0 && BTGet(bt, end - 1) == baseValue; end--)
NOOP;
return end;
}
/* randomRange -- picks random range within table
*
* The function first picks a uniformly distributed <base> within the table.
*
* It then scans forward a binary exponentially distributed
* number of "edges" in the table (that is, transitions between set and
* reset) to get <end>. Note that there is a 50% chance that <end> will
* be the next edge, a 25% chance it will be the edge after, etc., until
* the end of the table.
*
* Finally it picks a <limit> uniformly distributed in the range
* [base+1, limit].
*
* Hence there is a somewhat better than 50% chance that the range will be
* all either set or reset.
*/
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) */
/* after base */
Index limit; /* a randomly chosen value in (base, limit]. */
base = fbmRnd(ArraySize);
do {
end = nextEdge(state->allocTable, ArraySize, base);
} while(end < ArraySize && fbmRnd(2) == 0); /* p=0.5 exponential */
Insist(end > base);
limit = base + 1 + fbmRnd(end - base);
*baseReturn = addrOfIndex(state, base);
*limitReturn = addrOfIndex(state, limit);
}
static void allocate(TestState state, Addr base, Addr limit)
{
Res res;
Index ib, il; /* Indexed for base and limit */
Bool isFree;
RangeStruct range, oldRange;
Addr outerBase, outerLimit; /* interval containing [ib, il) */
ib = indexOfAddr(state, base);
il = indexOfAddr(state, limit);
isFree = BTIsResRange(state->allocTable, ib, il);
NAllocateTried++;
if (isFree) {
Size left, right, total; /* Sizes of block and two fragments */
outerBase =
addrOfIndex(state, lastEdge(state->allocTable, ArraySize, ib));
outerLimit =
addrOfIndex(state, nextEdge(state->allocTable, ArraySize, il - 1));
left = AddrOffset(outerBase, base);
right = AddrOffset(limit, outerLimit);
total = AddrOffset(outerBase, outerLimit);
/* TODO: check these values */
testlib_unused(left);
testlib_unused(right);
testlib_unused(total);
} else {
outerBase = outerLimit = NULL;
}
RangeInit(&range, base, limit);
res = LandDelete(&oldRange, state->land, &range);
if (verbose) {
printf("allocate: [%p,%p) -- %s\n",
(void *)base, (void *)limit, isFree ? "succeed" : "fail");
describe(state);
}
if (!isFree) {
die_expect((mps_res_t)res, MPS_RES_FAIL,
"Succeeded in deleting allocated block");
} else { /* isFree */
die_expect((mps_res_t)res, MPS_RES_OK,
"failed to delete free block");
Insist(RangeBase(&oldRange) == outerBase);
Insist(RangeLimit(&oldRange) == outerLimit);
NAllocateSucceeded++;
BTSetRange(state->allocTable, ib, il);
}
}
static void deallocate(TestState state, Addr base, Addr limit)
{
Res res;
Index ib, il;
Bool isAllocated;
Addr outerBase = base, outerLimit = limit; /* interval containing [ib, il) */
RangeStruct range, freeRange; /* interval returned by the manager */
ib = indexOfAddr(state, base);
il = indexOfAddr(state, limit);
isAllocated = BTIsSetRange(state->allocTable, ib, il);
NDeallocateTried++;
if (isAllocated) {
Size left, right, total; /* Sizes of block and two fragments */
/* Find the free blocks adjacent to the allocated block */
if (ib > 0 && !BTGet(state->allocTable, ib - 1)) {
outerBase =
addrOfIndex(state, lastEdge(state->allocTable, ArraySize, ib - 1));
} else {
outerBase = base;
}
if (il < ArraySize && !BTGet(state->allocTable, il)) {
outerLimit =
addrOfIndex(state, nextEdge(state->allocTable, ArraySize, il));
} else {
outerLimit = limit;
}
left = AddrOffset(outerBase, base);
right = AddrOffset(limit, outerLimit);
total = AddrOffset(outerBase, outerLimit);
/* TODO: check these values */
testlib_unused(left);
testlib_unused(right);
testlib_unused(total);
}
RangeInit(&range, base, limit);
res = LandInsert(&freeRange, state->land, &range);
if (verbose) {
printf("deallocate: [%p,%p) -- %s\n",
(void *)base, (void *)limit, isAllocated ? "succeed" : "fail");
describe(state);
}
if (!isAllocated) {
die_expect((mps_res_t)res, MPS_RES_FAIL,
"succeeded in inserting non-allocated block");
} else { /* isAllocated */
die_expect((mps_res_t)res, MPS_RES_OK,
"failed to insert allocated block");
NDeallocateSucceeded++;
BTResRange(state->allocTable, ib, il);
Insist(RangeBase(&freeRange) == outerBase);
Insist(RangeLimit(&freeRange) == outerLimit);
}
}
static void find(TestState state, Size size, Bool high, FindDelete findDelete)
{
Bool expected, found;
Index expectedBase, expectedLimit;
RangeStruct foundRange, oldRange;
Addr remainderBase, remainderLimit;
Addr origBase, origLimit;
Size oldSize, newSize;
origBase = origLimit = NULL;
expected = (high ? BTFindLongResRangeHigh : BTFindLongResRange)
(&expectedBase, &expectedLimit, state->allocTable,
(Index)0, (Index)ArraySize, (Count)size);
if (expected) {
oldSize = (expectedLimit - expectedBase) * state->align;
remainderBase = origBase = addrOfIndex(state, expectedBase);
remainderLimit = origLimit = addrOfIndex(state, expectedLimit);
switch(findDelete) {
case FindDeleteNONE:
/* do nothing */
break;
case FindDeleteENTIRE:
remainderBase = remainderLimit;
break;
case FindDeleteLOW:
expectedLimit = expectedBase + size;
remainderBase = addrOfIndex(state, expectedLimit);
break;
case FindDeleteHIGH:
expectedBase = expectedLimit - size;
remainderLimit = addrOfIndex(state, expectedBase);
break;
default:
cdie(0, "invalid findDelete");
break;
}
if (findDelete != FindDeleteNONE) {
newSize = AddrOffset(remainderBase, remainderLimit);
}
/* TODO: check these values */
testlib_unused(oldSize);
testlib_unused(newSize);
}
found = (high ? LandFindLast : LandFindFirst)
(&foundRange, &oldRange, state->land, size * state->align, findDelete);
if (verbose) {
printf("find %s %lu: ", high ? "last" : "first",
(unsigned long)(size * state->align));
if (expected) {
printf("expecting [%p,%p)\n",
(void *)addrOfIndex(state, expectedBase),
(void *)addrOfIndex(state, expectedLimit));
} else {
printf("expecting this not to be found\n");
}
if (found) {
printf(" found [%p,%p)\n", (void *)RangeBase(&foundRange),
(void *)RangeLimit(&foundRange));
} else {
printf(" not found\n");
}
}
Insist(found == expected);
if (found) {
Insist(expectedBase == indexOfAddr(state, RangeBase(&foundRange)));
Insist(expectedLimit == indexOfAddr(state, RangeLimit(&foundRange)));
if (findDelete != FindDeleteNONE) {
Insist(RangeBase(&oldRange) == origBase);
Insist(RangeLimit(&oldRange) == origLimit);
BTSetRange(state->allocTable, expectedBase, expectedLimit);
}
}
return;
}
static void test(TestState state, unsigned n) {
Addr base, limit;
unsigned i;
Size size;
Bool high;
FindDelete findDelete = FindDeleteNONE;
BTSetRange(state->allocTable, 0, ArraySize); /* Initially all allocated */
check(state);
for(i = 0; i < n; i++) {
switch(fbmRnd(3)) {
case 0:
randomRange(&base, &limit, state);
allocate(state, base, limit);
break;
case 1:
randomRange(&base, &limit, state);
deallocate(state, base, limit);
break;
case 2:
size = fbmRnd(ArraySize / 10) + 1;
high = fbmRnd(2) ? TRUE : FALSE;
switch(fbmRnd(6)) {
default: findDelete = FindDeleteNONE; break;
case 3: findDelete = FindDeleteLOW; break;
case 4: findDelete = FindDeleteHIGH; break;
case 5: findDelete = FindDeleteENTIRE; break;
}
find(state, size, high, findDelete);
break;
default:
cdie(0, "invalid rnd(3)");
return;
}
if ((i + 1) % 1000 == 0)
check(state);
}
}
#define testArenaSIZE (((size_t)4)<<20)
extern int main(int argc, char *argv[])
{
mps_arena_t mpsArena;
Arena arena;
TestStateStruct state;
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;
NAllocateTried = NAllocateSucceeded = NDeallocateTried =
NDeallocateSucceeded = 0;
die(mps_arena_create(&mpsArena, mps_arena_class_vm(), testArenaSIZE),
"mps_arena_create");
arena = (Arena)mpsArena; /* avoid pun */
die((mps_res_t)BTCreate(&allocTable, arena, ArraySize),
"failed to create alloc table");
/* We're not going to use this block, but I feel unhappy just */
/* inventing addresses. */
die((mps_res_t)ControlAlloc(&p, arena, ArraySize * align,
/* withReservoirPermit */ FALSE),
"failed to allocate block");
dummyBlock = p; /* avoid pun */
if (verbose) {
printf("Allocated block [%p,%p)\n", (void*)dummyBlock,
(char *)dummyBlock + ArraySize);
}
/* 1. Test CBS */
MPS_ARGS_BEGIN(args) {
die((mps_res_t)LandInit(cbs, CBSFastLandClassGet(), arena, align, NULL, args),
"failed to initialise CBS");
} MPS_ARGS_END(args);
state.align = align;
state.block = dummyBlock;
state.allocTable = allocTable;
state.land = cbs;
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");
state.land = fl;
test(&state, nFLOperations);
LandFinish(fl);
/* 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(CBSFastBlockStruct));
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, CBSBlockPool, mfs);
die((mps_res_t)LandInit(cbs, CBSFastLandClassGet(), 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);
printf("\nNumber of allocations attempted: %"PRIuLONGEST"\n",
(ulongest_t)NAllocateTried);
printf("Number of allocations succeeded: %"PRIuLONGEST"\n",
(ulongest_t)NAllocateSucceeded);
printf("Number of deallocations attempted: %"PRIuLONGEST"\n",
(ulongest_t)NDeallocateTried);
printf("Number of deallocations succeeded: %"PRIuLONGEST"\n",
(ulongest_t)NDeallocateSucceeded);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
return 0;
}
/* C. COPYRIGHT AND LICENSE
*
* Copyright (c) 2001-2014 Ravenbrook Limited <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Redistributions in any form must be accompanied by information on how
* to obtain complete source code for this software and any accompanying
* software that uses this software. The source code must either be
* included in the distribution or be available for no more than the cost
* of distribution plus a nominal fee, and must be freely redistributable
* under reasonable conditions. For an executable file, complete source
* code means the source code for all modules it contains. It does not
* include source code for modules or files that typically accompany the
* major components of the operating system on which the executable file
* runs.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

View file

@ -6,7 +6,8 @@
* DESIGN
*
* See <design/arenavm/> and <design/locus/> for basic locus stuff.
* See <design/trace/> for chains.
* See <design/trace/> for chains. See <design/strategy/> for the
* collection strategy.
*/
#include "chain.h"
@ -88,8 +89,6 @@ static Bool GenDescCheck(GenDesc gen)
/* nothing to check for capacity */
CHECKL(gen->mortality >= 0.0);
CHECKL(gen->mortality <= 1.0);
CHECKL(gen->proflow >= 0.0);
CHECKL(gen->proflow <= 1.0);
CHECKD_NOSIG(Ring, &gen->locusRing);
return TRUE;
}
@ -157,9 +156,9 @@ Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount,
gens[i].zones = ZoneSetEMPTY;
gens[i].capacity = params[i].capacity;
gens[i].mortality = params[i].mortality;
gens[i].proflow = 1.0; /* @@@@ temporary */
RingInit(&gens[i].locusRing);
gens[i].sig = GenDescSig;
AVERT(GenDesc, &gens[i]);
}
res = ControlAlloc(&p, arena, sizeof(ChainStruct), FALSE);
@ -251,7 +250,9 @@ GenDesc ChainGen(Chain chain, Index gen)
}
/* PoolGenAlloc -- allocate a segment in a pool generation */
/* PoolGenAlloc -- allocate a segment in a pool generation and update
* accounting
*/
Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class, Size size,
Bool withReservoirPermit, ArgList args)
@ -293,6 +294,12 @@ Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class, Size size,
EVENT3(ArenaGenZoneAdd, arena, gen, moreZones);
}
size = SegSize(seg);
pgen->totalSize += size;
STATISTIC_STAT ({
++ pgen->segs;
pgen->freeSize += size;
});
*segReturn = seg;
return ResOK;
}
@ -418,8 +425,13 @@ Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool)
pgen->pool = pool;
pgen->gen = gen;
RingInit(&pgen->genRing);
pgen->totalSize = (Size)0;
pgen->newSize = (Size)0;
STATISTIC(pgen->segs = 0);
pgen->totalSize = 0;
STATISTIC(pgen->freeSize = 0);
pgen->newSize = 0;
STATISTIC(pgen->oldSize = 0);
pgen->newDeferredSize = 0;
STATISTIC(pgen->oldDeferredSize = 0);
pgen->sig = PoolGenSig;
AVERT(PoolGen, pgen);
@ -433,6 +445,15 @@ Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool)
void PoolGenFinish(PoolGen pgen)
{
AVERT(PoolGen, pgen);
AVER(pgen->totalSize == 0);
AVER(pgen->newSize == 0);
AVER(pgen->newDeferredSize == 0);
STATISTIC_STAT ({
AVER(pgen->segs == 0);
AVER(pgen->freeSize == 0);
AVER(pgen->oldSize == 0);
AVER(pgen->oldDeferredSize == 0);
});
pgen->sig = SigInvalid;
RingRemove(&pgen->genRing);
@ -448,11 +469,202 @@ Bool PoolGenCheck(PoolGen pgen)
CHECKU(Pool, pgen->pool);
CHECKU(GenDesc, pgen->gen);
CHECKD_NOSIG(Ring, &pgen->genRing);
CHECKL(pgen->newSize <= pgen->totalSize);
STATISTIC_STAT ({
CHECKL((pgen->totalSize == 0) == (pgen->segs == 0));
CHECKL(pgen->totalSize >= pgen->segs * ArenaAlign(PoolArena(pgen->pool)));
CHECKL(pgen->totalSize == pgen->freeSize + pgen->newSize + pgen->oldSize
+ pgen->newDeferredSize + pgen->oldDeferredSize);
});
return TRUE;
}
/* PoolGenAccountForFill -- accounting for allocation
*
* Call this when the pool allocates memory to the client program via
* BufferFill. The deferred flag indicates whether the accounting of
* this memory (for the purpose of scheduling collections) should be
* deferred until later.
*
* See <design/strategy/#accounting.op.fill>
*/
void PoolGenAccountForFill(PoolGen pgen, Size size, Bool deferred)
{
AVERT(PoolGen, pgen);
AVERT(Bool, deferred);
STATISTIC_STAT ({
AVER(pgen->freeSize >= size);
pgen->freeSize -= size;
});
if (deferred)
pgen->newDeferredSize += size;
else
pgen->newSize += size;
}
/* PoolGenAccountForEmpty -- accounting for emptying a buffer
*
* Call this when the client program returns memory (that was never
* condemned) to the pool via BufferEmpty. The deferred flag is as for
* PoolGenAccountForFill.
*
* See <design/strategy/#accounting.op.empty>
*/
void PoolGenAccountForEmpty(PoolGen pgen, Size unused, Bool deferred)
{
AVERT(PoolGen, pgen);
AVERT(Bool, deferred);
if (deferred) {
AVER(pgen->newDeferredSize >= unused);
pgen->newDeferredSize -= unused;
} else {
AVER(pgen->newSize >= unused);
pgen->newSize -= unused;
}
STATISTIC(pgen->freeSize += unused);
}
/* PoolGenAccountForAge -- accounting for condemning
*
* Call this when memory is condemned via PoolWhiten. The size
* parameter should be the amount of memory that is being condemned
* for the first time. The deferred flag is as for PoolGenAccountForFill.
*
* See <design/strategy/#accounting.op.age>
*/
void PoolGenAccountForAge(PoolGen pgen, Size size, Bool deferred)
{
AVERT(PoolGen, pgen);
if (deferred) {
AVER(pgen->newDeferredSize >= size);
pgen->newDeferredSize -= size;
STATISTIC(pgen->oldDeferredSize += size);
} else {
AVER(pgen->newSize >= size);
pgen->newSize -= size;
STATISTIC(pgen->oldSize += size);
}
}
/* PoolGenAccountForReclaim -- accounting for reclaiming
*
* Call this when reclaiming memory, passing the amount of memory that
* was reclaimed. The deferred flag is as for PoolGenAccountForFill.
*
* See <design/strategy/#accounting.op.reclaim>
*/
void PoolGenAccountForReclaim(PoolGen pgen, Size reclaimed, Bool deferred)
{
AVERT(PoolGen, pgen);
AVERT(Bool, deferred);
STATISTIC_STAT ({
if (deferred) {
AVER(pgen->oldDeferredSize >= reclaimed);
pgen->oldDeferredSize -= reclaimed;
} else {
AVER(pgen->oldSize >= reclaimed);
pgen->oldSize -= reclaimed;
}
pgen->freeSize += reclaimed;
});
}
/* PoolGenUndefer -- finish deferring accounting
*
* Call this when exiting ramp mode, passing the amount of old
* (condemned at least once) and new (never condemned) memory whose
* accounting was deferred (for example, during a ramp).
*
* See <design/strategy/#accounting.op.undefer>
*/
void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize)
{
AVERT(PoolGen, pgen);
STATISTIC_STAT ({
AVER(pgen->oldDeferredSize >= oldSize);
pgen->oldDeferredSize -= oldSize;
pgen->oldSize += oldSize;
});
AVER(pgen->newDeferredSize >= newSize);
pgen->newDeferredSize -= newSize;
pgen->newSize += newSize;
}
/* PoolGenAccountForSegSplit -- accounting for splitting a segment */
void PoolGenAccountForSegSplit(PoolGen pgen)
{
AVERT(PoolGen, pgen);
STATISTIC_STAT ({
AVER(pgen->segs >= 1); /* must be at least one segment to split */
++ pgen->segs;
});
}
/* PoolGenAccountForSegMerge -- accounting for merging a segment */
void PoolGenAccountForSegMerge(PoolGen pgen)
{
AVERT(PoolGen, pgen);
STATISTIC_STAT ({
AVER(pgen->segs >= 2); /* must be at least two segments to merge */
-- pgen->segs;
});
}
/* PoolGenFree -- free a segment and update accounting
*
* Pass the amount of memory in the segment that is accounted as free,
* old, or new, respectively. The deferred flag is as for
* PoolGenAccountForFill.
*
* See <design/strategy/#accounting.op.free>
*/
void PoolGenFree(PoolGen pgen, Seg seg, Size freeSize, Size oldSize,
Size newSize, Bool deferred)
{
Size size;
AVERT(PoolGen, pgen);
AVERT(Seg, seg);
size = SegSize(seg);
AVER(freeSize + oldSize + newSize == size);
/* Pretend to age and reclaim the contents of the segment to ensure
* that the entire segment is accounted as free. */
PoolGenAccountForAge(pgen, newSize, deferred);
PoolGenAccountForReclaim(pgen, oldSize + newSize, deferred);
AVER(pgen->totalSize >= size);
pgen->totalSize -= size;
STATISTIC_STAT ({
AVER(pgen->segs > 0);
-- pgen->segs;
AVER(pgen->freeSize >= size);
pgen->freeSize -= size;
});
SegFree(seg);
}
/* LocusInit -- initialize the locus module */
void LocusInit(Arena arena)
@ -466,9 +678,9 @@ void LocusInit(Arena arena)
gen->zones = ZoneSetEMPTY;
gen->capacity = 0; /* unused */
gen->mortality = 0.51;
gen->proflow = 0.0;
RingInit(&gen->locusRing);
gen->sig = GenDescSig;
AVERT(GenDesc, gen);
}

View file

@ -152,6 +152,19 @@ 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.
*
* We use PointerAdd rather than a cast to avoid "warning C4306: 'type
* cast' : conversion from 'unsigned int' to 'Pointer' of greater
* size" on platform w3i6mv.
*/
#define UNUSED_POINTER PointerAdd(0, 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
@ -169,6 +182,19 @@ typedef const struct SrcIdStruct {
((type *)(void *)((char *)(p) - offsetof(type, field)))
/* BOOLFIELD -- declare a Boolean bitfield
*
* A Boolean bitfield needs to be unsigned (not Bool), so that its
* values are 0 and 1 (not 0 and -1), in order to avoid a sign
* conversion (which would be a compiler error) when assigning TRUE to
* the field.
*
* See <design/type/#bool.bitfield>
*/
#define BOOLFIELD(name) unsigned name : 1
/* BITFIELD -- coerce a value into a bitfield
*
* This coerces value to the given width and type in a way that avoids

View file

@ -87,6 +87,9 @@ extern Addr (AddrAlignDown)(Addr addr, Align align);
#define AddrIsAligned(p, a) WordIsAligned((Word)(p), a)
#define AddrAlignUp(p, a) ((Addr)WordAlignUp((Word)(p), a))
#define AddrRoundUp(p, r) ((Addr)WordRoundUp((Word)(p), r))
#define ReadonlyAddrAdd(p, s) ((ReadonlyAddr)((const char *)(p) + (s)))
#define SizeIsAligned(s, a) WordIsAligned((Word)(s), a)
#define SizeAlignUp(s, a) ((Size)WordAlignUp((Word)(s), a))
@ -281,13 +284,11 @@ extern BufferClass PoolNoBufferClass(void);
/* Abstract Pool Classes Interface -- see <code/poolabs.c> */
extern void PoolClassMixInAllocFree(PoolClass class);
extern void PoolClassMixInBuffer(PoolClass class);
extern void PoolClassMixInScan(PoolClass class);
extern void PoolClassMixInFormat(PoolClass class);
extern void PoolClassMixInCollect(PoolClass class);
extern AbstractPoolClass AbstractPoolClassGet(void);
extern AbstractAllocFreePoolClass AbstractAllocFreePoolClassGet(void);
extern AbstractBufferPoolClass AbstractBufferPoolClassGet(void);
extern AbstractBufferPoolClass AbstractSegBufPoolClassGet(void);
extern AbstractScanPoolClass AbstractScanPoolClassGet(void);
@ -495,8 +496,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);
@ -815,7 +816,7 @@ extern AllocPattern AllocPatternRamp(void);
extern AllocPattern AllocPatternRampCollectAll(void);
/* FindDelete -- see <code/cbs.c> and <code/freelist.c> */
/* FindDelete -- see <code/land.c> */
extern Bool FindDeleteCheck(FindDelete findDelete);
@ -1002,6 +1003,37 @@ extern Size VMReserved(VM vm);
extern Size VMMapped(VM vm);
/* Land Interface -- see <design/land/> */
extern Bool LandCheck(Land land);
#define LandArena(land) ((land)->arena)
#define LandAlignment(land) ((land)->alignment)
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);
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);
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 Bool 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))
#define DEFINE_LAND_CLASS(className, var) \
DEFINE_ALIAS_CLASS(className, LandClass, var)
#define IsLandSubclass(land, className) \
IsSubclassPoly((land)->class, className ## Get())
/* Stack Probe */
extern void StackProbe(Size depth);

View file

@ -25,19 +25,19 @@
/* stress -- create a pool of the requested type and allocate in it */
static mps_res_t stress(mps_class_t class, size_t (*size)(size_t i),
mps_arena_t arena, ...)
static mps_res_t stress(mps_arena_t arena, size_t (*size)(size_t i),
const char *name, mps_class_t pool_class,
mps_arg_s *args)
{
mps_res_t res;
mps_pool_t pool;
va_list arg;
size_t i, k;
int *ps[testSetSIZE];
size_t ss[testSetSIZE];
va_start(arg, arena);
res = mps_pool_create_v(&pool, arena, class, arg);
va_end(arg);
printf("%s\n", name);
res = mps_pool_create_k(&pool, arena, pool_class, args);
if (res != MPS_RES_OK)
return res;
@ -87,7 +87,7 @@ static mps_res_t stress(mps_class_t class, size_t (*size)(size_t i),
}
/* randomSize -- produce sizes both latge and small */
/* randomSize -- produce sizes both large and small */
static size_t randomSize(size_t i)
{
@ -99,7 +99,7 @@ static size_t randomSize(size_t i)
}
/* randomSize8 -- produce sizes both latge and small, 8-byte aligned */
/* randomSize8 -- produce sizes both large and small, 8-byte aligned */
static size_t randomSize8(size_t i)
{
@ -121,61 +121,90 @@ static size_t fixedSize(size_t i)
static mps_pool_debug_option_s bothOptions = {
/* .fence_template = */ (const void *)"postpostpostpost",
/* .fence_size = */ MPS_PF_ALIGN,
/* .free_template = */ (const void *)"DEAD",
/* .fence_template = */ "post",
/* .fence_size = */ 4,
/* .free_template = */ "DEAD",
/* .free_size = */ 4
};
static mps_pool_debug_option_s fenceOptions = {
/* .fence_template = */ (const void *)"\0XXX ''\"\"'' XXX\0",
/* .fence_size = */ 16,
/* .fence_template = */ "123456789abcdef",
/* .fence_size = */ 15,
/* .free_template = */ NULL,
/* .free_size = */ 0
};
/* testInArena -- test all the pool classes in the given arena */
static void testInArena(mps_arena_t arena, mps_pool_debug_option_s *options)
static void testInArena(mps_arena_class_t arena_class, mps_arg_s *arena_args,
mps_pool_debug_option_s *options)
{
/* IWBN to test MVFFDebug, but the MPS doesn't support debugging */
/* cross-segment allocation (possibly MVFF ought not to). */
printf("MVFF\n");
die(stress(mps_class_mvff(), randomSize8, arena,
(size_t)65536, (size_t)32, (mps_align_t)MPS_PF_ALIGN, TRUE, TRUE, TRUE),
"stress MVFF");
printf("MV debug\n");
die(stress(mps_class_mv_debug(), randomSize, arena,
options, (size_t)65536, (size_t)32, (size_t)65536),
"stress MV debug");
mps_arena_t arena;
printf("MFS\n");
fixedSizeSize = 13;
die(stress(mps_class_mfs(), fixedSize, arena, (size_t)100000, fixedSizeSize),
die(mps_arena_create_k(&arena, arena_class, arena_args),
"mps_arena_create");
MPS_ARGS_BEGIN(args) {
mps_align_t align = sizeof(void *) << (rnd() % 4);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE);
die(stress(arena, randomSize8, "MVFF", mps_class_mvff(), args),
"stress MVFF");
} MPS_ARGS_END(args);
MPS_ARGS_BEGIN(args) {
mps_align_t align = sizeof(void *) << (rnd() % 4);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, options);
die(stress(arena, randomSize8, "MVFF debug", mps_class_mvff_debug(), args),
"stress MVFF debug");
} MPS_ARGS_END(args);
MPS_ARGS_BEGIN(args) {
mps_align_t align = (mps_align_t)1 << (rnd() % 6);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
die(stress(arena, randomSize, "MV", mps_class_mv(), args),
"stress MV");
} MPS_ARGS_END(args);
MPS_ARGS_BEGIN(args) {
mps_align_t align = (mps_align_t)1 << (rnd() % 6);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, options);
die(stress(arena, randomSize, "MV debug", mps_class_mv_debug(), args),
"stress MV debug");
} MPS_ARGS_END(args);
MPS_ARGS_BEGIN(args) {
fixedSizeSize = 1 + rnd() % 64;
MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, fixedSizeSize);
MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, 100000);
die(stress(arena, fixedSize, "MFS", mps_class_mfs(), args),
"stress MFS");
} MPS_ARGS_END(args);
printf("MV\n");
die(stress(mps_class_mv(), randomSize, arena,
(size_t)65536, (size_t)32, (size_t)65536),
"stress MV");
mps_arena_destroy(arena);
}
int main(int argc, char *argv[])
{
mps_arena_t arena;
testlib_init(argc, argv);
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
"mps_arena_create");
testInArena(arena, &bothOptions);
mps_arena_destroy(arena);
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
testInArena(mps_arena_class_vm(), args, &bothOptions);
} MPS_ARGS_END(args);
die(mps_arena_create(&arena, mps_arena_class_vm(), smallArenaSIZE),
"mps_arena_create");
testInArena(arena, &fenceOptions);
mps_arena_destroy(arena);
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, smallArenaSIZE);
testInArena(mps_arena_class_vm(), args, &fenceOptions);
} MPS_ARGS_END(args);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
return 0;

View file

@ -592,7 +592,53 @@ typedef struct GlobalsStruct {
} GlobalsStruct;
/* LandClassStruct -- land class structure
*
* See <design/land/>.
*/
#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 */
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 */
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 */
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 <design/land/>, <code/land.c>
*/
#define LandSig ((Sig)0x5197A4D9) /* SIGnature LAND */
typedef struct LandStruct {
Sig sig; /* <design/sig/> */
LandClass class; /* land class structure */
Arena arena; /* owning arena */
Align alignment; /* alignment of addresses */
Bool inLand; /* prevent reentrance */
} LandStruct;
/* CBSStruct -- coalescing block structure
*
* CBS is a Land implementation that maintains a collection of
* disjoint ranges in a splay tree.
*
* See <code/cbs.c>.
*/
@ -600,21 +646,58 @@ 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;
Bool fastFind; /* maintain and use size property? */
Bool zoned; /* maintain and use zone property? */
Bool inCBS; /* prevent reentrance */
Pool blockPool; /* pool that manages blocks */
Size blockStructSize; /* size of block structure */
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; /* sig at end because embeded */
Sig sig; /* .class.end-sig */
} 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 <code/failover.c>.
*/
#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
* disjoint ranges in an address-ordered freelist.
*
* See <code/freelist.c>.
*/
#define FreelistSig ((Sig)0x519F6331) /* SIGnature FREEL */
typedef union FreelistBlockUnion *FreelistBlock;
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;
/* ArenaStruct -- generic arena
*
* See <code/arena.c>. */
@ -648,9 +731,9 @@ typedef struct mps_arena_s {
Tree chunkTree; /* all the chunks */
Serial chunkSerial; /* next chunk number */
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? */

View file

@ -33,6 +33,7 @@ typedef void (*Fun)(void); /* <design/type/#fun> */
typedef MPS_T_WORD Word; /* <design/type/#word> */
typedef unsigned char Byte; /* <design/type/#byte> */
typedef struct AddrStruct *Addr; /* <design/type/#addr> */
typedef const struct AddrStruct *ReadonlyAddr; /* <design/type/#readonlyaddr> */
typedef Word Size; /* <design/type/#size> */
typedef Word Count; /* <design/type/#count> */
typedef Word Index; /* <design/type/#index> */
@ -76,7 +77,6 @@ typedef struct LockStruct *Lock; /* <code/lock.c>* */
typedef struct mps_pool_s *Pool; /* <design/pool/> */
typedef struct mps_class_s *PoolClass; /* <code/poolclas.c> */
typedef PoolClass AbstractPoolClass; /* <code/poolabs.c> */
typedef PoolClass AbstractAllocFreePoolClass; /* <code/poolabs.c> */
typedef PoolClass AbstractBufferPoolClass; /* <code/poolabs.c> */
typedef PoolClass AbstractSegBufPoolClass; /* <code/poolabs.c> */
typedef PoolClass AbstractScanPoolClass; /* <code/poolabs.c> */
@ -108,7 +108,10 @@ typedef struct AllocPatternStruct *AllocPattern;
typedef struct AllocFrameStruct *AllocFrame; /* <design/alloc-frame/> */
typedef struct ReservoirStruct *Reservoir; /* <design/reservoir/> */
typedef struct StackContextStruct *StackContext;
typedef unsigned FindDelete; /* <design/cbs/> */
typedef struct RangeStruct *Range; /* <design/range/> */
typedef struct LandStruct *Land; /* <design/land/> */
typedef struct LandClassStruct *LandClass; /* <design/land/> */
typedef unsigned FindDelete; /* <design/land/> */
/* Arena*Method -- see <code/mpmst.h#ArenaClassStruct> */
@ -261,6 +264,22 @@ typedef struct TraceStartMessageStruct *TraceStartMessage;
typedef struct TraceMessageStruct *TraceMessage; /* trace end */
/* Land*Method -- see <design/land/> */
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)(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)(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high);
typedef Res (*LandDescribeMethod)(Land land, mps_lib_FILE *stream);
/* CONSTANTS */
@ -281,22 +300,9 @@ typedef struct TraceMessageStruct *TraceMessage; /* trace end */
#define RankSetEMPTY BS_EMPTY(RankSet)
#define RankSetUNIV ((RankSet)((1u << RankLIMIT) - 1))
#define AttrFMT ((Attr)(1<<0)) /* <design/type/#attr> */
#define AttrSCAN ((Attr)(1<<1))
#define AttrPM_NO_READ ((Attr)(1<<2))
#define AttrPM_NO_WRITE ((Attr)(1<<3))
#define AttrALLOC ((Attr)(1<<4))
#define AttrFREE ((Attr)(1<<5))
#define AttrBUF ((Attr)(1<<6))
#define AttrBUF_RESERVE ((Attr)(1<<7))
#define AttrBUF_ALLOC ((Attr)(1<<8))
#define AttrGC ((Attr)(1<<9))
#define AttrINCR_RB ((Attr)(1<<10))
#define AttrINCR_WB ((Attr)(1<<11))
#define AttrMOVINGGC ((Attr)(1<<12))
#define AttrMASK (AttrFMT | AttrSCAN | AttrPM_NO_READ | \
AttrPM_NO_WRITE | AttrALLOC | AttrFREE | \
AttrBUF | AttrBUF_RESERVE | AttrBUF_ALLOC | \
AttrGC | AttrINCR_RB | AttrINCR_WB | AttrMOVINGGC)
#define AttrGC ((Attr)(1<<1))
#define AttrMOVINGGC ((Attr)(1<<2))
#define AttrMASK (AttrFMT | AttrGC | AttrMOVINGGC)
/* Segment preferences */
@ -407,7 +413,7 @@ enum {
};
/* FindDelete operations -- see <design/cbs/> and <design/freelist/> */
/* FindDelete operations -- see <design/land/> */
enum {
FindDeleteNONE = 1, /* don't delete after finding */

View file

@ -76,6 +76,8 @@
#include "freelist.c"
#include "sa.c"
#include "nailboard.c"
#include "land.c"
#include "failover.c"
/* Additional pool classes */

View file

@ -13,7 +13,7 @@
* `MPS_` or `_mps_` and may use any identifiers with these prefixes in
* future.
*
* .naming.internal: Any idenfitier beginning with underscore is for
* .naming.internal: Any identifier beginning with an underscore is for
* internal use within the interface and may change or be withdrawn without
* warning.
*
@ -323,9 +323,9 @@ typedef struct _mps_sac_s {
/* .sacc: Keep in sync with <code/sac.h>. */
typedef struct mps_sac_class_s {
size_t _block_size;
size_t _cached_count;
unsigned _frequency;
size_t mps_block_size;
size_t mps_cached_count;
unsigned mps_frequency;
} mps_sac_class_s;
#define mps_sac_classes_s mps_sac_class_s

View file

@ -91,11 +91,11 @@
22B2BC3D18B643B300C33E63 /* PBXTargetDependency */,
2291A5E6175CB207001D4920 /* PBXTargetDependency */,
2291A5E8175CB20E001D4920 /* PBXTargetDependency */,
3114A65B156E95B4001E0AA3 /* PBXTargetDependency */,
3114A5CC156E932C001E0AA3 /* PBXTargetDependency */,
3114A5EA156E93C4001E0AA3 /* PBXTargetDependency */,
224CC79D175E187C002FF81B /* PBXTargetDependency */,
22B2BC3F18B643B700C33E63 /* PBXTargetDependency */,
3114A65B156E95B4001E0AA3 /* PBXTargetDependency */,
2231BB6D18CA986B002D6322 /* PBXTargetDependency */,
31D60034156D3D5A00337B26 /* PBXTargetDependency */,
2286E4C918F4389E004111E2 /* PBXTargetDependency */,
@ -161,7 +161,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 */; };
22C2ACA718BE400A006B3677 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
@ -796,7 +796,7 @@
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 3114A64B156E9596001E0AA3;
remoteInfo = fbmtest;
remoteInfo = landtest;
};
3114A674156E9619001E0AA3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
@ -1415,7 +1415,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 = "<group>"; };
2291A5E9175CB4EC001D4920 /* landtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = landtest.c; sourceTree = "<group>"; };
2291A5EA175CB503001D4920 /* abq.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = abq.h; sourceTree = "<group>"; };
2291A5EB175CB53E001D4920 /* range.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = range.c; sourceTree = "<group>"; };
2291A5EC175CB53E001D4920 /* range.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = range.h; sourceTree = "<group>"; };
@ -1430,6 +1430,11 @@
22E30E831886FF1400D98EA9 /* nailboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nailboard.h; sourceTree = "<group>"; };
22F846AF18F4379C00982BA7 /* lockut.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lockut.c; sourceTree = "<group>"; };
22F846BD18F437B900982BA7 /* lockut */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lockut; sourceTree = BUILT_PRODUCTS_DIR; };
22C5C99A18EC6AEC004C63D4 /* failover.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = failover.c; sourceTree = "<group>"; };
22C5C99B18EC6AEC004C63D4 /* failover.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = failover.h; sourceTree = "<group>"; };
22C5C99C18EC6AEC004C63D4 /* land.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = land.c; sourceTree = "<group>"; };
22DD93E118ED815F00240DD2 /* failover.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = failover.txt; path = ../design/failover.txt; sourceTree = "<group>"; };
22DD93E218ED815F00240DD2 /* land.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = land.txt; path = ../design/land.txt; sourceTree = "<group>"; };
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 = "<group>"; };
22FACED1188807FF000FDBC1 /* airtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = airtest.c; sourceTree = "<group>"; };
@ -1485,7 +1490,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 = "<group>"; };
3114A645156E9525001E0AA3 /* abq.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = abq.c; sourceTree = "<group>"; };
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 = "<group>"; };
3114A67C156E9668001E0AA3 /* mv2test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mv2test; sourceTree = BUILT_PRODUCTS_DIR; };
@ -2137,6 +2142,7 @@
31160D9C1899540D0071EB17 /* config.txt */,
31160D9D1899540D0071EB17 /* critical-path.txt */,
31160D9E1899540D0071EB17 /* diag.txt */,
22DD93E118ED815F00240DD2 /* failover.txt */,
31160D9F1899540D0071EB17 /* finalize.txt */,
31160DA01899540D0071EB17 /* fix.txt */,
31160DA11899540D0071EB17 /* freelist.txt */,
@ -2146,6 +2152,7 @@
31160DA51899540D0071EB17 /* interface-c.txt */,
31160DA61899540D0071EB17 /* io.txt */,
31160DA71899540D0071EB17 /* keyword-arguments.txt */,
22DD93E218ED815F00240DD2 /* land.txt */,
31160DA81899540D0071EB17 /* lib.txt */,
31160DA91899540D0071EB17 /* lock.txt */,
31160DAA1899540D0071EB17 /* locus.txt */,
@ -2216,7 +2223,6 @@
3114A613156E944A001E0AA3 /* bttest.c */,
2291A5AA175CAA9B001D4920 /* exposet0.c */,
2291A5AB175CAA9B001D4920 /* expt825.c */,
2291A5E9175CB4EC001D4920 /* fbmtest.c */,
3114A5CD156E9369001E0AA3 /* finalcv.c */,
3114A5E5156E93B9001E0AA3 /* finaltest.c */,
3124CAC6156BE48D00753214 /* fmtdy.c */,
@ -2230,6 +2236,7 @@
22FACED6188807FF000FDBC1 /* fmtscheme.c */,
22FACED7188807FF000FDBC1 /* fmtscheme.h */,
224CC79E175E3202002FF81B /* fotest.c */,
2291A5E9175CB4EC001D4920 /* landtest.c */,
2231BB6818CA9834002D6322 /* locbwcss.c */,
31D60036156D3E0200337B26 /* lockcov.c */,
2231BB6918CA983C002D6322 /* locusss.c */,
@ -2321,7 +2328,7 @@
3114A605156E9430001E0AA3 /* bttest */,
3114A61C156E9485001E0AA3 /* teletest */,
3114A633156E94DB001E0AA3 /* abqtest */,
3114A64C156E9596001E0AA3 /* fbmtest */,
3114A64C156E9596001E0AA3 /* landtest */,
3114A662156E95D9001E0AA3 /* btcv */,
3114A67C156E9668001E0AA3 /* mv2test */,
3114A695156E971B001E0AA3 /* messtest */,
@ -2376,10 +2383,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 */,
@ -3011,9 +3021,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 */,
@ -3024,9 +3034,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 */ = {
@ -3411,11 +3421,11 @@
318DA8C31892B0F30089718C /* djbench */,
2291A5D3175CB05F001D4920 /* exposet0 */,
2291A5C1175CAFCA001D4920 /* expt825 */,
3114A64B156E9596001E0AA3 /* fbmtest */,
3114A5BC156E9315001E0AA3 /* finalcv */,
3114A5D5156E93A0001E0AA3 /* finaltest */,
224CC78C175E1821002FF81B /* fotest */,
6313D46718A400B200EB03EF /* gcbench */,
3114A64B156E9596001E0AA3 /* landtest */,
2231BB4C18CA97D8002D6322 /* locbwcss */,
31D60026156D3D3E00337B26 /* lockcov */,
2231BB5A18CA97DC002D6322 /* locusss */,
@ -3801,7 +3811,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2291A5ED175CB5E2001D4920 /* fbmtest.c in Sources */,
2291A5ED175CB5E2001D4920 /* landtest.c in Sources */,
3114A672156E95F6001E0AA3 /* testlib.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -4341,7 +4351,7 @@
};
3114A65B156E95B4001E0AA3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 3114A64B156E9596001E0AA3 /* fbmtest */;
target = 3114A64B156E9596001E0AA3 /* landtest */;
targetProxy = 3114A65A156E95B4001E0AA3 /* PBXContainerItemProxy */;
};
3114A675156E9619001E0AA3 /* PBXTargetDependency */ = {
@ -6104,7 +6114,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "fbmtest" */ = {
3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "landtest" */ = {
isa = XCConfigurationList;
buildConfigurations = (
3114A654156E9596001E0AA3 /* Debug */,

View file

@ -9,22 +9,6 @@
#include "mps.h"
/* The mvt pool class has five extra parameters to mps_pool_create:
* mps_res_t mps_pool_create(mps_pool_t * pool, mps_arena_t arena,
* mps_class_t mvt_class,
* size_t minimum_size,
* size_t mean_size,
* size_t maximum_size,
* mps_count_t reserve_depth
* mps_count_t fragmentation_limit);
* minimum_, mean_, and maximum_size are the mimimum, mean, and
* maximum (typical) size of objects expected to be allocated in the
* pool. reserve_depth is a measure of the expected hysteresis of the
* object population. fragmentation_limit is a percentage (between 0
* and 100): if the free space managed by the pool exceeds the
* specified percentage, the pool will resort to a "first fit"
* allocation policy.
*/
extern mps_class_t mps_class_mvt(void);
/* The mvt pool class supports two extensions to the pool protocol:

View file

@ -6,14 +6,10 @@
#include <stdio.h>
#include <stdarg.h>
#include "mpstd.h"
#include <time.h>
#include "mpscmvt.h"
#include "mps.h"
typedef mps_word_t mps_count_t; /* machine word (target dep.) */
#include "mpslib.h"
#include "mpsavm.h"
#include "testlib.h"
@ -71,11 +67,11 @@ static size_t randomSize(unsigned long i)
#define TEST_SET_SIZE 1234
#define TEST_LOOPS 27
static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size)
static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size, mps_align_t align)
{
mps_res_t res;
size = alignUp(size, MPS_PF_ALIGN);
size = alignUp(size, align);
do {
MPS_RESERVE_BLOCK(res, *p, ap, size);
@ -87,8 +83,9 @@ static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size)
}
static mps_res_t stress(mps_class_t class, mps_arena_t arena,
size_t (*size)(unsigned long i), mps_arg_s args[])
static mps_res_t stress(mps_arena_t arena, mps_align_t align,
size_t (*size)(unsigned long i),
mps_class_t class, mps_arg_s args[])
{
mps_res_t res;
mps_ap_t ap;
@ -105,7 +102,7 @@ static mps_res_t stress(mps_class_t class, mps_arena_t arena,
for(i=0; i<TEST_SET_SIZE; ++i) {
ss[i] = (*size)(i);
res = make((mps_addr_t *)&ps[i], ap, ss[i]);
res = make((mps_addr_t *)&ps[i], ap, ss[i], align);
if(res != MPS_RES_OK)
ss[i] = 0;
else
@ -144,7 +141,7 @@ static mps_res_t stress(mps_class_t class, mps_arena_t arena,
/* allocate some new objects */
for(i=x; i<TEST_SET_SIZE; ++i) {
size_t s = (*size)(i);
res = make((mps_addr_t *)&ps[i], ap, s);
res = make((mps_addr_t *)&ps[i], ap, s, align);
if(res != MPS_RES_OK)
break;
ss[i] = s;
@ -166,33 +163,29 @@ static mps_res_t stress(mps_class_t class, mps_arena_t arena,
}
static void stress_with_arena_class(mps_arena_class_t aclass, Bool zoned)
static void test_in_arena(mps_arena_class_t arena_class, mps_arg_s *arena_args)
{
mps_arena_t arena;
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, zoned);
die(mps_arena_create_k(&arena, aclass, args),
"mps_arena_create");
} MPS_ARGS_END(args);
die(mps_arena_create_k(&arena, arena_class, arena_args),
"mps_arena_create");
size_min = MPS_PF_ALIGN;
size_mean = 42;
size_max = 8192;
MPS_ARGS_BEGIN(args) {
mps_align_t align = sizeof(void *) << (rnd() % 4);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
MPS_ARGS_ADD(args, MPS_KEY_MIN_SIZE, size_min);
MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, size_mean);
MPS_ARGS_ADD(args, MPS_KEY_MAX_SIZE, size_max);
MPS_ARGS_ADD(args, MPS_KEY_MVT_RESERVE_DEPTH, TEST_SET_SIZE/2);
MPS_ARGS_ADD(args, MPS_KEY_MVT_FRAG_LIMIT, 0.3);
die(stress(mps_class_mvt(), arena, randomSize, args), "stress MVT");
die(stress(arena, align, randomSize, mps_class_mvt(), args), "stress MVT");
} MPS_ARGS_END(args);
mps_arena_destroy(arena);
return;
}
@ -200,8 +193,16 @@ int main(int argc, char *argv[])
{
testlib_init(argc, argv);
stress_with_arena_class(mps_arena_class_vm(), TRUE);
stress_with_arena_class(mps_arena_class_vm(), FALSE);
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
test_in_arena(mps_arena_class_vm(), args);
} MPS_ARGS_END(args);
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE);
test_in_arena(mps_arena_class_vm(), args);
} MPS_ARGS_END(args);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
return 0;

View file

@ -285,7 +285,6 @@ Res PoolAlloc(Addr *pReturn, Pool pool, Size size,
AVER(pReturn != NULL);
AVERT(Pool, pool);
AVER(PoolHasAttr(pool, AttrALLOC));
AVER(size > 0);
AVERT(Bool, withReservoirPermit);
@ -315,7 +314,6 @@ Res PoolAlloc(Addr *pReturn, Pool pool, Size size,
void PoolFree(Pool pool, Addr old, Size size)
{
AVERT(Pool, pool);
AVER(PoolHasAttr(pool, AttrFREE));
AVER(old != NULL);
/* The pool methods should check that old is in pool. */
AVER(size > 0);
@ -380,7 +378,6 @@ Res PoolScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg)
AVER(totalReturn != NULL);
AVERT(ScanState, ss);
AVERT(Pool, pool);
AVER(PoolHasAttr(pool, AttrSCAN));
AVERT(Seg, seg);
AVER(ss->arena == pool->arena);

View file

@ -18,7 +18,6 @@
*
* .hierarchy: define the following hierarchy of abstract pool classes:
* AbstractPoolClass - implements init, finish, describe
* AbstractAllocFreePoolClass - implements alloc & free
* AbstractBufferPoolClass - implements the buffer protocol
* AbstractSegBufPoolClass - uses SegBuf buffer class
* AbstractScanPoolClass - implements basic scanning
@ -31,7 +30,6 @@ SRCID(poolabs, "$Id$");
typedef PoolClassStruct AbstractPoolClassStruct;
typedef PoolClassStruct AbstractAllocFreePoolClassStruct;
typedef PoolClassStruct AbstractBufferPoolClassStruct;
typedef PoolClassStruct AbstractSegBufPoolClassStruct;
typedef PoolClassStruct AbstractScanPoolClassStruct;
@ -49,23 +47,11 @@ typedef PoolClassStruct AbstractCollectPoolClassStruct;
*/
/* PoolClassMixInAllocFree -- mix in the protocol for Alloc / Free */
void PoolClassMixInAllocFree(PoolClass class)
{
/* Can't check class because it's not initialized yet */
class->attr |= (AttrALLOC | AttrFREE);
class->alloc = PoolTrivAlloc;
class->free = PoolTrivFree;
}
/* PoolClassMixInBuffer -- mix in the protocol for buffer reserve / commit */
void PoolClassMixInBuffer(PoolClass class)
{
/* Can't check class because it's not initialized yet */
class->attr |= AttrBUF;
class->bufferFill = PoolTrivBufferFill;
class->bufferEmpty = PoolTrivBufferEmpty;
/* By default, buffered pools treat frame operations as NOOPs */
@ -81,7 +67,6 @@ void PoolClassMixInBuffer(PoolClass class)
void PoolClassMixInScan(PoolClass class)
{
/* Can't check class because it's not initialized yet */
class->attr |= AttrSCAN;
class->access = PoolSegAccess;
class->blacken = PoolTrivBlacken;
class->grey = PoolTrivGrey;
@ -164,12 +149,6 @@ DEFINE_CLASS(AbstractPoolClass, class)
class->sig = PoolClassSig;
}
DEFINE_CLASS(AbstractAllocFreePoolClass, class)
{
INHERIT_CLASS(class, AbstractPoolClass);
PoolClassMixInAllocFree(class);
}
DEFINE_CLASS(AbstractBufferPoolClass, class)
{
INHERIT_CLASS(class, AbstractPoolClass);

View file

@ -45,7 +45,6 @@ typedef struct amcGenStruct {
PoolGenStruct pgen;
RingStruct amcRing; /* link in list of gens in pool */
Buffer forward; /* forwarding buffer */
Count segs; /* number of segs in gen */
Sig sig; /* <code/misc.h#sig> */
} amcGenStruct;
@ -72,12 +71,19 @@ enum {
/* amcSegStruct -- AMC-specific fields appended to GCSegStruct
*
* .seg-ramp-new: The "new" flag is usually true, and indicates that the
* segment has been counted towards the pool generation's newSize. It is
* set to FALSE otherwise. This is used by both ramping and hash array
* allocations. TODO: The code for this is scrappy and needs refactoring,
* and the *reasons* for setting these flags need properly documenting.
* RB 2013-07-17
* .seq.old: The "old" flag is FALSE if the segment has never been
* collected, and so its size is accounted against the pool
* generation's newSize; it is TRUE if the segment has been collected
* at least once, and so its size is accounted against the pool
* generation's oldSize.
*
* .seg.deferred: The "deferred" flag is TRUE if its size accounting
* in the pool generation has been deferred. This is set if the
* segment was created in ramping mode (and so we don't want it to
* contribute to the pool generation's newSize and so provoke a
* collection via TracePoll), and by hash array allocations (where we
* don't want the allocation to provoke a collection that makes the
* location dependency stale immediately).
*/
typedef struct amcSegStruct *amcSeg;
@ -88,7 +94,8 @@ typedef struct amcSegStruct {
GCSegStruct gcSegStruct; /* superclass fields must come first */
amcGen gen; /* generation this segment belongs to */
Nailboard board; /* nailboard for this segment or NULL if none */
Bool new; /* .seg-ramp-new */
BOOLFIELD(old); /* .seg.old */
BOOLFIELD(deferred); /* .seg.deferred */
Sig sig; /* <code/misc.h#sig> */
} amcSegStruct;
@ -106,7 +113,8 @@ static Bool amcSegCheck(amcSeg amcseg)
CHECKD(Nailboard, amcseg->board);
CHECKL(SegNailed(amcSeg2Seg(amcseg)) != TraceSetEMPTY);
}
CHECKL(BoolCheck(amcseg->new));
/* CHECKL(BoolCheck(amcseg->old)); <design/type/#bool.bitfield.check> */
/* CHECKL(BoolCheck(amcseg->deferred)); <design/type/#bool.bitfield.check> */
return TRUE;
}
@ -141,7 +149,8 @@ static Res AMCSegInit(Seg seg, Pool pool, Addr base, Size size,
amcseg->gen = amcgen;
amcseg->board = NULL;
amcseg->new = TRUE;
amcseg->old = FALSE;
amcseg->deferred = FALSE;
amcseg->sig = amcSegSig;
AVERT(amcSeg, amcseg);
@ -478,7 +487,6 @@ typedef struct AMCStruct { /* <design/poolamc/#struct> */
ATTRIBUTE_UNUSED
static Bool amcGenCheck(amcGen gen)
{
Arena arena;
AMC amc;
CHECKS(amcGen, gen);
@ -487,9 +495,7 @@ static Bool amcGenCheck(amcGen gen)
CHECKU(AMC, amc);
CHECKD(Buffer, gen->forward);
CHECKD_NOSIG(Ring, &gen->amcRing);
CHECKL((gen->pgen.totalSize == 0) == (gen->segs == 0));
arena = amc->poolStruct.arena;
CHECKL(gen->pgen.totalSize >= gen->segs * ArenaAlign(arena));
return TRUE;
}
@ -666,7 +672,6 @@ static Res amcGenCreate(amcGen *genReturn, AMC amc, GenDesc gen)
if(res != ResOK)
goto failGenInit;
RingInit(&amcgen->amcRing);
amcgen->segs = 0;
amcgen->forward = buffer;
amcgen->sig = amcGenSig;
@ -693,8 +698,6 @@ static void amcGenDestroy(amcGen gen)
Arena arena;
AVERT(amcGen, gen);
AVER(gen->segs == 0);
AVER(gen->pgen.totalSize == 0);
EVENT1(AMCGenDestroy, gen);
arena = PoolArena(amcGenPool(gen));
@ -720,7 +723,7 @@ static Res amcGenDescribe(amcGen gen, mps_lib_FILE *stream)
" amcGen $P {\n", (WriteFP)gen,
" buffer $P\n", gen->forward,
" segs $U, totalSize $U, newSize $U\n",
(WriteFU)gen->segs,
(WriteFU)gen->pgen.segs,
(WriteFU)gen->pgen.totalSize,
(WriteFU)gen->pgen.newSize,
" } amcGen\n", NULL);
@ -942,21 +945,19 @@ static void AMCFinish(Pool pool)
RING_FOR(node, &amc->genRing, nextNode) {
amcGen gen = RING_ELT(amcGen, amcRing, node);
BufferDetach(gen->forward, pool);
/* Maintain invariant < totalSize. */
gen->pgen.newSize = (Size)0;
}
ring = PoolSegRing(pool);
RING_FOR(node, ring, nextNode) {
Seg seg = SegOfPoolRing(node);
Size size;
amcGen gen = amcSegGen(seg);
--gen->segs;
size = SegSize(seg);
gen->pgen.totalSize -= size;
SegFree(seg);
amcSeg amcseg = Seg2amcSeg(seg);
AVERT(amcSeg, amcseg);
PoolGenFree(&gen->pgen, seg,
0,
amcseg->old ? SegSize(seg) : 0,
amcseg->old ? 0 : SegSize(seg),
amcseg->deferred);
}
/* Disassociate forwarding buffers from gens before they are */
@ -992,7 +993,6 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn,
amcGen gen;
PoolGen pgen;
amcBuf amcbuf;
Bool isRamping;
AVERT(Pool, pool);
amc = Pool2AMC(pool);
@ -1013,7 +1013,7 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn,
pgen = &gen->pgen;
/* Create and attach segment. The location of this segment is */
/* expressed as a generation number. We rely on the arena to */
/* expressed via the pool generation. We rely on the arena to */
/* organize locations appropriately. */
alignedSize = SizeAlignUp(size, ArenaAlign(arena));
MPS_ARGS_BEGIN(args) {
@ -1031,23 +1031,17 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn,
else
SegSetRankAndSummary(seg, BufferRankSet(buffer), RefSetUNIV);
/* Put the segment in the generation indicated by the buffer. */
++gen->segs;
pgen->totalSize += alignedSize;
/* If ramping, or if the buffer is intended for allocating
hash table arrays, don't count it towards newSize. */
isRamping = (amc->rampMode == RampRAMPING &&
buffer == amc->rampGen->forward &&
gen == amc->rampGen);
if (isRamping || amcbuf->forHashArrays) {
Seg2amcSeg(seg)->new = FALSE;
} else {
pgen->newSize += alignedSize;
/* If ramping, or if the buffer is intended for allocating hash
* table arrays, defer the size accounting. */
if ((amc->rampMode == RampRAMPING
&& buffer == amc->rampGen->forward
&& gen == amc->rampGen)
|| amcbuf->forHashArrays)
{
Seg2amcSeg(seg)->deferred = TRUE;
}
base = SegBase(seg);
*baseReturn = base;
if(alignedSize < AMCLargeSegPAGES * ArenaAlign(arena)) {
/* Small or Medium segment: give the buffer the entire seg. */
limit = AddrAdd(base, alignedSize);
@ -1069,6 +1063,9 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn,
ShieldCover(arena, seg);
}
}
PoolGenAccountForFill(pgen, SegSize(seg), Seg2amcSeg(seg)->deferred);
*baseReturn = base;
*limitReturn = limit;
return ResOK;
}
@ -1111,6 +1108,11 @@ static void AMCBufferEmpty(Pool pool, Buffer buffer,
(*pool->format->pad)(init, size);
ShieldCover(arena, seg);
}
/* The unused part of the buffer is not reused by AMC, so we pass 0
* for the unused argument. This call therefore has no effect on the
* accounting, but we call it anyway for consistency. */
PoolGenAccountForEmpty(&amcSegGen(seg)->pgen, 0, Seg2amcSeg(seg)->deferred);
}
@ -1174,16 +1176,19 @@ static void AMCRampEnd(Pool pool, Buffer buf)
NOTREACHED;
}
/* Adjust amc->rampGen->pgen.newSize: Now count all the segments */
/* in the ramp generation as new (except if they're white). */
/* Now all the segments in the ramp generation contribute to the
* pool generation's sizes. */
RING_FOR(node, PoolSegRing(pool), nextNode) {
Seg seg = SegOfPoolRing(node);
if(amcSegGen(seg) == amc->rampGen && !Seg2amcSeg(seg)->new
amcSeg amcseg = Seg2amcSeg(seg);
if(amcSegGen(seg) == amc->rampGen
&& amcseg->deferred
&& SegWhite(seg) == TraceSetEMPTY)
{
pgen->newSize += SegSize(seg);
Seg2amcSeg(seg)->new = TRUE;
PoolGenUndefer(pgen,
amcseg->old ? SegSize(seg) : 0,
amcseg->old ? 0 : SegSize(seg));
amcseg->deferred = FALSE;
}
}
}
@ -1197,14 +1202,17 @@ static void AMCRampEnd(Pool pool, Buffer buf)
*/
static Res AMCWhiten(Pool pool, Trace trace, Seg seg)
{
Size condemned = 0;
amcGen gen;
AMC amc;
Buffer buffer;
amcSeg amcseg;
Res res;
AVERT(Pool, pool);
AVERT(Trace, trace);
AVERT(Seg, seg);
amcseg = Seg2amcSeg(seg);
buffer = SegBuffer(seg);
if(buffer != NULL) {
@ -1260,14 +1268,14 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg)
/* @@@@ We could subtract all the nailed grains. */
/* Relies on unsigned arithmetic wrapping round */
/* on under- and overflow (which it does). */
trace->condemned -= AddrOffset(BufferScanLimit(buffer),
BufferLimit(buffer));
condemned -= AddrOffset(BufferScanLimit(buffer), BufferLimit(buffer));
}
}
}
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
trace->condemned += SegSize(seg);
condemned += SegSize(seg);
trace->condemned += condemned;
amc = Pool2AMC(pool);
AVERT(AMC, amc);
@ -1291,9 +1299,9 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg)
gen = amcSegGen(seg);
AVERT(amcGen, gen);
if(Seg2amcSeg(seg)->new) {
gen->pgen.newSize -= SegSize(seg);
Seg2amcSeg(seg)->new = FALSE;
if (!amcseg->old) {
PoolGenAccountForAge(&gen->pgen, SegSize(seg), amcseg->deferred);
amcseg->old = TRUE;
}
/* Ensure we are forwarding into the right generation. */
@ -1994,9 +2002,7 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg)
/* We may not free a buffered seg. */
AVER(SegBuffer(seg) == NULL);
--gen->segs;
gen->pgen.totalSize -= SegSize(seg);
SegFree(seg);
PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, Seg2amcSeg(seg)->deferred);
} else {
/* Seg retained */
STATISTIC_STAT( {
@ -2040,7 +2046,6 @@ static void AMCReclaim(Pool pool, Trace trace, Seg seg)
{
AMC amc;
amcGen gen;
Size size;
AVERT_CRITICAL(Pool, pool);
amc = Pool2AMC(pool);
@ -2073,13 +2078,9 @@ static void AMCReclaim(Pool pool, Trace trace, Seg seg)
/* segs should have been nailed anyway). */
AVER(SegBuffer(seg) == NULL);
--gen->segs;
size = SegSize(seg);
gen->pgen.totalSize -= size;
trace->reclaimSize += SegSize(seg);
trace->reclaimSize += size;
SegFree(seg);
PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, Seg2amcSeg(seg)->deferred);
}

View file

@ -55,7 +55,7 @@ Bool AMSSegCheck(AMSSeg amsseg)
CHECKL(amsseg->grains == AMSGrains(amsseg->ams, SegSize(seg)));
CHECKL(amsseg->grains > 0);
CHECKL(amsseg->grains >= amsseg->free + amsseg->newAlloc);
CHECKL(amsseg->grains == amsseg->freeGrains + amsseg->oldGrains + amsseg->newGrains);
CHECKL(BoolCheck(amsseg->allocTableInUse));
if (!amsseg->allocTableInUse)
@ -94,7 +94,7 @@ void AMSSegFreeWalk(AMSSeg amsseg, FreeBlockStepMethod f, void *p)
pool = SegPool(AMSSeg2Seg(amsseg));
seg = AMSSeg2Seg(amsseg);
if (amsseg->free == 0)
if (amsseg->freeGrains == 0)
return;
if (amsseg->allocTableInUse) {
Index base, limit, next;
@ -107,10 +107,8 @@ void AMSSegFreeWalk(AMSSeg amsseg, FreeBlockStepMethod f, void *p)
(*f)(AMS_INDEX_ADDR(seg, base), AMS_INDEX_ADDR(seg, limit), pool, p);
next = limit + 1;
}
} else {
if ( amsseg->firstFree < amsseg->grains )
(*f)(AMS_INDEX_ADDR(seg, amsseg->firstFree), SegLimit(seg), pool, p);
}
} else if (amsseg->firstFree < amsseg->grains)
(*f)(AMS_INDEX_ADDR(seg, amsseg->firstFree), SegLimit(seg), pool, p);
}
@ -129,7 +127,7 @@ void AMSSegFreeCheck(AMSSeg amsseg)
AVERT(AMSSeg, amsseg);
if (amsseg->free == 0)
if (amsseg->freeGrains == 0)
return;
/* If it's not a debug class, don't bother walking. */
@ -241,8 +239,9 @@ static Res AMSSegInit(Seg seg, Pool pool, Addr base, Size size,
goto failNextMethod;
amsseg->grains = size >> ams->grainShift;
amsseg->free = amsseg->grains;
amsseg->newAlloc = (Count)0;
amsseg->freeGrains = amsseg->grains;
amsseg->oldGrains = (Count)0;
amsseg->newGrains = (Count)0;
amsseg->marksChanged = FALSE; /* <design/poolams/#marked.unused> */
amsseg->ambiguousFixes = FALSE;
@ -263,7 +262,6 @@ static Res AMSSegInit(Seg seg, Pool pool, Addr base, Size size,
&amsseg->segRing);
amsseg->sig = AMSSegSig;
ams->size += size;
AVERT(AMSSeg, amsseg);
return ResOK;
@ -299,8 +297,6 @@ static void AMSSegFinish(Seg seg)
RingRemove(&amsseg->segRing);
RingFinish(&amsseg->segRing);
AVER(ams->size >= SegSize(seg));
ams->size -= SegSize(seg);
amsseg->sig = SigInvalid;
/* finish the superclass fields last */
@ -359,7 +355,7 @@ static Res AMSSegMerge(Seg seg, Seg segHi,
/* checks for .grain-align */
AVER(allGrains == AddrOffset(base, limit) >> ams->grainShift);
/* checks for .empty */
AVER(amssegHi->free == hiGrains);
AVER(amssegHi->freeGrains == hiGrains);
AVER(!amssegHi->marksChanged);
/* .alloc-early */
@ -393,8 +389,9 @@ static Res AMSSegMerge(Seg seg, Seg segHi,
MERGE_TABLES(nonwhiteTable, BTSetRange);
amsseg->grains = allGrains;
amsseg->free = amsseg->free + amssegHi->free;
amsseg->newAlloc = amsseg->newAlloc + amssegHi->newAlloc;
amsseg->freeGrains = amsseg->freeGrains + amssegHi->freeGrains;
amsseg->oldGrains = amsseg->oldGrains + amssegHi->oldGrains;
amsseg->newGrains = amsseg->newGrains + amssegHi->newGrains;
/* other fields in amsseg are unaffected */
RingRemove(&amssegHi->segRing);
@ -402,6 +399,7 @@ static Res AMSSegMerge(Seg seg, Seg segHi,
amssegHi->sig = SigInvalid;
AVERT(AMSSeg, amsseg);
PoolGenAccountForSegMerge(&ams->pgen);
return ResOK;
failSuper:
@ -443,7 +441,7 @@ static Res AMSSegSplit(Seg seg, Seg segHi,
/* checks for .grain-align */
AVER(allGrains == amsseg->grains);
/* checks for .empty */
AVER(amsseg->free >= hiGrains);
AVER(amsseg->freeGrains >= hiGrains);
if (amsseg->allocTableInUse) {
AVER(BTIsResRange(amsseg->allocTable, loGrains, allGrains));
} else {
@ -485,9 +483,11 @@ static Res AMSSegSplit(Seg seg, Seg segHi,
amsseg->grains = loGrains;
amssegHi->grains = hiGrains;
amsseg->free -= hiGrains;
amssegHi->free = hiGrains;
amssegHi->newAlloc = (Count)0;
AVER(amsseg->freeGrains >= hiGrains);
amsseg->freeGrains -= hiGrains;
amssegHi->freeGrains = hiGrains;
amssegHi->oldGrains = (Count)0;
amssegHi->newGrains = (Count)0;
amssegHi->marksChanged = FALSE; /* <design/poolams/#marked.unused> */
amssegHi->ambiguousFixes = FALSE;
@ -505,6 +505,7 @@ static Res AMSSegSplit(Seg seg, Seg segHi,
amssegHi->sig = AMSSegSig;
AVERT(AMSSeg, amsseg);
AVERT(AMSSeg, amssegHi);
PoolGenAccountForSegSplit(&ams->pgen);
return ResOK;
failSuper:
@ -553,6 +554,9 @@ static Res AMSSegDescribe(Seg seg, mps_lib_FILE *stream)
res = WriteF(stream,
" AMS $P\n", (WriteFP)amsseg->ams,
" grains $W\n", (WriteFW)amsseg->grains,
" freeGrains $W\n", (WriteFW)amsseg->freeGrains,
" oldGrains $W\n", (WriteFW)amsseg->oldGrains,
" newGrains $W\n", (WriteFW)amsseg->newGrains,
NULL);
if (res != ResOK) return res;
if (amsseg->allocTableInUse)
@ -731,9 +735,15 @@ static void AMSSegsDestroy(AMS ams)
ring = PoolSegRing(AMS2Pool(ams));
RING_FOR(node, ring, next) {
Seg seg = SegOfPoolRing(node);
AVER(Seg2AMSSeg(seg)->ams == ams);
AMSSegFreeCheck(Seg2AMSSeg(seg));
SegFree(seg);
AMSSeg amsseg = Seg2AMSSeg(seg);
AVERT(AMSSeg, amsseg);
AVER(amsseg->ams == ams);
AMSSegFreeCheck(amsseg);
PoolGenFree(&ams->pgen, seg,
AMSGrainsSize(ams, amsseg->freeGrains),
AMSGrainsSize(ams, amsseg->oldGrains),
AMSGrainsSize(ams, amsseg->newGrains),
FALSE);
}
}
@ -836,8 +846,6 @@ Res AMSInitInternal(AMS ams, Format format, Chain chain, unsigned gen,
ams->segsDestroy = AMSSegsDestroy;
ams->segClass = AMSSegClassGet;
ams->size = 0;
ams->sig = AMSSig;
AVERT(AMS, ams);
return ResOK;
@ -904,15 +912,17 @@ static Bool amsSegAlloc(Index *baseReturn, Index *limitReturn,
} else {
if (amsseg->firstFree > amsseg->grains - grains)
return FALSE;
base = amsseg->firstFree; limit = amsseg->grains;
base = amsseg->firstFree;
limit = amsseg->grains;
amsseg->firstFree = limit;
}
/* We don't place buffers on white segments, so no need to adjust colour. */
AVER(!amsseg->colourTablesInUse);
amsseg->free -= limit - base;
amsseg->newAlloc += limit - base;
AVER(amsseg->freeGrains >= limit - base);
amsseg->freeGrains -= limit - base;
amsseg->newGrains += limit - base;
*baseReturn = base;
*limitReturn = limit;
return TRUE;
@ -958,12 +968,15 @@ static Res AMSBufferFill(Addr *baseReturn, Addr *limitReturn,
RING_FOR(node, ring, nextNode) {
AMSSeg amsseg = RING_ELT(AMSSeg, segRing, node);
AVERT_CRITICAL(AMSSeg, amsseg);
if (amsseg->free >= AMSGrains(ams, size)) {
if (amsseg->freeGrains >= AMSGrains(ams, size)) {
seg = AMSSeg2Seg(amsseg);
if (SegRankSet(seg) == rankSet && SegBuffer(seg) == NULL
if (SegRankSet(seg) == rankSet
&& SegBuffer(seg) == NULL
/* Can't use a white or grey segment, see d.m.p.fill.colour. */
&& SegWhite(seg) == TraceSetEMPTY && SegGrey(seg) == TraceSetEMPTY) {
&& SegWhite(seg) == TraceSetEMPTY
&& SegGrey(seg) == TraceSetEMPTY)
{
b = amsSegAlloc(&base, &limit, seg, size);
if (b)
goto found;
@ -983,10 +996,10 @@ static Res AMSBufferFill(Addr *baseReturn, Addr *limitReturn,
baseAddr = AMS_INDEX_ADDR(seg, base); limitAddr = AMS_INDEX_ADDR(seg, limit);
DebugPoolFreeCheck(pool, baseAddr, limitAddr);
allocatedSize = AddrOffset(baseAddr, limitAddr);
ams->pgen.totalSize += allocatedSize;
ams->pgen.newSize += allocatedSize;
*baseReturn = baseAddr; *limitReturn = limitAddr;
PoolGenAccountForFill(&ams->pgen, allocatedSize, FALSE);
*baseReturn = baseAddr;
*limitReturn = limitAddr;
return ResOK;
}
@ -1040,9 +1053,9 @@ static void AMSBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit)
/* The nonwhiteTable is shared with allocTable and in use, so we
* mustn't start using allocTable. In this case we know: 1. the
* segment has been condemned (because colour tables are turned
* on in AMSCondemn); 2. the segment has not yet been reclaimed
* on in AMSWhiten); 2. the segment has not yet been reclaimed
* (because colour tables are turned off in AMSReclaim); 3. the
* unused portion of the buffer is black (see AMSCondemn). So we
* unused portion of the buffer is black (see AMSWhiten). So we
* need to whiten the unused portion of the buffer. The
* allocTable will be turned back on (if necessary) in
* AMSReclaim, when we know that the nonwhite grains are exactly
@ -1061,20 +1074,19 @@ static void AMSBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit)
if (amsseg->colourTablesInUse)
AMS_RANGE_WHITEN(seg, initIndex, limitIndex);
amsseg->free += limitIndex - initIndex;
/* The unused portion of the buffer must be new, since it's not condemned. */
AVER(amsseg->newAlloc >= limitIndex - initIndex);
amsseg->newAlloc -= limitIndex - initIndex;
amsseg->freeGrains += limitIndex - initIndex;
/* Unused portion of the buffer must be new, since it's not condemned. */
AVER(amsseg->newGrains >= limitIndex - initIndex);
amsseg->newGrains -= limitIndex - initIndex;
size = AddrOffset(init, limit);
ams->pgen.totalSize -= size;
ams->pgen.newSize -= size;
PoolGenAccountForEmpty(&ams->pgen, size, FALSE);
}
/* amsRangeCondemn -- Condemn a part of an AMS segment
/* amsRangeWhiten -- Condemn a part of an AMS segment
* Allow calling it with base = limit, to simplify the callers.
*/
static void amsRangeCondemn(Seg seg, Index base, Index limit)
static void amsRangeWhiten(Seg seg, Index base, Index limit)
{
if (base != limit) {
AMSSeg amsseg = Seg2AMSSeg(seg);
@ -1087,9 +1099,9 @@ static void amsRangeCondemn(Seg seg, Index base, Index limit)
}
/* AMSCondemn -- the pool class segment condemning method */
/* AMSWhiten -- the pool class segment condemning method */
static Res AMSCondemn(Pool pool, Trace trace, Seg seg)
static Res AMSWhiten(Pool pool, Trace trace, Seg seg)
{
AMS ams;
AMSSeg amsseg;
@ -1139,23 +1151,24 @@ static Res AMSCondemn(Pool pool, Trace trace, Seg seg)
scanLimitIndex = AMS_ADDR_INDEX(seg, BufferScanLimit(buffer));
limitIndex = AMS_ADDR_INDEX(seg, BufferLimit(buffer));
amsRangeCondemn(seg, 0, scanLimitIndex);
amsRangeWhiten(seg, 0, scanLimitIndex);
if (scanLimitIndex < limitIndex)
AMS_RANGE_BLACKEN(seg, scanLimitIndex, limitIndex);
amsRangeCondemn(seg, limitIndex, amsseg->grains);
amsRangeWhiten(seg, limitIndex, amsseg->grains);
/* We didn't condemn the buffer, subtract it from the count. */
uncondemned = limitIndex - scanLimitIndex;
} else { /* condemn whole seg */
amsRangeCondemn(seg, 0, amsseg->grains);
amsRangeWhiten(seg, 0, amsseg->grains);
uncondemned = (Count)0;
}
trace->condemned += SegSize(seg) - AMSGrainsSize(ams, uncondemned);
/* The unused part of the buffer is new allocation by definition. */
ams->pgen.newSize -= AMSGrainsSize(ams, amsseg->newAlloc - uncondemned);
amsseg->newAlloc = uncondemned;
/* The unused part of the buffer remains new: the rest becomes old. */
PoolGenAccountForAge(&ams->pgen, AMSGrainsSize(ams, amsseg->newGrains - uncondemned), FALSE);
amsseg->oldGrains += amsseg->newGrains - uncondemned;
amsseg->newGrains = uncondemned;
amsseg->marksChanged = FALSE; /* <design/poolams/#marked.condemn> */
amsseg->ambiguousFixes = FALSE;
trace->condemned += AMSGrainsSize(ams, amsseg->oldGrains);
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
@ -1561,8 +1574,7 @@ static void AMSReclaim(Pool pool, Trace trace, Seg seg)
{
AMS ams;
AMSSeg amsseg;
Count nowFree, grains;
Size reclaimedSize;
Count nowFree, grains, reclaimedGrains;
PoolDebugMixin debug;
AVERT(Pool, pool);
@ -1607,21 +1619,26 @@ static void AMSReclaim(Pool pool, Trace trace, Seg seg)
}
}
reclaimedSize = (nowFree - amsseg->free) << ams->grainShift;
amsseg->free = nowFree;
trace->reclaimSize += reclaimedSize;
ams->pgen.totalSize -= reclaimedSize;
reclaimedGrains = nowFree - amsseg->freeGrains;
AVER(amsseg->oldGrains >= reclaimedGrains);
amsseg->oldGrains -= reclaimedGrains;
amsseg->freeGrains += reclaimedGrains;
PoolGenAccountForReclaim(&ams->pgen, AMSGrainsSize(ams, reclaimedGrains), FALSE);
trace->reclaimSize += AMSGrainsSize(ams, reclaimedGrains);
/* preservedInPlaceCount is updated on fix */
trace->preservedInPlaceSize += (grains - amsseg->free) << ams->grainShift;
trace->preservedInPlaceSize += AMSGrainsSize(ams, amsseg->oldGrains);
/* Ensure consistency of segment even if are just about to free it */
amsseg->colourTablesInUse = FALSE;
SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace));
if (amsseg->free == grains && SegBuffer(seg) == NULL) {
if (amsseg->freeGrains == grains && SegBuffer(seg) == NULL)
/* No survivors */
SegFree(seg);
}
PoolGenFree(&ams->pgen, seg,
AMSGrainsSize(ams, amsseg->freeGrains),
AMSGrainsSize(ams, amsseg->oldGrains),
AMSGrainsSize(ams, amsseg->newGrains),
FALSE);
}
@ -1662,8 +1679,6 @@ static Res AMSDescribe(Pool pool, mps_lib_FILE *stream)
"AMS $P {\n", (WriteFP)ams,
" pool $P ($U)\n",
(WriteFP)pool, (WriteFU)pool->serial,
" size $W\n",
(WriteFW)ams->size,
" grain shift $U\n", (WriteFU)ams->grainShift,
NULL);
if (res != ResOK) return res;
@ -1708,7 +1723,7 @@ DEFINE_CLASS(AMSPoolClass, this)
this->bufferClass = RankBufClassGet;
this->bufferFill = AMSBufferFill;
this->bufferEmpty = AMSBufferEmpty;
this->whiten = AMSCondemn;
this->whiten = AMSWhiten;
this->blacken = AMSBlacken;
this->scan = AMSScan;
this->fix = AMSFix;
@ -1756,10 +1771,9 @@ Bool AMSCheck(AMS ams)
CHECKS(AMS, ams);
CHECKD(Pool, AMS2Pool(ams));
CHECKL(IsSubclassPoly(AMS2Pool(ams)->class, AMSPoolClassGet()));
CHECKL(PoolAlignment(AMS2Pool(ams)) == ((Size)1 << ams->grainShift));
CHECKL(PoolAlignment(AMS2Pool(ams)) == AMSGrainsSize(ams, (Size)1));
CHECKL(PoolAlignment(AMS2Pool(ams)) == AMS2Pool(ams)->format->alignment);
CHECKD(PoolGen, &ams->pgen);
CHECKL(SizeIsAligned(ams->size, ArenaAlign(PoolArena(AMS2Pool(ams)))));
CHECKL(FUNCHECK(ams->segSize));
CHECKD_NOSIG(Ring, &ams->segRing);
CHECKL(FUNCHECK(ams->allocRing));

View file

@ -57,9 +57,10 @@ typedef struct AMSSegStruct {
GCSegStruct gcSegStruct; /* superclass fields must come first */
AMS ams; /* owning ams */
RingStruct segRing; /* ring that this seg belongs to */
Count grains; /* number of grains */
Count free; /* number of free grains */
Count newAlloc; /* number of grains allocated since last GC */
Count grains; /* total grains */
Count freeGrains; /* free grains */
Count oldGrains; /* grains allocated prior to last collection */
Count newGrains; /* grains allocated since last collection */
Bool allocTableInUse; /* allocTable is used */
Index firstFree; /* 1st free grain, if allocTable is not used */
BT allocTable; /* set if grain is allocated */

View file

@ -85,7 +85,6 @@ typedef struct AWLStruct {
PoolStruct poolStruct;
Shift alignShift;
PoolGenStruct pgen; /* generation representing the pool */
Size size; /* allocated size in bytes */
Count succAccesses; /* number of successive single accesses */
FindDependentMethod findDependent; /* to find a dependent object */
awlStatTotalStruct stats;
@ -93,6 +92,7 @@ typedef struct AWLStruct {
} AWLStruct, *AWL;
#define Pool2AWL(pool) PARENT(AWLStruct, poolStruct, pool)
#define AWLGrainsSize(awl, grains) ((grains) << (awl)->alignShift)
static Bool AWLCheck(AWL awl);
@ -101,6 +101,8 @@ static Bool AWLCheck(AWL awl);
/* Conversion between indexes and Addrs */
#define awlIndexOfAddr(base, awl, p) \
(AddrOffset((base), (p)) >> (awl)->alignShift)
#define awlAddrOfIndex(base, awl, i) \
AddrAdd(base, AWLGrainsSize(awl, i))
/* AWLSegStruct -- AWL segment subclass
@ -117,8 +119,10 @@ typedef struct AWLSegStruct {
BT scanned;
BT alloc;
Count grains;
Count free; /* number of free grains */
Count singleAccesses; /* number of accesses processed singly */
Count freeGrains; /* free grains */
Count oldGrains; /* grains allocated prior to last collection */
Count newGrains; /* grains allocated since last collection */
Count singleAccesses; /* number of accesses processed singly */
awlStatSegStruct stats;
Sig sig;
} AWLSegStruct, *AWLSeg;
@ -138,9 +142,8 @@ static Bool AWLSegCheck(AWLSeg awlseg)
CHECKL(awlseg->mark != NULL);
CHECKL(awlseg->scanned != NULL);
CHECKL(awlseg->alloc != NULL);
/* Can't do any real check on ->grains */
CHECKL(awlseg->grains > 0);
CHECKL(awlseg->free <= awlseg->grains);
CHECKL(awlseg->grains == awlseg->freeGrains + awlseg->oldGrains + awlseg->newGrains);
return TRUE;
}
@ -223,10 +226,12 @@ static Res AWLSegInit(Seg seg, Pool pool, Addr base, Size size,
BTResRange(awlseg->scanned, 0, bits);
BTResRange(awlseg->alloc, 0, bits);
SegSetRankAndSummary(seg, rankSet, RefSetUNIV);
awlseg->free = bits;
awlseg->sig = AWLSegSig;
awlseg->freeGrains = bits;
awlseg->oldGrains = (Count)0;
awlseg->newGrains = (Count)0;
awlseg->singleAccesses = 0;
awlStatSegInit(awlseg);
awlseg->sig = AWLSegSig;
AVERT(AWLSeg, awlseg);
return ResOK;
@ -500,7 +505,7 @@ static Bool AWLSegAlloc(Addr *baseReturn, Addr *limitReturn,
AVERT(AWLSeg, awlseg);
AVERT(AWL, awl);
AVER(size > 0);
AVER(size << awl->alignShift >= size);
AVER(AWLGrainsSize(awl, size) >= size);
seg = AWLSeg2Seg(awlseg);
if (size > SegSize(seg))
@ -508,9 +513,8 @@ static Bool AWLSegAlloc(Addr *baseReturn, Addr *limitReturn,
n = size >> awl->alignShift;
if (!BTFindLongResRange(&i, &j, awlseg->alloc, 0, awlseg->grains, n))
return FALSE;
awl->size += size;
*baseReturn = AddrAdd(SegBase(seg), i << awl->alignShift);
*limitReturn = AddrAdd(SegBase(seg), j << awl->alignShift);
*baseReturn = awlAddrOfIndex(SegBase(seg), awl, i);
*limitReturn = awlAddrOfIndex(SegBase(seg),awl, j);
return TRUE;
}
@ -575,8 +579,6 @@ static Res AWLInit(Pool pool, ArgList args)
goto failGenInit;
awl->alignShift = SizeLog2(PoolAlignment(pool));
awl->size = (Size)0;
awl->succAccesses = 0;
awlStatTotalInit(awl);
awl->sig = AWLSig;
@ -606,8 +608,13 @@ static void AWLFinish(Pool pool)
ring = &pool->segRing;
RING_FOR(node, ring, nextNode) {
Seg seg = SegOfPoolRing(node);
AVERT(Seg, seg);
SegFree(seg);
AWLSeg awlseg = Seg2AWLSeg(seg);
AVERT(AWLSeg, awlseg);
PoolGenFree(&awl->pgen, seg,
AWLGrainsSize(awl, awlseg->freeGrains),
AWLGrainsSize(awl, awlseg->oldGrains),
AWLGrainsSize(awl, awlseg->newGrains),
FALSE);
}
awl->sig = SigInvalid;
PoolGenFinish(&awl->pgen);
@ -646,10 +653,11 @@ static Res AWLBufferFill(Addr *baseReturn, Addr *limitReturn,
/* Only try to allocate in the segment if it is not already */
/* buffered, and has the same ranks as the buffer. */
if (SegBuffer(seg) == NULL && SegRankSet(seg) == BufferRankSet(buffer))
if (awlseg->free << awl->alignShift >= size
&& AWLSegAlloc(&base, &limit, awlseg, awl, size))
goto found;
if (SegBuffer(seg) == NULL
&& SegRankSet(seg) == BufferRankSet(buffer)
&& AWLGrainsSize(awl, awlseg->freeGrains) >= size
&& AWLSegAlloc(&base, &limit, awlseg, awl, size))
goto found;
}
/* No free space in existing awlsegs, so create new awlseg */
@ -673,7 +681,10 @@ static Res AWLBufferFill(Addr *baseReturn, Addr *limitReturn,
/* Shouldn't this depend on trace phase? @@@@ */
BTSetRange(awlseg->mark, i, j);
BTSetRange(awlseg->scanned, i, j);
awlseg->free -= j - i;
AVER(awlseg->freeGrains >= j - i);
awlseg->freeGrains -= j - i;
awlseg->newGrains += j - i;
PoolGenAccountForFill(&awl->pgen, AddrOffset(base, limit), FALSE);
}
*baseReturn = base;
*limitReturn = limit;
@ -709,7 +720,10 @@ static void AWLBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit)
AVER(i <= j);
if (i < j) {
BTResRange(awlseg->alloc, i, j);
awlseg->free += j - i;
AVER(awlseg->newGrains >= j - i);
awlseg->newGrains -= j - i;
awlseg->freeGrains += j - i;
PoolGenAccountForEmpty(&awl->pgen, AddrOffset(init, limit), FALSE);
}
}
@ -735,6 +749,7 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg)
AWL awl;
AWLSeg awlseg;
Buffer buffer;
Count uncondemned;
/* All parameters checked by generic PoolWhiten. */
@ -750,15 +765,13 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg)
if(buffer == NULL) {
awlRangeWhiten(awlseg, 0, awlseg->grains);
trace->condemned += SegSize(seg);
uncondemned = (Count)0;
} else {
/* Whiten everything except the buffer. */
Addr base = SegBase(seg);
Index scanLimitIndex = awlIndexOfAddr(base, awl,
BufferScanLimit(buffer));
Index limitIndex = awlIndexOfAddr(base, awl,
BufferLimit(buffer));
Index scanLimitIndex = awlIndexOfAddr(base, awl, BufferScanLimit(buffer));
Index limitIndex = awlIndexOfAddr(base, awl, BufferLimit(buffer));
uncondemned = limitIndex - scanLimitIndex;
awlRangeWhiten(awlseg, 0, scanLimitIndex);
awlRangeWhiten(awlseg, limitIndex, awlseg->grains);
@ -769,14 +782,12 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg)
AVER(BTIsSetRange(awlseg->mark, scanLimitIndex, limitIndex));
AVER(BTIsSetRange(awlseg->scanned, scanLimitIndex, limitIndex));
}
/* We didn't condemn the buffer, subtract it from the count. */
/* @@@@ We could subtract all the free grains. */
trace->condemned += SegSize(seg)
- AddrOffset(BufferScanLimit(buffer),
BufferLimit(buffer));
}
PoolGenAccountForAge(&awl->pgen, AWLGrainsSize(awl, awlseg->newGrains - uncondemned), FALSE);
awlseg->oldGrains += awlseg->newGrains - uncondemned;
awlseg->newGrains = uncondemned;
trace->condemned += AWLGrainsSize(awl, awlseg->oldGrains);
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
return ResOK;
}
@ -1088,12 +1099,12 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg)
Addr base;
AWL awl;
AWLSeg awlseg;
Buffer buffer;
Index i;
Count oldFree;
Format format;
Count reclaimedGrains = (Count)0;
Count preservedInPlaceCount = (Count)0;
Size preservedInPlaceSize = (Size)0;
Size freed; /* amount reclaimed, in bytes */
AVERT(Pool, pool);
AVERT(Trace, trace);
@ -1107,8 +1118,9 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg)
format = pool->format;
base = SegBase(seg);
buffer = SegBuffer(seg);
i = 0; oldFree = awlseg->free;
i = 0;
while(i < awlseg->grains) {
Addr p, q;
Index j;
@ -1117,16 +1129,13 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg)
++i;
continue;
}
p = AddrAdd(base, i << awl->alignShift);
if(SegBuffer(seg) != NULL) {
Buffer buffer = SegBuffer(seg);
if(p == BufferScanLimit(buffer)
&& BufferScanLimit(buffer) != BufferLimit(buffer))
{
i = awlIndexOfAddr(base, awl, BufferLimit(buffer));
continue;
}
p = awlAddrOfIndex(base, awl, i);
if (buffer != NULL
&& p == BufferScanLimit(buffer)
&& BufferScanLimit(buffer) != BufferLimit(buffer))
{
i = awlIndexOfAddr(base, awl, BufferLimit(buffer));
continue;
}
q = format->skip(AddrAdd(p, format->headerSize));
q = AddrSub(q, format->headerSize);
@ -1143,20 +1152,30 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg)
BTResRange(awlseg->mark, i, j);
BTSetRange(awlseg->scanned, i, j);
BTResRange(awlseg->alloc, i, j);
awlseg->free += j - i;
reclaimedGrains += j - i;
}
i = j;
}
AVER(i == awlseg->grains);
freed = (awlseg->free - oldFree) << awl->alignShift;
awl->size -= freed;
trace->reclaimSize += freed;
AVER(reclaimedGrains <= awlseg->grains);
AVER(awlseg->oldGrains >= reclaimedGrains);
awlseg->oldGrains -= reclaimedGrains;
awlseg->freeGrains += reclaimedGrains;
PoolGenAccountForReclaim(&awl->pgen, AWLGrainsSize(awl, reclaimedGrains), FALSE);
trace->reclaimSize += AWLGrainsSize(awl, reclaimedGrains);
trace->preservedInPlaceCount += preservedInPlaceCount;
trace->preservedInPlaceSize += preservedInPlaceSize;
SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace));
/* @@@@ never frees a segment, see job001687. */
return;
if (awlseg->freeGrains == awlseg->grains && buffer == NULL)
/* No survivors */
PoolGenFree(&awl->pgen, seg,
AWLGrainsSize(awl, awlseg->freeGrains),
AWLGrainsSize(awl, awlseg->oldGrains),
AWLGrainsSize(awl, awlseg->newGrains),
FALSE);
}
@ -1304,7 +1323,7 @@ static Bool AWLCheck(AWL awl)
CHECKS(AWL, awl);
CHECKD(Pool, &awl->poolStruct);
CHECKL(awl->poolStruct.class == AWLPoolClassGet());
CHECKL((Align)1 << awl->alignShift == awl->poolStruct.alignment);
CHECKL(AWLGrainsSize(awl, (Count)1) == awl->poolStruct.alignment);
/* Nothing to check about succAccesses. */
CHECKL(FUNCHECK(awl->findDependent));
/* Don't bother to check stats. */

View file

@ -30,6 +30,7 @@ typedef struct LOStruct {
#define PoolPoolLO(pool) PARENT(LOStruct, poolStruct, pool)
#define LOPool(lo) (&(lo)->poolStruct)
#define LOGrainsSize(lo, grains) ((grains) << (lo)->alignShift)
/* forward declaration */
@ -47,8 +48,9 @@ typedef struct LOSegStruct {
LO lo; /* owning LO */
BT mark; /* mark bit table */
BT alloc; /* alloc bit table */
Count free; /* number of free grains */
Count newAlloc; /* number of grains allocated since last GC */
Count freeGrains; /* free grains */
Count oldGrains; /* grains allocated prior to last collection */
Count newGrains; /* grains allocated since last collection */
Sig sig; /* <code/misc.h#sig> */
} LOSegStruct;
@ -60,6 +62,7 @@ typedef struct LOSegStruct {
static Res loSegInit(Seg seg, Pool pool, Addr base, Size size,
Bool reservoirPermit, ArgList args);
static void loSegFinish(Seg seg);
static Count loSegGrains(LOSeg loseg);
/* LOSegClass -- Class definition for LO segments */
@ -87,8 +90,8 @@ static Bool LOSegCheck(LOSeg loseg)
CHECKL(loseg->mark != NULL);
CHECKL(loseg->alloc != NULL);
/* Could check exactly how many bits are set in the alloc table. */
CHECKL(loseg->free + loseg->newAlloc
<= SegSize(LOSegSeg(loseg)) >> loseg->lo->alignShift);
CHECKL(loseg->freeGrains + loseg->oldGrains + loseg->newGrains
== SegSize(LOSegSeg(loseg)) >> loseg->lo->alignShift);
return TRUE;
}
@ -105,7 +108,7 @@ static Res loSegInit(Seg seg, Pool pool, Addr base, Size size,
Size tablebytes; /* # bytes in each control array */
Arena arena;
/* number of bits needed in each control array */
Count bits;
Count grains;
void *p;
AVERT(Seg, seg);
@ -125,8 +128,8 @@ static Res loSegInit(Seg seg, Pool pool, Addr base, Size size,
AVER(SegWhite(seg) == TraceSetEMPTY);
bits = size >> lo->alignShift;
tablebytes = BTSize(bits);
grains = size >> lo->alignShift;
tablebytes = BTSize(grains);
res = ControlAlloc(&p, arena, tablebytes, reservoirPermit);
if(res != ResOK)
goto failMarkTable;
@ -135,11 +138,12 @@ static Res loSegInit(Seg seg, Pool pool, Addr base, Size size,
if(res != ResOK)
goto failAllocTable;
loseg->alloc = p;
BTResRange(loseg->alloc, 0, bits);
BTSetRange(loseg->mark, 0, bits);
BTResRange(loseg->alloc, 0, grains);
BTSetRange(loseg->mark, 0, grains);
loseg->lo = lo;
loseg->free = bits;
loseg->newAlloc = (Count)0;
loseg->freeGrains = grains;
loseg->oldGrains = (Count)0;
loseg->newGrains = (Count)0;
loseg->sig = LOSegSig;
AVERT(LOSeg, loseg);
return ResOK;
@ -162,7 +166,7 @@ static void loSegFinish(Seg seg)
Pool pool;
Arena arena;
Size tablesize;
Count bits;
Count grains;
AVERT(Seg, seg);
loseg = SegLOSeg(seg);
@ -172,8 +176,8 @@ static void loSegFinish(Seg seg)
AVERT(LO, lo);
arena = PoolArena(pool);
bits = SegSize(seg) >> lo->alignShift;
tablesize = BTSize(bits);
grains = loSegGrains(loseg);
tablesize = BTSize(grains);
ControlFree(arena, (Addr)loseg->alloc, tablesize);
ControlFree(arena, (Addr)loseg->mark, tablesize);
loseg->sig = SigInvalid;
@ -185,7 +189,7 @@ static void loSegFinish(Seg seg)
ATTRIBUTE_UNUSED
static Count loSegBits(LOSeg loseg)
static Count loSegGrains(LOSeg loseg)
{
LO lo;
Size size;
@ -204,7 +208,7 @@ static Count loSegBits(LOSeg loseg)
(AddrOffset((base), (p)) >> (lo)->alignShift)
#define loAddrOfIndex(base, lo, i) \
(AddrAdd((base), (i) << (lo)->alignShift))
(AddrAdd((base), LOGrainsSize((lo), (i))))
/* loSegFree -- mark block from baseIndex to limitIndex free */
@ -213,12 +217,11 @@ static void loSegFree(LOSeg loseg, Index baseIndex, Index limitIndex)
{
AVERT(LOSeg, loseg);
AVER(baseIndex < limitIndex);
AVER(limitIndex <= loSegBits(loseg));
AVER(limitIndex <= loSegGrains(loseg));
AVER(BTIsSetRange(loseg->alloc, baseIndex, limitIndex));
BTResRange(loseg->alloc, baseIndex, limitIndex);
BTSetRange(loseg->mark, baseIndex, limitIndex);
loseg->free += limitIndex - baseIndex;
}
@ -233,7 +236,7 @@ static Bool loSegFindFree(Addr *bReturn, Addr *lReturn,
LO lo;
Seg seg;
Count agrains;
Count bits;
Count grains;
Addr segBase;
AVER(bReturn != NULL);
@ -248,23 +251,22 @@ static Bool loSegFindFree(Addr *bReturn, Addr *lReturn,
/* of the allocation request */
agrains = size >> lo->alignShift;
AVER(agrains >= 1);
AVER(agrains <= loseg->free);
AVER(agrains <= loseg->freeGrains);
AVER(size <= SegSize(seg));
if(SegBuffer(seg) != NULL) {
if(SegBuffer(seg) != NULL)
/* Don't bother trying to allocate from a buffered segment */
return FALSE;
}
bits = SegSize(seg) >> lo->alignShift;
grains = loSegGrains(loseg);
if(!BTFindLongResRange(&baseIndex, &limitIndex, loseg->alloc,
0, bits, agrains)) {
0, grains, agrains)) {
return FALSE;
}
/* check that BTFindLongResRange really did find enough space */
AVER(baseIndex < limitIndex);
AVER((limitIndex-baseIndex) << lo->alignShift >= size);
AVER(LOGrainsSize(lo, limitIndex - baseIndex) >= size);
segBase = SegBase(seg);
*bReturn = loAddrOfIndex(segBase, lo, baseIndex);
*lReturn = loAddrOfIndex(segBase, lo, limitIndex);
@ -312,7 +314,7 @@ static void loSegReclaim(LOSeg loseg, Trace trace)
{
Addr p, base, limit;
Bool marked;
Count bytesReclaimed = (Count)0;
Count reclaimedGrains = (Count)0;
Seg seg;
LO lo;
Format format;
@ -370,23 +372,30 @@ static void loSegReclaim(LOSeg loseg, Trace trace)
Index j = loIndexOfAddr(base, lo, q);
/* This object is not marked, so free it */
loSegFree(loseg, i, j);
bytesReclaimed += AddrOffset(p, q);
reclaimedGrains += j - i;
}
p = q;
}
AVER(p == limit);
AVER(bytesReclaimed <= SegSize(seg));
trace->reclaimSize += bytesReclaimed;
lo->pgen.totalSize -= bytesReclaimed;
AVER(reclaimedGrains <= loSegGrains(loseg));
AVER(loseg->oldGrains >= reclaimedGrains);
loseg->oldGrains -= reclaimedGrains;
loseg->freeGrains += reclaimedGrains;
PoolGenAccountForReclaim(&lo->pgen, LOGrainsSize(lo, reclaimedGrains), FALSE);
trace->reclaimSize += LOGrainsSize(lo, reclaimedGrains);
trace->preservedInPlaceCount += preservedInPlaceCount;
trace->preservedInPlaceSize += preservedInPlaceSize;
SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace));
if(!marked) {
SegFree(seg);
}
if (!marked)
PoolGenFree(&lo->pgen, seg,
LOGrainsSize(lo, loseg->freeGrains),
LOGrainsSize(lo, loseg->oldGrains),
LOGrainsSize(lo, loseg->newGrains),
FALSE);
}
/* This walks over _all_ objects in the heap, whether they are */
@ -399,7 +408,7 @@ static void LOWalk(Pool pool, Seg seg,
Addr base;
LO lo;
LOSeg loseg;
Index i, limit;
Index i, grains;
Format format;
AVERT(Pool, pool);
@ -416,10 +425,10 @@ static void LOWalk(Pool pool, Seg seg,
AVERT(Format, format);
base = SegBase(seg);
limit = SegSize(seg) >> lo->alignShift;
grains = loSegGrains(loseg);
i = 0;
while(i < limit) {
while(i < grains) {
/* object is a slight misnomer because it might point to a */
/* free grain */
Addr object = loAddrOfIndex(base, lo, i);
@ -531,10 +540,12 @@ static void LOFinish(Pool pool)
RING_FOR(node, &pool->segRing, nextNode) {
Seg seg = SegOfPoolRing(node);
LOSeg loseg = SegLOSeg(seg);
AVERT(LOSeg, loseg);
UNUSED(loseg); /* <code/mpm.c#check.unused> */
SegFree(seg);
PoolGenFree(&lo->pgen, seg,
LOGrainsSize(lo, loseg->freeGrains),
LOGrainsSize(lo, loseg->oldGrains),
LOGrainsSize(lo, loseg->newGrains),
FALSE);
}
PoolGenFinish(&lo->pgen);
@ -569,7 +580,7 @@ static Res LOBufferFill(Addr *baseReturn, Addr *limitReturn,
Seg seg = SegOfPoolRing(node);
loseg = SegLOSeg(seg);
AVERT(LOSeg, loseg);
if((loseg->free << lo->alignShift) >= size
if(LOGrainsSize(lo, loseg->freeGrains) >= size
&& loSegFindFree(&base, &limit, loseg, size))
goto found;
}
@ -594,12 +605,12 @@ static Res LOBufferFill(Addr *baseReturn, Addr *limitReturn,
AVER(BTIsResRange(loseg->alloc, baseIndex, limitIndex));
AVER(BTIsSetRange(loseg->mark, baseIndex, limitIndex));
BTSetRange(loseg->alloc, baseIndex, limitIndex);
loseg->free -= limitIndex - baseIndex;
loseg->newAlloc += limitIndex - baseIndex;
AVER(loseg->freeGrains >= limitIndex - baseIndex);
loseg->freeGrains -= limitIndex - baseIndex;
loseg->newGrains += limitIndex - baseIndex;
}
lo->pgen.totalSize += AddrOffset(base, limit);
lo->pgen.newSize += AddrOffset(base, limit);
PoolGenAccountForFill(&lo->pgen, AddrOffset(base, limit), FALSE);
*baseReturn = base;
*limitReturn = limit;
@ -618,7 +629,7 @@ static void LOBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit)
Addr base, segBase;
Seg seg;
LOSeg loseg;
Index baseIndex, initIndex, limitIndex;
Index initIndex, limitIndex;
AVERT(Pool, pool);
lo = PARENT(LOStruct, poolStruct, pool);
@ -643,21 +654,17 @@ static void LOBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit)
AVER(init <= SegLimit(seg));
/* convert base, init, and limit, to quantum positions */
baseIndex = loIndexOfAddr(segBase, lo, base);
initIndex = loIndexOfAddr(segBase, lo, init);
limitIndex = loIndexOfAddr(segBase, lo, limit);
/* Record the unused portion at the end of the buffer */
/* as being free. */
AVER(baseIndex == limitIndex
|| BTIsSetRange(loseg->alloc, baseIndex, limitIndex));
if(initIndex != limitIndex) {
/* Free the unused portion of the buffer (this must be "new", since
* it's not condemned). */
loSegFree(loseg, initIndex, limitIndex);
lo->pgen.totalSize -= AddrOffset(init, limit);
/* All of the buffer must be new, since buffered segs are not condemned. */
AVER(loseg->newAlloc >= limitIndex - baseIndex);
loseg->newAlloc -= limitIndex - initIndex;
lo->pgen.newSize -= AddrOffset(init, limit);
AVER(loseg->newGrains >= limitIndex - initIndex);
loseg->newGrains -= limitIndex - initIndex;
loseg->freeGrains += limitIndex - initIndex;
PoolGenAccountForEmpty(&lo->pgen, AddrOffset(init, limit), FALSE);
}
}
@ -667,7 +674,9 @@ static void LOBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit)
static Res LOWhiten(Pool pool, Trace trace, Seg seg)
{
LO lo;
Count bits;
LOSeg loseg;
Buffer buffer;
Count grains, uncondemned;
AVERT(Pool, pool);
lo = PoolPoolLO(pool);
@ -677,21 +686,32 @@ static Res LOWhiten(Pool pool, Trace trace, Seg seg)
AVERT(Seg, seg);
AVER(SegWhite(seg) == TraceSetEMPTY);
if(SegBuffer(seg) == NULL) {
LOSeg loseg = SegLOSeg(seg);
AVERT(LOSeg, loseg);
loseg = SegLOSeg(seg);
AVERT(LOSeg, loseg);
grains = loSegGrains(loseg);
bits = SegSize(seg) >> lo->alignShift;
/* Allocated objects should be whitened, free areas should */
/* be left "black". */
BTCopyInvertRange(loseg->alloc, loseg->mark, 0, bits);
/* @@@@ We could subtract all the free grains. */
trace->condemned += SegSize(seg);
lo->pgen.newSize -= loseg->newAlloc << lo->alignShift;
loseg->newAlloc = (Count)0;
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
/* Whiten allocated objects; leave free areas black. */
buffer = SegBuffer(seg);
if (buffer != NULL) {
Addr base = SegBase(seg);
Index scanLimitIndex = loIndexOfAddr(base, lo, BufferScanLimit(buffer));
Index limitIndex = loIndexOfAddr(base, lo, BufferLimit(buffer));
uncondemned = limitIndex - scanLimitIndex;
if (0 < scanLimitIndex)
BTCopyInvertRange(loseg->alloc, loseg->mark, 0, scanLimitIndex);
if (limitIndex < grains)
BTCopyInvertRange(loseg->alloc, loseg->mark, limitIndex, grains);
} else {
uncondemned = (Count)0;
BTCopyInvertRange(loseg->alloc, loseg->mark, 0, grains);
}
PoolGenAccountForAge(&lo->pgen, LOGrainsSize(lo, loseg->newGrains - uncondemned), FALSE);
loseg->oldGrains += loseg->newGrains - uncondemned;
loseg->newGrains = uncondemned;
trace->condemned += LOGrainsSize(lo, loseg->oldGrains);
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
return ResOK;
}
@ -815,7 +835,7 @@ static Bool LOCheck(LO lo)
CHECKD(Pool, &lo->poolStruct);
CHECKL(lo->poolStruct.class == EnsureLOPoolClass());
CHECKL(ShiftCheck(lo->alignShift));
CHECKL((Align)1 << lo->alignShift == PoolAlignment(&lo->poolStruct));
CHECKL(LOGrainsSize(lo, (Count)1) == PoolAlignment(&lo->poolStruct));
CHECKD(PoolGen, &lo->pgen);
return TRUE;
}

View file

@ -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;
}
@ -327,7 +329,7 @@ static Res MFSDescribe(Pool pool, mps_lib_FILE *stream)
DEFINE_POOL_CLASS(MFSPoolClass, this)
{
INHERIT_CLASS(this, AbstractAllocFreePoolClass);
INHERIT_CLASS(this, AbstractPoolClass);
this->name = "MFS";
this->size = sizeof(MFSStruct);
this->offset = offsetof(MFSStruct, poolStruct);

View file

@ -861,7 +861,6 @@ DEFINE_POOL_CLASS(MRGPoolClass, this)
this->name = "MRG";
this->size = sizeof(MRGStruct);
this->offset = offsetof(MRGStruct, poolStruct);
this->attr |= AttrSCAN;
this->init = MRGInit;
this->finish = MRGFinish;
this->grey = PoolTrivGrey;

View file

@ -217,6 +217,7 @@ static void MVDebugVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs)
static Res MVInit(Pool pool, ArgList args)
{
Align align = MV_ALIGN_DEFAULT;
Size extendBy = MV_EXTEND_BY_DEFAULT;
Size avgSize = MV_AVG_SIZE_DEFAULT;
Size maxSize = MV_MAX_SIZE_DEFAULT;
@ -226,6 +227,8 @@ static Res MVInit(Pool pool, ArgList args)
Res res;
ArgStruct arg;
if (ArgPick(&arg, args, MPS_KEY_ALIGN))
align = arg.val.align;
if (ArgPick(&arg, args, MPS_KEY_EXTEND_BY))
extendBy = arg.val.size;
if (ArgPick(&arg, args, MPS_KEY_MEAN_SIZE))
@ -233,12 +236,14 @@ static Res MVInit(Pool pool, ArgList args)
if (ArgPick(&arg, args, MPS_KEY_MAX_SIZE))
maxSize = arg.val.size;
AVERT(Align, align);
AVER(extendBy > 0);
AVER(avgSize > 0);
AVER(avgSize <= extendBy);
AVER(maxSize > 0);
AVER(extendBy <= maxSize);
pool->alignment = align;
mv = Pool2MV(pool);
arena = PoolArena(pool);
@ -626,6 +631,7 @@ static void MVFree(Pool pool, Addr old, Size size)
AVERT(MV, mv);
AVER(old != (Addr)0);
AVER(AddrIsAligned(old, pool->alignment));
AVER(size > 0);
size = SizeAlignUp(size, pool->alignment);
@ -791,7 +797,6 @@ static Res MVDescribe(Pool pool, mps_lib_FILE *stream)
DEFINE_POOL_CLASS(MVPoolClass, this)
{
INHERIT_CLASS(this, AbstractBufferPoolClass);
PoolClassMixInAllocFree(this);
this->name = "MV";
this->size = sizeof(MVStruct);
this->offset = offsetof(MVStruct, poolStruct);

View file

@ -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"
@ -51,8 +52,9 @@ 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 Freelist MVTFreelist(MVT mvt);
static Land MVTCBS(MVT mvt);
static Land MVTFreelist(MVT mvt);
static Land MVTFailover(MVT mvt);
/* Types */
@ -62,6 +64,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 */
/* <design/poolmvt/#arch.parameters> */
Size minSize; /* Pool parameter */
@ -136,7 +139,6 @@ DEFINE_POOL_CLASS(MVTPoolClass, this)
this->name = "MVT";
this->size = sizeof(MVTStruct);
this->offset = offsetof(MVTStruct, poolStruct);
this->attr |= AttrFREE;
this->varargs = MVTVarargs;
this->init = MVTInit;
this->finish = MVTFinish;
@ -162,15 +164,21 @@ static ABQ MVTABQ(MVT mvt)
}
static CBS MVTCBS(MVT mvt)
static Land MVTCBS(MVT mvt)
{
return &mvt->cbsStruct;
return &mvt->cbsStruct.landStruct;
}
static Freelist MVTFreelist(MVT mvt)
static Land MVTFreelist(MVT mvt)
{
return &mvt->flStruct;
return &mvt->flStruct.landStruct;
}
static Land MVTFailover(MVT mvt)
{
return &mvt->foStruct.landStruct;
}
@ -246,7 +254,12 @@ static Res MVTInit(Pool pool, ArgList args)
fragLimit = (Count)(arg.val.d * 100);
}
AVER(SizeIsAligned(align, MPS_PF_ALIGN));
AVERT(Align, align);
/* This restriction on the alignment is necessary because of the use
* of a Freelist to store the free address ranges in low-memory
* situations. See <design/freelist/#impl.grain.align>.
*/
AVER(AlignIsAligned(align, FreelistMinimumAlignment));
AVER(0 < minSize);
AVER(minSize <= meanSize);
AVER(meanSize <= maxSize);
@ -263,19 +276,29 @@ 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);
res = LandInit(MVTCBS(mvt), CBSFastLandClassGet(), arena, align, mvt,
mps_args_none);
if (res != ResOK)
goto failCBS;
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;
res = FreelistInit(MVTFreelist(mvt), align);
if (res != ResOK)
goto failFreelist;
pool->alignment = align;
mvt->reuseSize = reuseSize;
mvt->fillSize = fillSize;
@ -338,10 +361,12 @@ static Res MVTInit(Pool pool, ArgList args)
reserveDepth, fragLimit);
return ResOK;
failFreelist:
ABQFinish(arena, MVTABQ(mvt));
failABQ:
CBSFinish(MVTCBS(mvt));
LandFinish(MVTFailover(mvt));
failFailover:
LandFinish(MVTFreelist(mvt));
failFreelist:
LandFinish(MVTCBS(mvt));
failCBS:
AVER(res != ResOK);
return res;
@ -359,6 +384,7 @@ static Bool MVTCheck(MVT mvt)
CHECKD(CBS, &mvt->cbsStruct);
CHECKD(ABQ, &mvt->abqStruct);
CHECKD(Freelist, &mvt->flStruct);
CHECKD(Failover, &mvt->foStruct);
CHECKL(mvt->reuseSize >= 2 * mvt->fillSize);
CHECKL(mvt->fillSize >= mvt->maxSize);
CHECKL(mvt->maxSize >= mvt->meanSize);
@ -408,10 +434,11 @@ static void MVTFinish(Pool pool)
SegFree(SegOfPoolRing(node));
}
/* Finish the Freelist, ABQ and CBS structures */
FreelistFinish(MVTFreelist(mvt));
/* Finish the ABQ, Failover, Freelist and CBS structures */
ABQFinish(arena, MVTABQ(mvt));
CBSFinish(MVTCBS(mvt));
LandFinish(MVTFailover(mvt));
LandFinish(MVTFreelist(mvt));
LandFinish(MVTCBS(mvt));
}
@ -601,14 +628,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)
{
@ -697,8 +717,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.
<design/poolmvt/#arch.contingency.fragmentation-limit> */
the free lists. <design/poolmvt/#arch.contingency.fragmentation-limit> */
if (mvt->available >= mvt->availLimit) {
METER_ACC(mvt->fragLimitContingencies, minSize);
if (MVTContingencyFill(baseReturn, limitReturn, mvt, minSize))
@ -739,6 +758,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;
@ -784,8 +804,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)
{
@ -794,18 +814,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. */
FreelistFlushToCBS(MVTFreelist(mvt), MVTCBS(mvt));
RangeInit(&range, base, limit);
res = CBSInsert(&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, MVTFailover(mvt), &range);
if (res != ResOK)
return res;
@ -814,7 +825,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);
}
@ -822,8 +833,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)
{
@ -834,27 +845,7 @@ static Res MVTDelete(MVT mvt, Addr base, Addr limit)
AVER(base < limit);
RangeInit(&range, base, limit);
res = CBSDelete(&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
* <design/cbs/#function.cbs.delete.fail>. Handle this by
* deleting the whole of rangeOld (which requires no
* allocation) and re-inserting the fragments. */
RangeStruct rangeOld2;
res = CBSDelete(&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 = FreelistDelete(&rangeOld, MVTFreelist(mvt), &range);
}
res = LandDelete(&rangeOld, MVTFailover(mvt), &range);
if (res != ResOK)
return res;
AVER(RangesNest(&rangeOld, &range));
@ -863,7 +854,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.
@ -1028,15 +1019,15 @@ 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 = 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 = FreelistDescribe(MVTFreelist(mvt), stream);
if(res != ResOK) return res;
METER_WRITE(mvt->segAllocs, stream);
METER_WRITE(mvt->segFrees, stream);
METER_WRITE(mvt->bufferFills, stream);
@ -1213,13 +1204,20 @@ static Bool MVTReturnSegs(MVT mvt, Range range, Arena arena)
}
/* MVTRefillCallback -- called from CBSIterate or FreelistIterate at
* the behest of MVTRefillABQIfEmpty
/* MVTRefillABQIfEmpty -- refill the ABQ from the free lists if it is
* empty.
*/
static Bool MVTRefillCallback(MVT mvt, Range range)
static Bool MVTRefillVisitor(Land land, Range range,
void *closureP, Size closureS)
{
AVERT(ABQ, MVTABQ(mvt));
AVERT(Range, range);
MVT mvt;
AVERT(Land, land);
mvt = closureP;
AVERT(MVT, mvt);
AVER(closureS == UNUSED_SIZE);
UNUSED(closureS);
if (RangeSize(range) < mvt->reuseSize)
return TRUE;
@ -1228,80 +1226,54 @@ static Bool MVTRefillCallback(MVT mvt, Range range)
return MVTReserve(mvt, range);
}
static Bool MVTCBSRefillCallback(CBS cbs, Range range,
void *closureP, Size closureS)
{
MVT mvt;
AVERT(CBS, cbs);
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
*/
static void MVTRefillABQIfEmpty(MVT mvt, Size size)
{
AVERT(MVT, mvt);
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);
CBSIterate(MVTCBS(mvt), &MVTCBSRefillCallback, mvt, 0);
FreelistIterate(MVTFreelist(mvt), &MVTFreelistRefillCallback, mvt, 0);
/* The iteration stops if the ABQ overflows, so may finish or not. */
(void)LandIterate(MVTFailover(mvt), MVTRefillVisitor, mvt, UNUSED_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;
RangeStruct range;
Arena arena;
Size min;
/* meters */
Count steps;
Count hardSteps;
} MVTContigencyStruct;
} MVTContigencyClosureStruct, *MVTContigencyClosure;
/* MVTContingencyCallback -- called from CBSIterate or FreelistIterate
* at the behest of MVTContingencySearch.
*/
static Bool MVTContingencyCallback(MVTContigency cl, Range range)
static Bool MVTContingencyVisitor(Land land, Range range,
void *closureP, Size closureS)
{
MVT mvt;
Size size;
Addr base, limit;
MVTContigencyClosure cl;
AVER(cl != NULL);
AVERT(Land, land);
AVERT(Range, range);
AVER(closureP != NULL);
cl = closureP;
mvt = cl->mvt;
AVERT(MVT, mvt);
AVERT(Range, range);
AVER(closureS == UNUSED_SIZE);
UNUSED(closureS);
base = RangeBase(range);
limit = RangeLimit(range);
@ -1314,7 +1286,6 @@ static Bool MVTContingencyCallback(MVTContigency cl, 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;
}
@ -1322,7 +1293,6 @@ static Bool MVTContingencyCallback(MVTContigency cl, Range range)
cl->hardSteps++;
if (MVTCheckFit(base, limit, cl->min, cl->arena)) {
RangeInit(&cl->range, base, limit);
cl->found = TRUE;
return FALSE;
}
@ -1330,46 +1300,18 @@ static Bool MVTContingencyCallback(MVTContigency cl, Range range)
return TRUE;
}
static Bool MVTCBSContingencyCallback(CBS cbs, Range range,
void *closureP, Size closureS)
{
MVTContigency cl = closureP;
UNUSED(cbs);
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 */
static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn,
MVT mvt, Size min)
{
MVTContigencyStruct cls;
MVTContigencyClosureStruct cls;
cls.mvt = mvt;
cls.found = FALSE;
cls.arena = PoolArena(MVT2Pool(mvt));
cls.min = min;
cls.steps = 0;
cls.hardSteps = 0;
FreelistFlushToCBS(MVTFreelist(mvt), MVTCBS(mvt));
CBSIterate(MVTCBS(mvt), MVTCBSContingencyCallback, (void *)&cls, 0);
FreelistIterate(MVTFreelist(mvt), MVTFreelistContingencyCallback,
(void *)&cls, 0);
if (!cls.found)
if (LandIterate(MVTFailover(mvt), MVTContingencyVisitor, &cls, UNUSED_SIZE))
return FALSE;
AVER(RangeSize(&cls.range) >= min);
@ -1386,6 +1328,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;
@ -1415,12 +1358,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 CBS _mps_mvt_cbs(mps_pool_t);
CBS _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);

View file

@ -21,6 +21,7 @@
#include "mpscmvff.h"
#include "dbgpool.h"
#include "cbs.h"
#include "failover.h"
#include "freelist.h"
#include "mpm.h"
@ -47,9 +48,9 @@ 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 */
Bool firstFit; /* as opposed to last fit */
Bool slotHigh; /* prefers high part of large block */
Sig sig; /* <design/sig/> */
@ -58,10 +59,9 @@ 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 MVFFOfCBS(cbs) PARENT(MVFFStruct, cbsStruct, cbs)
#define FreelistOfMVFF(mvff) (&((mvff)->flStruct))
#define MVFFOfFreelist(fl) PARENT(MVFFStruct, flStruct, fl)
#define CBSOfMVFF(mvff) (&((mvff)->cbsStruct.landStruct))
#define FreelistOfMVFF(mvff) (&((mvff)->flStruct.landStruct))
#define FailoverOfMVFF(mvff) (&((mvff)->foStruct.landStruct))
static Bool MVFFCheck(MVFF mvff);
@ -80,48 +80,29 @@ 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
* segments (see MVFFFreeSegs).
* Updates rangeIO to be maximally coalesced range containing given
* range. Does not attempt to free segments (see MVFFFreeSegs).
*/
static Res MVFFAddToFreeList(Addr *baseIO, Addr *limitIO, MVFF mvff) {
Res res;
RangeStruct range, newRange;
AVER(baseIO != NULL);
AVER(limitIO != NULL);
static Res MVFFInsert(Range rangeIO, MVFF mvff) {
AVERT(Range, rangeIO);
AVERT(MVFF, mvff);
RangeInit(&range, *baseIO, *limitIO);
res = CBSInsert(&newRange, CBSOfMVFF(mvff), &range);
if (ResIsAllocFailure(res)) {
/* CBS ran out of memory for splay nodes: add range to emergency
* free list instead. */
res = FreelistInsert(&newRange, FreelistOfMVFF(mvff), &range);
}
if (res == ResOK) {
mvff->free += RangeSize(&range);
*baseIO = RangeBase(&newRange);
*limitIO = RangeLimit(&newRange);
}
return res;
return LandInsert(rangeIO, FailoverOfMVFF(mvff), rangeIO);
}
/* 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;
@ -131,72 +112,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 = CBSDelete(&oldRange, CBSOfMVFF(mvff), &range);
if (res == ResOK) {
mvff->free -= RangeSize(&range);
} else if (ResIsAllocFailure(res)) {
/* CBS ran out of memory for splay nodes, which must mean that
* there were fragments on both sides: see
* <design/cbs/#function.cbs.delete.fail>. Handle this by
* deleting the whole of oldRange (which requires no
* allocation) and re-inserting the fragments. */
RangeStruct oldRange2;
res = CBSDelete(&oldRange2, CBSOfMVFF(mvff), &oldRange);
AVER(res == ResOK);
AVER(RangesEqual(&oldRange2, &oldRange));
mvff->free -= RangeSize(&oldRange);
AVER(RangeBase(&oldRange) != segBase);
{
Addr leftBase = RangeBase(&oldRange);
Addr leftLimit = segBase;
res = MVFFAddToFreeList(&leftBase, &leftLimit, mvff);
}
AVER(RangeLimit(&oldRange) != segLimit);
{
Addr rightBase = segLimit;
Addr rightLimit = RangeLimit(&oldRange);
res = MVFFAddToFreeList(&rightBase, &rightLimit, mvff);
}
} else if (res == ResFAIL) {
/* Not found in the CBS: must be found in the Freelist. */
res = FreelistDelete(&oldRange, FreelistOfMVFF(mvff), &range);
AVER(res == ResOK);
mvff->free -= RangeSize(&range);
}
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);
AVER(mvff->total >= RangeSize(&delRange));
mvff->total -= RangeSize(&delRange);
}
/* Avoid calling SegNext if the next segment would fail */
/* Avoid calling SegFindAboveAddr 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);
@ -212,8 +163,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)
@ -224,7 +175,7 @@ static Res MVFFAddSeg(Seg *segReturn,
Seg seg;
Res res;
Align align;
Addr base, limit;
RangeStruct range;
AVERT(MVFF, mvff);
AVER(size > 0);
@ -259,12 +210,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. */
@ -274,50 +224,32 @@ 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))));
FreelistFlushToCBS(FreelistOfMVFF(mvff), CBSOfMVFF(mvff));
findDelete = mvff->slotHigh ? FindDeleteHIGH : FindDeleteLOW;
foundBlock =
(mvff->firstFit ? CBSFindFirst : CBSFindLast)
(&range, &oldRange, CBSOfMVFF(mvff), size, findDelete);
if (!foundBlock) {
/* Failed to find a block in the CBS: try the emergency free list
* as well. */
foundBlock =
(mvff->firstFit ? FreelistFindFirst : FreelistFindLast)
(&range, &oldRange, FreelistOfMVFF(mvff), size, findDelete);
}
if (foundBlock) {
*baseReturn = RangeBase(&range);
*limitReturn = RangeLimit(&range);
mvff->free -= size;
}
(mvff->firstFit ? LandFindFirst : LandFindLast)
(rangeReturn, &oldRange, FailoverOfMVFF(mvff), size, findDelete);
return foundBlock;
}
@ -330,7 +262,7 @@ static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size,
{
Res res;
MVFF mvff;
Addr base, limit;
RangeStruct range;
Bool foundBlock;
AVERT(Pool, pool);
@ -343,29 +275,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;
}
@ -376,7 +307,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);
@ -387,42 +318,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);
FreelistFlushToCBS(FreelistOfMVFF(mvff), CBSOfMVFF(mvff));
if (CBSFindLargest(range, oldRange, CBSOfMVFF(mvff), size, findDelete))
return TRUE;
if (FreelistFindLargest(range, oldRange, FreelistOfMVFF(mvff),
size, findDelete))
return TRUE;
return FALSE;
}
/* MVFFBufferFill -- Fill the buffer
*
@ -447,18 +352,17 @@ 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);
AVER(RangeSize(&range) >= size);
mvff->free -= RangeSize(&range);
*baseReturn = RangeBase(&range);
*limitReturn = RangeLimit(&range);
@ -473,21 +377,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 (RangeIsEmpty(&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;
}
@ -531,7 +436,7 @@ static Res MVFFInit(Pool pool, ArgList args)
{
Size extendBy = MVFF_EXTEND_BY_DEFAULT;
Size avgSize = MVFF_AVG_SIZE_DEFAULT;
Size align = MVFF_ALIGN_DEFAULT;
Align align = MVFF_ALIGN_DEFAULT;
Bool slotHigh = MVFF_SLOT_HIGH_DEFAULT;
Bool arenaHigh = MVFF_ARENA_HIGH_DEFAULT;
Bool firstFit = MVFF_FIRST_FIT_DEFAULT;
@ -570,7 +475,12 @@ static Res MVFFInit(Pool pool, ArgList args)
AVER(extendBy > 0); /* .arg.check */
AVER(avgSize > 0); /* .arg.check */
AVER(avgSize <= extendBy); /* .arg.check */
AVER(SizeIsAligned(align, MPS_PF_ALIGN));
AVERT(Align, align);
/* This restriction on the alignment is necessary because of the use
* of a Freelist to store the free address ranges in low-memory
* situations. <design/freelist/#impl.grain.align>.
*/
AVER(AlignIsAligned(align, FreelistMinimumAlignment));
AVERT(Bool, slotHigh);
AVERT(Bool, arenaHigh);
AVERT(Bool, firstFit);
@ -596,16 +506,25 @@ static Res MVFFInit(Pool pool, ArgList args)
SegPrefExpress(mvff->segPref, arenaHigh ? SegPrefHigh : SegPrefLow, NULL);
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;
res = CBSInit(CBSOfMVFF(mvff), arena, (void *)mvff, align,
/* fastFind */ TRUE, /* zoned */ FALSE, args);
res = LandInit(CBSOfMVFF(mvff), CBSFastLandClassGet(), arena, align, mvff,
mps_args_none);
if (res != ResOK)
goto failInit;
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);
@ -613,7 +532,11 @@ static Res MVFFInit(Pool pool, ArgList args)
BOOLOF(slotHigh), BOOLOF(arenaHigh), BOOLOF(firstFit));
return ResOK;
failInit:
failFailoverInit:
LandFinish(CBSOfMVFF(mvff));
failCBSInit:
LandFinish(FreelistOfMVFF(mvff));
failFreelistInit:
ControlFree(arena, p, sizeof(SegPrefStruct));
return res;
}
@ -625,7 +548,6 @@ static void MVFFFinish(Pool pool)
{
MVFF mvff;
Arena arena;
Seg seg;
Ring ring, node, nextNode;
AVERT(Pool, pool);
@ -634,20 +556,24 @@ 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));
CBSFinish(CBSOfMVFF(mvff));
FreelistFinish(FreelistOfMVFF(mvff));
LandFinish(FailoverOfMVFF(mvff));
LandFinish(FreelistOfMVFF(mvff));
LandFinish(CBSOfMVFF(mvff));
mvff->sig = SigInvalid;
}
@ -686,16 +612,15 @@ 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;
res = CBSDescribe(CBSOfMVFF(mvff), stream);
res = LandDescribe(CBSOfMVFF(mvff), stream);
if (res != ResOK)
return res;
res = FreelistDescribe(FreelistOfMVFF(mvff), stream);
res = LandDescribe(FreelistOfMVFF(mvff), stream);
if (res != ResOK)
return res;
@ -707,7 +632,7 @@ static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream)
DEFINE_POOL_CLASS(MVFFPoolClass, this)
{
INHERIT_CLASS(this, AbstractAllocFreePoolClass);
INHERIT_CLASS(this, AbstractPoolClass);
PoolClassMixInBuffer(this);
this->name = "MVFF";
this->size = sizeof(MVFFStruct);
@ -764,13 +689,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 <design/poolmvff/#design.arena-enter> */
@ -802,11 +729,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 >= mvff->free);
CHECKL(SizeIsAligned(mvff->free, PoolAlignment(MVFF2Pool(mvff))));
CHECKL(SizeIsAligned(mvff->total, ArenaAlign(PoolArena(MVFF2Pool(mvff)))));
CHECKD(CBS, CBSOfMVFF(mvff));
CHECKD(Freelist, FreelistOfMVFF(mvff));
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;
@ -815,12 +742,10 @@ 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) {
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);

View file

@ -270,7 +270,7 @@ DEFINE_POOL_CLASS(NPoolClass, this)
this->name = "N";
this->size = sizeof(PoolNStruct);
this->offset = offsetof(PoolNStruct, poolStruct);
this->attr |= (AttrALLOC | AttrBUF | AttrFREE | AttrGC | AttrSCAN);
this->attr |= AttrGC;
this->init = NInit;
this->finish = NFinish;
this->alloc = NAlloc;

View file

@ -15,7 +15,6 @@ SRCID(range, "$Id$");
Bool RangeCheck(Range range)
{
CHECKS(Range, range);
CHECKL(range->base <= range->limit);
return TRUE;
@ -29,14 +28,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;
}
Res RangeDescribe(Range range, mps_lib_FILE *stream)

View file

@ -14,15 +14,8 @@
#include "mpmtypes.h"
/* Signatures */
#define RangeSig ((Sig)0x5196A493) /* SIGnature RANGE */
/* Prototypes */
typedef struct RangeStruct *Range;
#define RangeBase(range) ((range)->base)
#define RangeLimit(range) ((range)->limit)
#define RangeSize(range) (AddrOffset(RangeBase(range), RangeLimit(range)))
@ -30,6 +23,7 @@ typedef struct RangeStruct *Range;
#define RangeIsEmpty(range) (RangeSize(range) == 0)
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);
@ -46,7 +40,6 @@ extern void RangeCopy(Range to, Range from);
/* Types */
typedef struct RangeStruct {
Sig sig;
Addr base;
Addr limit;
} RangeStruct;

View file

@ -117,10 +117,10 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount,
/* to be large enough, but that gets complicated, if you have to */
/* merge classes because of the adjustment. */
for (i = 0; i < classesCount; ++i) {
AVER(classes[i]._block_size > 0);
AVER(SizeIsAligned(classes[i]._block_size, PoolAlignment(pool)));
AVER(prevSize < classes[i]._block_size);
prevSize = classes[i]._block_size;
AVER(classes[i].mps_block_size > 0);
AVER(SizeIsAligned(classes[i].mps_block_size, PoolAlignment(pool)));
AVER(prevSize < classes[i].mps_block_size);
prevSize = classes[i].mps_block_size;
/* no restrictions on count */
/* no restrictions on frequency */
}
@ -128,7 +128,7 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount,
/* Calculate frequency scale */
for (i = 0; i < classesCount; ++i) {
unsigned oldFreq = totalFreq;
totalFreq += classes[i]._frequency;
totalFreq += classes[i].mps_frequency;
AVER(oldFreq <= totalFreq); /* check for overflow */
UNUSED(oldFreq); /* <code/mpm.c#check.unused> */
}
@ -136,10 +136,10 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount,
/* Find middle one */
totalFreq /= 2;
for (i = 0; i < classesCount; ++i) {
if (totalFreq < classes[i]._frequency) break;
totalFreq -= classes[i]._frequency;
if (totalFreq < classes[i].mps_frequency) break;
totalFreq -= classes[i].mps_frequency;
}
if (totalFreq <= classes[i]._frequency / 2)
if (totalFreq <= classes[i].mps_frequency / 2)
middleIndex = i;
else
middleIndex = i + 1; /* there must exist another class at i+1 */
@ -155,9 +155,9 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount,
/* It's important this matches SACFind. */
esac = ExternalSACOfSAC(sac);
for (j = middleIndex + 1, i = 0; j < classesCount; ++j, i += 2) {
esac->_freelists[i]._size = classes[j]._block_size;
esac->_freelists[i]._size = classes[j].mps_block_size;
esac->_freelists[i]._count = 0;
esac->_freelists[i]._count_max = classes[j]._cached_count;
esac->_freelists[i]._count_max = classes[j].mps_cached_count;
esac->_freelists[i]._blocks = NULL;
}
esac->_freelists[i]._size = SizeMAX;
@ -165,19 +165,19 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount,
esac->_freelists[i]._count_max = 0;
esac->_freelists[i]._blocks = NULL;
for (j = middleIndex, i = 1; j > 0; --j, i += 2) {
esac->_freelists[i]._size = classes[j-1]._block_size;
esac->_freelists[i]._size = classes[j-1].mps_block_size;
esac->_freelists[i]._count = 0;
esac->_freelists[i]._count_max = classes[j]._cached_count;
esac->_freelists[i]._count_max = classes[j].mps_cached_count;
esac->_freelists[i]._blocks = NULL;
}
esac->_freelists[i]._size = 0;
esac->_freelists[i]._count = 0;
esac->_freelists[i]._count_max = classes[j]._cached_count;
esac->_freelists[i]._count_max = classes[j].mps_cached_count;
esac->_freelists[i]._blocks = NULL;
/* finish init */
esac->_trapped = FALSE;
esac->_middle = classes[middleIndex]._block_size;
esac->_middle = classes[middleIndex].mps_block_size;
sac->pool = pool;
sac->classesCount = classesCount;
sac->middleIndex = middleIndex;

View file

@ -7,6 +7,7 @@
#include "mpscmv.h"
#include "mpscmvff.h"
#include "mpscmfs.h"
#include "mpslib.h"
#include "mpsavm.h"
#include "mps.h"
@ -15,9 +16,7 @@
#include "mpslib.h"
#include <stdio.h>
#include "mpstd.h"
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
@ -28,9 +27,6 @@
#define testSetSIZE 200
#define testLOOPS 10
#define topClassSIZE 0xA00
#define classCOUNT 4
/* make -- allocate an object */
@ -45,25 +41,36 @@ static mps_res_t make(mps_addr_t *p, mps_sac_t sac, size_t size)
/* stress -- create a pool of the requested type and allocate in it */
static mps_res_t stress(mps_class_t class,
size_t classes_count, mps_sac_classes_s *classes,
size_t (*size)(size_t i), mps_arena_t arena, ...)
static mps_res_t stress(mps_arena_t arena, mps_align_t align,
size_t (*size)(size_t i),
const char *name, mps_class_t pool_class,
mps_arg_s *args)
{
mps_res_t res;
mps_pool_t pool;
mps_sac_t sac;
va_list arg;
size_t i, k;
int *ps[testSetSIZE];
size_t ss[testSetSIZE];
mps_sac_classes_s classes[4] = {
{1, 1, 1},
{2, 1, 2},
{16, 9, 5},
{100, 9, 4},
};
size_t classes_count = sizeof classes / sizeof *classes;
for (i = 0; i < classes_count; ++i) {
classes[i].mps_block_size *= alignUp(align, sizeof(void *));
}
va_start(arg, arena);
res = mps_pool_create_v(&pool, arena, class, arg);
va_end(arg);
printf("%s\n", name);
res = mps_pool_create_k(&pool, arena, pool_class, args);
if (res != MPS_RES_OK)
return res;
die(mps_sac_create(&sac, pool, classes_count, classes), "SACCreate");
die(mps_sac_create(&sac, pool, classes_count, classes),
"SACCreate");
/* allocate a load of objects */
for (i = 0; i < testSetSIZE; ++i) {
@ -125,9 +132,9 @@ static mps_res_t stress(mps_class_t class,
}
/* randomSize8 -- produce sizes both latge and small */
/* randomSize -- produce sizes both large and small */
static size_t randomSize8(size_t i)
static size_t randomSize(size_t i)
{
size_t maxSize = 2 * 160 * 0x2000;
size_t size;
@ -138,58 +145,97 @@ static size_t randomSize8(size_t i)
}
/* testInArena -- test all the pool classes in the given arena */
/* fixedSize -- produce always the same size */
static size_t fixedSizeSize = 0;
static size_t fixedSize(size_t i)
{
testlib_unused(i);
return fixedSizeSize;
}
static mps_pool_debug_option_s debugOptions = {
/* .fence_template = */ (const void *)"postpostpostpost",
/* .fence_size = */ MPS_PF_ALIGN,
/* .free_template = */ (const void *)"DEAD",
/* .fence_template = */ "post",
/* .fence_size = */ 4,
/* .free_template = */ "DEAD",
/* .free_size = */ 4
};
static mps_sac_classes_s classes[4] = {
{MPS_PF_ALIGN, 1, 1},
{MPS_PF_ALIGN * 2, 1, 2},
{128 + MPS_PF_ALIGN, 9, 5},
{topClassSIZE, 9, 4}
};
static void testInArena(mps_arena_t arena)
/* testInArena -- test all the pool classes in the given arena */
static void testInArena(mps_arena_class_t arena_class, mps_arg_s *arena_args)
{
printf("MVFF\n\n");
die(stress(mps_class_mvff(), classCOUNT, classes, randomSize8, arena,
(size_t)65536, (size_t)32, (mps_align_t)MPS_PF_ALIGN, TRUE, TRUE, TRUE),
"stress MVFF");
printf("MV debug\n\n");
die(stress(mps_class_mv_debug(), classCOUNT, classes, randomSize8, arena,
&debugOptions, (size_t)65536, (size_t)32, (size_t)65536),
"stress MV debug");
printf("MV\n\n");
die(stress(mps_class_mv(), classCOUNT, classes, randomSize8, arena,
(size_t)65536, (size_t)32, (size_t)65536),
"stress MV");
mps_arena_t arena;
die(mps_arena_create_k(&arena, arena_class, arena_args),
"mps_arena_create");
MPS_ARGS_BEGIN(args) {
mps_align_t align = sizeof(void *) << (rnd() % 4);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE);
die(stress(arena, align, randomSize, "MVFF", mps_class_mvff(), args),
"stress MVFF");
} MPS_ARGS_END(args);
MPS_ARGS_BEGIN(args) {
mps_align_t align = sizeof(void *) << (rnd() % 4);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &debugOptions);
die(stress(arena, align, randomSize, "MVFF debug",
mps_class_mvff_debug(), args),
"stress MVFF debug");
} MPS_ARGS_END(args);
MPS_ARGS_BEGIN(args) {
mps_align_t align = (mps_align_t)1 << (rnd() % 6);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
die(stress(arena, align, randomSize, "MV", mps_class_mv(), args),
"stress MV");
} MPS_ARGS_END(args);
MPS_ARGS_BEGIN(args) {
mps_align_t align = (mps_align_t)1 << (rnd() % 6);
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &debugOptions);
die(stress(arena, align, randomSize, "MV debug",
mps_class_mv_debug(), args),
"stress MV debug");
} MPS_ARGS_END(args);
MPS_ARGS_BEGIN(args) {
fixedSizeSize = MPS_PF_ALIGN * (1 + rnd() % 100);
MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, fixedSizeSize);
die(stress(arena, fixedSizeSize, fixedSize, "MFS", mps_class_mfs(), args),
"stress MFS");
} MPS_ARGS_END(args);
mps_arena_destroy(arena);
}
int main(int argc, char *argv[])
{
mps_arena_t arena;
testlib_init(argc, argv);
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
"mps_arena_create");
testInArena(arena);
mps_arena_destroy(arena);
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
testInArena(mps_arena_class_vm(), args);
} MPS_ARGS_END(args);
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE);
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args),
"mps_arena_create");
testInArena(mps_arena_class_vm(), args);
} MPS_ARGS_END(args);
testInArena(arena);
mps_arena_destroy(arena);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
return 0;

View file

@ -640,6 +640,10 @@ Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at,
AVER(at < limit);
AVERT(Bool, withReservoirPermit);
/* Can only split a buffered segment if the entire buffer is below
* the split point. */
AVER(SegBuffer(seg) == NULL || BufferLimit(SegBuffer(seg)) <= at);
ShieldFlush(arena); /* see <design/seg/#split-merge.shield> */
/* Allocate the new segment object from the control pool */
@ -738,8 +742,6 @@ Bool SegCheck(Seg seg)
CHECKL(seg->sm == AccessSetEMPTY);
CHECKL(seg->pm == AccessSetEMPTY);
} else {
/* Segments with ranks may only belong to scannable pools. */
CHECKL(PoolHasAttr(pool, AttrSCAN));
/* <design/seg/#field.rankSet.single>: The Tracer only permits */
/* one rank per segment [ref?] so this field is either empty or a */
/* singleton. */

View file

@ -339,7 +339,7 @@ static Res AMSTInit(Pool pool, ArgList args)
AVERT(Pool, pool);
AVERT(ArgList, args);
if (ArgPick(&arg, args, MPS_KEY_CHAIN))
chain = arg.val.chain;
else {
@ -402,7 +402,7 @@ static Bool AMSSegIsFree(Seg seg)
AMSSeg amsseg;
AVERT(Seg, seg);
amsseg = Seg2AMSSeg(seg);
return(amsseg->free == amsseg->grains);
return amsseg->freeGrains == amsseg->grains;
}
@ -436,7 +436,7 @@ static Bool AMSSegRegionIsFree(Seg seg, Addr base, Addr limit)
* Used as a means of overriding the behaviour of AMSBufferFill.
* The code is similar to AMSBufferEmpty.
*/
static void AMSUnallocateRange(Seg seg, Addr base, Addr limit)
static void AMSUnallocateRange(AMS ams, Seg seg, Addr base, Addr limit)
{
AMSSeg amsseg;
Index baseIndex, limitIndex;
@ -464,8 +464,10 @@ static void AMSUnallocateRange(Seg seg, Addr base, Addr limit)
BTResRange(amsseg->allocTable, baseIndex, limitIndex);
}
}
amsseg->free += limitIndex - baseIndex;
amsseg->newAlloc -= limitIndex - baseIndex;
amsseg->freeGrains += limitIndex - baseIndex;
AVER(amsseg->newGrains >= limitIndex - baseIndex);
amsseg->newGrains -= limitIndex - baseIndex;
PoolGenAccountForEmpty(&ams->pgen, AddrOffset(base, limit), FALSE);
}
@ -474,7 +476,7 @@ static void AMSUnallocateRange(Seg seg, Addr base, Addr limit)
* Used as a means of overriding the behaviour of AMSBufferFill.
* The code is similar to AMSUnallocateRange.
*/
static void AMSAllocateRange(Seg seg, Addr base, Addr limit)
static void AMSAllocateRange(AMS ams, Seg seg, Addr base, Addr limit)
{
AMSSeg amsseg;
Index baseIndex, limitIndex;
@ -502,9 +504,10 @@ static void AMSAllocateRange(Seg seg, Addr base, Addr limit)
BTSetRange(amsseg->allocTable, baseIndex, limitIndex);
}
}
AVER(amsseg->free >= limitIndex - baseIndex);
amsseg->free -= limitIndex - baseIndex;
amsseg->newAlloc += limitIndex - baseIndex;
AVER(amsseg->freeGrains >= limitIndex - baseIndex);
amsseg->freeGrains -= limitIndex - baseIndex;
amsseg->newGrains += limitIndex - baseIndex;
PoolGenAccountForFill(&ams->pgen, AddrOffset(base, limit), FALSE);
}
@ -529,6 +532,7 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn,
PoolClass super;
Addr base, limit;
Arena arena;
AMS ams;
AMST amst;
Bool b;
Seg seg;
@ -540,6 +544,7 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn,
AVER(limitReturn != NULL);
/* other parameters are checked by next method */
arena = PoolArena(pool);
ams = Pool2AMS(pool);
amst = Pool2AMST(pool);
/* call next method */
@ -561,14 +566,14 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn,
Seg mergedSeg;
Res mres;
AMSUnallocateRange(seg, base, limit);
AMSUnallocateRange(ams, seg, base, limit);
mres = SegMerge(&mergedSeg, segLo, seg, withReservoirPermit);
if (ResOK == mres) { /* successful merge */
AMSAllocateRange(mergedSeg, base, limit);
AMSAllocateRange(ams, mergedSeg, base, limit);
/* leave range as-is */
} else { /* failed to merge */
AVER(amst->failSegs); /* deliberate fails only */
AMSAllocateRange(seg, base, limit);
AMSAllocateRange(ams, seg, base, limit);
}
}
@ -579,13 +584,13 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn,
Addr mid = AddrAdd(base, half);
Seg segLo, segHi;
Res sres;
AMSUnallocateRange(seg, mid, limit);
AMSUnallocateRange(ams, seg, mid, limit);
sres = SegSplit(&segLo, &segHi, seg, mid, withReservoirPermit);
if (ResOK == sres) { /* successful split */
limit = mid; /* range is lower segment */
} else { /* failed to split */
AVER(amst->failSegs); /* deliberate fails only */
AMSAllocateRange(seg, mid, limit);
AMSAllocateRange(ams, seg, mid, limit);
}
}

View file

@ -964,7 +964,8 @@ Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn,
* it splays the tree to the first node, and returns the new root.
*
* SplayTreeNext takes a tree and splays it to the successor of a key
* and returns the new root. Returns TreeEMPTY is there are no successors.
* and returns the new root. Returns TreeEMPTY is there are no
* successors.
*
* SplayTreeFirst and SplayTreeNext do not require the tree to remain
* unmodified.
@ -1019,7 +1020,7 @@ Tree SplayTreeNext(SplayTree splay, TreeKey oldKey) {
*/
static Res SplayNodeDescribe(Tree node, mps_lib_FILE *stream,
SplayNodeDescribeMethod nodeDescribe) {
TreeDescribeMethod nodeDescribe) {
Res res;
if (!TreeCheck(node)) return ResFAIL;
@ -1330,13 +1331,27 @@ void SplayNodeRefresh(SplayTree splay, Tree node)
}
/* SplayNodeInit -- initialize client property without splaying */
void SplayNodeInit(SplayTree splay, Tree node)
{
AVERT(SplayTree, splay);
AVERT(Tree, node);
AVER(!TreeHasLeft(node)); /* otherwise, call SplayNodeRefresh */
AVER(!TreeHasRight(node)); /* otherwise, call SplayNodeRefresh */
AVER(SplayHasUpdate(splay)); /* otherwise, why call? */
splay->updateNode(splay, node);
}
/* SplayTreeDescribe -- Describe a splay tree
*
* See <design/splay/#function.splay.tree.describe>.
*/
Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream,
SplayNodeDescribeMethod nodeDescribe) {
TreeDescribeMethod nodeDescribe) {
Res res;
if (!TESTT(SplayTree, splay)) return ResFAIL;

View file

@ -19,7 +19,6 @@ typedef Bool (*SplayTestNodeMethod)(SplayTree splay, Tree node,
void *closureP, Size closureS);
typedef Bool (*SplayTestTreeMethod)(SplayTree splay, Tree node,
void *closureP, Size closureS);
typedef Res (*SplayNodeDescribeMethod)(Tree node, mps_lib_FILE *stream);
typedef void (*SplayUpdateNodeMethod)(SplayTree splay, Tree node);
extern void SplayTrivUpdate(SplayTree splay, Tree node);
@ -70,9 +69,10 @@ extern Bool SplayFindLast(Tree *nodeReturn, SplayTree splay,
void *closureP, Size closureS);
extern void SplayNodeRefresh(SplayTree splay, Tree node);
extern void SplayNodeInit(SplayTree splay, Tree node);
extern Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream,
SplayNodeDescribeMethod nodeDescribe);
TreeDescribeMethod nodeDescribe);
extern void SplayDebugUpdate(SplayTree splay, Tree tree);
extern Count SplayDebugCount(SplayTree splay);

View file

@ -634,33 +634,6 @@ static Res traceFlip(Trace trace)
return res;
}
/* traceCopySizes -- preserve size information for later use
*
* A PoolGen's newSize is important information that we want to emit in
* a diagnostic message at TraceStart. In order to do that we must copy
* the information before Whiten changes it. This function does that.
*/
static void traceCopySizes(Trace trace)
{
Ring node, nextNode;
Index i;
Arena arena = trace->arena;
RING_FOR(node, &arena->chainRing, nextNode) {
Chain chain = RING_ELT(Chain, chainRing, node);
for(i = 0; i < chain->genCount; ++i) {
Ring n, nn;
GenDesc desc = &chain->gens[i];
RING_FOR(n, &desc->locusRing, nn) {
PoolGen gen = RING_ELT(PoolGen, genRing, n);
gen->newSizeAtCreate = gen->newSize;
}
}
}
return;
}
/* TraceCreate -- create a Trace object
*
@ -677,6 +650,17 @@ static void traceCopySizes(Trace trace)
* This code is written to be adaptable to allocating Trace objects
* dynamically. */
static void TraceCreatePoolGen(GenDesc gen)
{
Ring n, nn;
RING_FOR(n, &gen->locusRing, nn) {
PoolGen pgen = RING_ELT(PoolGen, genRing, n);
EVENT11(TraceCreatePoolGen, gen, gen->capacity, gen->mortality, gen->zones,
pgen->pool, pgen->totalSize, pgen->freeSize, pgen->newSize,
pgen->oldSize, pgen->newDeferredSize, pgen->oldDeferredSize);
}
}
Res TraceCreate(Trace *traceReturn, Arena arena, int why)
{
TraceId ti;
@ -747,7 +731,24 @@ Res TraceCreate(Trace *traceReturn, Arena arena, int why)
/* .. _request.dylan.160098: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/160098 */
ShieldSuspend(arena);
traceCopySizes(trace);
STATISTIC_STAT ({
/* Iterate over all chains, all GenDescs within a chain, and all
* PoolGens within a GenDesc. */
Ring node;
Ring nextNode;
RING_FOR(node, &arena->chainRing, nextNode) {
Chain chain = RING_ELT(Chain, chainRing, node);
Index i;
for (i = 0; i < chain->genCount; ++i) {
GenDesc gen = &chain->gens[i];
TraceCreatePoolGen(gen);
}
}
/* Now do topgen GenDesc, and all PoolGens within it. */
TraceCreatePoolGen(&arena->topGen);
});
*traceReturn = trace;
return ResOK;
@ -1574,9 +1575,9 @@ double TraceWorkFactor = 0.25;
*
* TraceStart should be passed a trace with state TraceINIT, i.e.,
* recently returned from TraceCreate, with some condemned segments
* added. mortality is the fraction of the condemned set expected to
* survive. finishingTime is relative to the current polling clock, see
* <design/arena/#poll.clock>.
* added. mortality is the fraction of the condemned set expected not
* to survive. finishingTime is relative to the current polling clock,
* see <design/arena/#poll.clock>.
*
* .start.black: All segments are black w.r.t. a newly allocated trace.
* However, if TraceStart initialized segments to black when it
@ -1598,18 +1599,6 @@ static Res rootGrey(Root root, void *p)
}
static void TraceStartPoolGen(Chain chain, GenDesc desc, Bool top, Index i)
{
Ring n, nn;
RING_FOR(n, &desc->locusRing, nn) {
PoolGen gen = RING_ELT(PoolGen, genRing, n);
EVENT10(TraceStartPoolGen, chain, BOOLOF(top), i, desc,
desc->capacity, desc->mortality, desc->zones,
gen->pool, gen->totalSize, gen->newSizeAtCreate);
}
}
/* TraceStart -- start a trace whose white set has been established
*
* The main job of TraceStart is to set up the grey list for a trace. The
@ -1674,26 +1663,6 @@ Res TraceStart(Trace trace, double mortality, double finishingTime)
} while (SegNext(&seg, arena, seg));
}
STATISTIC_STAT ({
/* @@ */
/* Iterate over all chains, all GenDescs within a chain, */
/* (and all PoolGens within a GenDesc). */
Ring node;
Ring nextNode;
Index i;
RING_FOR(node, &arena->chainRing, nextNode) {
Chain chain = RING_ELT(Chain, chainRing, node);
for(i = 0; i < chain->genCount; ++i) {
GenDesc desc = &chain->gens[i];
TraceStartPoolGen(chain, desc, FALSE, i);
}
}
/* Now do topgen GenDesc (and all PoolGens within it). */
TraceStartPoolGen(NULL, &arena->topGen, TRUE, 0);
});
res = RootsIterate(ArenaGlobals(arena), rootGrey, (void *)trace);
AVER(res == ResOK);

View file

@ -216,12 +216,12 @@ 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;
}
TreeInit(&chunk->chunkTree);
@ -232,13 +232,13 @@ Res ChunkInit(Chunk chunk, Arena arena,
ArenaChunkInsert(arena, &chunk->chunkTree);
/* 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. */
@ -259,10 +259,10 @@ void ChunkFinish(Chunk chunk)
AVER(BTIsResRange(chunk->allocTable, 0, chunk->pages));
arena = ChunkArena(chunk);
if (arena->hasFreeCBS)
ArenaFreeCBSDelete(arena,
PageIndexBase(chunk, chunk->allocBase),
chunk->limit);
if (arena->hasFreeLand)
ArenaFreeLandDelete(arena,
PageIndexBase(chunk, chunk->allocBase),
chunk->limit);
chunk->sig = SigInvalid;

View file

@ -38,9 +38,6 @@ typedef union PagePoolUnion {
*
* .tract: Tracts represent the grains of memory allocation from
* the arena. See <design/arena/>.
*
* .bool: The hasSeg field is a boolean, but can't be represented
* as type Bool. See <design/arena/#tract.field.hasSeg>.
*/
typedef struct TractStruct { /* Tract structure */
@ -48,7 +45,7 @@ typedef struct TractStruct { /* Tract structure */
void *p; /* pointer for use of owning pool */
Addr base; /* Base address of the tract */
TraceSet white : TraceLIMIT; /* traces for which tract is white */
unsigned hasSeg : 1; /* does tract have a seg in p? See .bool */
BOOLFIELD(hasSeg); /* does tract have a seg in p? */
} TractStruct;

View file

@ -25,6 +25,8 @@ typedef struct TreeStruct {
Tree left, right;
} TreeStruct;
typedef Res (*TreeDescribeMethod)(Tree tree, mps_lib_FILE *stream);
/* TreeKey and TreeCompare -- ordered binary trees
*

View file

@ -275,11 +275,11 @@ associating their own data with each allocation grain.
_`.tract.structure`: The tract structure definition looks like this::
typedef struct TractStruct { /* Tract structure */
PagePoolUnion pool; /* MUST BE FIRST (<design/arena/#tract.field> pool) */
void *p; /* pointer for use of owning pool */
Addr base; /* Base address of the tract */
TraceSet white : TraceLIMIT; /* traces for which tract is white */
unsigned hasSeg : 1; /* does tract have a seg in p? See .bool */
PagePoolUnion pool; /* MUST BE FIRST (design.mps.arena.tract.field.pool) */
void *p; /* pointer for use of owning pool */
Addr base; /* Base address of the tract */
TraceSet white : TRACE_MAX; /* traces for which tract is white */
BOOLFIELD(hasSeg); /* does tract have a seg in p? */
} TractStruct;
_`.tract.field.pool`: The pool.pool field indicates to which pool the tract
@ -304,10 +304,9 @@ use it for any purpose.
_`.tract.field.hasSeg`: The ``hasSeg`` bit-field is a Boolean which
indicates whether the ``p`` field is being used by the segment module.
If this field is ``TRUE``, then the value of ``p`` is a ``Seg``.
``hasSeg`` has type ``unsigned:1`` rather than ``Bool:1`` to avoid
sign conversion problems when converting the bit-field value. See
`design.mps.type.bool.bitfield`_.
If this field is ``TRUE``, then the value of ``p`` is a ``Seg``. See
`design.mps.type.bool.bitfield`_ for why this is declared using the
``BOOLFIELD`` macro.
.. _design.mps.type.bool.bitfield: type#bool.bitfield
@ -317,7 +316,7 @@ memory represented by the tract.
_`.tract.field.white`: The white bit-field indicates for which traces
the tract is white (`.req.fun.trans.white`_). This information is also
stored in the segment, but is duplicated here for efficiency during a
call to ``TraceFix`` (see `design.mps.trace.fix`_).
call to ``TraceFix()`` (see `design.mps.trace.fix`_).
.. _design.mps.trace.fix: trace#fix

View file

@ -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.poolmvt_, design.mps.poolmvff_.
.. _design.mps.poolmvt: poolmvt
.. _design.mps.poolmvff: poolmvff
_`.overview`: The "coalescing block structure" is a set of addresses
(or a subset of address space), with provision for efficient
@ -29,50 +32,27 @@ 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_), the CBS must satisfy:
.. _design.mps.land: land
_`.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`: 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
@ -80,180 +60,111 @@ 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`: The type of coalescing block structures. 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.
_`.function.class`: The function ``CBSLandClassGet()`` returns the CBS
class, a subclass of ``LandClass`` suitable for passing to
``LandCreate()`` or ``LandInit()``.
``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.
``LandClass CBSFastLandClassGet(void)``
``CBSInit()`` may take one keyword argument:
_`.function.class`: Returns a subclass of ``CBSLandClass`` that
maintains, for each subtree, the size of the largest block in that
subtree. This enables the ``LandFindFirst()``, ``LandFindLast()``, and
``LandFindLargest()`` generic functions.
* ``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.
``LandClass CBSZonedLandClassGet(void)``
``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`: Returns a subclass of ``CBSFastLandClass`` that
maintains, for each subtree, the union of the zone sets of all ranges
in that subtree. This enables the ``LandFindInZones()`` generic
function.
``Res CBSDelete(Range rangeReturn, CBS cbs, Range range)``
_`.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.
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.
When initializing a CBS, ``LandCreate()`` and ``LandInit()`` take the
following optional keyword arguments:
_`.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.
* ``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.
``void CBSIterate(CBS cbs, CBSIterateMethod iterate, void *closureP, Size closureS)``
* ``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.
_`.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.
* ``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.)
``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.
Limitations
...........
``Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, CBS cbs, Size size, FindDelete findDelete)``
_`.limit.find`: ``CBSLandClass`` does not support the
``LandFindFirst()``, ``LandFindLast()``, and ``LandFindLargest()``
generic functions (the subclasses do support these operations).
_`.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``.
_`.limit.zones`: ``CBSLandClass`` and ``CBSFastLandClass`` do not
support the ``LandFindInZones()`` generic function (the subclass
``CBSZonedLandClass`` does support this operation).
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::
_`.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.
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.
_`.limit.flush`: CBS cannot be used as the source in a call to
``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
_`.impl.splay`: The CBS is implemented using a splay tree (see
design.mps.splay_). Each splay tree node is embedded in a block
structure that represents a semi-open address range. The key passed
for comparison is the base of another range.
.. _design.mps.splay: splay
_`.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.splay.fast-find`: In the ``CBSFastLandClass`` class,
``cbsFindFirst()`` and ``cbsFindLast()`` use the update/refresh
facility of splay trees to store, in each block, 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()``.
_`.impl.splay.zones`: In the ``CBSZonedLandClass`` class,
``cbsFindInZones()`` uses the update/refresh facility of splay trees
to store, in each block, 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
@ -261,10 +172,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
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``.
``LandInsert()`` or ``LandDelete()``, and there is insufficient memory
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``.
The CBS block
@ -285,19 +196,12 @@ 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.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
@ -306,9 +210,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
@ -318,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
-----
@ -330,7 +239,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
----------------
@ -359,6 +267,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/

View file

@ -24,214 +24,196 @@ the MPM and the pool class implementations.
Pirinen, 1999-07-20.
Fields
------
_`.field`: These fields are provided by pool classes as part of the
``PoolClass`` object (see impl.h.mpmst.class). They form part of the
interface which allows the MPM to treat pools in a uniform manner.
_`.field.name`: The ``name`` field should be a short, pithy, cryptic
name for the pool class. It should typically start with ``"A"`` if
memory is managed by the garbage collector, and ``"M"`` if memory is
managed by alloc/free. Examples are "AMC", "MV".
_`.field.attr`: The ``attr`` field must be a bitset of pool class
attributes. See `design.mps.type.attr`_.
.. _design.mps.type.attr: type
_`.field.size`: The ``size`` field is the size of the pool instance
structure. For the ``PoolFoo`` class this can reasonably be expected
to be ``sizeof(PoolFooStruct)``.
_`.field.offset`: The ``offset`` field is the offset into the pool
instance structure of the generic ``PoolStruct``. Typically this field
is called ``poolStruct``, so something like ``offsetof(PoolFooStruct,
poolStruct)`` is typical. If possible, arrange for this to be zero.
Methods
-------
_`.methods`: These methods are provided by pool classes as part of the
``PoolClass`` object (see impl.h.mpmst.class). They form the interface
which allows the MPM to treat pools in a uniform manner.
_`.method`: These methods are provided by pool classes as part of the
``PoolClass`` object (see impl.h.mpmst.class). They form part of the
interface which allows the MPM to treat pools in a uniform manner.
The following description is based on the definition of the
``PoolClassStruct`` (impl.h.mpmst.class).
_`.method.unused`: If a pool class is not required to provide a
certain method, the class should assign the appropriate ``PoolNo``
method for that method to ensure that erroneous calls are detected. It
is not acceptable to use ``NULL``.
If a class is not required to provide a certain method then it should
set the appropriate ``PoolNo*`` method for that method. It is not
acceptable to use ``NULL``.
_`.method.trivial`: If a pool class if required to provide a certain
method, but the class provides no special behaviour in this case, it
should assign the appropriate ``PoolTriv`` method.
.. note::
_`.method.init`: The ``init`` field is the pool class's init method.
This method is called via the generic function ``PoolInit()``, which
is in turn called by ``PoolCreate()``. The generic function allocates
the pool's structure (using the ``size`` and ``offset`` fields),
initializes the ``PoolStruct`` (generic part), then calls the ``init``
method to do any class-specific initialization. Typically this means
initializing the fields in the pool instance structure. If ``init``
returns a non-OK result code the instance structure will be
deallocated and the code returned to the caller of ``PoolInit()`` or
``PoolCreate()``. Note that the ``PoolStruct`` isn't made fully valid
until ``PoolInit()`` returns, so the ``init`` method must not call
``PoolCheck()``.
There are also some ``PoolTriv*`` methods. David Jones, 1997-08-19.
_`.method.finish`: The ``finish`` field is the pool class's finish
method. This method is called via the generic function
``PoolFinish()``, which is in turn called by ``PoolDestroy()``. It is
expected to finalise the pool instance structure, release any
resources allocated to the pool, and release the memory associated
with the pool instance structure. Note that the pool is valid when it
is passed to ``finish``. The ``PoolStruct`` (generic part) is finished
when the pool class's ``finish`` method returns.
_`.method.name`: The name field should be a short, pithy, cryptic name
for the pool class. Examples are "AMC", "MV".
_`.method.alloc`: The ``alloc`` field is the pool class's allocation
method. This method is called via the generic function
``PoolAlloc()``. It is expected to return a pointer to a fresh (that
is, not overlapping with any other live object) object of the required
size. Failure to allocate should be indicated by returning an
appropriate error code, and in such a case, ``*pReturn`` should not be
updated. Pool classes are not required to provide this method.
The ``size`` field is the size of the pool instance structure. For the
``Foo`` ``PoolClass`` this can reasonably be expected to be
``sizeof(FooStruct)``.
The ``offset`` field is the offset into the pool instance structure of
the generic ``PoolStruct``. Typically this field is called
``poolStruct``, so something like ``offsetof(FooStruct, poolStruct)``
is typical. If possible, arrange for this to be zero.
The ``init`` field is the class's init method. This method is called
via the generic function ``PoolInit()``, which is in turn called by
``PoolCreate()``. The generic function allocates the pool's structure
(using the size and offset information), initializes the
``PoolStruct`` (generic part) then calls the ``init`` method to do any
class-specific initialization. Typically this means initializing the
fields in the class instance structure. If ``init`` returns a non-OK
result code the instance structure will be deallocated and the code
returned to the caller of ``PoolInit()``` or ``PoolCreate()``. Note that
the ``PoolStruct`` isn't made fully valid until ``PoolInit()`` returns.
The ``finish`` field is the class's finish method. This method is
called via the generic function ``PoolFinish()``, which is in turn
called by ``PoolDestroy()``. It is expected to finalise the pool
instance structure and release any resources allocated to the pool, it
is expected to release the memory associated with the pool instance
structure. Note that the pool is valid when it is passed to
``finish``. The ``PoolStruct`` (generic part) is finished off when the
class's ``finish`` method returns.
The ``alloc`` field is the class's allocation method. This method is
called via the generic function ``PoolAlloc()``. It is expected to
return a pointer to a fresh (that is, not overlapping with any other
live object) object of the required size. Failure to allocate should
be indicated by returning an appropriate Error code, and in such a
case, ``*pReturn`` should not be updated. Classes are not required to
provide this method, but they should provide at least one of ``alloc``
and ``bufferCreate``.
.. note::
There is no ``bufferCreate``. Gareth Rees, 2013-04-14.
The ``free_`` field is the class's free method. This is intended
primarily for manual style pools. this method is called via the
generic function ``PoolFree()``. The parameters to this method are
_`.method.free`: The ``free`` method is the pool class's free method.
This is intended primarily for manual style pools. This method is
called via the generic function ``PoolFree()``. The parameters are
required to correspond to a previous allocation request (possibly via
a buffer). It is an assertion by the client that the indicated object
is no longer required and the resources associated with it can be
recycled. Pools are not required to provide this method.
recycled. Pool classes are not required to provide this method.
The ``bufferInit`` field is the class's buffer initialization method.
It is called by the generic function ``BufferCreate()``, which allocates
the buffer descriptor and initializes the generic fields. The pool may
optionally adjust these fields or fill in extra values when
``bufferInit`` is called, but often pools set ``bufferInit`` to
``PoolTrivBufferInit()`` because they don't need to do any. If
``bufferInit`` returns a result code other than ``ResOK``, the buffer
structure is deallocated and the code is returned to the called of
``BufferCreate()``. Note that the ``BufferStruct`` isn't fully valid
until ``BufferCreate()`` returns.
_`.method.bufferInit`: The ``bufferInit`` method is the pool class's
buffer initialization method. It is called by the generic function
``BufferCreate()``, which allocates the buffer descriptor and
initializes the generic fields. The pool may optionally adjust these
fields or fill in extra values. If ``bufferInit`` returns a result
code other than ``ResOK``, the buffer structure is deallocated and the
result code is returned to the caller of ``BufferCreate()``. Note that
the ``BufferStruct`` isn't fully valid until ``BufferCreate()``
returns. Pool classes are not required to provide this method.
The ``bufferFinish`` field is the class's buffer finishing method. It
is called by the the generic function ``BufferDestroy()``. The pool is
expected to detach the buffer from any memory and prepare the buffer
for destruction. The class is expected to release the resources
associated with the buffer structure, and any unreserved memory in the
buffer may be recycled. It is illegal for a buffer to be destroyed
when there are pending allocations on it (that is, an allocation has
been reserved, but not committed) and this is checked in the generic
function. This method should be provided if and only if
``bufferCreate`` is provided. [there is no ``bufferCreate`` -- drj
1997-08-19]
_`.method.bufferFinish`: The ``bufferFinish`` method is the pool
class's buffer finishing method. It is called by the the generic
function ``BufferDestroy()``. The pool is expected to detach the
buffer from any memory and prepare the buffer for destruction. The
pool is expected to release the resources associated with the buffer
structure, and any unreserved memory in the buffer may be recycled. It
is illegal for a buffer to be destroyed when there are pending
allocations on it (that is, an allocation has been reserved, but not
committed) and this is checked in the generic function. This method
must be provided if and only if ``bufferInit`` is provided.
The ``condemn`` field is used to condemn a pool. This method is called
via the generic function ``PoolCondemn()``. The class is expected to
condemn a subset (possible the whole set) of objects it manages and
participate in a global trace to determine liveness. The class should
register the refsig of the condemned set with the trace using
``TraceCondemn()``. The class should expect fix requests (via the fix
method below) during a global trace. Classes are not required to
provide this method, but it is expected that automatic style classes
will. This interface is expected to change in the future.
_`.method.access`: The ``access`` method is used to handle client
access. This method is called via the generic functions
``ArenaAccess()`` and ``PoolAccess()``. It indicates that the client
has attempted to access the specified region, but has been denied and
the request trapped due to a protection state. The pool should perform
any work necessary to remove the protection whilst still preserving
appropriate invariants (typically this will be scanning work). Pool
classes are not required to provide this method, and not doing so
indicates they never protect any memory managed by the pool.
.. note::
_`.method.whiten`: The ``whiten`` method is used to condemn a segment
belonging to a pool. This method is called via the generic function
``PoolWhiten()``. The pool is expected to condemn a subset (but
typically all) of the objects in the segment and prepare the segment
for participation in a global trace to determine liveness. The pool
should expect fix requests (via the ``fix`` method below) during a
global trace. Pool classes that automatically reclaim dead objects
must provide this method, and must additionally set the ``AttrGC``
attribute.
``condemn`` now takes an action and a segment and should condemn
the segment (turn it white) if it corresponds to the
interpretation of the action. David Jones, 1997-08-19.
_`.method.grey`: The ``grey`` method is used to greyen a segment
belonging to a pool. This method is called via the generic function
``PoolGrey()``. The pool should set all of the objects in the segment
(excepting any set that has been condemned in this trace) to be grey,
that is, ready for scanning. The pool should arrange that any
appropriate invariants are preserved, possibly by using the protection
interface (see `design.mps.prot`_). Pool classes are not required to
provide this method, and not doing so indicates that all instances of
this class will have no fixable or traceable references in them.
It is now called ``whiten``. David Jones, 1998-02-02.
.. _design.mps.prot: prot
The ``mark`` field is used to mark an entire pool. This method is
called via the generic function ``PoolMark()``. The class should
consider all of its objects, except any set that has been condemned in
this trace, to be marked, that is ready for scanning. The class should
arrange that any appropriate invariants are preserved possibly by the
Protection interface. Classes are not required to provide this method,
and not doing so indicates that all instances of this class will have
no fixable or traceable references in them.
_`.method.blacken`: The ``blacken`` method is used to blacken a
segment belonging to a pool. This method is called via the generic
function ``PoolBlacken()`` when it is known that the segment cannot
refer to the white set. The pool must blacken all grey objects in the
segment. Pool classes are not required to provide this method, and not
doing so indicates that all instances of this class will have no
fixable or traceable references in them.
.. note::
_`.method.scan`: The ``scan`` method is used to scan a segment. This
method is called via the generic function ``PoolScan()``. The pool
must scan all the known grey objects on the segment and it may also
accumulate a summary of *all* the objects on the segment. If it
succeeds in accumulating such a summary it must indicate that it has
done so by setting the ``totalReturn`` parameter to ``TRUE``. Pool
classes are not required to provide this method, and not doing so
indicates that all instances of this class will have no fixable or
traceable reference in them.
``mark`` is no longer present: ``grey`` turns an entire segment
grey. David Jones, 1997-08-19.
_`.method.fix`: The ``fix`` method is used to perform fixing. This
method is called via the generic function ``TraceFix()``. It indicates
that the specified reference has been found and the pool should
consider the object to be live. There is provision for adjusting the
value of the reference (to allow for classes that move objects). not
required to provide this method. Pool classes that automatically
reclaim dead objects must provide this method, and must additionally
set the ``AttrGC`` attribute. Pool classes that may move objects must
also set the ``AttrMOVINGGC`` attribute.
The ``scan`` field is used to perform scanning. This method is called
via the generic function ``PoolScan()``. The class should scan the
segment specified. It should scan all the known live (marked, that is,
those objects on which fix has been called) on the segment and
accumulate a summary of *all* the objects on the segment. This means
that mark and sweep pools may have to jump through hoops a little bit
(see design.mps.poolasm.summary for a pedagogical example). Classes
are not required to provide this method, and not doing so indicates
that all instances of this class will have no fixable or traceable
reference in them.
_`.method.fixEmergency`: The ``fixEmergency`` method is used to
perform fixing in "emergency" situations. It must complete its work
without allocating memory (perhaps by using some approximation, or by
running more slowly). Pool classes must provide this method if they
provide the ``fix`` method.
.. note::
_`.method.reclaim`: The ``reclaim`` method is used to reclaim memory
in a segment. This method is called via the generic function
``PoolReclaim()``. It indicates that any remaining white objects in
the segment have now been proved unreachable, hence are dead. The pool
should reclaim the resources associated with the dead objects. Pool
classes are not required to provide this method. If they do, they must
set the ``AttrGC`` attribute.
The ``scan`` method now takes an extra return parameter which
classes should use to indicate whether they scanned all objects in
segment or not. Classes should return summary only of object they
scanned. Caller of this method (``TraceScan()``) is responsible
for updating summaries correctly when not a total scan. Hence no
jumping through hoops required. David Jones, 1998-01-30.
_`.method.walk`: The ``walk`` method is used by the heap walker. The
``walk`` method should apply the visitor function (along with its
closure parameters and the object format) to all *black* objects in
the segment. Padding objects may or may not be included in the walk at
the classes discretion, in any case in will be the responsibility of
the client to do something sensible with padding objects. Forwarding
objects are never included in the walk. Pool classes need not provide
this method. If they do, they must set the ``AttrFMT`` attribute.
The ``fix`` field is used to perform fixing. This method is called via
the function ``TraceFix()``. It indicates that the specified reference
has been found and the class should consider the object live. There is
provision for adjusting the value of the reference (to allow for
classes that move objects). Classes are not required to provide this
method, and not doing so indicates that the class is not automatic
style (ie it does not use global tracing to determine liveness).
The ``reclaim`` field is used to reclaim memory. This method is called
via the generic function ``PoolReclaim()``. It indicates that the trace
has fixed all references to reachable objects.
.. note::
Actually it indicates that any remaining white objects have now
been proved unreachable, hence are dead. David Jones, 1997-08-19.
The class should consider objects that have been condemned and not
fixed in this trace to be dead and may reclaim the resources
associated with them. Classes are not required to provide this method.
.. note::
``reclaim`` is now called on each segment. David Jones,
1997-08-19.
The ``access`` field is used to indicate client access. This method is
called via the generic functions ``SpaceAccess()`` and
``PoolAccess()``. It indicates that the client has attempted to access
the specified region, but has been denied and the request trapped due
to a protection state. The class should perform any work necessary to
remove the protection whilst still preserving appropriate invariants
(typically this will be scanning work). Classes are not required to
provide this method, and not doing so indicates they never protect any
memory managed by the pool.
.. note::
``access`` is no longer present. David Jones, 1997-08-19.
_`.method.act`: ``act`` is called when the MPM has decided to execute
an action that the class declared. The Class should arrange execution
of the associated work (usually by beginning an incremental trace).
_`.method.walk`: ``walk`` is used by the heap walker. ``walk`` is only
required to be implemented by classes which specify the AttrFMT
attribute (formatted pools). The ``walk`` method should apply the
passed in function (along with its closure variables (which are also
passed in) and the object format) to all *black* objects in the
segment. Padding objects may or may not be included in the walk at the
classes discretion, in any case in will be the responsibility of the
client to do something sensible with padding objects.
.. note::
What about broken hearts? David Jones, 1998-01-30.
The ``describe`` field is used to print out a description of a pool.
This method is called via the generic function ``PoolDescribe()``. The
class should emit an textual description of the pool's contents onto
the specified stream. Each line should begin with two spaces. Classes
are not required to provide this method.
_`.method.describe`: The ``describe`` field is used to print out a
description of a pool. This method is called via the generic function
``PoolDescribe()``. The class should emit an textual description of
the pool's contents onto the specified stream. Each line should begin
with two spaces. Classes are not required to provide this method.
Events
@ -269,6 +251,8 @@ Document history
- 2013-03-12 GDR_ Converted to reStructuredText.
- 2014-06-08 GDR_ Bring method descriptions up to date.
.. _RB: http://www.ravenbrook.com/consultants/rb/
.. _GDR: http://www.ravenbrook.com/consultants/gdr/

150
mps/design/failover.txt Normal file
View file

@ -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.
<http://www.ravenbrook.com/>. 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.**

View file

@ -41,174 +41,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
@ -221,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 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
@ -239,14 +119,31 @@ _`.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
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
-----------------------------
@ -256,7 +153,7 @@ 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
``freelistFindLargest()``. It's not clear that this would actually be
an improvement.
@ -266,6 +163,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/

View file

@ -45,21 +45,23 @@ arena_ The design of the MPS arena
arenavm_ Virtual memory arena
bt_ Bit tables
buffer_ Allocation buffers and allocation points
cbs_ Coalescing block structures
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 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
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
@ -68,15 +70,15 @@ message-gc_ Messages sent when garbage collection begins or ends
nailboard_ Nailboards for ambiguously referenced segments
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
poolmfs_ The design of the manual fixed small memory 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
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_ Manual Rank Guardian pool class
poolmv_ Manual Variable pool class
poolmvt_ Manual Variable Temporal pool class
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
@ -122,6 +124,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
@ -130,6 +133,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

352
mps/design/land.txt Normal file
View file

@ -0,0 +1,352 @@
.. mode: -*- rst -*-
Lands
=====
:Tag: design.mps.land
: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 *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.
``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.)
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.
``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
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.
_`.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,
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.
_`.function.delete.alias`: It is acceptable for ``rangeReturn`` and
``range`` to share storage.
``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
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)
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(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, 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
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 indicates the cause of the failure.
``Res LandDescribe(Land land, mps_lib_FILE *stream)``
_`.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.
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
-------
_`.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
----------------
- 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.
<http://www.ravenbrook.com/>. 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.**

View file

@ -70,6 +70,11 @@ an ``AVER()`` has fired. Naturally, if the information required for
the dump has been corrupted, it will fail, as softly as possible
(source @@@@).
_`.req.portable`: Client code that uses these features must be easily
portable to all the supported platforms. (Source: job003749_.)
.. _job003749: http://www.ravenbrook.com/project/mps/issue/job003749/
.. note::
There are more requirements, especially about memory dumps and
@ -90,6 +95,11 @@ specified as a byte/word which used repeatedly to fill the fencepost.
_`.fence.content.template`: The content could be given as a template
which is of the right size and is simply copied onto the fencepost.
_`.fence.content.template.repeat`: The content could be given as a
template which is copied repeatedly until the fencepost is full. (This
would avoid the need to specify different templates on different
architectures, and so help meet `.req.portable`_.)
_`.fence.walk`: `.req.fencepost.check`_ requires the ability to find
all the allocated objects. In formatted pools, this is not a problem.
In unformatted pools, we could use the walker. It's a feasible
@ -233,14 +243,14 @@ to pools. In particular, clients will be able to use tagging and
fenceposting separately on each pool.
_`.fence.size`: Having fenceposts of adjustable size and pattern is
quite useful. We feel that restricting the size to an integral
multiple of the [pool or format?] alignment is harmless and simplifies
the implementation enormously.
useful. Restricting the size to an integral multiple of the [pool or
format?] alignment would simplify the implementation but breaks
`.req.portable`_.
_`.fence.template`: We use templates (`.fence.content.template`_) to
fill in the fenceposts, but we do not give any guarantees about the
location of the fenceposts, only that they're properly aligned. This
leaves us the opportunity to do tail-only fenceposting, if we choose.
location of the fenceposts. This leaves us the opportunity to do
tail-only fenceposting, if we choose.
_`.fence.slop`: [see impl.c.dbgpool.FenceAlloc @@@@]
@ -416,6 +426,8 @@ Document History
- 2013-04-14 GDR_ Converted to reStructuredText.
- 2014-04-09 GDR_ Added newly discovered requirement `.req.portable`_.
.. _RB: http://www.ravenbrook.com/consultants/rb/
.. _GDR: http://www.ravenbrook.com/consultants/gdr/

View file

@ -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 <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
-------

View file

@ -25,8 +25,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.
@ -55,6 +55,12 @@ empty.
Initialize ``dest`` to be a copy of ``src``.
``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

View file

@ -0,0 +1,427 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="613.48077"
height="173.08984"
id="svg3079"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="splay-assemble.svg">
<defs
id="defs3081">
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible">
<path
id="path3929"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="468.97256"
inkscape:cy="37.753236"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1204"
inkscape:window-height="920"
inkscape:window-x="139"
inkscape:window-y="0"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<inkscape:grid
type="xygrid"
id="grid3087"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata3084">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(20.211521,-49.088745)">
<path
inkscape:connector-curvature="0"
id="path3873"
d="m 45.528859,138.17859 40,-40 40.000001,40"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="ccc" />
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3859"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(-49.471141,-29.183593)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="44.134338"
y="182.17859"
id="text3861"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3863"
x="44.134338"
y="182.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">A</tspan></text>
<path
transform="translate(30.528859,-29.183593)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3865"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3867"
y="182.17859"
x="126.92338"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="182.17859"
x="126.92338"
id="tspan3869"
sodipodi:role="line">B</tspan></text>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3089"
sodipodi:cx="115"
sodipodi:cy="157.36218"
sodipodi:rx="5"
sodipodi:ry="5"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
transform="translate(-29.471141,-59.183594)" />
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3877"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(82.528859,-49.183594)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="177.5992"
y="162.17859"
id="text3879"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3881"
x="177.5992"
y="162.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">R</tspan></text>
<path
transform="translate(-69.471141,-19.183593)"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
sodipodi:ry="5"
sodipodi:rx="5"
sodipodi:cy="157.36218"
sodipodi:cx="115"
id="path3875"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<text
sodipodi:linespacing="125%"
id="text3909"
y="102.36218"
x="281.88461"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="102.36218"
x="281.88461"
sodipodi:role="line"
id="tspan3913"></tspan><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="132.36218"
x="281.88461"
sodipodi:role="line"
id="tspan3561">assemble</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow1Mend)"
d="m 242.25962,152.36218 80,0"
id="path3915"
inkscape:connector-curvature="0" />
<text
sodipodi:linespacing="125%"
id="text4361"
y="102.17859"
x="67.528877"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="102.17859"
x="67.528877"
id="tspan4363"
sodipodi:role="line">x</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="29.528843"
y="142.17859"
id="text4365"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4367"
x="29.528843"
y="142.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">y</tspan></text>
<path
transform="translate(-101.47114,-49.183594)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3050"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3052"
y="162.17859"
x="-6.5414691"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="162.17859"
x="-6.5414691"
id="tspan3054"
sodipodi:role="line">L</tspan></text>
<path
sodipodi:nodetypes="ccc"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 395.52886,118.17859 92,-60.000001 92,60.000001"
id="path3601"
inkscape:connector-curvature="0" />
<path
transform="translate(352.52886,10.816406)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3603"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3605"
y="222.17859"
x="446.13434"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="222.17859"
x="446.13434"
id="tspan3607"
sodipodi:role="line">A</tspan></text>
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3609"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(432.52886,10.816406)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="528.92334"
y="222.17859"
id="text3611"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3613"
x="528.92334"
y="222.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">B</tspan></text>
<path
transform="translate(372.52886,-99.183591)"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
sodipodi:ry="5"
sodipodi:rx="5"
sodipodi:cy="157.36218"
sodipodi:cx="115"
id="path3615"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
transform="translate(484.52886,-49.183595)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3617"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3619"
y="162.17859"
x="579.59918"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="162.17859"
x="579.59918"
id="tspan3621"
sodipodi:role="line">R</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="469.52887"
y="62.178589"
id="text3625"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3627"
x="469.52887"
y="62.178589"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">x</tspan></text>
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3633"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(300.52886,-49.183595)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="395.45853"
y="162.17859"
id="text3635"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3637"
x="395.45853"
y="162.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">L</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 470,93.089844 -40,-40"
id="path3639"
inkscape:connector-curvature="0"
transform="translate(-22.471141,85.088745)" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 550,93.089844 40,-40"
id="path3641"
inkscape:connector-curvature="0"
transform="translate(-22.471141,85.088745)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,437 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="529.48077"
height="173.08984"
id="svg3079"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="splay-link-left.svg">
<defs
id="defs3081">
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible">
<path
id="path3929"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="471.23218"
inkscape:cy="73.753236"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1204"
inkscape:window-height="920"
inkscape:window-x="7"
inkscape:window-y="19"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<inkscape:grid
type="xygrid"
id="grid3087"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata3084">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(22.471141,-85.088745)">
<path
inkscape:connector-curvature="0"
id="path3873"
d="m 441.26924,134.17859 -40,-40 -40,40"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="ccc" />
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3859"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="matrix(-1,0,0,1,536.26924,-33.183593)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="441.26923"
y="178.17859"
id="text3861"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3863"
x="441.26923"
y="178.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">B</tspan></text>
<path
transform="matrix(-1,0,0,1,456.26924,-33.183593)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3865"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3867"
y="178.17859"
x="359.87473"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="178.17859"
x="359.87473"
id="tspan3869"
sodipodi:role="line">A</tspan></text>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3089"
sodipodi:cx="115"
sodipodi:cy="157.36218"
sodipodi:rx="5"
sodipodi:ry="5"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
transform="matrix(-1,0,0,1,516.26924,-63.183594)" />
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3877"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="matrix(-1,0,0,1,404.26924,-53.183594)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="306.83173"
y="158.17859"
id="text3879"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3881"
x="306.83173"
y="158.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">L</tspan></text>
<path
transform="matrix(-1,0,0,1,556.26924,-23.183593)"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
sodipodi:ry="5"
sodipodi:rx="5"
sodipodi:cy="157.36218"
sodipodi:cx="115"
id="path3875"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<text
sodipodi:linespacing="125%"
id="text3909"
y="118.36218"
x="222.87442"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="118.36218"
x="222.87442"
id="tspan3911"
sodipodi:role="line">link</tspan><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="148.36218"
x="222.87442"
sodipodi:role="line"
id="tspan3913">left</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow1Mend)"
d="m 264.53848,168.36218 -80,0"
id="path3915"
inkscape:connector-curvature="0" />
<text
sodipodi:linespacing="125%"
id="text4361"
y="98.178589"
x="419.26923"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="98.178589"
x="419.26923"
id="tspan4363"
sodipodi:role="line">x</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="457.26926"
y="138.17859"
id="text4365"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4367"
x="457.26926"
y="138.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">y</tspan></text>
<path
transform="matrix(-1,0,0,1,588.26924,-53.183594)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3050"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3052"
y="158.17859"
x="490.90207"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="158.17859"
x="490.90207"
id="tspan3054"
sodipodi:role="line">R</tspan></text>
<path
sodipodi:nodetypes="ccc"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -11.72114,126.67859 42.99038,47.5 -40.0000004,40"
id="path3094"
inkscape:connector-curvature="0" />
<path
transform="matrix(-1,0,0,1,176.26924,-53.183594)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3096"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3098"
y="158.17859"
x="81.269226"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="158.17859"
x="81.269226"
id="tspan3100"
sodipodi:role="line">B</tspan></text>
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3102"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="matrix(-1,0,0,1,86.26924,46.816406)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-10.125278"
y="258.17859"
id="text3104"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3106"
x="-10.125278"
y="258.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">A</tspan></text>
<path
transform="matrix(-1,0,0,1,146.26924,16.816405)"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
sodipodi:ry="5"
sodipodi:rx="5"
sodipodi:cy="157.36218"
sodipodi:cx="115"
id="path3108"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
transform="matrix(-1,0,0,1,86.26924,-53.183595)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3110"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3112"
y="158.17859"
x="-11.168246"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="158.17859"
x="-11.168246"
id="tspan3114"
sodipodi:role="line">L</tspan></text>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3116"
sodipodi:cx="115"
sodipodi:cy="157.36218"
sodipodi:rx="5"
sodipodi:ry="5"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
transform="matrix(-1,0,0,1,196.26924,-43.183594)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="49.269192"
y="178.17859"
id="text3118"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3120"
x="49.269192"
y="178.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">x</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3122"
y="118.17859"
x="97.269257"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="118.17859"
x="97.269257"
id="tspan3124"
sodipodi:role="line">y</tspan></text>
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3126"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="matrix(-1,0,0,1,232.28848,-53.183593)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="134.9213"
y="158.17859"
id="text3128"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3130"
x="134.9213"
y="158.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">R</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,437 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="529.48077"
height="173.08984"
id="svg3079"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="splay-link-right.svg">
<defs
id="defs3081">
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible">
<path
id="path3929"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="471.23218"
inkscape:cy="73.753236"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1204"
inkscape:window-height="920"
inkscape:window-x="139"
inkscape:window-y="0"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<inkscape:grid
type="xygrid"
id="grid3087"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata3084">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(22.471141,-85.088745)">
<path
inkscape:connector-curvature="0"
id="path3873"
d="m 43.26924,134.17859 40,-40 40,40"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="ccc" />
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3859"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(-51.73076,-33.183593)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="41.874722"
y="178.17859"
id="text3861"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3863"
x="41.874722"
y="178.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">A</tspan></text>
<path
transform="translate(28.26924,-33.183593)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3865"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3867"
y="178.17859"
x="124.66376"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="178.17859"
x="124.66376"
id="tspan3869"
sodipodi:role="line">B</tspan></text>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3089"
sodipodi:cx="115"
sodipodi:cy="157.36218"
sodipodi:rx="5"
sodipodi:ry="5"
d="m 120,157.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
transform="translate(-31.73076,-63.183594)" />
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3877"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(80.26924,-53.183594)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="175.33957"
y="158.17859"
id="text3879"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3881"
x="175.33957"
y="158.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">R</tspan></text>
<path
transform="translate(-71.73076,-23.183593)"
d="m 120,157.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
sodipodi:ry="5"
sodipodi:rx="5"
sodipodi:cy="157.36218"
sodipodi:cx="115"
id="path3875"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<text
sodipodi:linespacing="125%"
id="text3909"
y="118.36218"
x="259.625"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="118.36218"
x="259.625"
id="tspan3911"
sodipodi:role="line">link</tspan><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="148.36218"
x="259.625"
sodipodi:role="line"
id="tspan3913">right</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow1Mend)"
d="m 220,168.36218 80,0"
id="path3915"
inkscape:connector-curvature="0" />
<text
sodipodi:linespacing="125%"
id="text4361"
y="98.178589"
x="65.269257"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="98.178589"
x="65.269257"
id="tspan4363"
sodipodi:role="line">x</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="27.269224"
y="138.17859"
id="text4365"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4367"
x="27.269224"
y="138.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">y</tspan></text>
<path
transform="translate(-103.73076,-53.183594)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3050"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3052"
y="158.17859"
x="-8.8010883"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="158.17859"
x="-8.8010883"
id="tspan3054"
sodipodi:role="line">L</tspan></text>
<path
sodipodi:nodetypes="ccc"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 496.25962,126.67859 -42.99038,47.5 40,40"
id="path3094"
inkscape:connector-curvature="0" />
<path
transform="translate(308.26924,-53.183594)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3096"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3098"
y="158.17859"
x="401.87473"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="158.17859"
x="401.87473"
id="tspan3100"
sodipodi:role="line">A</tspan></text>
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3102"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(398.26924,46.816406)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="494.66376"
y="258.17859"
id="text3104"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3106"
x="494.66376"
y="258.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">B</tspan></text>
<path
transform="translate(338.26924,16.816405)"
d="m 120,157.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
sodipodi:ry="5"
sodipodi:rx="5"
sodipodi:cy="157.36218"
sodipodi:cx="115"
id="path3108"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
transform="translate(398.26924,-53.183595)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3110"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3112"
y="158.17859"
x="493.33954"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="158.17859"
x="493.33954"
id="tspan3114"
sodipodi:role="line">R</tspan></text>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3116"
sodipodi:cx="115"
sodipodi:cy="157.36218"
sodipodi:rx="5"
sodipodi:ry="5"
d="m 120,157.36218 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
transform="translate(288.26924,-43.183594)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="435.26929"
y="178.17859"
id="text3118"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3120"
x="435.26929"
y="178.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">x</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3122"
y="118.17859"
x="387.26923"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="118.17859"
x="387.26923"
id="tspan3124"
sodipodi:role="line">y</tspan></text>
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3126"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(252.25,-53.183593)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="347.17969"
y="158.17859"
id="text3128"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3130"
x="347.17969"
y="158.17859"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">L</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,405 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="407.48077"
height="133.40625"
id="svg3079"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="splay-rotate-right.svg">
<defs
id="defs3081">
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart"
style="overflow:visible">
<path
id="path3926"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
transform="matrix(0.4,0,0,0.4,4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible">
<path
id="path3929"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="180.49339"
inkscape:cy="51.253232"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1204"
inkscape:window-height="920"
inkscape:window-x="179"
inkscape:window-y="20"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<inkscape:grid
type="xygrid"
id="grid3087"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata3084">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-56.259619,-83.272339)">
<path
inkscape:connector-curvature="0"
id="path3873"
d="m 110,132.36218 40,-39.999997 40,39.999997"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="ccc" />
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3859"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(-25,5)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="70"
y="216.36218"
id="text3861"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3863"
x="70"
y="216.36218"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">A</tspan></text>
<path
transform="translate(55,5)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3865"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3867"
y="216.36218"
x="150"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="216.36218"
x="150"
id="tspan3869"
sodipodi:role="line">B</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 70,172.36218 40,-40 40,40"
id="path3871"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3089"
sodipodi:cx="115"
sodipodi:cy="157.36218"
sodipodi:rx="5"
sodipodi:ry="5"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
transform="translate(35,-65)" />
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3877"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(95,-35)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="190"
y="176.36218"
id="text3879"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3881"
x="190"
y="176.36218"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">C</tspan></text>
<path
transform="translate(-5,-25)"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
sodipodi:ry="5"
sodipodi:rx="5"
sodipodi:cy="157.36218"
sodipodi:cx="115"
id="path3875"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
sodipodi:nodetypes="ccc"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 410,132.36218 370,92.362183 330,132.36218"
id="path3883"
inkscape:connector-curvature="0" />
<path
transform="matrix(-1,0,0,1,545,5)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3885"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3887"
y="216.36218"
x="450"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="216.36218"
x="450"
id="tspan3889"
sodipodi:role="line">C</tspan></text>
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3891"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="matrix(-1,0,0,1,465,5)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="368.60547"
y="216.36218"
id="text3893"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3895"
x="368.60547"
y="216.36218"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">B</tspan></text>
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path3897"
d="m 450,172.36218 -40,-40 -40,40"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
transform="matrix(-1,0,0,1,485,-65)"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
sodipodi:ry="5"
sodipodi:rx="5"
sodipodi:cy="157.36218"
sodipodi:cx="115"
id="path3899"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
transform="matrix(-1,0,0,1,425,-35)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3901"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3903"
y="176.36218"
x="329.61328"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="176.36218"
x="329.61328"
id="tspan3905"
sodipodi:role="line">A</tspan></text>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3907"
sodipodi:cx="115"
sodipodi:cy="157.36218"
sodipodi:rx="5"
sodipodi:ry="5"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
transform="matrix(-1,0,0,1,525,-25)" />
<text
sodipodi:linespacing="125%"
id="text3909"
y="102.36218"
x="260"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="102.36218"
x="260"
id="tspan3911"
sodipodi:role="line">rotate</tspan><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="132.36218"
x="260"
sodipodi:role="line"
id="tspan3913">left</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:none"
d="m 220,152.36218 80,0"
id="path3915"
inkscape:connector-curvature="0" />
<text
sodipodi:linespacing="125%"
id="text4361"
y="96.362183"
x="132"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="96.362183"
x="132"
id="tspan4363"
sodipodi:role="line">x</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="94"
y="136.36218"
id="text4365"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4367"
x="94"
y="136.36218"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">y</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="388"
y="96.362183"
id="text4369"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4371"
x="388"
y="96.362183"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">x</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4373"
y="136.36218"
x="426"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="136.36218"
x="426"
id="tspan4375"
sodipodi:role="line">y</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,391 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="407.48077"
height="133.40625"
id="svg3079"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="splay-rotate-right.svg">
<defs
id="defs3081">
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible">
<path
id="path3929"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="180.49339"
inkscape:cy="41.753236"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1204"
inkscape:window-height="920"
inkscape:window-x="179"
inkscape:window-y="20"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<inkscape:grid
type="xygrid"
id="grid3087"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata3084">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-56.259619,-83.272339)">
<path
inkscape:connector-curvature="0"
id="path3873"
d="m 110,132.36218 40,-39.999997 40,39.999997"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="ccc" />
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3859"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(-25,5)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="70"
y="216.36218"
id="text3861"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3863"
x="70"
y="216.36218"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">A</tspan></text>
<path
transform="translate(55,5)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3865"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3867"
y="216.36218"
x="150"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="216.36218"
x="150"
id="tspan3869"
sodipodi:role="line">B</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 70,172.36218 40,-40 40,40"
id="path3871"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3089"
sodipodi:cx="115"
sodipodi:cy="157.36218"
sodipodi:rx="5"
sodipodi:ry="5"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
transform="translate(35,-65)" />
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3877"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="translate(95,-35)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="190"
y="176.36218"
id="text3879"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3881"
x="190"
y="176.36218"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">C</tspan></text>
<path
transform="translate(-5,-25)"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
sodipodi:ry="5"
sodipodi:rx="5"
sodipodi:cy="157.36218"
sodipodi:cx="115"
id="path3875"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
sodipodi:nodetypes="ccc"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 410,132.36218 370,92.362183 330,132.36218"
id="path3883"
inkscape:connector-curvature="0" />
<path
transform="matrix(-1,0,0,1,545,5)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3885"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3887"
y="216.36218"
x="450"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="216.36218"
x="450"
id="tspan3889"
sodipodi:role="line">C</tspan></text>
<path
sodipodi:type="star"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3891"
sodipodi:sides="3"
sodipodi:cx="95"
sodipodi:cy="182.36218"
sodipodi:r1="15"
sodipodi:r2="2.5"
sodipodi:arg1="-1.5707963"
sodipodi:arg2="-0.52359881"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:transform-center-y="-1.25"
transform="matrix(-1,0,0,1,465,5)" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="368.60547"
y="216.36218"
id="text3893"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3895"
x="368.60547"
y="216.36218"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">B</tspan></text>
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path3897"
d="m 450,172.36218 -40,-40 -40,40"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
transform="matrix(-1,0,0,1,485,-65)"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
sodipodi:ry="5"
sodipodi:rx="5"
sodipodi:cy="157.36218"
sodipodi:cx="115"
id="path3899"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc" />
<path
transform="matrix(-1,0,0,1,425,-35)"
inkscape:transform-center-y="-1.25"
d="m 95,167.36218 12.99038,22.5 -25.980761,0 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="-0.52359881"
sodipodi:arg1="-1.5707963"
sodipodi:r2="2.5"
sodipodi:r1="15"
sodipodi:cy="182.36218"
sodipodi:cx="95"
sodipodi:sides="3"
id="path3901"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="star" />
<text
sodipodi:linespacing="125%"
id="text3903"
y="176.36218"
x="329.61328"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="176.36218"
x="329.61328"
id="tspan3905"
sodipodi:role="line">A</tspan></text>
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="path3907"
sodipodi:cx="115"
sodipodi:cy="157.36218"
sodipodi:rx="5"
sodipodi:ry="5"
d="m 120,157.36218 c 0,2.76143 -2.23858,5 -5,5 -2.76142,0 -5,-2.23857 -5,-5 0,-2.76142 2.23858,-5 5,-5 2.76142,0 5,2.23858 5,5 z"
transform="matrix(-1,0,0,1,525,-25)" />
<text
sodipodi:linespacing="125%"
id="text3909"
y="102.36218"
x="260"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="102.36218"
x="260"
id="tspan3911"
sodipodi:role="line">rotate</tspan><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="132.36218"
x="260"
sodipodi:role="line"
id="tspan3913">right</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow1Mend)"
d="m 220,152.36218 80,0"
id="path3915"
inkscape:connector-curvature="0" />
<text
sodipodi:linespacing="125%"
id="text4361"
y="96.362183"
x="132"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="96.362183"
x="132"
id="tspan4363"
sodipodi:role="line">x</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="94"
y="136.36218"
id="text4365"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4367"
x="94"
y="136.36218"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">y</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="388"
y="96.362183"
id="text4369"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4371"
x="388"
y="96.362183"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana">x</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4373"
y="136.36218"
x="426"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
xml:space="preserve"><tspan
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Verdana;-inkscape-font-specification:Verdana"
y="136.36218"
x="426"
id="tspan4375"
sodipodi:role="line">y</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -6,7 +6,7 @@ Splay trees
:Tag: design.mps.splay
:Author: Gavin Matthews
:Date: 1998-05-01
:Status: draft document
:Status: complete design
:Revision: $Id$
:Copyright: See `Copyright and License`_.
:Index terms: pair: splay trees; design
@ -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).
@ -43,42 +47,46 @@ usage patterns. Unused nodes have essentially no time overhead.
Definitions
-----------
_`.def.splay-tree`: A "Splay Tree" is a self-adjusting binary tree as
described in paper.st85(0), paper.sleator96(0).
_`.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 datastructure sense to
mean an element of a tree (see also `.type.splay.node`_).
_`.def.node`: A *node* is used in the typical data structure sense to
mean an element of a tree (see also `.type.tree`_).
_`.def.key`: A "key" is a value associated with each node; the keys
_`.def.key`: A *key* is a value associated with each node; the keys
are totally ordered by a client provided comparator.
_`.def.comparator`: A "comparator" is a function that compares keys to
determine their ordering (see also `.type.splay.compare.method`_).
_`.def.comparator`: A *comparator* is a function that compares keys to
determine their ordering (see also `.type.tree.compare.method`_).
_`.def.successor`: Node *N1* is the "successor" of node *N2* if *N1*
and *N2* are both in the same tree, and the key of *N1* immediately
follows the key of *N2* in the ordering of all keys for the tree.
_`.def.successor`: Node *N*\ :subscript:`2` is the *successor* of node
*N*\ :subscript:`1` if *N*\ :subscript:`1` and *N*\ :subscript:`2` are
both in the same tree, and the key of *N*\ :subscript:`2` immediately
follows the key of *N*\ :subscript:`1` in the ordering of all keys for
the tree.
_`.def.left-child`: Each node *N* contains a "left child", which is a
_`.def.left-child`: Each node *N* contains a *left child*, which is a
(possibly empty) sub-tree of nodes. The key of *N* is ordered after
the keys of all nodes in this sub-tree.
_`.def.right-child`: Each node *N* contains a "right child", which is
_`.def.right-child`: Each node *N* contains a *right child*, which is
a (possibly empty) sub-tree of nodes. The key of *N* is ordered before
the keys of all nodes in this sub-tree.
_`.def.neighbour`: A node *N* which has key *Kn* is a "neighbour" of a
key *K* if either *Kn* is the first key in the total order which
compares greater than *K* or if *Kn* is the last key in the total
order which compares less than *K*.
_`.def.neighbour`: The *left neighbour* of a key *K* is the node *N*
with the largest key that compares less than *K* in the total order.
The *right neighbour* of a key *K* is the node *N* with the smaller
key that compares greater than *K* in the total order. A node is a
*neighbour* of a key if it is either the left or right neighbour of
the key.
_`.def.first`: A node is the "first" node in a set of nodes if its key
_`.def.first`: A node is the *first* node in a set of nodes if its key
compares less than the keys of all other nodes in the set.
_`.def.last`: A node is the "last" node in a set of nodes if its key
_`.def.last`: A node is the *last* node in a set of nodes if its key
compares greater than the keys of all other nodes in the set.
_`.def.client-property`: A "client property" is a value that the
_`.def.client-property`: A *client property* is a value that the
client may associate with each node in addition to the key (a block
size, for example). This splay tree implementation provides support
for efficiently finding the first or last nodes with suitably large
@ -89,32 +97,27 @@ 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
ordered for a comparator.
_`.req.tree`: The keys must be associated with nodes arranged in a
Splay Tree.
_`.req.fast`: Common operations must have low amortized cost.
_`.req.splay`: Common operations must balance the tree by splaying it,
to achieve low amortized cost (see paper.st85(0)).
_`.req.add`: Must be able to add new members. This is a common
_`.req.add`: Must be able to add new nodes. This is a common
operation.
_`.req.remove`: Must be able to remove members. This is a common
_`.req.remove`: Must be able to remove nodes. This is a common
operation.
_`.req.locate`: Must be able to locate a member, given a key. This is
_`.req.locate`: Must be able to locate a node, given a key. This is
a common operation.
_`.req.neighbours`: Must be able to locate the neighbouring members
(in order) of a non-member, given a key (see `.def.neighbour`_). This
is a common operation.
_`.req.neighbours`: Must be able to locate the neighbouring nodes of a
key (see `.def.neighbour`_). This is a common operation.
_`.req.iterate`: Must be able to iterate over all members in order
_`.req.iterate`: Must be able to iterate over all nodes in key order
with reasonable efficiency.
_`.req.protocol`: Must support detection of protocol violations.
@ -141,10 +144,73 @@ _`.req.root`: Must be able to find the root of a splay tree (if one
exists).
External types
--------------
Generic binary tree interface
-----------------------------
Types
.....
``typedef struct TreeStruct *Tree``
_`.type.tree`: ``Tree`` is the type of a node in a binary tree.
``Tree`` contains no fields to store the key associated with the node,
or the client property. Again, it is intended that the ``TreeStruct``
can be embedded in another structure, and that this is how the
association will be made (see `.usage.client-node`_ for an example).
No convenience functions are provided for allocation or deallocation.
``typedef void *TreeKey``
_`.type.treekey`: ``TreeKey`` is the type of a key associated with a
node in a binary tree. It is an alias for ``void *`` but expresses the
intention.
``typedef TreeKey (*TreeKeyMethod)(Tree tree)``
_`.type.tree.key.method`: A function of type ``TreeKey`` returns the
key associated with a node in a binary tree. (Since there is no space
in a ``TreeStruct`` to store a key, it is expected that the
``TreeStruct`` is embedded in another structure from which the key can
be extracted.)
``typedef Compare (*TreeCompare)(Tree tree, TreeKey key)``
_`.type.tree.compare.method`: A function of type ``TreeCompare`` is
required to compare ``key`` with the key the client associates with
that splay tree node ``tree``, and return the appropriate Compare
value (see `.usage.compare`_ for an example). The function compares a
key with a node, rather than a pair of keys or nodes as might seem
more obvious. This is because the details of the mapping between nodes
and keys is left to the client (see `.type.tree`_), and the splaying
operations compare keys with nodes (see `.impl.splay`_).
``typedef Res (*TreeDescribeMethod)(Tree tree, mps_lib_FILE *stream)``
_`.type.tree.describe.method`: A function of type
``TreeDescribeMethod`` is required to write (via ``WriteF()``) a
client-oriented representation of the splay node. The output should be
non-empty, short, and without newline characters. This is provided for
debugging purposes only.
Functions
.........
``Bool TreeCheck(Tree tree)``
_`.function.tree.check`: This is a check function for the
``Tree`` type (see guide.impl.c.adt.method.check and
design.mps.check_).
.. _design.mps.check: check
Splay tree interface
--------------------
Types
.....
``typedef struct SplayTreeStruct SplayTreeStruct``
``typedef struct SplayTreeStruct *SplayTree``
_`.type.splay.tree`: ``SplayTree`` is the type of the main object at
@ -153,39 +219,7 @@ the root of the splay tree. It is intended that the
`.usage.client-tree`_ for an example). No convenience functions are
provided for allocation or deallocation.
``typedef struct TreeStruct TreeStruct``
``typedef struct TreeStruct *Tree``
_`.type.splay.node`: ``Tree`` is the type of a binary tree, used as the
representation of the nodes of the splay tree.
``Tree`` contains no fields to store the key
associated with the node, or the client property. Again, it is
intended that the ``TreeStruct`` can be embedded in another
structure, and that this is how the association will be made (see
`.usage.client-node`_ for an example). No convenience functions are
provided for allocation or deallocation.
``typedef Compare (*TreeCompare)(Tree tree, TreeKey key)``
_`.type.splay.compare.method`: A function of type
``TreeCompare`` is required to compare ``key`` with the key the
client associates with that splay tree node ``tree``, and return the
appropriate Compare value (see `.usage.compare`_ for an example). The
function compares a key with a node, rather than a pair of keys or
nodes as might seem more obvious. This is because the details of the
mapping between nodes and keys is left to the client (see
`.type.splay.node`_), and the splaying operations compare keys with
nodes (see `.impl.splay`_).
``typedef Res (*SplayNodeDescribeMethod)(Tree tree, mps_lib_FILE *stream)``
_`.type.splay.node.describe.method`: A function of type
``SplayNodeDescribeMethod`` is required to write (via ``WriteF()``) a
client-oriented representation of the splay node. The output should be
non-empty, short, and without return characters. This is provided for
debugging purposes only.
``typedef Bool (*SplayTestNodeMethod)(SplayTree splay, Tree tree, void *closureP, unsigned long closureS)``
``typedef Bool (*SplayTestNodeMethod)(SplayTree splay, Tree tree, void *closureP, Size closureS)``
_`.type.splay.test.node.method`: A function of type
``SplayTestNodeMethod`` required to determine whether the node itself
@ -194,7 +228,7 @@ meets some client determined property (see `.prop`_ and
``closureS`` describe the environment for the function (see
`.function.splay.find.first`_ and `.function.splay.find.last`_).
``typedef Bool (*SplayTestTreeMethod)(SplayTree splay, Tree tree, void *closureP, unsigned long closureS)``
``typedef Bool (*SplayTestTreeMethod)(SplayTree splay, Tree tree, void *closureP, Size closureS)``
_`.type.splay.test.tree.method`: A function of type
``SplayTestTreeMethod`` is required to determine whether any of the
@ -210,46 +244,39 @@ environment for the function (see `.function.splay.find.first`_ and
``typedef void (*SplayUpdateNodeMethod)(SplayTree splay, Tree tree)``
_`.type.splay.update.node.method`: A function of type
``SplayUpdateNodeMethod`` is required to update any client
datastructures associated with a node to maintain some client
determined property (see `.prop`_) given that the children of the node
have changed. (See
`.usage.callback`_ for an example)
``SplayUpdateNodeMethod`` is required to update any client data
structures associated with a node to maintain some client determined
property (see `.prop`_) given that the children of the node have
changed. (See `.usage.callback`_ for an example)
External functions
------------------
Functions
.........
_`.function.no-thread`: The interface functions are not designed to be
either thread-safe or re-entrant. Clients of the interface are
responsible for synchronization, and for ensuring that client-provided
methods invoked by the splay module (`.type.splay.compare.method`_,
`.type.splay.test.node.method`_, `.type.splay.test.tree.method`_,
`.type.splay.update.node.method`_) do not call functions of the splay
module.
methods invoked by the splay module (`.type.tree.compare.method`_,
`.type.tree.key.method`_, `.type.splay.test.node.method`_,
`.type.splay.test.tree.method`_, `.type.splay.update.node.method`_) do
not call functions of the splay module.
``Bool SplayTreeCheck(SplayTree splay)``
_`.function.splay.tree.check`: This is a check function for the
SplayTree type (see guide.impl.c.adt.method.check &
design.mps.check(0)).
``SplayTree`` type (see guide.impl.c.adt.method.check and
design.mps.check_).
``Bool SplayNodeCheck(Tree tree)``
_`.function.splay.node.check`: This is a check function for the
``Tree`` type (see guide.impl.c.adt.method.check &
design.mps.check(0)).
``void SplayTreeInit(SplayTree splay, SplayCompareMethod compare, SplayUpdateNodeMethod updateNode)``
``void SplayTreeInit(SplayTree splay, TreeCompareMethod compare, TreeKeyMethod nodeKey, SplayUpdateNodeMethod updateNode)``
_`.function.splay.tree.init`: This function initialises a
``SplayTree`` (see guide.impl.c.adt.method.init). It requires a
``compare`` method that defines a total ordering on nodes (see
`.req.order`_); the effect of supplying a compare method that does not
implement a total ordering is undefined. It also requires an
``updateNode`` method, which will be used to keep client properties up
to date when the tree structure changes; the value
``SplayTrivUpdate`` may be used for this method if there is no
``SplayTree`` (see guide.impl.c.adt.method.init). The ``nodeKey``
function extracts a key from a tree node, and the ``compare`` function
defines a total ordering on keys of nodes (see `.req.order`_). The
effect of supplying a compare method that does not implement a total
ordering is undefined. The ``updateNode`` method is used to keep
client properties up to date when the tree structure changes; the
value ``SplayTrivUpdate`` may be used for this method if there is no
need to maintain client properties. (See `.usage.initialization`_ for
an example use).
@ -259,7 +286,7 @@ _`.function.splay.tree.finish`: This function clears the fields of a
``SplayTree`` (see guide.impl.c.adt.method.finish). Note that it does
not attempt to finish or deallocate any associated ``Tree``
objects; clients wishing to destroy a non-empty ``SplayTree`` must
first explicitly descend the tree and call ``SplayNodeFinish()`` on
first explicitly descend the tree and call ``TreeFinish()`` on
each node from the bottom up.
``Bool SplayTreeInsert(SplayTree splay, Tree tree, void *key)``
@ -281,84 +308,75 @@ given node does not compare ``CompareEQUAL`` with the given key, then
function first splays the tree at the given key. (See `.usage.delete`_
for an example use).
``Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, void *key)``
``Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key)``
_`.function.splay.tree.search`: This function searches the splay tree
for a node that compares ``CompareEQUAL`` to the given key (see
`.req.locate`_). It splays the tree at the key. It returns ``FALSE``
if there is no such node in the tree, otherwise ``*nodeReturn`` will
be set to the node.
_`.function.splay.tree.find`: Search the splay tree for a node that
compares ``CompareEQUAL`` to the given key (see `.req.locate`_), and
splay the tree at the key. Return ``FALSE`` if there is no such node
in the tree, otherwise set ``*nodeReturn`` to the node and return
``TRUE``.
``Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, SplayTree splay, void *key)``
``Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, SplayTree splay, TreeKey key)``
_`.function.splay.tree.neighbours`: This function searches a splay
tree for the two nodes that are the neighbours of the given key (see
`.req.neighbours`_). It splays the tree at the key. ``*leftReturn``
will be the neighbour which compares less than the key if such a
neighbour exists; otherwise it will be ``TreeEMPTY``. ``*rightReturn`` will
be the neighbour which compares greater than the key if such a
neighbour exists; otherwise it will be ``TreeEMPTY``. The function returns
``FALSE`` if any node in the tree compares ``CompareEQUAL`` with the
given key. (See `.usage.insert`_ for an example use).
_`.function.splay.tree.neighbours`: Search a splay tree for the two
nodes that are the neighbours of the given key (see
`.req.neighbours`_). Splay the tree at the key. If any node in the
tree compares ``CompareEQUAL`` with the given key, return ``FALSE``.
Otherwise return ``TRUE``, set ``*leftReturn`` to the left neighbour
of the key (or ``TreeEMPTY`` if the key has no left neighbour), and
set ``*rightReturn`` to the right neighbour of the key (or
``TreeEMPTY`` if the key has no right neighbour). See `.usage.insert`_
for an example of use.
``Tree SplayTreeFirst(SplayTree splay, void *zeroKey)``
``Tree SplayTreeFirst(SplayTree splay)``
_`.function.splay.tree.first`: This function splays the tree at the
first node, and returns that node (see `.req.iterate`_). The supplied
key should compare ``CompareLESS`` with all nodes in the tree. It will
return ``TreeEMPTY`` if the tree has no nodes.
_`.function.splay.tree.first`: If the tree has no nodes, return
``TreeEMPTY``. Otherwise, splay the tree at the first node, and return
that node (see `.req.iterate`_).
``Tree SplayTreeNext(SplayTree splay, Tree oldNode, void *oldKey)``
``Tree SplayTreeNext(SplayTree splay, TreeKey key)``
_`.function.splay.tree.next`: This function receives a node and key
and returns the successor node to that node (see `.req.iterate`_).
This function is intended for use in iteration when the received node
will be the current root of the tree, but is robust against being
interspersed with other splay operations (provided the old node still
exists). The supplied key must compare ``CompareEQUAL`` to the
supplied node. Note that use of this function rebalances the tree for
each node accessed. If many nodes are accessed as a result of multiple
uses, the resultant tree will be generally well balanced. But if the
tree was previously beneficially balanced for a small working set of
accesses, then this local optimization will be lost. (see
`.future.parent`_).
_`.function.splay.tree.next`: If the tree contains a right neighbour
for ``key``, splay the tree at that node and return it. Otherwise
return ``TreeEMPTY``. See `.req.iterate`_.
``Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, SplayNodeDescribeMethod nodeDescribe)``
``Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, TreeDescribeMethod nodeDescribe)``
_`.function.splay.tree.describe`: This function prints (using
``WriteF``) to the stream a textual representation of the given splay
tree, using ``nodeDescribe`` to print client-oriented representations
of the nodes (see `.req.debug`_).
_`.function.splay.tree.describe`: Print (using ``WriteF``) a textual
representation of the given splay tree to the stream, using
``nodeDescribe`` to print client-oriented representations of the nodes
(see `.req.debug`_).
``Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, unsigned long closureS)``
``Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, Size closureS)``
_`.function.splay.find.first`: ``SplayFindFirst()`` finds the first node
in the tree that satisfies some client property (as determined by the
``testNode`` and ``testTree`` methods) (see `.req.property.find`_).
``closureP`` and ``closureS`` are arbitrary values, and are passed to
the ``testNode`` and ``testTree`` methods which may use the values as
closure environments. If there is no satisfactory node, then ``FALSE``
is returned, otherwise ``*nodeReturn`` is set to the node. (See
`.usage.delete`_ for an example use).
_`.function.splay.find.first`: Find the first node in the tree that
satisfies some client property, as determined by the ``testNode`` and
``testTree`` methods (see `.req.property.find`_). ``closureP`` and
``closureS`` are arbitrary values, and are passed to the ``testNode``
and ``testTree`` methods which may use the values as closure
environments. If there is no satisfactory node, return ``FALSE``;
otherwise set ``*nodeReturn`` to the node and return ``TRUE``. See
`.usage.delete`_ for an example.
``Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, unsigned long closureS)``
``Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, Size closureS)``
_`.function.splay.find.last`: ``SplayFindLast()`` finds the last node
in the tree that satisfies some client property (as determined by the
``testNode`` and ``testTree`` methods) (see `.req.property.find`_).
``closureP`` and ``closureS`` are arbitrary values, and are passed to
the ``testNode`` and ``testTree`` methods which may use the values as
closure environments. If there is no satisfactory node, then ``FALSE``
is returned, otherwise ``*nodeReturn`` is set to the node.
_`.function.splay.find.last`: As ``SplayFindFirst()``, but find the
last node in the tree that satisfies the client property.
``void SplayNodeRefresh(SplayTree splay, Tree tree, void *key)``
``void SplayNodeRefresh(SplayTree splay, Tree tree, TreeKey key)``
_`.function.splay.node.refresh`: ``SplayNodeRefresh()`` must be called
whenever the client property (see `.prop`_) at a node changes (see
`.req.property.change`_). It will call the ``updateNode`` method on
the given node, and any other nodes that may require update. The
_`.function.splay.node.refresh`: Call the ``updateNode`` method on the
given node, and on any other nodes that may require updating. The
client key for the node must also be supplied; the function splays the
tree at this key. (See `.usage.insert`_ for an example use).
tree at this key. (See `.usage.insert`_ for an example use). This
function must be called whenever the client property (see `.prop`_) at
a node changes (see `.req.property.change`_).
``void SplayNodeUpdate(SplayTree splay, Tree node)``
_`.function.splay.node.update`: Call the ``updateNode`` method on the
given node, but leave other nodes unchanged. This may be called when a
new node is created, to get the client property off the ground.
Client-determined properties
@ -386,7 +404,7 @@ tree at the specified node, which may provoke calls to the
``updateNode`` method will also be called whenever a new splay node is
inserted into the tree.
_`.prop.example`: For example, if implementing an address ordered tree
_`.prop.example`: For example, if implementing an address-ordered tree
of free blocks using a splay tree, a client might choose to use the
base address of each block as the key for each node, and the size of
each block as the client property. The client can then maintain as a
@ -396,7 +414,7 @@ last block of at least a given size. See `.usage.callback`_ for an
example ``updateNode`` method for such a client.
_`.prop.ops`: The splay operations must cause client properties for
nodes to be updated in the following circumstances:- (see `.impl`_ for
nodes to be updated in the following circumstances (see `.impl`_ for
details):
_`.prop.ops.rotate`: rotate left, rotate right -- We need to update
@ -424,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
@ -443,16 +460,16 @@ _`.usage.client-tree`: Tree structure to embed a ``SplayTree`` (see
/* no obvious client fields for this simple example */
} FreeTreeStruct;
_`.usage.client-node`: Node structure to embed a Tree (see `.type.splay.node`_)::
_`.usage.client-node`: Node structure to embed a ``Tree`` (see `.type.tree`_)::
typedef struct FreeBlockStruct {
TreeStruct treeStruct; /* embedded splay node */
Addr base; /* base address of block is also the key */
Size size; /* size of block is also the client property */
Size maxSize; /* cached value for maximum size in subtree */
TreeStruct treeStruct; /* embedded splay node */
Addr base; /* base address of block is also the key */
Size size; /* size of block is also the client property */
Size maxSize; /* cached value for maximum size in subtree */
} FreeBlockStruct;
_`.usage.callback`: updateNode callback method (see
_`.usage.callback`: ``updateNode`` callback method (see
`.type.splay.update.node.method`_)::
void FreeBlockUpdateNode(SplayTree splay, Tree tree)
@ -462,18 +479,18 @@ _`.usage.callback`: updateNode callback method (see
/* the cached value for the left subtree (if any) and the cached */
/* value of the right subtree (if any) */
FreeBlock freeNode = FreeBlockOfSplayNode(tree);
FreeBlock freeNode = FreeBlockOfTree(tree);
Size maxSize = freeNode.size;
if (TreeHasLeft(tree)) {
FreeBlock leftNode = FreeBlockOfSplayNode(TreeLeft(tree));
FreeBlock leftNode = FreeBlockOfTree(TreeLeft(tree));
if(leftNode.maxSize > maxSize)
maxSize = leftNode->maxSize;
}
if (TreeHasRight(tree)) {
FreeBlock rightNode = FreeBlockOfSplayNode(TreeRight(tree));
FreeBlock rightNode = FreeBlockOfTree(TreeRight(tree));
if(rightNode.maxSize > maxSize)
maxSize = rightNode->maxSize;
}
@ -481,13 +498,13 @@ _`.usage.callback`: updateNode callback method (see
freeNode->maxSize = maxSize;
}
_`.usage.compare`: Comparison function (see `.type.splay.compare.method`_)::
_`.usage.compare`: Comparison function (see `.type.tree.compare.method`_)::
Compare FreeBlockCompare(Tree tree, TreeKey key) {
Addr base1, base2, limit2;
FreeBlock freeNode = FreeBlockOfSplayNode(tree);
FreeBlock freeNode = FreeBlockOfTree(tree);
base1 = (Addr *)key;
base1 = (Addr)key;
base2 = freeNode->base;
limit2 = AddrAdd(base2, freeNode->size);
@ -503,13 +520,13 @@ _`.usage.test.tree`: Test tree function (see
`.type.splay.test.tree.method`_)::
Bool FreeBlockTestTree(SplayTree splay, Tree tree
void *closureP, unsigned long closureS) {
void *closureP, Size closureS) {
/* Closure environment has wanted size as value of closureS. */
/* Look at the cached value for the node to see if any */
/* blocks in the subtree are big enough. */
Size size = (Size)closureS;
FreeBlock freeNode = FreeBlockOfSplayNode(tree);
Size size = closureS;
FreeBlock freeNode = FreeBlockOfTree(tree);
return freeNode->maxSize >= size;
}
@ -517,30 +534,30 @@ _`.usage.test.node`: Test node function (see
`.type.splay.test.node.method`_)::
Bool FreeBlockTestNode(SplayTree splay, Tree tree
void *closureP, unsigned long closureS) {
void *closureP, Size closureS) {
/* Closure environment has wanted size as value of closureS. */
/* Look at the size of the node to see if is big enough. */
Size size = (Size)closureS;
FreeBlock freeNode = FreeBlockOfSplayNode(tree);
Size size = closureS;
FreeBlock freeNode = FreeBlockOfTree(tree);
return freeNode->size >= size;
}
_`.usage.initialization`: Client's initialization function (see
`.function.splay.tree.init`_)::
void FreeTreeInit(FreeTree tree) {
void FreeTreeInit(FreeTree freeTree) {
/* Initialize the embedded splay tree. */
SplayTreeInit(&tree->splayTree, FreeBlockCompare, FreeBlockUpdateNode);
SplayTreeInit(&freeTree->splayTree, FreeBlockCompare, FreeBlockUpdateNode);
}
_`.usage.insert`: Client function to add a new free block into the
tree, merging it with an existing block if possible::
void FreeTreeInsert(FreeTree tree, Addr base, Addr limit) {
SplayTree splayTree = &tree->splayTree;
void FreeTreeInsert(FreeTree freeTree, Addr base, Addr limit) {
SplayTree splayTree = &freeTree->splayTree;
Tree leftNeighbour, rightNeighbour;
void *key = (void *)base; /* use the base of the block as the key */
TreeKey key = base; /* use the base of the block as the key */
Res res;
/* Look for any neighbouring blocks. (.function.splay.tree.neighbours) */
@ -556,7 +573,7 @@ tree, merging it with an existing block if possible::
/* The client housekeeping is left as an exercise to the reader. */
/* This changes the size of a block, which is the client */
/* property of the splay node. See `.function.splay.node.refresh`_ */
SplayNodeRefresh(tree, leftNeighbour, key);
SplayNodeRefresh(splayTree, leftNeighbour, key);
} else if (rightNeighbour != TreeEMPTY &&
FreeBlockBaseOfSplayNode(rightNeighbour) == limit) {
@ -564,18 +581,19 @@ tree, merging it with an existing block if possible::
/* The client housekeeping is left as an exercise to the reader. */
/* This changes the size of a block, which is the client */
/* property of the splay node. See `.function.splay.node.refresh`_ */
SplayNodeRefresh(tree, rightNeighbour, key);
SplayNodeRefresh(splayTree, rightNeighbour, key);
} else {
/* Not contiguous - so insert a new node */
FreeBlock newBlock = (FreeBlock)allocate(sizeof(FreeBlockStruct));
splayNode = &newBlock->splayNode;
Tree newTree = &newBlock->treeStruct;
newBlock->base = base;
newBlock->size = AddrOffset(base, limit);
SplayNodeInit(splayNode); /* `.function.splay.node.init`_ */
TreeInit(newTree); /* `.function.tree.init`_ */
SplayNodeUpdate(splayTree, newTree); /* `.function.splay.node.update`_ */
/* `.function.splay.tree.insert`_ */
res = SplayTreeInsert(splayTree, splayNode, key);
res = SplayTreeInsert(splayTree, newTree, key);
AVER(res == ResOK); /* this client doesn't duplicate free blocks */
}
}
@ -585,8 +603,8 @@ given size in address order. For simplicity, this allocates the entire
block::
Bool FreeTreeAllocate(Addr *baseReturn, Size *sizeReturn,
FreeTree tree, Size size) {
SplayTree splayTree = &tree->splayTree;
FreeTree freeTree, Size size) {
SplayTree splayTree = &freeTree->splayTree;
Tree splayNode;
Bool found;
@ -594,10 +612,10 @@ block::
/* closureP parameter is not used. See `.function.splay.find.first.`_ */
found = SplayFindFirst(&splayNode, splayTree,
FreeBlockTestNode, FreeBlockTestTree,
NULL, (unsigned long)size);
NULL, size);
if (found) {
FreeBlock freeNode = FreeBlockOfSplayNode(splayNode);
FreeBlock freeNode = FreeBlockOfTree(splayNode);
Void *key = (void *)freeNode->base; /* use base of block as the key */
Res res;
@ -605,7 +623,7 @@ block::
*baseReturn = freeNode->base;
*sizeReturn = freeNode->size;
/* remove the node from the splay tree - `.function.splay.tree.delete`_ */
/* `.function.splay.tree.delete`_ */
res = SplayTreeDelete(splayTree, splayNode, key);
AVER(res == ResOK); /* Must be possible to delete node */
@ -624,9 +642,9 @@ block::
Implementation
--------------
_`.impl`: For more details of how splay trees work, see paper.st85(0).
_`.impl`: For more details of how splay trees work, see [ST85]_.
For more details of how to implement operations on splay trees, see
paper.sleator96(0). Here we describe the operations involved.
[Sleator96]_. Here we describe the operations involved.
Top-down splaying
@ -634,22 +652,21 @@ Top-down splaying
_`.impl.top-down`: The method chosen to implement the splaying
operation is called "top-down splay". This is described as "procedure
top-down splay" in paper.st85(0) - although the implementation here
additionally permits attempts to access items which are not known to
be in the tree. Top-down splaying is particularly efficient for the
common case where the location of the node in a tree is not known at
the start of an operation. Tree restructuring happens as the tree is
descended, whilst looking for the node.
top-down splay" in [ST85]_, but the implementation here additionally
permits attempts to access items which are not known to be in the
tree. Top-down splaying is particularly efficient for the common case
where the location of the node in a tree is not known at the start of
an operation. Tree restructuring happens as the tree is descended,
whilst looking for the node.
_`.impl.splay`: The key to the operation of the splay tree is the
internal function ``SplaySplay()``. It searches the tree for a node
with a given key. In the process, it
brings the found node, or an arbitrary neighbour if not found, to the
root of the tree. This "bring-to-root" operation is performed top-down
during the search, and it is not the simplest possible bring-to-root
operation, but the resulting tree is well-balanced, and will give good
amortised cost for future calls to ``SplaySplay()``. (See
paper.st85(0))
with a given key. In the process, it brings the found node, or an
arbitrary neighbour if not found, to the root of the tree. This
"bring-to-root" operation is performed top-down during the search, and
it is not the simplest possible bring-to-root operation, but the
resulting tree is well-balanced, and will give good amortised cost for
future calls to ``SplaySplay()``. See [ST85]_.
_`.impl.splay.how`: To perform this top-down splay, the tree is broken
into three parts, a left tree, a middle tree and a right tree. We
@ -663,25 +680,28 @@ they form a partition with the ordering left, middle, right. The splay
is then performed by comparing the middle tree with the following six
cases, and performing the indicated operations, until none apply.
_`.impl.splay.cases`: Note that paper.st85(0)(Fig. 3) describes only 3
cases: zig, zig-zig and zig-zag. The additional cases described here
are the symmetric variants which are respectively called zag, zag-zag
and zag-zig. In the descriptions of these cases, ``root`` is the root
of the middle tree; ``node->left`` is the left child of ``node``;
``node->right`` is the right child of ``node``. The comparison
operators (``<``, ``>``, ``==``) are defined to compare a key and a
node in the obvious way by comparing the supplied key with the node's
associated key.
_`.impl.splay.cases`: Note that figure 3 of [ST85]_ describes only 3
cases: *zig*, *zig-zig* and *zig-zag*. The additional cases described
here are the symmetric variants which are respectively called *zag*,
*zag-zag* and *zag-zig*. In the descriptions of these cases, ``root``
is the root of the middle tree; ``node->left`` is the left child of
``node``; ``node->right`` is the right child of ``node``. The
comparison operators (``<``, ``>``, ``==``) are defined to compare a
key and a node in the obvious way by comparing the supplied key with
the node's associated key.
_`.impl.splay.zig`: The "zig" case is where ``key < root``, and either:
_`.impl.splay.zig`: The "zig" case is where ``key < root``, and
either:
- ``key == root->left``;
- ``key < root->left && root->left->left == NULL``; or
- ``key > root->left && root->left->right == NULL``.
The operation for the zig case is: link right (see `.impl.link.right`_).
The operation for the zig case is: link right (see
`.impl.link.right`_).
_`.impl.splay.zag`: The "zag" case is where ``key > root``, and either:
_`.impl.splay.zag`: The "zag" case is where ``key > root``, and
either:
- ``key == root->right``;
- ``key < root->right && root->right->left == NULL``; or
@ -740,48 +760,58 @@ _`.impl.splay.terminal.not-found`: The other typical terminal cases are:
- ``key < root && root->left == NULL``; and
- ``key > root && root->right == NULL``.
In these cases, the splay operation is complete, the three trees are assembled
(see `.impl.assemble`_), and "not found" is returned.
In these cases, the splay operation is complete, the three trees are
assembled (see `.impl.assemble`_), and "not found" is returned.
_`.impl.rotate.left`: The "rotate left" operation (see paper.st85(0)
Fig. 1) rearranges the middle tree as follows (where any of sub-trees
_`.impl.rotate.left`: The "rotate left" operation (see [ST85]_
figure 1) rearranges the middle tree as follows (where any of sub-trees
A, B and C may be empty):
[missing diagram]
.. figure:: splay-rotate-left.svg
:align: center
:alt: Diagram: the rotate left operation.
_`.impl.rotate.right`: The "rotate right" operation (see paper.st85(0)
Fig. 1) rearranges the middle tree as follows (where any of sub-trees
_`.impl.rotate.right`: The "rotate right" operation (see [ST85]_
figure 1) rearranges the middle tree as follows (where any of sub-trees
A, B and C may be empty):
[missing diagram]
.. figure:: splay-rotate-right.svg
:align: center
:alt: Diagram: the rotate right operation.
_`.impl.link.left`: The "link left" operation (see paper.st85(0) Fig.
_`.impl.link.left`: The "link left" operation (see [ST85]_ figure
11a for symmetric variant) rearranges the left and middle trees as
follows (where any of sub-trees A, B, L and R may be empty):
[missing diagram]
.. figure:: splay-link-left.svg
:align: center
:alt: Diagram: the link left operation.
The last node of the left tree is now x.
_`.impl.link.right`: The "link right" operation (see paper.st85(0)
Fig. 11a) rearranges the middle and right trees as follows (where any
of sub-trees A, B, L and R may be empty):
_`.impl.link.right`: The "link right" operation (see [ST85]_ figure
11a) rearranges the middle and right trees as follows (where any of
sub-trees A, B, L and R may be empty):
[missing diagram]
.. figure:: splay-link-right.svg
:align: center
:alt: Diagram: the link left operation.
The first node of the right tree is now x.
_`.impl.assemble`: The "assemble" operation (see paper.st85(0)
Fig. 12) merges the left and right trees with the middle tree as
follows (where any of sub-trees A, B, L and R may be empty):
_`.impl.assemble`: The "assemble" operation (see [ST85]_ figure 12)
merges the left and right trees with the middle tree as follows (where
any of sub-trees A, B, L and R may be empty):
[missing diagram]
.. figure:: splay-assemble.svg
:align: center
:alt: Diagram: the assemble operation.
Top-level operations
....................
_`.impl.insert`: ``SplayTreeInsert()``: (See paper.sleator96(0), chapter
_`.impl.insert`: ``SplayTreeInsert()``: (See [Sleator96]_, chapter
4, function insert). If the tree has no nodes, [how does it smell?]
add the inserted node and we're done; otherwise splay the tree around
the supplied key. If the splay successfully found a matching node,
@ -790,7 +820,7 @@ the old (newly splayed, but non-matching) root as its left or right
child as appropriate, and the opposite child of the old root as the
other child of the new root.
_`.impl.delete`: ``SplayTreeDelete()``: (See paper.sleator96(0), chapter
_`.impl.delete`: ``SplayTreeDelete()``: (See [Sleator96]_, chapter
4, function delete). Splay the tree around the supplied key. Check
that the newly splayed root is the same node as given by the caller,
and that it matches the key; return failure if not. If the given node
@ -877,8 +907,8 @@ _`.future.parent`: The iterator could be made more efficient (in an
amortized sense) if it didn't splay at each node. To implement this
(whilst meeting `.req.stack`_) we really need parent pointers from the
nodes. We could use the (first-child, right-sibling/parent) trick
described in paper.st85 to implement this, at a slight cost to all
other tree operations, and an increase in code complexity. paper.st85
described in [ST85]_ to implement this, at a slight cost to all
other tree operations, and an increase in code complexity. [ST85]_
doesn't describe how to distinguish the first-child between left-child
and right-child, and the right-sibling/parent between right-sibling
and parent. One could either use the comparator to make these

View file

@ -182,46 +182,117 @@ occupies.
Note that this zoneset can never shrink.
Parameters
..........
_`.param.intro`: A generation has two parameters, *capacity* and
*mortality*, specified by the client program.
_`.param.capacity`: The *capacity* of a generation is the amount of
*new* allocation in that generation (that is, allocation since the
last time the generation was condemned) that will cause the generation
to be collected by ``TracePoll()``.
_`.param.capacity.misnamed`: The name *capacity* is unfortunate since
it suggests that the total amount of memory in the generation will not
exceed this value. But that will only be the case for pool classes
that always promote survivors to another generation. When there is
*old* allocation in the generation (that is, prior to the last time
the generation was condemned), as there is in the case of non-moving
pool classes, the size of a generation is unrelated to its capacity.
_`.param.mortality`: The *mortality* of a generation is the proportion
(between 0 and 1) of memory in the generation that is expected to be
dead when the generation is collected. It is used in ``TraceStart()``
to estimate the amount of data that will have to be scanned in order
to complete the trace.
Accounting
..........
- ``gen[N].mortality``
_`.accounting.intro`: Pool generations maintain the sizes of various
categories of data allocated in that generation for that pool. This
accounting information is reported via the event system, but also used
in two places:
- Specified by the client.
- TODO: fill in how this is used.
_`.accounting.poll`: ``ChainDeferral()`` uses the *new size* of each
generation to determine which generations in the chain are over
capacity and so might need to be collected via ``TracePoll()``.
- ``gen[N].capacity``
_`.accounting.condemn`: ``ChainCondemnAuto()`` uses the *new size* of
each generation to determine which generations in the chain will be
collected; it also uses the *total size* of the generation to compute
the mortality.
- Specified by the client.
- TODO: fill in how this is used.
_`.accounting.check`: Computing the new size for a pool generation is
far from straightforward: see job003772_ for some (former) errors in
this code. In order to assist with checking that this has been
computed correctly, the locus module uses a double-entry book-keeping
system to account for every byte in each pool generation. This uses
six accounts:
- ``amcSeg->new``
.. _job003772: http://www.ravenbrook.com/project/mps/issue/job003772/
- TODO: fill this in
_`.account.total`: Memory acquired from the arena.
- ``pgen->totalSize``:
_`.account.total.negated`: From the point of view of the double-entry
system, the *total* should be negative as it is owing to the arena,
but it is inconvenient to represent negative sizes, and so the
positive value is stored instead.
- incremented by ``AMCBufferFill()``;
- decremented by ``amcReclaimNailed()`` and ``AMCReclaim()``;
- added up by ``GenDescTotalSize(gen)``.
_`.account.total.negated.justification`: We don't have a type for
signed sizes; but if we represented it in two's complement using the
unsigned ``Size`` type then Clang's unsigned integer overflow detector
would complain.
- ``pgen->newSize``:
_`.account.free`: Memory that is not in use (free or lost to
fragmentation).
- incremented by ``AMCBufferFill()`` (*when not ramping*) and ``AMCRampEnd()``;
- decremented by ``AMCWhiten()``,
- added up by ``GenDescNewSize(gen)``.
_`.account.new`: Memory in use by the client program, allocated
since the last time the generation was condemned.
- ``gen[N].proflow``:
_`.account.old`: Memory in use by the client program, allocated
prior to the last time the generation was condemned.
- set to 1.0 by ``ChainCreate()``;
- ``arena->topGen.proflow`` set to 0.0 by ``LocusInit(arena)``;
- *The value of this field is never used*.
_`.account.newDeferred`: Memory in use by the client program,
allocated since the last time the generation was condemned, but which
should not cause collections via ``TracePoll()``. (Due to ramping; see
below.)
_`.account.oldDeferred`: Memory in use by the client program,
allocated prior to the last time the generation was condemned, but
which should not cause collections via ``TracePoll()``. (Due to
ramping; see below.)
- ``pgen->newSizeAtCreate``:
_`.accounting.op`: The following operations are provided:
_`.accounting.op.alloc`: Allocate a segment in a pool generation.
Debit *total*, credit *free*. (But see `.account.total.negated`_.)
_`.accounting.op.free`: Free a segment. First, ensure that the
contents of the segment are accounted as free, by artificially ageing
any memory accounted as *new* or *newDeferred* (see
`.accounting.op.age`_) and then artifically reclaiming any memory
accounted as *old* or *oldDeferred* (see `.accounting.op.reclaim`_).
Finally, debit *free*, credit *total*. (But see
`.account.total.negated`_.)
_`.accounting.op.fill`: Allocate memory, for example by filling a
buffer. Debit *free*, credit *new* or *newDeferred*.
_`.accounting.op.empty`: Deallocate memory, for example by emptying
the unused portion of a buffer. Debit *new* or *newDeferred*, credit
*free*.
_`.accounting.op.age`: Condemn memory. Debit *new* or *newDeferred*,
credit *old* or *oldDeferred*.
_`.accounting.op.reclaim`: Reclaim dead memory. Debit *old* or
*oldDeferred*, credit *free*.
_`.accounting.op.undefer`: Stop deferring the accounting of memory. Debit *oldDeferred*, credit *old*. Debit *newDeferred*, credit *new*.
- set by ``traceCopySizes()`` (that is its purpose);
- output in the ``TraceStartPoolGen`` telemetry event.
Ramps
.....
@ -296,29 +367,31 @@ Reclaiming any AMC segment:
Now, some deductions:
#. When OUTSIDE, the count is always zero, because (a) it starts that
way, and the only ways to go OUTSIDE are (b) by leaving an outermost
ramp (count goes to zero) or (c) by reclaiming when the count is zero.
way, and the only ways to go OUTSIDE are (b) by leaving an
outermost ramp (count goes to zero) or (c) by reclaiming when the
count is zero.
#. When BEGIN, the count is never zero (consider the transitions to
BEGIN and the transition to zero).
BEGIN and the transition to zero).
#. When RAMPING, the count is never zero (again consider transitions to
RAMPING and the transition to zero).
#. When RAMPING, the count is never zero (again consider transitions
to RAMPING and the transition to zero).
#. When FINISH, the count can be anything (the transition to FINISH has
zero count, but the Enter transition when FINISH can change that and
then it can increment to any value).
#. When FINISH, the count can be anything (the transition to FINISH
has zero count, but the Enter transition when FINISH can change
that and then it can increment to any value).
#. When COLLECTING, the count can be anything (from the previous fact,
and the transition to COLLECTING).
and the transition to COLLECTING).
#. *This is a bug!!* The ramp generation is not always reset (to forward
to the after-ramp generation). If we get into FINISH and then see
another ramp before the next condemnation of the ramp generation, we
will Enter followed by Leave. The Enter will keep us in FINISH, and
the Leave will take us back to OUTSIDE, skipping the transition to the
COLLECTING state which is what resets the ramp generation forwarding
buffer. [TODO: check whether I made an issue and/or fixed it; NB 2013-06-04]
#. *This is a bug!!* The ramp generation is not always reset (to
forward to the after-ramp generation). If we get into FINISH and
then see another ramp before the next condemnation of the ramp
generation, we will Enter followed by Leave. The Enter will keep us
in FINISH, and the Leave will take us back to OUTSIDE, skipping the
transition to the COLLECTING state which is what resets the ramp
generation forwarding buffer. [TODO: check whether I made an issue
and/or fixed it; NB 2013-06-04]
The simplest change to fix this is to change the behaviour of the Leave
transition, which should only take us OUTSIDE if we are in BEGIN or

View file

@ -72,6 +72,8 @@ Interface. ``mps_addr_t`` is defined to be the same as ``void *``, so
using the MPS C Interface confines the memory manager to the same
address space as the client data.
_`.addr.readonly`: For read-only addresses, see `.readonlyaddr`_.
``typedef Word Align``
@ -89,28 +91,26 @@ C Interface.
``typedef unsigned Attr``
_`.attr`: Pool attributes. A bitset of pool or pool class
attributes, which are:
_`.attr`: Pool attributes. A bitset of pool class attributes, which
are:
=================== ===================================================
Attribute Description
=================== ===================================================
``AttrALLOC`` Supports the ``PoolAlloc`` interface.
``AttrBUF`` Supports the buffer interface.
``AttrFMT`` Contains formatted objects.
Used to decide which pools to walk.
``AttrFREE`` Supports the ``PoolFree`` interface.
``AttrGC`` Is garbage collecting, that is, parts may be
reclaimed. Used to decide which segments are
condemned.
``AttrMOVINGGC`` Is moving, that is, objects may move in memory.
Used to update the set of zones that might have
moved and so implement location dependency.
``AttrSCAN`` Contains references and must be scanned.
=================== ===================================================
There is an attribute field in the pool class (``PoolClassStruct``)
which declares the attributes of that class.
which declares the attributes of that class. See `design.mps.class-interface.field.attr`_.
.. _design.mps.class-interface.field.attr: class-interface
``typedef int Bool``
@ -155,9 +155,17 @@ _`.bool.bitfield`: When a Boolean needs to be stored in a bitfield,
the type of the bitfield must be ``unsigned:1``, not ``Bool:1``.
(That's because the two values of the type ``Bool:1`` are ``0`` and
``-1``, which means that assigning ``TRUE`` would require a sign
conversion.) To avoid warnings about loss of data from GCC with the
``-Wconversion`` option, ``misc.h`` provides the ``BOOLOF`` macro for
coercing a value to an unsigned single-bit field.
conversion.) To make it clear why this is done, ``misc.h`` provides
the ``BOOLFIELD`` macro.
_`.bool.bitfield.assign`: To avoid warnings about loss of data from
GCC with the ``-Wconversion`` option, ``misc.h`` provides the
``BOOLOF`` macro for coercing a value to an unsigned single-bit field.
_`.bool.bitfield.check`: A Boolean bitfield cannot have an incorrect
value, and if you call ``BoolCheck()`` on such a bitfield then GCC 4.2
issues the warning "comparison is always true due to limited range of
data type". When avoiding such a warning, reference this tag.
``typedef unsigned BufferMode``
@ -373,6 +381,13 @@ their integer values.
_`.rankset`: ``RankSet`` is a set of ranks, represented as a bitset.
``typedef const struct AddrStruct *ReadonlyAddr``
_`.readonlyaddr`: ``ReadonlyAddr`` is the type used for managed
addresses that an interface promises it will only read through, never
write. Otherwise it is identical to ``Addr``.
``typedef Addr Ref``
_`.ref`: ``Ref`` is a reference to a managed object (as opposed to any

3877
mps/manual/source/bib.rst Normal file

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,34 @@
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('.'))
# -- Project configuration -----------------------------------------------------
# The same set of sources builds the Memory Pool System documentation
# and the Memory Management Reference, depending on whether the MMREF
# environment variable is set.
if os.environ.get('MMREF'):
project = u'Memory Management Reference'
master_doc = 'index'
html_theme = 'mmref'
version = '4'
release = '4.0'
else:
project = u'Memory Pool System'
master_doc = 'index'
html_theme = 'mps'
html_sidebars = {
'**': ['localtoc.html', 'relations.html', 'links.html', 'contact.html'],
}
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)),
'../../code/version.c')) as f:
for line in f:
m = re.match(r'#define MPS_RELEASE "release/((\d+\.\d+)\.\d+)"', line)
if m:
release, version = m.groups()
break
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
@ -40,24 +68,9 @@
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Memory Pool System'
copyright = date.today().strftime(u'%Y, Ravenbrook Limited')
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)),
'../../code/version.c')) as f:
for line in f:
m = re.match(r'#define MPS_RELEASE "release/((\d+\.\d+)\.\d+)"', line)
if m:
release, version = m.groups()
break
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
@ -98,10 +111,6 @@
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'mps'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
@ -119,7 +128,7 @@
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
html_logo = 'diagrams/logo.png'
html_logo = 'images/logo.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
@ -129,7 +138,7 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = []
html_static_path = ['images']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
@ -139,11 +148,6 @@
# typographically correct entities.
html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'**': ['localtoc.html', 'relations.html', 'links.html', 'contact.html'],
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

View file

@ -1,7 +1,7 @@
.. _license:
.. index::
single: copyright
single: license
.. _license:
.. include:: ../../license.txt

View file

@ -10,13 +10,16 @@ Design
cbs
config
critical-path
failover
freelist
guide.hex.trans
guide.impl.c.format
interface-c
keyword-arguments
land
nailboard
range
ring
sig
splay
type

View file

@ -53,7 +53,6 @@ Old design
scan
seg
shield
splay
sso1al
strategy
telemetry

View file

@ -22,11 +22,11 @@
Arena Attr Bool BootBlock BT Buffer BufferMode Byte Chain Chunk
Clock Compare Count Epoch FindDelete Format FrameState Fun GenDesc
Globals Index Land LD Lock Message MessageType MutatorFaultContext
Page Pointer Pool PoolGen PThreadext Range Rank RankSet Ref RefSet
Res Reservoir Ring Root RootMode RootVar ScanState Seg SegBuf
SegPref SegPrefKind Serial Shift Sig Size Space SplayNode
SplayTree StackContext Thread Trace TraceId TraceSet TraceStartWhy
TraceState ULongest VM Word ZoneSet
Page Pointer Pool PoolGen PThreadext Range Rank RankSet
ReadonlyAddr Ref RefSet Res Reservoir Ring Root RootMode RootVar
ScanState Seg SegBuf SegPref SegPrefKind Serial Shift Sig Size
Space SplayNode SplayTree StackContext Thread Trace TraceId
TraceSet TraceStartWhy TraceState ULongest VM Word ZoneSet
'''
@ -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)

View file

@ -103,6 +103,26 @@ Memory Management Glossary: A
.. seealso:: :term:`virtual address space`, :term:`physical address space`.
address space layout randomization
.. aka:: *ASLR*.
The random placement in :term:`address space` of the
:term:`stack`, data segment, :term:`heap`, and so on, of a
process.
The purpose of ASLR is to make it harder for an attacker to
exploit buffer overflow bugs, by making it harder to determine
the addresses of data structures.
.. mps:specific::
ASLR also makes it hard to prepare a repeatable test case
for a program that performs computation based on the
addresses of objects, for example, hashing objects by
their address. See :ref:`guide-debug-aslr` for techniques
to deal with this.
address translation cache
.. see:: :term:`translation lookaside buffer`.
@ -182,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_k`.
alive
@ -389,6 +413,10 @@ Memory Management Glossary: A
class of :term:`arenas`. Arena classes include
:term:`client arenas` and :term:`virtual memory arenas`.
ASLR
.. see:: :term:`address space layout randomization`.
assertion
A declaration in a program of a condition that is expected
@ -452,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

View file

@ -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
<http://www.azstarnet.com/%7Efosnp/factsaboutsaguaros.html>`_.
<http://en.wikipedia.org/wiki/Saguaro>`_.
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

View file

@ -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

View file

@ -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*.

View file

@ -133,6 +133,12 @@ Memory Management Glossary: I
.. bibref:: :ref:`Appel et al. (1988) <AEL88>`, :ref:`Boehm et al. (1991) <BDS91>`.
.. 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 <trace>`,

View file

@ -7,6 +7,7 @@ Memory Management Glossary
.. toctree::
:maxdepth: 1
:hidden:
a
b
@ -31,3 +32,594 @@ Memory Management Glossary
v
w
z
All
===
:term:`absolute address <physical address>`
:term:`activation frame <activation record>`
:term:`activation record`
:term:`activation stack <control stack>`
:term:`active <live>`
:term:`address`
:term:`address space`
:term:`address space layout randomization`
:term:`address translation cache <translation lookaside buffer>`
:term:`address-ordered first fit`
:term:`aging space`
:term:`algebraic data type`
:term:`alignment`
:term:`alive <live>`
: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 <address space layout randomization>`
:term:`assertion`
:term:`asynchronous garbage collector`
:term:`ATC <translation lookaside buffer>`
:term:`atomic object <leaf object>`
:term:`automatic memory management`
:term:`automatic storage duration`
:term:`backing store`
:term:`barrier (1)`
:term:`barrier (2)`
:term:`barrier hit <protection fault>`
:term:`base pointer`
:term:`best fit`
:term:`BIBOP`
:term:`big bag of pages <BIBOP>`
:term:`binary buddies`
:term:`bit array <bitmap>`
:term:`bit table <bitmap>`
:term:`bit vector <bitmap>`
:term:`bitmap`
:term:`bitmapped fit`
:term:`bitmask`
:term:`bitset <bitmap>`
:term:`black`
:term:`blacklisting`
:term:`black-listing`
:term:`block`
:term:`bounds error <overwriting 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 <C90>`
:term:`C90`
:term:`C99`
:term:`cache (1)`
:term:`cache (2)`
:term:`cache memory <cache (1)>`
:term:`cache policy`
:term:`caching (3)`
:term:`cactus stack`
:term:`card`
:term:`card marking`
:term:`cell <object>`
:term:`Cheney collector`
:term:`Cheney scan <Cheney collector>`
:term:`clamped state`
:term:`client arena`
:term:`client object`
:term:`client pointer`
:term:`client program <mutator>`
:term:`closure`
:term:`coalesce`
:term:`collect`
:term:`collection <collection cycle>`
:term:`collection cycle`
:term:`collector (1) <garbage collector>`
:term:`collector (2)`
:term:`color`
:term:`colour`
:term:`commit limit`
:term:`committed (1) <mapped>`
:term:`committed (2)`
:term:`compactifying <compaction>`
:term:`compaction`
:term:`composite object`
:term:`comprehensive`
:term:`concurrent garbage collection <parallel garbage collection>`
:term:`condemned set`
:term:`connected`
:term:`cons (1)`
:term:`cons (2) <allocate>`
: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 <free (1)>`
:term:`debugging pool`
:term:`deferred coalescing`
:term:`deferred reference counting`
:term:`dependent object`
:term:`derived pointer <interior pointer>`
:term:`derived type`
:term:`destructor (1)`
:term:`destructor (2)`
:term:`DGC <distributed garbage collection>`
: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 <dynamic memory>`
:term:`dynamic allocation <heap allocation>`
:term:`dynamic extent`
:term:`dynamic memory`
:term:`dynamic RAM <dynamic memory>`
:term:`ecru <off-white>`
: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 <control stack>`
:term:`exit table`
:term:`extent <lifetime>`
: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 <memory 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 <in-band header>`
:term:`free (1)`
:term:`free (2)`
:term:`free (3)`
:term:`free (4) <unmapped>`
:term:`free block`
:term:`free block chain`
:term:`free list`
:term:`free store <heap>`
:term:`freestore <heap>`
:term:`from space`
:term:`fromspace`
:term:`function pointer`
:term:`function record <activation record>`
:term:`garbage`
:term:`garbage collection`
:term:`garbage collector`
:term:`GB <gigabyte>`
:term:`GC <garbage collection>`
:term:`General Protection Fault`
:term:`generation`
:term:`generation chain`
:term:`generation scavenging <generational garbage collection>`
:term:`generational garbage collection`
:term:`generational hypothesis`
:term:`gigabyte`
:term:`good fit`
:term:`GPF <General Protection Fault>`
:term:`grain`
:term:`graph`
:term:`gray`
:term:`grey`
:term:`gray list`
:term:`grey list`
:term:`handle`
:term:`header <in-band 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 <value 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 <generational hypothesis>`
: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 <kilobyte>`
:term:`keyword argument`
:term:`kilobyte`
:term:`large object area`
:term:`large page <huge page>`
:term:`leaf object`
:term:`leak <memory leak>`
:term:`life <lifetime>`
: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 <memory location>`
:term:`location dependency`
:term:`lock free`
:term:`logical address <virtual address>`
:term:`longword <doubleword>`
:term:`machine word <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 <megabyte>`
:term:`megabyte`
:term:`memoization <caching (3)>`
:term:`memory (1)`
:term:`memory (2)`
:term:`memory (3) <main memory>`
:term:`memory (4)`
:term:`memory bandwidth`
:term:`memory cache <cache (1)>`
:term:`memory hierarchy <storage hierarchy>`
:term:`memory leak`
:term:`memory location`
:term:`memory management`
:term:`Memory Management Unit <MMU>`
:term:`memory manager`
:term:`memory mapping`
:term:`memory protection <protection>`
:term:`message`
:term:`message queue`
:term:`message type`
:term:`misaligned <unaligned>`
:term:`miss`
:term:`miss rate`
:term:`mmap`
:term:`MMU`
:term:`mostly-copying garbage collection`
:term:`mostly-exact garbage collection <semi-conservative garbage collection>`
:term:`mostly-precise garbage collection <semi-conservative garbage collection>`
:term:`moving garbage collector`
:term:`moving memory manager`
:term:`mutable`
:term:`mutator`
:term:`nailing <pinning>`
:term:`natural alignment`
:term:`nepotism`
:term:`next fit`
:term:`new space`
:term:`newspace <tospace>`
:term:`node`
:term:`non-moving garbage collector`
:term:`non-moving memory manager`
:term:`nursery generation <nursery space>`
:term:`nursery space`
:term:`object`
:term:`object format`
:term:`object pointer`
:term:`off-white`
:term:`old space <fromspace>`
:term:`oldspace <fromspace>`
: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 <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 <physical memory (2)>`
:term:`pig in the python`
:term:`pig in the snake <pig in the python>`
:term:`pinning`
:term:`placement policy <allocation policy>`
:term:`platform`
:term:`plinth`
:term:`pointer`
:term:`pool`
:term:`pool class`
:term:`precise garbage collection <exact garbage collection>`
:term:`precise reference <exact reference>`
:term:`precise root <exact root>`
:term:`premature free`
:term:`premature promotion <premature tenuring>`
:term:`premature tenuring`
:term:`primary storage <main memory>`
:term:`promotion`
:term:`protectable root`
:term:`protection`
:term:`protection exception <protection fault>`
:term:`protection fault`
:term:`protection violation <protection fault>`
:term:`quadword`
:term:`RAM`
:term:`random access memory <RAM>`
:term:`ramp allocation`
:term:`rank`
:term:`rash`
:term:`raw <unwrapped>`
:term:`reachable`
:term:`read barrier`
:term:`read fault`
:term:`read-only memory <ROM>`
:term:`real memory (1)`
:term:`real memory (2) <physical memory (1)>`
: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 <copying 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 <two-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 <memory leak>`
:term:`spare commit limit`
:term:`spare committed memory`
:term:`spaghetti stack <cactus stack>`
:term:`splat`
:term:`split`
:term:`SRAM <static memory (1)>`
:term:`SSB <sequential store buffer>`
:term:`stack`
:term:`stack allocation`
:term:`stack frame`
:term:`stack record <stack frame>`
:term:`static allocation`
:term:`static memory (1)`
:term:`static memory (2)`
:term:`static object`
:term:`static RAM <static memory (1)>`
:term:`static storage duration`
:term:`stepper function`
:term:`sticky reference count <limited-field reference count>`
:term:`stop-and-copy collection`
:term:`storage <memory (1)>`
:term:`storage hierarchy`
:term:`storage level`
:term:`storage management <memory management>`
:term:`store (1)`
:term:`store (2) <memory (1)>`
:term:`stretchy vector`
: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 <huge page>`
:term:`sure reference <exact reference>`
:term:`swap space`
:term:`swapped in`
:term:`swapped out`
:term:`swapping`
:term:`sweeping`
:term:`synchronous garbage collector`
:term:`tabling <caching (3)>`
:term:`tag`
:term:`tagged architecture`
:term:`tagged reference`
:term:`TB (1) <terabyte>`
:term:`TB (2) <translation lookaside buffer>`
:term:`telemetry filter`
:term:`telemetry label`
:term:`telemetry stream`
:term:`tenuring <promotion>`
:term:`terabyte`
:term:`termination <finalization>`
:term:`thrash`
:term:`thread`
:term:`threatened set <condemned set>`
:term:`TLB <translation lookaside buffer>`
: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 <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 <exact garbage collection>`
:term:`type punning`
:term:`unaligned`
:term:`unboxed`
:term:`unclamped state`
:term:`undead`
:term:`unmapped`
:term:`unreachable`
:term:`unsure reference <ambiguous reference>`
:term:`unwrapped`
:term:`use after free <premature 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 <stepper function>`
:term:`VM (1) <virtual memory>`
: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 <zero count table>`
:term:`zero count table`

View file

@ -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 <scan>` 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`.

View file

@ -535,6 +535,11 @@ Memory Management Glossary: M
.. bibref:: :ref:`Bartlett (1989) <BARTLETT89>`, :ref:`Yip (1991) <YIP91>`.
.. mps:specific::
The :ref:`pool-amc` pool class implements mostly-copying
garbage collection.
mostly-exact garbage collection
.. see:: :term:`semi-conservative garbage collection`.

View file

@ -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_k`.

View file

@ -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 <incremental garbage collection>`, but harder. The solution
typically involves :term:`barrier (1)`.
typically involves :term:`barriers (1)`.
.. similar:: :term:`incremental <incremental garbage collection>`.
@ -222,7 +222,7 @@ Memory Management Glossary: P
.. link::
`Class java.lang.ref.PhantomReference <http://download.java.net/jdk8/docs/api/java/lang/ref/PhantomReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
`Class java.lang.ref.PhantomReference <http://docs.oracle.com/javase/8/docs/api/java/lang/ref/PhantomReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
phantom reference
@ -239,7 +239,7 @@ Memory Management Glossary: P
.. link::
`Class java.lang.ref.PhantomReference <http://download.java.net/jdk8/docs/api/java/lang/ref/PhantomReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
`Class java.lang.ref.PhantomReference <http://docs.oracle.com/javase/8/docs/api/java/lang/ref/PhantomReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
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_k`.
pig in the snake
.. see:: :term:`pig in the python`.

View file

@ -93,7 +93,7 @@ Memory Management Glossary: R
.. link::
`Package java.lang.ref <http://download.java.net/jdk8/docs/api/java/lang/ref/package-summary.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
`Package java.lang.ref <http://docs.oracle.com/javase/8/docs/api/java/lang/ref/package-summary.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
read barrier
@ -317,7 +317,7 @@ Memory Management Glossary: R
.. link::
`Package java.lang.ref <http://download.java.net/jdk8/docs/api/java/lang/ref/package-summary.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
`Package java.lang.ref <http://docs.oracle.com/javase/8/docs/api/java/lang/ref/package-summary.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
.. bibref:: :ref:`Dybvig et al. (1993) <DBE93>`.
@ -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

View file

@ -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 <http://download.java.net/jdk8/docs/api/java/lang/ref/SoftReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
`Class java.lang.ref.SoftReference <http://docs.oracle.com/javase/8/docs/api/java/lang/ref/SoftReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
softly reachable
@ -453,7 +453,7 @@ Memory Management Glossary: S
.. link::
`Class java.lang.ref.SoftReference <http://download.java.net/jdk8/docs/api/java/lang/ref/SoftReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
`Class java.lang.ref.SoftReference <http://docs.oracle.com/javase/8/docs/api/java/lang/ref/SoftReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
space leak
@ -785,6 +785,26 @@ Memory Management Glossary: S
.. see:: :term:`memory (1)`.
stretchy vector
A :term:`vector <vector data type>` that may grow or shrink to
accommodate adding or removing elements. Named after the
``<stretchy-vector>`` 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 <http://opendylan.org/books/drm/Collection_Classes>`_.
.. 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 <live>`.
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`.

View file

@ -70,7 +70,7 @@ Memory Management Glossary: W
.. link::
`Class java.lang.ref.WeakReference <http://download.java.net/jdk8/docs/api/java/lang/ref/WeakReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
`Class java.lang.ref.WeakReference <http://docs.oracle.com/javase/8/docs/api/java/lang/ref/WeakReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
weak root
@ -134,7 +134,7 @@ Memory Management Glossary: W
.. link::
`Class java.lang.ref.WeakReference <http://download.java.net/jdk8/docs/api/java/lang/ref/WeakReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
`Class java.lang.ref.WeakReference <http://docs.oracle.com/javase/8/docs/api/java/lang/ref/WeakReference.html>`_, `Reference Objects and Garbage Collection <http://pawlan.com/monica/articles/refobjs/>`_.
weighted buddies

Some files were not shown because too many files have changed in this diff Show more