mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-17 01:34:21 +00:00
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:
commit
de2a30d207
141 changed files with 13283 additions and 3783 deletions
|
|
@ -9,3 +9,5 @@ notifications:
|
|||
email:
|
||||
- mps-travis@ravenbrook.com
|
||||
irc: "irc.freenode.net#memorypoolsystem"
|
||||
script:
|
||||
- ./configure --prefix=$PWD/prefix && make install && make test
|
||||
|
|
|
|||
|
|
@ -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@
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
163
mps/code/arena.c
163
mps/code/arena.c
|
|
@ -19,7 +19,7 @@ SRCID(arena, "$Id$");
|
|||
|
||||
#define ArenaControlPool(arena) MV2Pool(&(arena)->controlPoolStruct)
|
||||
#define ArenaCBSBlockPool(arena) (&(arena)->freeCBSBlockPoolStruct.poolStruct)
|
||||
#define ArenaFreeCBS(arena) (&(arena)->freeCBSStruct)
|
||||
#define ArenaFreeLand(arena) (&(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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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> */
|
||||
|
|
|
|||
692
mps/code/cbs.c
692
mps/code/cbs.c
File diff suppressed because it is too large
Load diff
|
|
@ -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 */
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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> \
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
360
mps/code/failover.c
Normal 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
69
mps/code/failover.h
Normal 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.
|
||||
*/
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
642
mps/code/land.c
Normal 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
637
mps/code/landtest.c
Normal 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.
|
||||
*/
|
||||
230
mps/code/locus.c
230
mps/code/locus.c
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
111
mps/code/mpmss.c
111
mps/code/mpmss.c
|
|
@ -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;
|
||||
|
|
|
|||
101
mps/code/mpmst.h
101
mps/code/mpmst.h
|
|
@ -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? */
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
#include "freelist.c"
|
||||
#include "sa.c"
|
||||
#include "nailboard.c"
|
||||
#include "land.c"
|
||||
#include "failover.c"
|
||||
|
||||
/* Additional pool classes */
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
142
mps/code/sacss.c
142
mps/code/sacss.c
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
||||
|
|
|
|||
|
|
@ -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
150
mps/design/failover.txt
Normal 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.**
|
||||
|
|
@ -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/
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
352
mps/design/land.txt
Normal 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.**
|
||||
|
|
@ -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/
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
-------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
427
mps/design/splay-assemble.svg
Normal file
427
mps/design/splay-assemble.svg
Normal 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 |
437
mps/design/splay-link-left.svg
Normal file
437
mps/design/splay-link-left.svg
Normal 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 |
437
mps/design/splay-link-right.svg
Normal file
437
mps/design/splay-link-right.svg
Normal 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 |
405
mps/design/splay-rotate-left.svg
Normal file
405
mps/design/splay-rotate-left.svg
Normal 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 |
391
mps/design/splay-rotate-right.svg
Normal file
391
mps/design/splay-rotate-right.svg
Normal 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 |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
3877
mps/manual/source/bib.rst
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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 = {}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.. _license:
|
||||
|
||||
.. index::
|
||||
single: copyright
|
||||
single: license
|
||||
|
||||
.. _license:
|
||||
|
||||
.. include:: ../../license.txt
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ Old design
|
|||
scan
|
||||
seg
|
||||
shield
|
||||
splay
|
||||
sso1al
|
||||
strategy
|
||||
telemetry
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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*.
|
||||
|
|
|
|||
|
|
@ -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>`,
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue