mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-20 20:07:36 +00:00
Merge branches 2014-04-15/mvffnoseg and 2014-04-23/awl into 2014-05-15/size.
Copied from Perforce Change: 186112 ServerID: perforce.ravenbrook.com
This commit is contained in:
commit
deb2c48609
62 changed files with 6421 additions and 2385 deletions
|
|
@ -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@
|
||||
|
|
|
|||
146
mps/code/arena.c
146
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;
|
||||
|
||||
|
|
@ -216,14 +216,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);
|
||||
|
|
@ -232,17 +233,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);
|
||||
|
|
@ -253,8 +254,8 @@ Res ArenaInit(Arena arena, ArenaClass class, Align alignment, ArgList args)
|
|||
return ResOK;
|
||||
|
||||
failReservoirInit:
|
||||
CBSFinish(ArenaFreeCBS(arena));
|
||||
failCBSInit:
|
||||
LandFinish(ArenaFreeLand(arena));
|
||||
failLandInit:
|
||||
PoolFinish(ArenaCBSBlockPool(arena));
|
||||
failMFSInit:
|
||||
GlobalsFinish(ArenaGlobals(arena));
|
||||
|
|
@ -304,15 +305,15 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args)
|
|||
goto failStripeSize;
|
||||
}
|
||||
|
||||
/* With the primary chunk initialised we can add page memory to the freeCBS
|
||||
/* With the primary chunk initialised we can add page memory to the freeLand
|
||||
that describes the free address space in the primary chunk. */
|
||||
arena->hasFreeCBS = TRUE;
|
||||
res = ArenaFreeCBSInsert(arena,
|
||||
PageIndexBase(arena->primary,
|
||||
arena->primary->allocBase),
|
||||
arena->primary->limit);
|
||||
arena->hasFreeLand = TRUE;
|
||||
res = ArenaFreeLandInsert(arena,
|
||||
PageIndexBase(arena->primary,
|
||||
arena->primary->allocBase),
|
||||
arena->primary->limit);
|
||||
if (res != ResOK)
|
||||
goto failPrimaryCBS;
|
||||
goto failPrimaryLand;
|
||||
|
||||
res = ControlInit(arena);
|
||||
if (res != ResOK)
|
||||
|
|
@ -329,7 +330,7 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args)
|
|||
failGlobalsCompleteCreate:
|
||||
ControlFinish(arena);
|
||||
failControlInit:
|
||||
failPrimaryCBS:
|
||||
failPrimaryLand:
|
||||
failStripeSize:
|
||||
(*class->finish)(arena);
|
||||
failInit:
|
||||
|
|
@ -378,14 +379,14 @@ 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. */
|
||||
that would use the CBSZoned. */
|
||||
MFSFinishTracts(ArenaCBSBlockPool(arena),
|
||||
arenaMFSPageFreeVisitor, NULL, 0);
|
||||
PoolFinish(ArenaCBSBlockPool(arena));
|
||||
|
|
@ -601,9 +602,10 @@ Res ControlDescribe(Arena arena, mps_lib_FILE *stream)
|
|||
|
||||
/* arenaAllocPage -- allocate one page from the arena
|
||||
*
|
||||
* This is a primitive allocator used to allocate pages for the arena CBS.
|
||||
* It is called rarely and can use a simple search. It may not use the
|
||||
* CBS or any pool, because it is used as part of the bootstrap.
|
||||
* This is a primitive allocator used to allocate pages for the arena
|
||||
* Land. It is called rarely and can use a simple search. It may not
|
||||
* use the Land or any pool, because it is used as part of the
|
||||
* bootstrap.
|
||||
*/
|
||||
|
||||
static Res arenaAllocPageInChunk(Addr *baseReturn, Chunk chunk, Pool pool)
|
||||
|
|
@ -685,7 +687,7 @@ static Res arenaExtendCBSBlockPool(Range pageRangeReturn, Arena arena)
|
|||
return ResOK;
|
||||
}
|
||||
|
||||
/* arenaExcludePage -- exclude CBS block pool's page from CBSs
|
||||
/* arenaExcludePage -- exclude CBS block pool's page from Land
|
||||
*
|
||||
* Exclude the page we specially allocated for the CBS block pool
|
||||
* so that it doesn't get reallocated.
|
||||
|
|
@ -696,20 +698,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;
|
||||
|
||||
|
|
@ -717,17 +719,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);
|
||||
}
|
||||
|
||||
|
|
@ -735,16 +737,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;
|
||||
|
||||
|
|
@ -752,7 +754,7 @@ static void arenaCBSInsertSteal(Range rangeReturn, Arena arena, Range rangeIO)
|
|||
AVERT(Arena, arena);
|
||||
AVERT(Range, rangeIO);
|
||||
|
||||
res = arenaCBSInsert(rangeReturn, arena, rangeIO);
|
||||
res = arenaLandInsert(rangeReturn, arena, rangeIO);
|
||||
|
||||
if (res != ResOK) {
|
||||
Addr pageBase;
|
||||
|
|
@ -773,22 +775,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;
|
||||
|
|
@ -796,7 +798,7 @@ Res ArenaFreeCBSInsert(Arena arena, Addr base, Addr limit)
|
|||
AVERT(Arena, arena);
|
||||
|
||||
RangeInit(&range, base, limit);
|
||||
res = arenaCBSInsert(&oldRange, arena, &range);
|
||||
res = arenaLandInsert(&oldRange, arena, &range);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
|
|
@ -809,7 +811,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.
|
||||
|
|
@ -820,13 +822,13 @@ Res ArenaFreeCBSInsert(Arena arena, Addr base, Addr limit)
|
|||
* so we can't test that path.
|
||||
*/
|
||||
|
||||
void ArenaFreeCBSDelete(Arena arena, Addr base, Addr limit)
|
||||
void ArenaFreeLandDelete(Arena arena, Addr base, Addr limit)
|
||||
{
|
||||
RangeStruct range, oldRange;
|
||||
Res res;
|
||||
|
||||
RangeInit(&range, base, limit);
|
||||
res = CBSDelete(&oldRange, ArenaFreeCBS(arena), &range);
|
||||
res = LandDelete(&oldRange, ArenaFreeLand(arena), &range);
|
||||
|
||||
/* Shouldn't be any other kind of failure because we were only deleting
|
||||
a non-coalesced block. See .chunk.no-coalesce and
|
||||
|
|
@ -835,7 +837,7 @@ void ArenaFreeCBSDelete(Arena arena, Addr base, Addr limit)
|
|||
}
|
||||
|
||||
|
||||
static Res arenaAllocFromCBS(Tract *tractReturn, ZoneSet zones, Bool high,
|
||||
static Res arenaAllocFromLand(Tract *tractReturn, ZoneSet zones, Bool high,
|
||||
Size size, Pool pool)
|
||||
{
|
||||
Arena arena;
|
||||
|
|
@ -858,7 +860,7 @@ static Res arenaAllocFromCBS(Tract *tractReturn, ZoneSet zones, Bool high,
|
|||
|
||||
/* Step 1. Find a range of address space. */
|
||||
|
||||
res = CBSFindInZones(&range, &oldRange, ArenaFreeCBS(arena),
|
||||
res = LandFindInZones(&range, &oldRange, ArenaFreeLand(arena),
|
||||
size, zones, high);
|
||||
|
||||
if (res == ResLIMIT) { /* found block, but couldn't store info */
|
||||
|
|
@ -867,7 +869,7 @@ static Res arenaAllocFromCBS(Tract *tractReturn, ZoneSet zones, Bool high,
|
|||
if (res != ResOK) /* disastrously short on memory */
|
||||
return res;
|
||||
arenaExcludePage(arena, &pageRange);
|
||||
res = CBSFindInZones(&range, &oldRange, ArenaFreeCBS(arena),
|
||||
res = LandFindInZones(&range, &oldRange, ArenaFreeLand(arena),
|
||||
size, zones, high);
|
||||
AVER(res != ResLIMIT);
|
||||
}
|
||||
|
|
@ -901,7 +903,7 @@ static Res arenaAllocFromCBS(Tract *tractReturn, ZoneSet zones, Bool high,
|
|||
|
||||
failMark:
|
||||
{
|
||||
Res insertRes = arenaCBSInsert(&oldRange, arena, &range);
|
||||
Res insertRes = arenaLandInsert(&oldRange, arena, &range);
|
||||
AVER(insertRes == ResOK); /* We only just deleted it. */
|
||||
/* If the insert does fail, we lose some address space permanently. */
|
||||
}
|
||||
|
|
@ -942,10 +944,10 @@ static Res arenaAllocPolicy(Tract *tractReturn, Arena arena, SegPref pref,
|
|||
}
|
||||
}
|
||||
|
||||
/* Plan A: allocate from the free CBS in the requested zones */
|
||||
/* Plan A: allocate from the free Land in the requested zones */
|
||||
zones = ZoneSetDiff(pref->zones, pref->avoid);
|
||||
if (zones != ZoneSetEMPTY) {
|
||||
res = arenaAllocFromCBS(&tract, zones, pref->high, size, pool);
|
||||
res = arenaAllocFromLand(&tract, zones, pref->high, size, pool);
|
||||
if (res == ResOK)
|
||||
goto found;
|
||||
}
|
||||
|
|
@ -957,7 +959,7 @@ static Res arenaAllocPolicy(Tract *tractReturn, Arena arena, SegPref pref,
|
|||
See also job003384. */
|
||||
moreZones = ZoneSetUnion(pref->zones, ZoneSetDiff(arena->freeZones, pref->avoid));
|
||||
if (moreZones != zones) {
|
||||
res = arenaAllocFromCBS(&tract, moreZones, pref->high, size, pool);
|
||||
res = arenaAllocFromLand(&tract, moreZones, pref->high, size, pool);
|
||||
if (res == ResOK)
|
||||
goto found;
|
||||
}
|
||||
|
|
@ -968,13 +970,13 @@ static Res arenaAllocPolicy(Tract *tractReturn, Arena arena, SegPref pref,
|
|||
if (res != ResOK)
|
||||
return res;
|
||||
if (zones != ZoneSetEMPTY) {
|
||||
res = arenaAllocFromCBS(&tract, zones, pref->high, size, pool);
|
||||
res = arenaAllocFromLand(&tract, zones, pref->high, size, pool);
|
||||
if (res == ResOK)
|
||||
goto found;
|
||||
}
|
||||
if (moreZones != zones) {
|
||||
zones = ZoneSetUnion(zones, ZoneSetDiff(arena->freeZones, pref->avoid));
|
||||
res = arenaAllocFromCBS(&tract, moreZones, pref->high, size, pool);
|
||||
res = arenaAllocFromLand(&tract, moreZones, pref->high, size, pool);
|
||||
if (res == ResOK)
|
||||
goto found;
|
||||
}
|
||||
|
|
@ -986,7 +988,7 @@ static Res arenaAllocPolicy(Tract *tractReturn, Arena arena, SegPref pref,
|
|||
/* TODO: log an event for this */
|
||||
evenMoreZones = ZoneSetDiff(ZoneSetUNIV, pref->avoid);
|
||||
if (evenMoreZones != moreZones) {
|
||||
res = arenaAllocFromCBS(&tract, evenMoreZones, pref->high, size, pool);
|
||||
res = arenaAllocFromLand(&tract, evenMoreZones, pref->high, size, pool);
|
||||
if (res == ResOK)
|
||||
goto found;
|
||||
}
|
||||
|
|
@ -995,7 +997,7 @@ static Res arenaAllocPolicy(Tract *tractReturn, Arena arena, SegPref pref,
|
|||
common ambiguous bit patterns pin them down, causing the zone check
|
||||
to give even more false positives permanently, and possibly retaining
|
||||
garbage indefinitely. */
|
||||
res = arenaAllocFromCBS(&tract, ZoneSetUNIV, pref->high, size, pool);
|
||||
res = arenaAllocFromLand(&tract, ZoneSetUNIV, pref->high, size, pool);
|
||||
if (res == ResOK)
|
||||
goto found;
|
||||
|
||||
|
|
@ -1113,7 +1115,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);
|
||||
|
||||
|
|
|
|||
677
mps/code/cbs.c
677
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 */
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -44,19 +43,19 @@ typedef struct PoolGenStruct *PoolGen;
|
|||
|
||||
typedef struct PoolGenStruct {
|
||||
Sig sig;
|
||||
Serial nr; /* generation number */
|
||||
Pool pool; /* pool this belongs to */
|
||||
Chain chain; /* chain this belongs to */
|
||||
GenDesc gen; /* generation this belongs to */
|
||||
/* link in ring of all PoolGen's in this GenDesc (locus) */
|
||||
RingStruct genRing;
|
||||
Size totalSize; /* total size of segs in gen in this pool */
|
||||
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;
|
||||
|
||||
|
||||
|
|
@ -84,16 +83,21 @@ extern Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace);
|
|||
extern void ChainStartGC(Chain chain, Trace trace);
|
||||
extern void ChainEndGC(Chain chain, Trace trace);
|
||||
extern size_t ChainGens(Chain chain);
|
||||
extern Res ChainAlloc(Seg *segReturn, Chain chain, Serial genNr,
|
||||
SegClass class, Size size, Pool pool,
|
||||
Bool withReservoirPermit, ArgList args);
|
||||
|
||||
extern Bool PoolGenCheck(PoolGen gen);
|
||||
extern Res PoolGenInit(PoolGen gen, Chain chain, Serial nr, Pool pool);
|
||||
extern void PoolGenFinish(PoolGen gen);
|
||||
extern void PoolGenFlip(PoolGen gen);
|
||||
#define PoolGenNr(gen) ((gen)->nr)
|
||||
extern GenDesc ChainGen(Chain chain, Index gen);
|
||||
|
||||
extern Bool PoolGenCheck(PoolGen pgen);
|
||||
extern Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool);
|
||||
extern void PoolGenFinish(PoolGen pgen);
|
||||
extern Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class,
|
||||
Size size, Bool withReservoirPermit, ArgList args);
|
||||
extern void PoolGenFree(PoolGen pgen, Seg seg);
|
||||
extern void PoolGenFill(PoolGen pgen, Size size, Bool deferred);
|
||||
extern void PoolGenEmpty(PoolGen pgen, Size unused, Bool deferred);
|
||||
extern void PoolGenAge(PoolGen pgen, Size aged, Bool deferred);
|
||||
extern void PoolGenReclaim(PoolGen pgen, Size reclaimed, Bool deferred);
|
||||
extern void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize);
|
||||
extern void PoolGenSegSplit(PoolGen pgen);
|
||||
extern void PoolGenSegMerge(PoolGen pgen);
|
||||
|
||||
#endif /* chain_h */
|
||||
|
||||
|
|
|
|||
|
|
@ -164,12 +164,54 @@ FMTHETST = fmthe.c fmtdy.c fmtno.c fmtdytst.c
|
|||
FMTSCM = fmtscheme.c
|
||||
PLINTH = mpsliban.c mpsioan.c
|
||||
EVENTPROC = eventcnv.c table.c
|
||||
MPMCOMMON = abq.c arena.c arenacl.c arenavm.c arg.c boot.c bt.c \
|
||||
buffer.c cbs.c dbgpool.c dbgpooli.c event.c format.c freelist.c \
|
||||
global.c ld.c locus.c message.c meter.c mpm.c mpsi.c 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)
|
||||
|
||||
|
||||
|
|
@ -246,11 +288,11 @@ TEST_TARGETS=\
|
|||
djbench \
|
||||
exposet0 \
|
||||
expt825 \
|
||||
fbmtest \
|
||||
finalcv \
|
||||
finaltest \
|
||||
fotest \
|
||||
gcbench \
|
||||
landtest \
|
||||
locbwcss \
|
||||
lockcov \
|
||||
lockut \
|
||||
|
|
@ -317,17 +359,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
|
||||
|
||||
|
|
@ -431,9 +481,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
|
||||
|
||||
|
|
@ -446,6 +493,9 @@ $(PFM)/$(VARIETY)/fotest: $(PFM)/$(VARIETY)/fotest.o \
|
|||
$(PFM)/$(VARIETY)/gcbench: $(PFM)/$(VARIETY)/gcbench.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/landtest: $(PFM)/$(VARIETY)/landtest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/locbwcss: $(PFM)/$(VARIETY)/locbwcss.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -168,9 +168,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)
|
||||
|
||||
|
|
@ -183,6 +180,9 @@ $(PFM)\$(VARIETY)\fotest.exe: $(PFM)\$(VARIETY)\fotest.obj \
|
|||
$(PFM)\$(VARIETY)\gcbench.exe: $(PFM)\$(VARIETY)\gcbench.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(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> \
|
||||
|
|
|
|||
|
|
@ -372,6 +372,7 @@
|
|||
#define MVFF_SLOT_HIGH_DEFAULT FALSE
|
||||
#define MVFF_ARENA_HIGH_DEFAULT FALSE
|
||||
#define MVFF_FIRST_FIT_DEFAULT TRUE
|
||||
#define MVFF_SPARE_DEFAULT 0.75
|
||||
|
||||
|
||||
/* Pool MVT Configuration -- see <code/poolmv2.c> */
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@
|
|||
*/
|
||||
|
||||
#define EVENT_VERSION_MAJOR ((unsigned)1)
|
||||
#define EVENT_VERSION_MEDIAN ((unsigned)1)
|
||||
#define EVENT_VERSION_MINOR ((unsigned)7)
|
||||
#define EVENT_VERSION_MEDIAN ((unsigned)2)
|
||||
#define EVENT_VERSION_MINOR ((unsigned)0)
|
||||
|
||||
|
||||
/* EVENT_LIST -- list of event types and general properties
|
||||
|
|
@ -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,18 +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, serial) /* pool gen serial number */ \
|
||||
PARAM(X, 9, W, totalSize) /* total size of pool gen */ \
|
||||
PARAM(X, 10, W, newSizeAtCreate) /* new size of pool gen at trace create */
|
||||
#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 */ \
|
||||
|
|
@ -733,7 +733,7 @@
|
|||
|
||||
#define EVENT_ArenaGenZoneAdd_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena) /* the arena */ \
|
||||
PARAM(X, 1, W, gen) /* the generation number */ \
|
||||
PARAM(X, 1, P, gendesc) /* the generation description */ \
|
||||
PARAM(X, 2, W, zoneSet) /* the new zoneSet */
|
||||
|
||||
#define EVENT_ArenaUseFreeZone_PARAMS(PARAM, X) \
|
||||
|
|
|
|||
339
mps/code/failover.c
Normal file
339
mps/code/failover.c
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/* 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>.
|
||||
*/
|
||||
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>.
|
||||
*/
|
||||
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, perhaps
|
||||
* because the primary is out of memory. Delete the whole of
|
||||
* oldRange, and re-insert the fragments (which might end up in
|
||||
* the secondary). See <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 && 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 && res != ResFAIL)
|
||||
res = LandInsert(&dummyRange, fo->secondary, &right);
|
||||
AVER(res == ResOK);
|
||||
}
|
||||
}
|
||||
if (res == ResOK) {
|
||||
AVER(RangesNest(&oldRange, range));
|
||||
RangeCopy(rangeReturn, &oldRange);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static void failoverIterate(Land land, LandVisitor visitor, void *closureP, Size closureS)
|
||||
{
|
||||
Failover fo;
|
||||
|
||||
AVERT(Land, land);
|
||||
fo = failoverOfLand(land);
|
||||
AVERT(Failover, fo);
|
||||
AVER(visitor != NULL);
|
||||
|
||||
LandIterate(fo->primary, visitor, closureP, closureS);
|
||||
LandIterate(fo->secondary, visitor, closureP, closureS);
|
||||
}
|
||||
|
||||
|
||||
static Bool failoverFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
Failover fo;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
fo = failoverOfLand(land);
|
||||
AVERT(Failover, fo);
|
||||
AVERT(FindDelete, findDelete);
|
||||
|
||||
/* See <design/failover/#impl.assume.flush>. */
|
||||
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>. */
|
||||
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>. */
|
||||
LandFlush(fo->primary, fo->secondary);
|
||||
|
||||
return LandFindLargest(rangeReturn, oldRangeReturn, fo->primary, size, findDelete)
|
||||
|| LandFindLargest(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete);
|
||||
}
|
||||
|
||||
|
||||
static Bool failoverFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high)
|
||||
{
|
||||
Failover fo;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
fo = failoverOfLand(land);
|
||||
AVERT(Failover, fo);
|
||||
/* AVERT(ZoneSet, zoneSet); */
|
||||
AVERT(Bool, high);
|
||||
|
||||
/* See <design/failover/#impl.assume.flush>. */
|
||||
LandFlush(fo->primary, fo->secondary);
|
||||
|
||||
return LandFindInZones(rangeReturn, oldRangeReturn, fo->primary, size, zoneSet, high)
|
||||
|| LandFindInZones(rangeReturn, oldRangeReturn, fo->secondary, size, zoneSet, high);
|
||||
}
|
||||
|
||||
|
||||
static Res failoverDescribe(Land land, mps_lib_FILE *stream)
|
||||
{
|
||||
Failover fo;
|
||||
Res res;
|
||||
|
||||
if (!TESTT(Land, land)) return ResFAIL;
|
||||
fo = failoverOfLand(land);
|
||||
if (!TESTT(Failover, fo)) return ResFAIL;
|
||||
if (stream == NULL) return ResFAIL;
|
||||
|
||||
res = WriteF(stream,
|
||||
"Failover $P {\n", (WriteFP)fo,
|
||||
" primary = $P ($S)\n", (WriteFP)fo->primary,
|
||||
fo->primary->class->name,
|
||||
" secondary = $P ($S)\n", (WriteFP)fo->secondary,
|
||||
fo->secondary->class->name,
|
||||
"}\n", NULL);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
DEFINE_LAND_CLASS(FailoverLandClass, class)
|
||||
{
|
||||
INHERIT_CLASS(class, LandClass);
|
||||
class->name = "FAILOVER";
|
||||
class->size = sizeof(FailoverStruct);
|
||||
class->init = failoverInit;
|
||||
class->finish = failoverFinish;
|
||||
class->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.
|
||||
*/
|
||||
|
|
@ -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,37 @@
|
|||
|
||||
/* 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. */
|
||||
|
||||
static Res OOMAlloc(Addr *pReturn, Pool pool, Size size,
|
||||
Bool withReservoirPermit)
|
||||
static Res oomAlloc(Addr *pReturn, Pool pool, Size size,
|
||||
Bool withReservoirPermit)
|
||||
{
|
||||
UNUSED(pReturn);
|
||||
UNUSED(pool);
|
||||
UNUSED(size);
|
||||
UNUSED(withReservoirPermit);
|
||||
return ResMEMORY;
|
||||
switch (rnd() % 4) {
|
||||
case 0:
|
||||
return ResRESOURCE;
|
||||
case 1:
|
||||
return ResMEMORY;
|
||||
case 2:
|
||||
return ResLIMIT;
|
||||
default:
|
||||
return ResCOMMIT_LIMIT;
|
||||
}
|
||||
}
|
||||
|
||||
extern PoolClass PoolClassOOM(void);
|
||||
extern PoolClass OOMPoolClassGet(void);
|
||||
DEFINE_POOL_CLASS(OOMPoolClass, this)
|
||||
{
|
||||
INHERIT_CLASS(this, AbstractAllocFreePoolClass);
|
||||
this->alloc = OOMAlloc;
|
||||
this->alloc = oomAlloc;
|
||||
this->size = sizeof(PoolStruct);
|
||||
AVERT(PoolClass, this);
|
||||
}
|
||||
|
|
@ -83,16 +92,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 +192,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 +211,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,65 @@
|
|||
* .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> */
|
||||
/* freelistEND -- the end of a list
|
||||
*
|
||||
* The end of a list should not be represented with NULL, as this is
|
||||
* ambiguous. However, freelistEND in fact a null pointer for
|
||||
* performance. To check whether you have it right, try temporarily
|
||||
* defining freelistEND as ((FreelistBlock)2) or similar (it must be
|
||||
* an even number because of the use of a tag).
|
||||
*/
|
||||
|
||||
#define freelistEND ((FreelistBlock)0)
|
||||
|
||||
|
||||
/* freelistMinimumAlignment -- the minimum allowed alignment for the
|
||||
* address ranges in a free list: see <design/freelist/#impl.grain.align>
|
||||
*/
|
||||
|
||||
#define freelistMinimumAlignment ((Align)sizeof(FreelistBlock))
|
||||
|
||||
|
||||
/* FreelistTag -- return the tag of word */
|
||||
|
||||
#define FreelistTag(word) ((word) & 1)
|
||||
|
||||
|
||||
/* FreelistTagSet -- return word updated with the tag set */
|
||||
|
||||
#define FreelistTagSet(word) ((FreelistBlock)((Word)(word) | 1))
|
||||
|
||||
|
||||
/* FreelistTagReset -- return word updated with the tag reset */
|
||||
|
||||
#define FreelistTagReset(word) ((FreelistBlock)((Word)(word) & ~(Word)1))
|
||||
|
||||
|
||||
/* FreelistTagCopy -- return 'to' updated to have the same tag as 'from' */
|
||||
|
||||
#define FreelistTagCopy(to, from) ((FreelistBlock)((Word)(to) | FreelistTag((Word)(from))))
|
||||
|
||||
|
||||
|
|
@ -51,7 +84,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 +98,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 +106,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 +139,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 +162,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 +176,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 +216,50 @@ 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'.
|
||||
*/
|
||||
|
||||
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 +270,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 +306,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 +333,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 +357,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 +391,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,25 +438,31 @@ Res FreelistDelete(Range rangeReturn, Freelist fl, Range range)
|
|||
}
|
||||
|
||||
|
||||
void FreelistIterate(Freelist fl, FreelistIterateMethod iterate,
|
||||
void *closureP, Size closureS)
|
||||
static void freelistIterate(Land land, LandVisitor visitor,
|
||||
void *closureP, Size closureS)
|
||||
{
|
||||
Freelist fl;
|
||||
FreelistBlock prev, cur, next;
|
||||
|
||||
AVERT(Land, land);
|
||||
fl = freelistOfLand(land);
|
||||
AVERT(Freelist, fl);
|
||||
AVER(FUNCHECK(iterate));
|
||||
AVER(FUNCHECK(visitor));
|
||||
|
||||
prev = NULL;
|
||||
prev = freelistEND;
|
||||
cur = fl->list;
|
||||
while (cur) {
|
||||
while (cur != freelistEND) {
|
||||
Bool delete = FALSE;
|
||||
RangeStruct range;
|
||||
Bool cont;
|
||||
RangeInit(&range, FreelistBlockBase(cur), FreelistBlockLimit(fl, cur));
|
||||
cont = (*iterate)(&delete, &range, closureP, closureS);
|
||||
cont = (*visitor)(&delete, land, &range, closureP, closureS);
|
||||
next = FreelistBlockNext(cur);
|
||||
if (delete) {
|
||||
Size size = FreelistBlockSize(fl, cur);
|
||||
freelistBlockSetPrevNext(fl, prev, next, -1);
|
||||
AVER(fl->size >= size);
|
||||
fl->size -= size;
|
||||
} else {
|
||||
prev = cur;
|
||||
}
|
||||
|
|
@ -388,14 +473,17 @@ void FreelistIterate(Freelist fl, FreelistIterateMethod iterate,
|
|||
}
|
||||
|
||||
|
||||
/* freelistFindDeleteFromBlock -- Find a chunk of 'size' bytes in
|
||||
* 'block' (which is known to be at least that big) and possibly
|
||||
* delete that chunk according to the instruction in 'findDelete'.
|
||||
* Return the range of that chunk in 'rangeReturn'. Return the
|
||||
* original range of the block in 'oldRangeReturn'. Update the block
|
||||
* list accordingly, using 'prev' which is the previous block in the
|
||||
* list, or NULL if 'block' is the first block in the list.
|
||||
/* freelistFindDeleteFromBlock -- delete size bytes from block
|
||||
*
|
||||
* Find a chunk of size bytes in block (which is known to be at least
|
||||
* that big) and possibly delete that chunk according to the
|
||||
* instruction in findDelete. Return the range of that chunk in
|
||||
* rangeReturn. Return the original range of the block in
|
||||
* oldRangeReturn. Update the block list accordingly, using prev,
|
||||
* which is previous in list or 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 +495,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 +535,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 +566,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 +603,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,19 +640,87 @@ 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(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)
|
||||
return ResFAIL;
|
||||
if (zoneSet == ZoneSetUNIV) {
|
||||
FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW;
|
||||
if ((*landFind)(rangeReturn, oldRangeReturn, land, size, fd))
|
||||
return ResOK;
|
||||
else
|
||||
return ResFAIL;
|
||||
}
|
||||
if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land)))
|
||||
return ResFAIL;
|
||||
|
||||
prev = 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)
|
||||
return ResFAIL;
|
||||
|
||||
freelistDeleteFromBlock(oldRangeReturn, fl, &foundRange, foundPrev, foundCur);
|
||||
RangeCopy(rangeReturn, &foundRange);
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* freelistDescribeVisitor -- visitor method for freelistDescribe
|
||||
*
|
||||
* Writes a decription of the range into the stream pointed to by
|
||||
* closureP.
|
||||
*/
|
||||
static Bool freelistDescribeIterateMethod(Bool *deleteReturn, Range range,
|
||||
void *closureP, Size closureS)
|
||||
|
||||
static Bool freelistDescribeVisitor(Bool *deleteReturn, Land land, Range range,
|
||||
void *closureP, Size closureS)
|
||||
{
|
||||
Res res;
|
||||
mps_lib_FILE *stream = closureP;
|
||||
|
||||
AVER(deleteReturn != NULL);
|
||||
AVERT(Range, range);
|
||||
AVER(stream != NULL);
|
||||
if (deleteReturn == NULL) return FALSE;
|
||||
if (!TESTT(Land, land)) return FALSE;
|
||||
if (!RangeCheck(range)) return FALSE;
|
||||
if (stream == NULL) return FALSE;
|
||||
UNUSED(closureS);
|
||||
|
||||
res = WriteF(stream,
|
||||
|
|
@ -564,64 +729,49 @@ static Bool freelistDescribeIterateMethod(Bool *deleteReturn, Range range,
|
|||
" {$U}\n", (WriteFU)RangeSize(range),
|
||||
NULL);
|
||||
|
||||
*deleteReturn = FALSE;
|
||||
return res == ResOK;
|
||||
}
|
||||
|
||||
|
||||
Res FreelistDescribe(Freelist fl, mps_lib_FILE *stream)
|
||||
static Res freelistDescribe(Land land, mps_lib_FILE *stream)
|
||||
{
|
||||
Freelist fl;
|
||||
Res res;
|
||||
|
||||
if (!TESTT(Land, land)) return ResFAIL;
|
||||
fl = freelistOfLand(land);
|
||||
if (!TESTT(Freelist, fl)) return ResFAIL;
|
||||
if (stream == NULL) return ResFAIL;
|
||||
|
||||
res = WriteF(stream,
|
||||
"Freelist $P {\n", (WriteFP)fl,
|
||||
" alignment = $U\n", (WriteFU)fl->alignment,
|
||||
" listSize = $U\n", (WriteFU)fl->listSize,
|
||||
NULL);
|
||||
|
||||
FreelistIterate(fl, freelistDescribeIterateMethod, stream, 0);
|
||||
LandIterate(land, freelistDescribeVisitor, stream, 0);
|
||||
|
||||
res = WriteF(stream, "}\n", NULL);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* freelistFlushIterateMethod -- Iterate method for
|
||||
* 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->findFirst = freelistFindFirst;
|
||||
class->findLast = freelistFindLast;
|
||||
class->findLargest = freelistFindLargest;
|
||||
class->findInZones = freelistFindInZones;
|
||||
class->describe = freelistDescribe;
|
||||
AVERT(LandClass, class);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,44 +9,13 @@
|
|||
#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;
|
||||
|
||||
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 */
|
||||
|
||||
|
|
|
|||
606
mps/code/land.c
Normal file
606
mps/code/land.c
Normal file
|
|
@ -0,0 +1,606 @@
|
|||
/* 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 function passed to LandIterate is not
|
||||
* allowed to call methods of that land. These functions enforce this.
|
||||
*
|
||||
* .enter-leave.simple: Some simple queries are fine to call from
|
||||
* visitor functions. These are marked with the tag of this comment.
|
||||
*/
|
||||
|
||||
static void landEnter(Land land)
|
||||
{
|
||||
/* Don't need to check as always called from interface function. */
|
||||
AVER(!land->inLand);
|
||||
land->inLand = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
static void landLeave(Land land)
|
||||
{
|
||||
/* Don't need to check as always called from interface function. */
|
||||
AVER(land->inLand);
|
||||
land->inLand = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* LandCheck -- check land */
|
||||
|
||||
Bool LandCheck(Land land)
|
||||
{
|
||||
/* .enter-leave.simple */
|
||||
CHECKS(Land, land);
|
||||
CHECKD(LandClass, land->class);
|
||||
CHECKU(Arena, land->arena);
|
||||
CHECKL(AlignCheck(land->alignment));
|
||||
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>
|
||||
*/
|
||||
|
||||
void LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS)
|
||||
{
|
||||
AVERT(Land, land);
|
||||
AVER(FUNCHECK(visitor));
|
||||
landEnter(land);
|
||||
|
||||
(*land->class->iterate)(land, visitor, closureP, closureS);
|
||||
|
||||
landLeave(land);
|
||||
}
|
||||
|
||||
|
||||
/* 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 res;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
AVER(SizeIsAligned(size, land->alignment));
|
||||
AVER(FindDeleteCheck(findDelete));
|
||||
landEnter(land);
|
||||
|
||||
res = (*land->class->findFirst)(rangeReturn, oldRangeReturn, land, size,
|
||||
findDelete);
|
||||
|
||||
landLeave(land);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* 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 res;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
AVER(SizeIsAligned(size, land->alignment));
|
||||
AVER(FindDeleteCheck(findDelete));
|
||||
landEnter(land);
|
||||
|
||||
res = (*land->class->findLast)(rangeReturn, oldRangeReturn, land, size,
|
||||
findDelete);
|
||||
|
||||
landLeave(land);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* 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 res;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
AVER(SizeIsAligned(size, land->alignment));
|
||||
AVER(FindDeleteCheck(findDelete));
|
||||
landEnter(land);
|
||||
|
||||
res = (*land->class->findLargest)(rangeReturn, oldRangeReturn, land, size,
|
||||
findDelete);
|
||||
|
||||
landLeave(land);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* LandFindInSize -- find range of given size in set of zones
|
||||
*
|
||||
* See <design/land/#function.find.zones>
|
||||
*/
|
||||
|
||||
Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high)
|
||||
{
|
||||
Res res;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
AVER(SizeIsAligned(size, land->alignment));
|
||||
/* AVER(ZoneSet, zoneSet); */
|
||||
AVERT(Bool, high);
|
||||
landEnter(land);
|
||||
|
||||
res = (*land->class->findInZones)(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);
|
||||
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>
|
||||
*/
|
||||
|
||||
void LandFlush(Land dest, Land src)
|
||||
{
|
||||
AVERT(Land, dest);
|
||||
AVERT(Land, src);
|
||||
|
||||
LandIterate(src, landFlushVisitor, dest, 0);
|
||||
}
|
||||
|
||||
|
||||
/* LandClassCheck -- check land class */
|
||||
|
||||
Bool LandClassCheck(LandClass class)
|
||||
{
|
||||
CHECKL(ProtocolClassCheck(&class->protocol));
|
||||
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(Bool *deleteReturn, Land land, Range range,
|
||||
void *closureP, Size closureS)
|
||||
{
|
||||
Size *size;
|
||||
|
||||
AVER(deleteReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
AVERT(Range, range);
|
||||
AVER(closureP != NULL);
|
||||
UNUSED(closureS);
|
||||
|
||||
size = closureP;
|
||||
*size += RangeSize(range);
|
||||
*deleteReturn = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Size LandSlowSize(Land land)
|
||||
{
|
||||
Size size = 0;
|
||||
LandIterate(land, landSizeVisitor, &size, 0);
|
||||
return size;
|
||||
}
|
||||
|
||||
static Res landNoInsert(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
AVERT(Range, range);
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
static Res landNoDelete(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
AVERT(Range, range);
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
static void landNoIterate(Land land, LandVisitor visitor, void *closureP, Size closureS)
|
||||
{
|
||||
AVERT(Land, land);
|
||||
AVER(visitor != NULL);
|
||||
UNUSED(closureP);
|
||||
UNUSED(closureS);
|
||||
NOOP;
|
||||
}
|
||||
|
||||
static Bool landNoFind(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
UNUSED(size);
|
||||
AVER(FindDeleteCheck(findDelete));
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
static Res landNoFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high)
|
||||
{
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
UNUSED(size);
|
||||
UNUSED(zoneSet);
|
||||
AVER(BoolCheck(high));
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
static Res landTrivDescribe(Land land, mps_lib_FILE *stream)
|
||||
{
|
||||
if (!TESTT(Land, land))
|
||||
return ResFAIL;
|
||||
if (stream == NULL)
|
||||
return ResFAIL;
|
||||
/* dispatching function does it all */
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
DEFINE_CLASS(LandClass, class)
|
||||
{
|
||||
INHERIT_CLASS(&class->protocol, ProtocolClass);
|
||||
class->name = "LAND";
|
||||
class->size = sizeof(LandStruct);
|
||||
class->init = landTrivInit;
|
||||
class->sizeMethod = landNoSize;
|
||||
class->finish = landTrivFinish;
|
||||
class->insert = landNoInsert;
|
||||
class->delete = landNoDelete;
|
||||
class->iterate = landNoIterate;
|
||||
class->findFirst = landNoFind;
|
||||
class->findLast = landNoFind;
|
||||
class->findLargest = landNoFind;
|
||||
class->findInZones = landNoFindInZones;
|
||||
class->describe = landTrivDescribe;
|
||||
class->sig = LandClassSig;
|
||||
AVERT(LandClass, class);
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2014 Ravenbrook Limited <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.
|
||||
*/
|
||||
|
|
@ -1,32 +1,34 @@
|
|||
/* fbmtest.c: FREE BLOCK MANAGEMENT TEST
|
||||
/* landtest.c: LAND TEST
|
||||
*
|
||||
* $Id$
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* The MPS contains two free block management modules:
|
||||
* The MPS contains three land implementations:
|
||||
*
|
||||
* 1. the CBS (Coalescing Block Structure) module maintains free
|
||||
* blocks in a splay tree for fast access with a cost in storage;
|
||||
* 1. the CBS (Coalescing Block Structure) module maintains blocks in
|
||||
* a splay tree for fast access with a cost in storage;
|
||||
*
|
||||
* 2. the Freelist module maintains free blocks in an address-ordered
|
||||
* 2. the Freelist module maintains blocks in an address-ordered
|
||||
* singly linked list for zero storage overhead with a cost in
|
||||
* performance.
|
||||
*
|
||||
* The two modules present identical interfaces, so we apply the same
|
||||
* test cases to both.
|
||||
* 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(fbmtest, "$Id$");
|
||||
SRCID(landtest, "$Id$");
|
||||
|
||||
|
||||
#define ArraySize ((Size)123456)
|
||||
|
|
@ -35,70 +37,53 @@ SRCID(fbmtest, "$Id$");
|
|||
* 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 unsigned FBMType;
|
||||
enum {
|
||||
FBMTypeCBS = 1,
|
||||
FBMTypeFreelist,
|
||||
FBMTypeLimit
|
||||
};
|
||||
|
||||
typedef struct FBMStateStruct {
|
||||
FBMType type;
|
||||
typedef struct TestStateStruct {
|
||||
Align align;
|
||||
BT allocTable;
|
||||
Addr block;
|
||||
union {
|
||||
CBS cbs;
|
||||
Freelist fl;
|
||||
} the;
|
||||
} FBMStateStruct, *FBMState;
|
||||
Land land;
|
||||
} TestStateStruct, *TestState;
|
||||
|
||||
typedef struct CheckFBMClosureStruct {
|
||||
FBMState state;
|
||||
typedef struct CheckTestClosureStruct {
|
||||
TestState state;
|
||||
Addr limit;
|
||||
Addr oldLimit;
|
||||
} CheckFBMClosureStruct, *CheckFBMClosure;
|
||||
} CheckTestClosureStruct, *CheckTestClosure;
|
||||
|
||||
|
||||
static Addr (addrOfIndex)(FBMState state, Index i)
|
||||
static Addr (addrOfIndex)(TestState state, Index i)
|
||||
{
|
||||
return AddrAdd(state->block, (i * state->align));
|
||||
}
|
||||
|
||||
|
||||
static Index (indexOfAddr)(FBMState state, Addr a)
|
||||
static Index (indexOfAddr)(TestState state, Addr a)
|
||||
{
|
||||
return (Index)(AddrOffset(state->block, a) / state->align);
|
||||
}
|
||||
|
||||
|
||||
static void describe(FBMState state) {
|
||||
switch (state->type) {
|
||||
case FBMTypeCBS:
|
||||
die(CBSDescribe(state->the.cbs, mps_lib_get_stdout()), "CBSDescribe");
|
||||
break;
|
||||
case FBMTypeFreelist:
|
||||
die(FreelistDescribe(state->the.fl, mps_lib_get_stdout()), "FreelistDescribe");
|
||||
break;
|
||||
default:
|
||||
cdie(0, "invalid state->type");
|
||||
break;
|
||||
}
|
||||
static void describe(TestState state) {
|
||||
die(LandDescribe(state->land, mps_lib_get_stdout()), "LandDescribe");
|
||||
}
|
||||
|
||||
|
||||
static Bool checkCallback(Range range, void *closureP, Size closureS)
|
||||
static Bool checkVisitor(Bool *deleteReturn, Land land, Range range,
|
||||
void *closureP, Size closureS)
|
||||
{
|
||||
Addr base, limit;
|
||||
CheckFBMClosure cl = (CheckFBMClosure)closureP;
|
||||
CheckTestClosure cl = closureP;
|
||||
|
||||
UNUSED(closureS);
|
||||
Insist(deleteReturn != NULL);
|
||||
testlib_unused(land);
|
||||
testlib_unused(closureS);
|
||||
Insist(cl != NULL);
|
||||
|
||||
base = RangeBase(range);
|
||||
|
|
@ -122,42 +107,15 @@ static Bool checkCallback(Range range, void *closureP, Size closureS)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static Bool checkCBSCallback(CBS cbs, Range range,
|
||||
void *closureP, Size closureS)
|
||||
static void check(TestState state)
|
||||
{
|
||||
UNUSED(cbs);
|
||||
return checkCallback(range, closureP, closureS);
|
||||
}
|
||||
|
||||
|
||||
static Bool checkFLCallback(Bool *deleteReturn, Range range,
|
||||
void *closureP, Size closureS)
|
||||
{
|
||||
*deleteReturn = FALSE;
|
||||
return checkCallback(range, closureP, closureS);
|
||||
}
|
||||
|
||||
|
||||
static void check(FBMState state)
|
||||
{
|
||||
CheckFBMClosureStruct closure;
|
||||
CheckTestClosureStruct closure;
|
||||
|
||||
closure.state = state;
|
||||
closure.limit = addrOfIndex(state, ArraySize);
|
||||
closure.oldLimit = state->block;
|
||||
|
||||
switch (state->type) {
|
||||
case FBMTypeCBS:
|
||||
CBSIterate(state->the.cbs, checkCBSCallback, (void *)&closure, 0);
|
||||
break;
|
||||
case FBMTypeFreelist:
|
||||
FreelistIterate(state->the.fl, checkFLCallback, (void *)&closure, 0);
|
||||
break;
|
||||
default:
|
||||
cdie(0, "invalid state->type");
|
||||
return;
|
||||
}
|
||||
LandIterate(state->land, checkVisitor, (void *)&closure, 0);
|
||||
|
||||
if (closure.oldLimit == state->block)
|
||||
Insist(BTIsSetRange(state->allocTable, 0,
|
||||
|
|
@ -243,7 +201,7 @@ static Index lastEdge(BT bt, Size size, Index base)
|
|||
* all either set or reset.
|
||||
*/
|
||||
|
||||
static void randomRange(Addr *baseReturn, Addr *limitReturn, FBMState state)
|
||||
static void randomRange(Addr *baseReturn, Addr *limitReturn, TestState state)
|
||||
{
|
||||
Index base; /* the start of our range */
|
||||
Index end; /* an edge (i.e. different from its predecessor) */
|
||||
|
|
@ -265,7 +223,7 @@ static void randomRange(Addr *baseReturn, Addr *limitReturn, FBMState state)
|
|||
}
|
||||
|
||||
|
||||
static void allocate(FBMState state, Addr base, Addr limit)
|
||||
static void allocate(TestState state, Addr base, Addr limit)
|
||||
{
|
||||
Res res;
|
||||
Index ib, il; /* Indexed for base and limit */
|
||||
|
|
@ -293,25 +251,15 @@ static void allocate(FBMState state, Addr base, Addr limit)
|
|||
total = AddrOffset(outerBase, outerLimit);
|
||||
|
||||
/* TODO: check these values */
|
||||
UNUSED(left);
|
||||
UNUSED(right);
|
||||
UNUSED(total);
|
||||
testlib_unused(left);
|
||||
testlib_unused(right);
|
||||
testlib_unused(total);
|
||||
} else {
|
||||
outerBase = outerLimit = NULL;
|
||||
}
|
||||
|
||||
RangeInit(&range, base, limit);
|
||||
switch (state->type) {
|
||||
case FBMTypeCBS:
|
||||
res = CBSDelete(&oldRange, state->the.cbs, &range);
|
||||
break;
|
||||
case FBMTypeFreelist:
|
||||
res = FreelistDelete(&oldRange, state->the.fl, &range);
|
||||
break;
|
||||
default:
|
||||
cdie(0, "invalid state->type");
|
||||
return;
|
||||
}
|
||||
res = LandDelete(&oldRange, state->land, &range);
|
||||
|
||||
if (verbose) {
|
||||
printf("allocate: [%p,%p) -- %s\n",
|
||||
|
|
@ -333,7 +281,7 @@ static void allocate(FBMState state, Addr base, Addr limit)
|
|||
}
|
||||
|
||||
|
||||
static void deallocate(FBMState state, Addr base, Addr limit)
|
||||
static void deallocate(TestState state, Addr base, Addr limit)
|
||||
{
|
||||
Res res;
|
||||
Index ib, il;
|
||||
|
|
@ -371,23 +319,13 @@ static void deallocate(FBMState state, Addr base, Addr limit)
|
|||
total = AddrOffset(outerBase, outerLimit);
|
||||
|
||||
/* TODO: check these values */
|
||||
UNUSED(left);
|
||||
UNUSED(right);
|
||||
UNUSED(total);
|
||||
testlib_unused(left);
|
||||
testlib_unused(right);
|
||||
testlib_unused(total);
|
||||
}
|
||||
|
||||
RangeInit(&range, base, limit);
|
||||
switch (state->type) {
|
||||
case FBMTypeCBS:
|
||||
res = CBSInsert(&freeRange, state->the.cbs, &range);
|
||||
break;
|
||||
case FBMTypeFreelist:
|
||||
res = FreelistInsert(&freeRange, state->the.fl, &range);
|
||||
break;
|
||||
default:
|
||||
cdie(0, "invalid state->type");
|
||||
return;
|
||||
}
|
||||
res = LandInsert(&freeRange, state->land, &range);
|
||||
|
||||
if (verbose) {
|
||||
printf("deallocate: [%p,%p) -- %s\n",
|
||||
|
|
@ -410,7 +348,7 @@ static void deallocate(FBMState state, Addr base, Addr limit)
|
|||
}
|
||||
|
||||
|
||||
static void find(FBMState state, Size size, Bool high, FindDelete findDelete)
|
||||
static void find(TestState state, Size size, Bool high, FindDelete findDelete)
|
||||
{
|
||||
Bool expected, found;
|
||||
Index expectedBase, expectedLimit;
|
||||
|
|
@ -454,23 +392,12 @@ static void find(FBMState state, Size size, Bool high, FindDelete findDelete)
|
|||
}
|
||||
|
||||
/* TODO: check these values */
|
||||
UNUSED(oldSize);
|
||||
UNUSED(newSize);
|
||||
testlib_unused(oldSize);
|
||||
testlib_unused(newSize);
|
||||
}
|
||||
|
||||
switch (state->type) {
|
||||
case FBMTypeCBS:
|
||||
found = (high ? CBSFindLast : CBSFindFirst)
|
||||
(&foundRange, &oldRange, state->the.cbs, size * state->align, findDelete);
|
||||
break;
|
||||
case FBMTypeFreelist:
|
||||
found = (high ? FreelistFindLast : FreelistFindFirst)
|
||||
(&foundRange, &oldRange, state->the.fl, size * state->align, findDelete);
|
||||
break;
|
||||
default:
|
||||
cdie(0, "invalid state->type");
|
||||
return;
|
||||
}
|
||||
found = (high ? LandFindLast : LandFindFirst)
|
||||
(&foundRange, &oldRange, state->land, size * state->align, findDelete);
|
||||
|
||||
if (verbose) {
|
||||
printf("find %s %lu: ", high ? "last" : "first",
|
||||
|
|
@ -506,7 +433,7 @@ static void find(FBMState state, Size size, Bool high, FindDelete findDelete)
|
|||
return;
|
||||
}
|
||||
|
||||
static void test(FBMState state, unsigned n) {
|
||||
static void test(TestState state, unsigned n) {
|
||||
Addr base, limit;
|
||||
unsigned i;
|
||||
Size size;
|
||||
|
|
@ -537,7 +464,7 @@ static void test(FBMState state, unsigned n) {
|
|||
find(state, size, high, findDelete);
|
||||
break;
|
||||
default:
|
||||
cdie(0, "invalid state->type");
|
||||
cdie(0, "invalid rnd(3)");
|
||||
return;
|
||||
}
|
||||
if ((i + 1) % 1000 == 0)
|
||||
|
|
@ -550,17 +477,24 @@ static void test(FBMState state, unsigned n) {
|
|||
extern int main(int argc, char *argv[])
|
||||
{
|
||||
mps_arena_t mpsArena;
|
||||
Arena arena; /* the ANSI arena which we use to allocate the BT */
|
||||
FBMStateStruct state;
|
||||
Arena arena;
|
||||
TestStateStruct state;
|
||||
void *p;
|
||||
Addr dummyBlock;
|
||||
BT allocTable;
|
||||
FreelistStruct flStruct;
|
||||
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 = sizeof(void *) << (rnd() % 4);
|
||||
align = (1 << rnd() % 4) * MPS_PF_ALIGN;
|
||||
|
||||
NAllocateTried = NAllocateSucceeded = NDeallocateTried =
|
||||
NDeallocateSucceeded = 0;
|
||||
|
|
@ -584,23 +518,67 @@ extern int main(int argc, char *argv[])
|
|||
(char *)dummyBlock + ArraySize);
|
||||
}
|
||||
|
||||
die((mps_res_t)CBSInit(&cbsStruct, arena, arena, align,
|
||||
/* fastFind */ TRUE, /* zoned */ FALSE, mps_args_none),
|
||||
"failed to initialise CBS");
|
||||
state.type = FBMTypeCBS;
|
||||
/* 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.the.cbs = &cbsStruct;
|
||||
state.land = cbs;
|
||||
test(&state, nCBSOperations);
|
||||
CBSFinish(&cbsStruct);
|
||||
LandFinish(cbs);
|
||||
|
||||
die((mps_res_t)FreelistInit(&flStruct, align),
|
||||
/* 2. Test Freelist */
|
||||
|
||||
die((mps_res_t)LandInit(fl, FreelistLandClassGet(), arena, align, NULL,
|
||||
mps_args_none),
|
||||
"failed to initialise Freelist");
|
||||
state.type = FBMTypeFreelist;
|
||||
state.the.fl = &flStruct;
|
||||
state.land = fl;
|
||||
test(&state, nFLOperations);
|
||||
FreelistFinish(&flStruct);
|
||||
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);
|
||||
|
||||
293
mps/code/locus.c
293
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);
|
||||
|
|
@ -214,7 +213,8 @@ void ChainDestroy(Chain chain)
|
|||
AVERT(Chain, chain);
|
||||
AVER(chain->activeTraces == TraceSetEMPTY);
|
||||
|
||||
arena = chain->arena; genCount = chain->genCount;
|
||||
arena = chain->arena;
|
||||
genCount = chain->genCount;
|
||||
RingRemove(&chain->chainRing);
|
||||
chain->sig = SigInvalid;
|
||||
for (i = 0; i < genCount; ++i) {
|
||||
|
|
@ -236,55 +236,73 @@ size_t ChainGens(Chain chain)
|
|||
}
|
||||
|
||||
|
||||
/* ChainAlloc -- allocate tracts in a generation */
|
||||
/* ChainGen -- return a generation in a chain, or the arena top generation */
|
||||
|
||||
Res ChainAlloc(Seg *segReturn, Chain chain, Serial genNr, SegClass class,
|
||||
Size size, Pool pool, Bool withReservoirPermit,
|
||||
ArgList args)
|
||||
GenDesc ChainGen(Chain chain, Index gen)
|
||||
{
|
||||
AVERT(Chain, chain);
|
||||
AVER(gen <= chain->genCount);
|
||||
|
||||
if (gen < chain->genCount)
|
||||
return &chain->gens[gen];
|
||||
else
|
||||
return &chain->arena->topGen;
|
||||
}
|
||||
|
||||
|
||||
/* PoolGenAlloc -- allocate a segment in a pool generation */
|
||||
|
||||
Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class, Size size,
|
||||
Bool withReservoirPermit, ArgList args)
|
||||
{
|
||||
SegPrefStruct pref;
|
||||
Res res;
|
||||
Seg seg;
|
||||
ZoneSet zones, moreZones;
|
||||
Arena arena;
|
||||
GenDesc gen;
|
||||
|
||||
AVERT(Chain, chain);
|
||||
AVER(genNr <= chain->genCount);
|
||||
AVER(segReturn != NULL);
|
||||
AVERT(PoolGen, pgen);
|
||||
AVERT(SegClass, class);
|
||||
AVER(size > 0);
|
||||
AVERT(Bool, withReservoirPermit);
|
||||
AVERT(ArgList, args);
|
||||
|
||||
arena = chain->arena;
|
||||
if (genNr < chain->genCount)
|
||||
zones = chain->gens[genNr].zones;
|
||||
else
|
||||
zones = arena->topGen.zones;
|
||||
arena = PoolArena(pgen->pool);
|
||||
gen = pgen->gen;
|
||||
zones = gen->zones;
|
||||
|
||||
SegPrefInit(&pref);
|
||||
pref.high = FALSE;
|
||||
pref.zones = zones;
|
||||
pref.avoid = ZoneSetBlacklist(arena);
|
||||
res = SegAlloc(&seg, class, &pref, size, pool, withReservoirPermit, args);
|
||||
res = SegAlloc(&seg, class, &pref, size, pgen->pool, withReservoirPermit,
|
||||
args);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
moreZones = ZoneSetUnion(zones, ZoneSetOfSeg(arena, seg));
|
||||
gen->zones = moreZones;
|
||||
|
||||
if (!ZoneSetSuper(zones, moreZones)) {
|
||||
/* Tracking the whole zoneset for each generation number gives
|
||||
* more understandable telemetry than just reporting the added
|
||||
/* Tracking the whole zoneset for each generation gives more
|
||||
* understandable telemetry than just reporting the added
|
||||
* zones. */
|
||||
EVENT3(ArenaGenZoneAdd, arena, genNr, moreZones);
|
||||
EVENT3(ArenaGenZoneAdd, arena, gen, moreZones);
|
||||
}
|
||||
|
||||
if (genNr < chain->genCount)
|
||||
chain->gens[genNr].zones = moreZones;
|
||||
else
|
||||
chain->arena->topGen.zones = moreZones;
|
||||
|
||||
size = SegSize(seg);
|
||||
pgen->totalSize += size;
|
||||
STATISTIC_STAT ({
|
||||
++ pgen->segs;
|
||||
pgen->freeSize += size;
|
||||
});
|
||||
*segReturn = seg;
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ChainDeferral -- time until next ephemeral GC for this chain */
|
||||
|
||||
double ChainDeferral(Chain chain)
|
||||
|
|
@ -394,59 +412,222 @@ void ChainEndGC(Chain chain, Trace trace)
|
|||
|
||||
/* PoolGenInit -- initialize a PoolGen */
|
||||
|
||||
Res PoolGenInit(PoolGen gen, Chain chain, Serial nr, Pool pool)
|
||||
Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool)
|
||||
{
|
||||
/* Can't check gen, because it's not been initialized. */
|
||||
AVER(gen != NULL);
|
||||
AVERT(Chain, chain);
|
||||
AVER(nr <= chain->genCount);
|
||||
/* Can't check pgen, because it's not been initialized. */
|
||||
AVER(pgen != NULL);
|
||||
AVERT(GenDesc, gen);
|
||||
AVERT(Pool, pool);
|
||||
AVER(PoolHasAttr(pool, AttrGC));
|
||||
|
||||
gen->nr = nr;
|
||||
gen->pool = pool;
|
||||
gen->chain = chain;
|
||||
RingInit(&gen->genRing);
|
||||
gen->totalSize = (Size)0;
|
||||
gen->newSize = (Size)0;
|
||||
gen->sig = PoolGenSig;
|
||||
pgen->pool = pool;
|
||||
pgen->gen = gen;
|
||||
RingInit(&pgen->genRing);
|
||||
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);
|
||||
|
||||
if(nr != chain->genCount) {
|
||||
RingAppend(&chain->gens[nr].locusRing, &gen->genRing);
|
||||
} else {
|
||||
/* Dynamic generation is linked to the arena, not the chain. */
|
||||
RingAppend(&chain->arena->topGen.locusRing, &gen->genRing);
|
||||
}
|
||||
AVERT(PoolGen, gen);
|
||||
RingAppend(&gen->locusRing, &pgen->genRing);
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* PoolGenFinish -- finish a PoolGen */
|
||||
|
||||
void PoolGenFinish(PoolGen gen)
|
||||
void PoolGenFinish(PoolGen pgen)
|
||||
{
|
||||
AVERT(PoolGen, gen);
|
||||
AVERT(PoolGen, pgen);
|
||||
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);
|
||||
});
|
||||
|
||||
gen->sig = SigInvalid;
|
||||
RingRemove(&gen->genRing);
|
||||
pgen->sig = SigInvalid;
|
||||
RingRemove(&pgen->genRing);
|
||||
}
|
||||
|
||||
|
||||
/* PoolGenCheck -- check a PoolGen */
|
||||
|
||||
Bool PoolGenCheck(PoolGen gen)
|
||||
Bool PoolGenCheck(PoolGen pgen)
|
||||
{
|
||||
CHECKS(PoolGen, gen);
|
||||
CHECKS(PoolGen, pgen);
|
||||
/* nothing to check about serial */
|
||||
CHECKU(Pool, gen->pool);
|
||||
CHECKU(Chain, gen->chain);
|
||||
CHECKD_NOSIG(Ring, &gen->genRing);
|
||||
CHECKL(gen->newSize <= gen->totalSize);
|
||||
CHECKU(Pool, pgen->pool);
|
||||
CHECKU(GenDesc, pgen->gen);
|
||||
CHECKD_NOSIG(Ring, &pgen->genRing);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/* PoolGenFill -- accounting for allocation
|
||||
*
|
||||
* The memory was free, is now new (or newDeferred).
|
||||
*/
|
||||
|
||||
void PoolGenFill(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;
|
||||
}
|
||||
|
||||
|
||||
/* PoolGenEmpty -- accounting for emptying a buffer
|
||||
*
|
||||
* The unused part of the buffer was new (or newDeferred) and is now free.
|
||||
*/
|
||||
|
||||
void PoolGenEmpty(PoolGen pgen, Size unused, Bool 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);
|
||||
}
|
||||
|
||||
|
||||
/* PoolGenAge -- accounting for condemning a segment
|
||||
*
|
||||
* The memory was new (or newDeferred), is now old (or oldDeferred)
|
||||
*/
|
||||
|
||||
void PoolGenAge(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* PoolGenReclaim -- accounting for reclaiming
|
||||
*
|
||||
* The reclaimed memory was old, and is now free.
|
||||
*/
|
||||
|
||||
void PoolGenReclaim(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 -- accounting for end of ramp mode
|
||||
*
|
||||
* The memory was oldDeferred or newDeferred, is now old or new.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/* PoolGenSegSplit -- accounting for splitting a segment */
|
||||
|
||||
void PoolGenSegSplit(PoolGen pgen)
|
||||
{
|
||||
AVERT(PoolGen, pgen);
|
||||
STATISTIC(++ pgen->segs);
|
||||
}
|
||||
|
||||
|
||||
/* PoolGenSegMerge -- accounting for merging a segment */
|
||||
|
||||
void PoolGenSegMerge(PoolGen pgen)
|
||||
{
|
||||
AVERT(PoolGen, pgen);
|
||||
STATISTIC_STAT ({
|
||||
AVER(pgen->segs > 0);
|
||||
-- pgen->segs;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* PoolGenFree -- free a segment and update accounting
|
||||
*
|
||||
* The segment is assumed to finish free.
|
||||
*/
|
||||
|
||||
void PoolGenFree(PoolGen pgen, Seg seg)
|
||||
{
|
||||
Size size;
|
||||
|
||||
AVERT(PoolGen, pgen);
|
||||
AVERT(Seg, seg);
|
||||
|
||||
size = SegSize(seg);
|
||||
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)
|
||||
|
|
@ -460,9 +641,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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -495,8 +495,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 +815,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 +1002,36 @@ 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 void LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS);
|
||||
extern Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete);
|
||||
extern Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete);
|
||||
extern Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete);
|
||||
extern Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high);
|
||||
extern Res LandDescribe(Land land, mps_lib_FILE *stream);
|
||||
extern void LandFlush(Land dest, Land src);
|
||||
|
||||
extern Size LandSlowSize(Land land);
|
||||
extern Bool LandClassCheck(LandClass class);
|
||||
extern LandClass LandClassGet(void);
|
||||
#define LAND_SUPERCLASS(className) ((LandClass)SUPERCLASS(className))
|
||||
#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);
|
||||
|
|
|
|||
100
mps/code/mpmst.h
100
mps/code/mpmst.h
|
|
@ -604,7 +604,52 @@ 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 */
|
||||
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>.
|
||||
*/
|
||||
|
|
@ -612,21 +657,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>. */
|
||||
|
|
@ -661,9 +743,9 @@ typedef struct mps_arena_s {
|
|||
Serial chunkSerial; /* next chunk number */
|
||||
ChunkCacheEntryStruct chunkCache; /* just one entry */
|
||||
|
||||
Bool hasFreeCBS; /* Is freeCBS available? */
|
||||
Bool hasFreeLand; /* Is freeLand available? */
|
||||
MFSStruct freeCBSBlockPoolStruct;
|
||||
CBSStruct freeCBSStruct;
|
||||
CBSStruct freeLandStruct;
|
||||
ZoneSet freeZones; /* zones not yet allocated */
|
||||
Bool zoned; /* use zoned allocation? */
|
||||
|
||||
|
|
|
|||
|
|
@ -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,20 @@ 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)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS);
|
||||
typedef void (*LandIterateMethod)(Land land, LandVisitor visitor, void *closureP, Size closureS);
|
||||
typedef Bool (*LandFindMethod)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete);
|
||||
typedef Res (*LandFindInZonesMethod)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high);
|
||||
typedef Res (*LandDescribeMethod)(Land land, mps_lib_FILE *stream);
|
||||
|
||||
|
||||
/* CONSTANTS */
|
||||
|
||||
|
||||
|
|
@ -407,7 +424,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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,9 @@ extern const struct mps_key_s _mps_key_max_size;
|
|||
extern const struct mps_key_s _mps_key_align;
|
||||
#define MPS_KEY_ALIGN (&_mps_key_align)
|
||||
#define MPS_KEY_ALIGN_FIELD align
|
||||
extern const struct mps_key_s _mps_key_spare;
|
||||
#define MPS_KEY_SPARE (&_mps_key_spare)
|
||||
#define MPS_KEY_SPARE_FIELD double
|
||||
extern const struct mps_key_s _mps_key_interior;
|
||||
#define MPS_KEY_INTERIOR (&_mps_key_interior)
|
||||
#define MPS_KEY_INTERIOR_FIELD b
|
||||
|
|
|
|||
|
|
@ -43,11 +43,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 */,
|
||||
|
|
@ -112,7 +112,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 */; };
|
||||
|
|
@ -719,7 +719,7 @@
|
|||
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 3114A64B156E9596001E0AA3;
|
||||
remoteInfo = fbmtest;
|
||||
remoteInfo = landtest;
|
||||
};
|
||||
3114A674156E9619001E0AA3 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
|
|
@ -1338,7 +1338,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>"; };
|
||||
|
|
@ -1353,6 +1353,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>"; };
|
||||
|
|
@ -1408,7 +1413,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; };
|
||||
|
|
@ -2060,6 +2065,7 @@
|
|||
31160D9C1899540D0071EB17 /* config.txt */,
|
||||
31160D9D1899540D0071EB17 /* critical-path.txt */,
|
||||
31160D9E1899540D0071EB17 /* diag.txt */,
|
||||
22DD93E118ED815F00240DD2 /* failover.txt */,
|
||||
31160D9F1899540D0071EB17 /* finalize.txt */,
|
||||
31160DA01899540D0071EB17 /* fix.txt */,
|
||||
31160DA11899540D0071EB17 /* freelist.txt */,
|
||||
|
|
@ -2069,6 +2075,7 @@
|
|||
31160DA51899540D0071EB17 /* interface-c.txt */,
|
||||
31160DA61899540D0071EB17 /* io.txt */,
|
||||
31160DA71899540D0071EB17 /* keyword-arguments.txt */,
|
||||
22DD93E218ED815F00240DD2 /* land.txt */,
|
||||
31160DA81899540D0071EB17 /* lib.txt */,
|
||||
31160DA91899540D0071EB17 /* lock.txt */,
|
||||
31160DAA1899540D0071EB17 /* locus.txt */,
|
||||
|
|
@ -2139,7 +2146,6 @@
|
|||
3114A613156E944A001E0AA3 /* bttest.c */,
|
||||
2291A5AA175CAA9B001D4920 /* exposet0.c */,
|
||||
2291A5AB175CAA9B001D4920 /* expt825.c */,
|
||||
2291A5E9175CB4EC001D4920 /* fbmtest.c */,
|
||||
3114A5CD156E9369001E0AA3 /* finalcv.c */,
|
||||
3114A5E5156E93B9001E0AA3 /* finaltest.c */,
|
||||
3124CAC6156BE48D00753214 /* fmtdy.c */,
|
||||
|
|
@ -2153,6 +2159,7 @@
|
|||
22FACED6188807FF000FDBC1 /* fmtscheme.c */,
|
||||
22FACED7188807FF000FDBC1 /* fmtscheme.h */,
|
||||
224CC79E175E3202002FF81B /* fotest.c */,
|
||||
2291A5E9175CB4EC001D4920 /* landtest.c */,
|
||||
2231BB6818CA9834002D6322 /* locbwcss.c */,
|
||||
31D60036156D3E0200337B26 /* lockcov.c */,
|
||||
2231BB6918CA983C002D6322 /* locusss.c */,
|
||||
|
|
@ -2244,7 +2251,7 @@
|
|||
3114A605156E9430001E0AA3 /* bttest */,
|
||||
3114A61C156E9485001E0AA3 /* teletest */,
|
||||
3114A633156E94DB001E0AA3 /* abqtest */,
|
||||
3114A64C156E9596001E0AA3 /* fbmtest */,
|
||||
3114A64C156E9596001E0AA3 /* landtest */,
|
||||
3114A662156E95D9001E0AA3 /* btcv */,
|
||||
3114A67C156E9668001E0AA3 /* mv2test */,
|
||||
3114A695156E971B001E0AA3 /* messtest */,
|
||||
|
|
@ -2299,10 +2306,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 */,
|
||||
|
|
@ -2934,9 +2944,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 */,
|
||||
|
|
@ -2947,9 +2957,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 */ = {
|
||||
|
|
@ -3330,11 +3340,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 */,
|
||||
|
|
@ -3664,7 +3674,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2291A5ED175CB5E2001D4920 /* fbmtest.c in Sources */,
|
||||
2291A5ED175CB5E2001D4920 /* landtest.c in Sources */,
|
||||
3114A672156E95F6001E0AA3 /* testlib.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -4183,7 +4193,7 @@
|
|||
};
|
||||
3114A65B156E95B4001E0AA3 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 3114A64B156E9596001E0AA3 /* fbmtest */;
|
||||
target = 3114A64B156E9596001E0AA3 /* landtest */;
|
||||
targetProxy = 3114A65A156E95B4001E0AA3 /* PBXContainerItemProxy */;
|
||||
};
|
||||
3114A675156E9619001E0AA3 /* PBXTargetDependency */ = {
|
||||
|
|
@ -5822,7 +5832,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "fbmtest" */ = {
|
||||
3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "landtest" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
3114A654156E9596001E0AA3 /* Debug */,
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ ARG_DEFINE_KEY(min_size, Size);
|
|||
ARG_DEFINE_KEY(mean_size, Size);
|
||||
ARG_DEFINE_KEY(max_size, Size);
|
||||
ARG_DEFINE_KEY(align, Align);
|
||||
ARG_DEFINE_KEY(spare, double);
|
||||
ARG_DEFINE_KEY(interior, Bool);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
unsigned old : 1; /* .seg.old */
|
||||
unsigned deferred : 1; /* .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));
|
||||
CHECKL(BoolCheck(amcseg->deferred));
|
||||
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);
|
||||
|
||||
|
|
@ -454,7 +463,6 @@ typedef struct AMCStruct { /* <design/poolamc/#struct> */
|
|||
RankSet rankSet; /* rankSet for entire pool */
|
||||
RingStruct genRing; /* ring of generations */
|
||||
Bool gensBooted; /* used during boot (init) */
|
||||
Chain chain; /* chain used by this pool */
|
||||
size_t gens; /* number of generations */
|
||||
amcGen *gen; /* (pointer to) array of generations */
|
||||
amcGen nursery; /* the default mutator generation */
|
||||
|
|
@ -488,9 +496,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;
|
||||
}
|
||||
|
||||
|
|
@ -642,12 +648,12 @@ DEFINE_BUFFER_CLASS(amcBufClass, class)
|
|||
|
||||
/* amcGenCreate -- create a generation */
|
||||
|
||||
static Res amcGenCreate(amcGen *genReturn, AMC amc, Serial genNr)
|
||||
static Res amcGenCreate(amcGen *genReturn, AMC amc, GenDesc gen)
|
||||
{
|
||||
Arena arena;
|
||||
Buffer buffer;
|
||||
Pool pool;
|
||||
amcGen gen;
|
||||
amcGen amcgen;
|
||||
Res res;
|
||||
void *p;
|
||||
|
||||
|
|
@ -657,25 +663,24 @@ static Res amcGenCreate(amcGen *genReturn, AMC amc, Serial genNr)
|
|||
res = ControlAlloc(&p, arena, sizeof(amcGenStruct), FALSE);
|
||||
if(res != ResOK)
|
||||
goto failControlAlloc;
|
||||
gen = (amcGen)p;
|
||||
amcgen = (amcGen)p;
|
||||
|
||||
res = BufferCreate(&buffer, EnsureamcBufClass(), pool, FALSE, argsNone);
|
||||
if(res != ResOK)
|
||||
goto failBufferCreate;
|
||||
|
||||
res = PoolGenInit(&gen->pgen, amc->chain, genNr, pool);
|
||||
res = PoolGenInit(&amcgen->pgen, gen, pool);
|
||||
if(res != ResOK)
|
||||
goto failGenInit;
|
||||
RingInit(&gen->amcRing);
|
||||
gen->segs = 0;
|
||||
gen->forward = buffer;
|
||||
gen->sig = amcGenSig;
|
||||
RingInit(&amcgen->amcRing);
|
||||
amcgen->forward = buffer;
|
||||
amcgen->sig = amcGenSig;
|
||||
|
||||
AVERT(amcGen, gen);
|
||||
AVERT(amcGen, amcgen);
|
||||
|
||||
RingAppend(&amc->genRing, &gen->amcRing);
|
||||
EVENT2(AMCGenCreate, amc, gen);
|
||||
*genReturn = gen;
|
||||
RingAppend(&amc->genRing, &amcgen->amcRing);
|
||||
EVENT2(AMCGenCreate, amc, amcgen);
|
||||
*genReturn = amcgen;
|
||||
return ResOK;
|
||||
|
||||
failGenInit:
|
||||
|
|
@ -694,8 +699,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));
|
||||
|
|
@ -710,21 +713,16 @@ static void amcGenDestroy(amcGen gen)
|
|||
|
||||
/* amcGenDescribe -- describe an AMC generation */
|
||||
|
||||
static Res amcGenDescribe(amcGen gen, mps_lib_FILE *stream)
|
||||
static Res amcGenDescribe(amcGen amcgen, mps_lib_FILE *stream)
|
||||
{
|
||||
Res res;
|
||||
|
||||
if(!TESTT(amcGen, gen))
|
||||
if(!TESTT(amcGen, amcgen))
|
||||
return ResFAIL;
|
||||
|
||||
res = WriteF(stream,
|
||||
" amcGen $P ($U) {\n",
|
||||
(WriteFP)gen, (WriteFU)amcGenNr(gen),
|
||||
" buffer $P\n", gen->forward,
|
||||
" segs $U, totalSize $U, newSize $U\n",
|
||||
(WriteFU)gen->segs,
|
||||
(WriteFU)gen->pgen.totalSize,
|
||||
(WriteFU)gen->pgen.newSize,
|
||||
" amcGen $P {\n", (WriteFP)amcgen,
|
||||
" buffer $P\n", (WriteFP)amcgen->forward,
|
||||
" } amcGen\n", NULL);
|
||||
return res;
|
||||
}
|
||||
|
|
@ -801,6 +799,7 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args)
|
|||
size_t genArraySize;
|
||||
size_t genCount;
|
||||
Bool interior = AMC_INTERIOR_DEFAULT;
|
||||
Chain chain;
|
||||
ArgStruct arg;
|
||||
|
||||
/* Suppress a warning about this structure not being used when there
|
||||
|
|
@ -821,14 +820,14 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args)
|
|||
ArgRequire(&arg, args, MPS_KEY_FORMAT);
|
||||
pool->format = arg.val.format;
|
||||
if (ArgPick(&arg, args, MPS_KEY_CHAIN))
|
||||
amc->chain = arg.val.chain;
|
||||
chain = arg.val.chain;
|
||||
else
|
||||
amc->chain = ArenaGlobals(arena)->defaultChain;
|
||||
chain = ArenaGlobals(arena)->defaultChain;
|
||||
if (ArgPick(&arg, args, MPS_KEY_INTERIOR))
|
||||
interior = arg.val.b;
|
||||
|
||||
AVERT(Format, pool->format);
|
||||
AVERT(Chain, amc->chain);
|
||||
AVERT(Chain, chain);
|
||||
pool->alignment = pool->format->alignment;
|
||||
amc->rankSet = rankSet;
|
||||
|
||||
|
|
@ -864,7 +863,7 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args)
|
|||
AVERT(AMC, amc);
|
||||
|
||||
/* Init generations. */
|
||||
genCount = ChainGens(amc->chain);
|
||||
genCount = ChainGens(chain);
|
||||
{
|
||||
void *p;
|
||||
|
||||
|
|
@ -874,12 +873,14 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args)
|
|||
if(res != ResOK)
|
||||
goto failGensAlloc;
|
||||
amc->gen = p;
|
||||
for(i = 0; i < genCount + 1; ++i) {
|
||||
res = amcGenCreate(&amc->gen[i], amc, (Serial)i);
|
||||
if(res != ResOK) {
|
||||
for (i = 0; i <= genCount; ++i) {
|
||||
res = amcGenCreate(&amc->gen[i], amc, ChainGen(chain, i));
|
||||
if (res != ResOK)
|
||||
goto failGenAlloc;
|
||||
}
|
||||
}
|
||||
res = amcGenCreate(&amc->gen[genCount], amc, &arena->topGen);
|
||||
if (res != ResOK)
|
||||
goto failGenAlloc;
|
||||
/* Set up forwarding buffers. */
|
||||
for(i = 0; i < genCount; ++i) {
|
||||
amcBufSetGen(amc->gen[i]->forward, amc->gen[i+1]);
|
||||
|
|
@ -944,21 +945,20 @@ 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);
|
||||
amcSeg amcseg = Seg2amcSeg(seg);
|
||||
|
||||
--gen->segs;
|
||||
size = SegSize(seg);
|
||||
gen->pgen.totalSize -= size;
|
||||
|
||||
SegFree(seg);
|
||||
if (!amcseg->old) {
|
||||
PoolGenAge(&gen->pgen, SegSize(seg), amcseg->deferred);
|
||||
amcseg->old = TRUE;
|
||||
}
|
||||
PoolGenReclaim(&gen->pgen, SegSize(seg), amcseg->deferred);
|
||||
PoolGenFree(&gen->pgen, seg);
|
||||
}
|
||||
|
||||
/* Disassociate forwarding buffers from gens before they are */
|
||||
|
|
@ -994,7 +994,6 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn,
|
|||
amcGen gen;
|
||||
PoolGen pgen;
|
||||
amcBuf amcbuf;
|
||||
Bool isRamping;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
amc = Pool2AMC(pool);
|
||||
|
|
@ -1015,13 +1014,13 @@ 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) {
|
||||
MPS_ARGS_ADD_FIELD(args, amcKeySegGen, p, gen);
|
||||
res = ChainAlloc(&seg, amc->chain, PoolGenNr(pgen), amcSegClassGet(),
|
||||
alignedSize, pool, withReservoirPermit, args);
|
||||
res = PoolGenAlloc(&seg, pgen, amcSegClassGet(), alignedSize,
|
||||
withReservoirPermit, args);
|
||||
} MPS_ARGS_END(args);
|
||||
if(res != ResOK)
|
||||
return res;
|
||||
|
|
@ -1033,23 +1032,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);
|
||||
|
|
@ -1071,6 +1064,9 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn,
|
|||
ShieldCover(arena, seg);
|
||||
}
|
||||
}
|
||||
|
||||
PoolGenFill(pgen, SegSize(seg), Seg2amcSeg(seg)->deferred);
|
||||
*baseReturn = base;
|
||||
*limitReturn = limit;
|
||||
return ResOK;
|
||||
}
|
||||
|
|
@ -1113,6 +1109,8 @@ static void AMCBufferEmpty(Pool pool, Buffer buffer,
|
|||
(*pool->format->pad)(init, size);
|
||||
ShieldCover(arena, seg);
|
||||
}
|
||||
|
||||
PoolGenEmpty(&amcSegGen(seg)->pgen, 0, Seg2amcSeg(seg)->deferred);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1176,16 +1174,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1199,14 +1200,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) {
|
||||
|
|
@ -1262,14 +1266,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);
|
||||
|
|
@ -1293,9 +1297,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) {
|
||||
PoolGenAge(&gen->pgen, SegSize(seg), amcseg->deferred);
|
||||
amcseg->old = TRUE;
|
||||
}
|
||||
|
||||
/* Ensure we are forwarding into the right generation. */
|
||||
|
|
@ -1996,9 +2000,8 @@ 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);
|
||||
PoolGenReclaim(&gen->pgen, SegSize(seg), Seg2amcSeg(seg)->deferred);
|
||||
PoolGenFree(&gen->pgen, seg);
|
||||
} else {
|
||||
/* Seg retained */
|
||||
STATISTIC_STAT( {
|
||||
|
|
@ -2042,7 +2045,6 @@ static void AMCReclaim(Pool pool, Trace trace, Seg seg)
|
|||
{
|
||||
AMC amc;
|
||||
amcGen gen;
|
||||
Size size;
|
||||
|
||||
AVERT_CRITICAL(Pool, pool);
|
||||
amc = Pool2AMC(pool);
|
||||
|
|
@ -2075,13 +2077,10 @@ 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);
|
||||
PoolGenReclaim(&gen->pgen, SegSize(seg), Seg2amcSeg(seg)->deferred);
|
||||
PoolGenFree(&gen->pgen, seg);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
PoolGenSegMerge(&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);
|
||||
PoolGenSegSplit(&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)
|
||||
|
|
@ -691,14 +695,14 @@ static Res AMSSegCreate(Seg *segReturn, Pool pool, Size size,
|
|||
if (res != ResOK)
|
||||
goto failSize;
|
||||
|
||||
res = ChainAlloc(&seg, ams->chain, ams->pgen.nr, (*ams->segClass)(),
|
||||
prefSize, pool, withReservoirPermit, argsNone);
|
||||
res = PoolGenAlloc(&seg, &ams->pgen, (*ams->segClass)(), prefSize,
|
||||
withReservoirPermit, argsNone);
|
||||
if (res != ResOK) { /* try to allocate one that's just large enough */
|
||||
Size minSize = SizeAlignUp(size, ArenaAlign(arena));
|
||||
if (minSize == prefSize)
|
||||
goto failSeg;
|
||||
res = ChainAlloc(&seg, ams->chain, ams->pgen.nr, (*ams->segClass)(),
|
||||
prefSize, pool, withReservoirPermit, argsNone);
|
||||
res = PoolGenAlloc(&seg, &ams->pgen, (*ams->segClass)(), prefSize,
|
||||
withReservoirPermit, argsNone);
|
||||
if (res != ResOK)
|
||||
goto failSeg;
|
||||
}
|
||||
|
|
@ -731,9 +735,17 @@ 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);
|
||||
AVER(amsseg->ams == ams);
|
||||
AMSSegFreeCheck(amsseg);
|
||||
PoolGenAge(&ams->pgen, AMSGrainsSize(ams, amsseg->newGrains), FALSE);
|
||||
amsseg->oldGrains += amsseg->newGrains;
|
||||
amsseg->newGrains = 0;
|
||||
PoolGenReclaim(&ams->pgen, AMSGrainsSize(ams, amsseg->oldGrains), FALSE);
|
||||
amsseg->freeGrains += amsseg->oldGrains;
|
||||
amsseg->oldGrains = 0;
|
||||
AVER(amsseg->freeGrains == amsseg->grains);
|
||||
PoolGenFree(&ams->pgen, seg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -822,8 +834,7 @@ Res AMSInitInternal(AMS ams, Format format, Chain chain, unsigned gen,
|
|||
pool->alignment = pool->format->alignment;
|
||||
ams->grainShift = SizeLog2(PoolAlignment(pool));
|
||||
|
||||
ams->chain = chain;
|
||||
res = PoolGenInit(&ams->pgen, ams->chain, gen, pool);
|
||||
res = PoolGenInit(&ams->pgen, ChainGen(chain, gen), pool);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
|
|
@ -837,8 +848,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;
|
||||
|
|
@ -905,15 +914,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;
|
||||
|
|
@ -959,12 +970,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;
|
||||
|
|
@ -984,10 +998,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;
|
||||
PoolGenFill(&ams->pgen, allocatedSize, FALSE);
|
||||
*baseReturn = baseAddr;
|
||||
*limitReturn = limitAddr;
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
|
@ -1041,9 +1055,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
|
||||
|
|
@ -1062,20 +1076,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;
|
||||
PoolGenEmpty(&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);
|
||||
|
|
@ -1088,9 +1101,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;
|
||||
|
|
@ -1140,23 +1153,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. */
|
||||
PoolGenAge(&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));
|
||||
|
||||
|
|
@ -1562,8 +1576,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);
|
||||
|
|
@ -1608,21 +1621,22 @@ 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;
|
||||
PoolGenReclaim(&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);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1663,11 +1677,7 @@ 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,
|
||||
" chain $P\n",
|
||||
(WriteFP)ams->chain,
|
||||
NULL);
|
||||
if (res != ResOK) return res;
|
||||
|
||||
|
|
@ -1711,7 +1721,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;
|
||||
|
|
@ -1759,11 +1769,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(Chain, ams->chain);
|
||||
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));
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ typedef Res (*AMSSegSizePolicyFunction)(Size *sizeReturn,
|
|||
typedef struct AMSStruct {
|
||||
PoolStruct poolStruct; /* generic pool structure */
|
||||
Shift grainShift; /* log2 of grain size */
|
||||
Chain chain; /* chain used by this pool */
|
||||
PoolGenStruct pgen; /* generation representing the pool */
|
||||
Size size; /* total segment size of the pool */
|
||||
AMSSegSizePolicyFunction segSize; /* SegSize policy */
|
||||
|
|
@ -58,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 */
|
||||
|
|
|
|||
|
|
@ -84,9 +84,7 @@ typedef Addr (*FindDependentMethod)(Addr object);
|
|||
typedef struct AWLStruct {
|
||||
PoolStruct poolStruct;
|
||||
Shift alignShift;
|
||||
Chain chain; /* dummy chain */
|
||||
PoolGenStruct pgen; /* generation representing the pool */
|
||||
Size size; /* allocated size in bytes */
|
||||
Count succAccesses; /* number of successive single accesses */
|
||||
FindDependentMethod findDependent; /* to find a dependent object */
|
||||
awlStatTotalStruct stats;
|
||||
|
|
@ -94,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);
|
||||
|
|
@ -102,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
|
||||
|
|
@ -118,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;
|
||||
|
|
@ -139,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;
|
||||
}
|
||||
|
||||
|
|
@ -224,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;
|
||||
|
||||
|
|
@ -473,8 +477,8 @@ static Res AWLSegCreate(AWLSeg *awlsegReturn,
|
|||
return ResMEMORY;
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD_FIELD(args, awlKeySegRankSet, u, rankSet);
|
||||
res = ChainAlloc(&seg, awl->chain, awl->pgen.nr, AWLSegClassGet(),
|
||||
size, pool, reservoirPermit, args);
|
||||
res = PoolGenAlloc(&seg, &awl->pgen, AWLSegClassGet(), size,
|
||||
reservoirPermit, args);
|
||||
} MPS_ARGS_END(args);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
|
@ -501,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))
|
||||
|
|
@ -509,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;
|
||||
}
|
||||
|
||||
|
|
@ -570,15 +573,12 @@ static Res AWLInit(Pool pool, ArgList args)
|
|||
|
||||
AVERT(Chain, chain);
|
||||
AVER(gen <= ChainGens(chain));
|
||||
awl->chain = chain;
|
||||
|
||||
res = PoolGenInit(&awl->pgen, chain, gen, pool);
|
||||
res = PoolGenInit(&awl->pgen, ChainGen(chain, gen), pool);
|
||||
if (res != ResOK)
|
||||
goto failGenInit;
|
||||
|
||||
awl->alignShift = SizeLog2(PoolAlignment(pool));
|
||||
awl->size = (Size)0;
|
||||
|
||||
awl->succAccesses = 0;
|
||||
awlStatTotalInit(awl);
|
||||
awl->sig = AWLSig;
|
||||
|
|
@ -608,8 +608,15 @@ 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);
|
||||
PoolGenAge(&awl->pgen, AWLGrainsSize(awl, awlseg->newGrains), FALSE);
|
||||
awlseg->oldGrains += awlseg->newGrains;
|
||||
awlseg->newGrains = 0;
|
||||
PoolGenReclaim(&awl->pgen, AWLGrainsSize(awl, awlseg->oldGrains), FALSE);
|
||||
awlseg->freeGrains += awlseg->oldGrains;
|
||||
awlseg->oldGrains = 0;
|
||||
AVER(awlseg->freeGrains == awlseg->grains);
|
||||
PoolGenFree(&awl->pgen, seg);
|
||||
}
|
||||
awl->sig = SigInvalid;
|
||||
PoolGenFinish(&awl->pgen);
|
||||
|
|
@ -648,10 +655,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 */
|
||||
|
|
@ -675,7 +683,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;
|
||||
PoolGenFill(&awl->pgen, AddrOffset(base, limit), FALSE);
|
||||
}
|
||||
*baseReturn = base;
|
||||
*limitReturn = limit;
|
||||
|
|
@ -711,7 +722,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;
|
||||
PoolGenEmpty(&awl->pgen, AddrOffset(init, limit), FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -737,6 +751,7 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg)
|
|||
AWL awl;
|
||||
AWLSeg awlseg;
|
||||
Buffer buffer;
|
||||
Count uncondemned;
|
||||
|
||||
/* All parameters checked by generic PoolWhiten. */
|
||||
|
||||
|
|
@ -752,15 +767,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);
|
||||
|
||||
|
|
@ -771,14 +784,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));
|
||||
}
|
||||
|
||||
PoolGenAge(&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;
|
||||
}
|
||||
|
|
@ -1090,12 +1101,13 @@ 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);
|
||||
|
|
@ -1109,8 +1121,10 @@ 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;
|
||||
oldFree = awlseg->freeGrains;
|
||||
while(i < awlseg->grains) {
|
||||
Addr p, q;
|
||||
Index j;
|
||||
|
|
@ -1119,16 +1133,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);
|
||||
|
|
@ -1145,20 +1156,26 @@ 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;
|
||||
PoolGenReclaim(&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);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1306,8 +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);
|
||||
CHECKD(Chain, awl->chain);
|
||||
CHECKL(AWLGrainsSize(awl, 1) == awl->poolStruct.alignment);
|
||||
/* Nothing to check about succAccesses. */
|
||||
CHECKL(FUNCHECK(awl->findDependent));
|
||||
/* Don't bother to check stats. */
|
||||
|
|
|
|||
|
|
@ -24,13 +24,13 @@ typedef struct LOStruct *LO;
|
|||
typedef struct LOStruct {
|
||||
PoolStruct poolStruct; /* generic pool structure */
|
||||
Shift alignShift; /* log_2 of pool alignment */
|
||||
Chain chain; /* chain used by this pool */
|
||||
PoolGenStruct pgen; /* generation representing the pool */
|
||||
Sig sig;
|
||||
} LOStruct;
|
||||
|
||||
#define PoolPoolLO(pool) PARENT(LOStruct, poolStruct, pool)
|
||||
#define LOPool(lo) (&(lo)->poolStruct)
|
||||
#define LOGrainsSize(lo, grains) ((grains) << (lo)->alignShift)
|
||||
|
||||
|
||||
/* forward declaration */
|
||||
|
|
@ -48,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;
|
||||
|
||||
|
|
@ -61,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 */
|
||||
|
|
@ -88,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;
|
||||
}
|
||||
|
||||
|
|
@ -106,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);
|
||||
|
|
@ -126,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;
|
||||
|
|
@ -136,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;
|
||||
|
|
@ -163,7 +166,7 @@ static void loSegFinish(Seg seg)
|
|||
Pool pool;
|
||||
Arena arena;
|
||||
Size tablesize;
|
||||
Count bits;
|
||||
Count grains;
|
||||
|
||||
AVERT(Seg, seg);
|
||||
loseg = SegLOSeg(seg);
|
||||
|
|
@ -173,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;
|
||||
|
|
@ -205,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 */
|
||||
|
|
@ -214,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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -234,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);
|
||||
|
|
@ -249,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);
|
||||
|
|
@ -293,9 +294,9 @@ static Res loSegCreate(LOSeg *loSegReturn, Pool pool, Size size,
|
|||
lo = PoolPoolLO(pool);
|
||||
AVERT(LO, lo);
|
||||
|
||||
res = ChainAlloc(&seg, lo->chain, lo->pgen.nr, EnsureLOSegClass(),
|
||||
SizeAlignUp(size, ArenaAlign(PoolArena(pool))),
|
||||
pool, withReservoirPermit, argsNone);
|
||||
res = PoolGenAlloc(&seg, &lo->pgen, EnsureLOSegClass(),
|
||||
SizeAlignUp(size, ArenaAlign(PoolArena(pool))),
|
||||
withReservoirPermit, argsNone);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
|
|
@ -313,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;
|
||||
|
|
@ -371,23 +372,26 @@ 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;
|
||||
PoolGenReclaim(&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);
|
||||
}
|
||||
|
||||
/* This walks over _all_ objects in the heap, whether they are */
|
||||
|
|
@ -400,7 +404,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);
|
||||
|
|
@ -417,10 +421,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);
|
||||
|
|
@ -475,9 +479,11 @@ static Res LOInit(Pool pool, ArgList args)
|
|||
Arena arena;
|
||||
Res res;
|
||||
ArgStruct arg;
|
||||
Chain chain;
|
||||
unsigned gen = LO_GEN_DEFAULT;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
AVERT(ArgList, args);
|
||||
|
||||
arena = PoolArena(pool);
|
||||
|
||||
|
|
@ -486,22 +492,22 @@ static Res LOInit(Pool pool, ArgList args)
|
|||
ArgRequire(&arg, args, MPS_KEY_FORMAT);
|
||||
pool->format = arg.val.format;
|
||||
if (ArgPick(&arg, args, MPS_KEY_CHAIN))
|
||||
lo->chain = arg.val.chain;
|
||||
chain = arg.val.chain;
|
||||
else {
|
||||
lo->chain = ArenaGlobals(arena)->defaultChain;
|
||||
chain = ArenaGlobals(arena)->defaultChain;
|
||||
gen = 1; /* avoid the nursery of the default chain by default */
|
||||
}
|
||||
if (ArgPick(&arg, args, MPS_KEY_GEN))
|
||||
gen = arg.val.u;
|
||||
|
||||
AVERT(Format, pool->format);
|
||||
AVERT(Chain, lo->chain);
|
||||
AVER(gen <= ChainGens(lo->chain));
|
||||
AVERT(Chain, chain);
|
||||
AVER(gen <= ChainGens(chain));
|
||||
|
||||
pool->alignment = pool->format->alignment;
|
||||
lo->alignShift = SizeLog2((Size)PoolAlignment(pool));
|
||||
|
||||
res = PoolGenInit(&lo->pgen, lo->chain, gen, pool);
|
||||
res = PoolGenInit(&lo->pgen, ChainGen(chain, gen), pool);
|
||||
if (res != ResOK)
|
||||
goto failGenInit;
|
||||
|
||||
|
|
@ -533,7 +539,15 @@ static void LOFinish(Pool pool)
|
|||
|
||||
AVERT(LOSeg, loseg);
|
||||
UNUSED(loseg); /* <code/mpm.c#check.unused> */
|
||||
SegFree(seg);
|
||||
|
||||
PoolGenAge(&lo->pgen, LOGrainsSize(lo, loseg->newGrains), FALSE);
|
||||
loseg->oldGrains += loseg->newGrains;
|
||||
loseg->newGrains = 0;
|
||||
PoolGenReclaim(&lo->pgen, LOGrainsSize(lo, loseg->oldGrains), FALSE);
|
||||
loseg->freeGrains += loseg->oldGrains;
|
||||
loseg->oldGrains = 0;
|
||||
AVER(loseg->freeGrains == loSegGrains(loseg));
|
||||
PoolGenFree(&lo->pgen, seg);
|
||||
}
|
||||
PoolGenFinish(&lo->pgen);
|
||||
|
||||
|
|
@ -568,7 +582,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;
|
||||
}
|
||||
|
|
@ -593,12 +607,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);
|
||||
PoolGenFill(&lo->pgen, AddrOffset(base, limit), FALSE);
|
||||
|
||||
*baseReturn = base;
|
||||
*limitReturn = limit;
|
||||
|
|
@ -646,17 +660,14 @@ static void LOBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit)
|
|||
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;
|
||||
PoolGenEmpty(&lo->pgen, AddrOffset(init, limit), FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -666,7 +677,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);
|
||||
|
|
@ -676,21 +689,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);
|
||||
}
|
||||
|
||||
PoolGenAge(&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;
|
||||
}
|
||||
|
||||
|
|
@ -814,8 +838,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));
|
||||
CHECKD(Chain, lo->chain);
|
||||
CHECKL(LOGrainsSize(lo, 1) == PoolAlignment(&lo->poolStruct));
|
||||
CHECKD(PoolGen, &lo->pgen);
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -168,15 +171,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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -269,19 +278,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;
|
||||
|
|
@ -344,10 +363,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;
|
||||
|
|
@ -365,6 +386,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);
|
||||
|
|
@ -414,10 +436,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));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -607,14 +630,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)
|
||||
{
|
||||
|
|
@ -703,8 +719,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))
|
||||
|
|
@ -790,8 +805,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)
|
||||
{
|
||||
|
|
@ -800,18 +815,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;
|
||||
|
||||
|
|
@ -828,8 +834,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)
|
||||
{
|
||||
|
|
@ -840,27 +846,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));
|
||||
|
|
@ -1034,15 +1020,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);
|
||||
|
|
@ -1219,13 +1205,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(Bool *deleteReturn, Land land, Range range,
|
||||
void *closureP, Size closureS)
|
||||
{
|
||||
AVERT(ABQ, MVTABQ(mvt));
|
||||
AVERT(Range, range);
|
||||
MVT mvt;
|
||||
|
||||
AVER(deleteReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
mvt = closureP;
|
||||
AVERT(MVT, mvt);
|
||||
UNUSED(closureS);
|
||||
|
||||
if (RangeSize(range) < mvt->reuseSize)
|
||||
return TRUE;
|
||||
|
|
@ -1234,55 +1227,27 @@ 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);
|
||||
LandIterate(MVTFailover(mvt), &MVTRefillVisitor, mvt, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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;
|
||||
|
|
@ -1292,22 +1257,24 @@ typedef struct MVTContigencyStruct
|
|||
/* 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(Bool *deleteReturn, Land land, Range range,
|
||||
void *closureP, Size closureS)
|
||||
{
|
||||
MVT mvt;
|
||||
Size size;
|
||||
Addr base, limit;
|
||||
MVTContigencyClosure cl;
|
||||
|
||||
AVER(cl != NULL);
|
||||
AVER(deleteReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
AVERT(Range, range);
|
||||
AVER(closureP != NULL);
|
||||
cl = closureP;
|
||||
mvt = cl->mvt;
|
||||
AVERT(MVT, mvt);
|
||||
AVERT(Range, range);
|
||||
UNUSED(closureS);
|
||||
|
||||
base = RangeBase(range);
|
||||
limit = RangeLimit(range);
|
||||
|
|
@ -1336,32 +1303,10 @@ 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;
|
||||
|
|
@ -1370,11 +1315,7 @@ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn,
|
|||
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);
|
||||
LandIterate(MVTFailover(mvt), MVTContingencyVisitor, (void *)&cls, 0);
|
||||
if (!cls.found)
|
||||
return FALSE;
|
||||
|
||||
|
|
@ -1392,6 +1333,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;
|
||||
|
|
@ -1421,12 +1363,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);
|
||||
|
|
|
|||
|
|
@ -6,23 +6,30 @@
|
|||
*
|
||||
* .purpose: This is a pool class for manually managed objects of
|
||||
* variable size where address-ordered first fit is an appropriate
|
||||
* policy. Provision is made to allocate in reverse. This pool
|
||||
* can allocate across segment boundaries.
|
||||
* policy. Provision is made to allocate in reverse.
|
||||
*
|
||||
* .design: <design/poolmvff>
|
||||
*
|
||||
*
|
||||
* TRANSGRESSIONS
|
||||
*
|
||||
* .trans.stat: mps_mvff_stat is a temporary hack for measurement purposes,
|
||||
* see .stat below.
|
||||
*
|
||||
* NOTE
|
||||
*
|
||||
* There's potential for up to 4% speed improvement by calling Land
|
||||
* methods statically instead of indirectly via the Land abstraction
|
||||
* (thus, cbsInsert instead of LandInsert, and so on).
|
||||
*/
|
||||
|
||||
#include "mpscmvff.h"
|
||||
#include "dbgpool.h"
|
||||
#include "cbs.h"
|
||||
#include "dbgpool.h"
|
||||
#include "failover.h"
|
||||
#include "freelist.h"
|
||||
#include "mpm.h"
|
||||
#include "mpscmvff.h"
|
||||
#include "mpscmfs.h"
|
||||
#include "poolmfs.h"
|
||||
|
||||
SRCID(poolmvff, "$Id$");
|
||||
|
||||
|
|
@ -42,26 +49,29 @@ extern PoolClass PoolClassMVFF(void);
|
|||
typedef struct MVFFStruct *MVFF;
|
||||
typedef struct MVFFStruct { /* MVFF pool outer structure */
|
||||
PoolStruct poolStruct; /* generic structure */
|
||||
SegPref segPref; /* the preferences for segments */
|
||||
Size extendBy; /* segment size to extend pool by */
|
||||
Size minSegSize; /* minimum size of segment */
|
||||
SegPrefStruct segPrefStruct; /* the preferences for allocation */
|
||||
Size extendBy; /* size to extend pool by */
|
||||
Size avgSize; /* client estimate of allocation size */
|
||||
Size total; /* total bytes in pool */
|
||||
Size free; /* total free bytes in pool */
|
||||
CBSStruct cbsStruct; /* free list */
|
||||
double spare; /* spare space fraction, see MVFFReduce */
|
||||
MFSStruct cbsBlockPoolStruct; /* stores blocks for CBSs */
|
||||
CBSStruct totalCBSStruct; /* all memory allocated from the arena */
|
||||
CBSStruct freeCBSStruct; /* 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/> */
|
||||
} MVFFStruct;
|
||||
|
||||
|
||||
#define Pool2MVFF(pool) PARENT(MVFFStruct, poolStruct, pool)
|
||||
#define MVFF2Pool(mvff) (&((mvff)->poolStruct))
|
||||
#define CBSOfMVFF(mvff) (&((mvff)->cbsStruct))
|
||||
#define MVFFOfCBS(cbs) PARENT(MVFFStruct, cbsStruct, cbs)
|
||||
#define FreelistOfMVFF(mvff) (&((mvff)->flStruct))
|
||||
#define MVFFOfFreelist(fl) PARENT(MVFFStruct, flStruct, fl)
|
||||
#define Pool2MVFF(pool) PARENT(MVFFStruct, poolStruct, pool)
|
||||
#define MVFF2Pool(mvff) (&((mvff)->poolStruct))
|
||||
#define MVFFTotalCBS(mvff) (&((mvff)->totalCBSStruct.landStruct))
|
||||
#define MVFFFreeCBS(mvff) (&((mvff)->freeCBSStruct.landStruct))
|
||||
#define MVFFFreelist(mvff) (&((mvff)->flStruct.landStruct))
|
||||
#define MVFFFailover(mvff) (&((mvff)->foStruct.landStruct))
|
||||
#define MVFFSegPref(mvff) (&((mvff)->segPrefStruct))
|
||||
#define MVFFBlockPool(mvff) (&((mvff)->cbsBlockPoolStruct.poolStruct))
|
||||
|
||||
static Bool MVFFCheck(MVFF mvff);
|
||||
|
||||
|
|
@ -80,151 +90,120 @@ typedef MVFFDebugStruct *MVFFDebug;
|
|||
#define MVFFDebug2MVFF(mvffd) (&((mvffd)->mvffStruct))
|
||||
|
||||
|
||||
/* MVFFAddToFreeList -- Add given range to free list
|
||||
/* MVFFReduce -- free tracts from given range
|
||||
*
|
||||
* Updates MVFF counters for additional free space. Returns maximally
|
||||
* coalesced range containing given range. Does not attempt to free
|
||||
* segments (see MVFFFreeSegs).
|
||||
* Given a free range, attempts to find entire tracts within it, and
|
||||
* returns them to the arena.
|
||||
*
|
||||
* This is usually called immediately after MVFFInsert. It is not
|
||||
* combined with MVFFInsert because the latter is also called when new
|
||||
* segments are added under MVFFAlloc.
|
||||
*/
|
||||
static Res MVFFAddToFreeList(Addr *baseIO, Addr *limitIO, MVFF mvff) {
|
||||
Res res;
|
||||
RangeStruct range, newRange;
|
||||
|
||||
AVER(baseIO != NULL);
|
||||
AVER(limitIO != NULL);
|
||||
AVERT(MVFF, mvff);
|
||||
RangeInit(&range, *baseIO, *limitIO);
|
||||
|
||||
res = CBSInsert(&newRange, CBSOfMVFF(mvff), &range);
|
||||
if (ResIsAllocFailure(res)) {
|
||||
/* CBS ran out of memory for splay nodes: add range to emergency
|
||||
* free list instead. */
|
||||
res = FreelistInsert(&newRange, FreelistOfMVFF(mvff), &range);
|
||||
}
|
||||
|
||||
if (res == ResOK) {
|
||||
mvff->free += RangeSize(&range);
|
||||
*baseIO = RangeBase(&newRange);
|
||||
*limitIO = RangeLimit(&newRange);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* MVFFFreeSegs -- Free segments from given range
|
||||
*
|
||||
* Given a free range, attempts to find entire segments within
|
||||
* it, and returns them to the arena, updating total size counter.
|
||||
*
|
||||
* This is usually called immediately after MVFFAddToFreeList.
|
||||
* It is not combined with MVFFAddToFreeList because the latter
|
||||
* is also called when new segments are added under MVFFAlloc.
|
||||
*/
|
||||
static void MVFFFreeSegs(MVFF mvff, Addr base, Addr limit)
|
||||
static void MVFFReduce(MVFF mvff)
|
||||
{
|
||||
Seg seg = NULL; /* suppress "may be used uninitialized" */
|
||||
Arena arena;
|
||||
Bool b;
|
||||
Addr segLimit; /* limit of the current segment when iterating */
|
||||
Addr segBase; /* base of the current segment when iterating */
|
||||
Res res;
|
||||
Size freeSize, freeLimit, targetFree;
|
||||
RangeStruct freeRange, oldFreeRange;
|
||||
Align align;
|
||||
|
||||
AVERT(MVFF, mvff);
|
||||
AVER(base < limit);
|
||||
/* Could profitably AVER that the given range is free, */
|
||||
/* but the CBS doesn't provide that facility. */
|
||||
|
||||
if (AddrOffset(base, limit) < mvff->minSegSize)
|
||||
return; /* not large enough for entire segments */
|
||||
|
||||
arena = PoolArena(MVFF2Pool(mvff));
|
||||
b = SegOfAddr(&seg, arena, base);
|
||||
AVER(b);
|
||||
align = ArenaAlign(arena);
|
||||
|
||||
segBase = SegBase(seg);
|
||||
segLimit = SegLimit(seg);
|
||||
/* Try to return memory when the amount of free memory exceeds a
|
||||
threshold fraction of the total memory. */
|
||||
|
||||
/* NOTE: If this code becomes very hot, then the test of whether there's
|
||||
a large free block in the CBS could be inlined, since it's a property
|
||||
stored at the root node. */
|
||||
|
||||
while(segLimit <= limit) { /* segment ends in range */
|
||||
if (segBase >= base) { /* segment starts in range */
|
||||
RangeStruct range, oldRange;
|
||||
RangeInit(&range, segBase, segLimit);
|
||||
freeLimit = (Size)(LandSize(MVFFTotalCBS(mvff)) * mvff->spare);
|
||||
freeSize = LandSize(MVFFFailover(mvff));
|
||||
if (freeSize < freeLimit)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
targetFree = freeLimit / 2;
|
||||
|
||||
AVER(res == ResOK);
|
||||
AVER(RangesNest(&oldRange, &range));
|
||||
/* Each time around this loop we either break, or we free at least
|
||||
one page back to the arena, thus ensuring that eventually the
|
||||
loop will terminate */
|
||||
|
||||
/* 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);
|
||||
}
|
||||
while (freeSize > targetFree
|
||||
&& LandFindLargest(&freeRange, &oldFreeRange, MVFFFailover(mvff),
|
||||
align, FindDeleteNONE))
|
||||
{
|
||||
RangeStruct pageRange, oldRange;
|
||||
Size size;
|
||||
Res res;
|
||||
Addr base, limit;
|
||||
|
||||
/* Avoid calling SegNext if the next segment would fail */
|
||||
/* the loop test, mainly because there might not be a */
|
||||
/* next segment. */
|
||||
if (segLimit == limit) /* segment ends at end of range */
|
||||
AVER(RangesEqual(&freeRange, &oldFreeRange));
|
||||
|
||||
base = AddrAlignUp(RangeBase(&freeRange), align);
|
||||
limit = AddrAlignDown(RangeLimit(&freeRange), align);
|
||||
|
||||
/* Give up if the block is too small to contain a whole page when
|
||||
aligned, even though it might be masking smaller better aligned
|
||||
pages that we could return, because CBSFindLargest won't be able
|
||||
to find those. */
|
||||
if (base >= limit)
|
||||
break;
|
||||
|
||||
b = SegFindAboveAddr(&seg, arena, segBase);
|
||||
AVER(b);
|
||||
segBase = SegBase(seg);
|
||||
segLimit = SegLimit(seg);
|
||||
}
|
||||
size = AddrOffset(base, limit);
|
||||
|
||||
return;
|
||||
/* Don't return (much) more than we need to. */
|
||||
if (size > freeSize - targetFree)
|
||||
size = SizeAlignUp(freeSize - targetFree, align);
|
||||
|
||||
/* Calculate the range of pages we can return to the arena near the
|
||||
top end of the free memory (because we're first fit). */
|
||||
RangeInit(&pageRange, AddrSub(limit, size), limit);
|
||||
AVER(!RangeIsEmpty(&pageRange));
|
||||
AVER(RangesNest(&freeRange, &pageRange));
|
||||
AVER(RangeIsAligned(&pageRange, align));
|
||||
|
||||
/* Delete the range from the free list before attempting to delete
|
||||
it from the total allocated memory, so that we don't have
|
||||
dangling blocks in the free list, even for a moment. If we fail
|
||||
to delete from the TotalCBS we add back to the free list, which
|
||||
can't fail. */
|
||||
|
||||
res = LandDelete(&oldRange, MVFFFailover(mvff), &pageRange);
|
||||
if (res != ResOK)
|
||||
break;
|
||||
freeSize -= RangeSize(&pageRange);
|
||||
AVER(freeSize == LandSize(MVFFFailover(mvff)));
|
||||
|
||||
res = LandDelete(&oldRange, MVFFTotalCBS(mvff), &pageRange);
|
||||
if (res != ResOK) {
|
||||
RangeStruct coalescedRange;
|
||||
res = LandInsert(&coalescedRange, MVFFFailover(mvff), &pageRange);
|
||||
AVER(res == ResOK);
|
||||
break;
|
||||
}
|
||||
|
||||
ArenaFree(RangeBase(&pageRange), RangeSize(&pageRange), MVFF2Pool(mvff));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* MVFFAddSeg -- Allocates a new segment from the arena
|
||||
/* MVFFExtend -- allocate a new range 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.
|
||||
* Allocate a new range from the arena (with the given
|
||||
* withReservoirPermit flag) of at least the specified size. The
|
||||
* specified size should be pool-aligned. Add it to the allocated and
|
||||
* free lists.
|
||||
*/
|
||||
static Res MVFFAddSeg(Seg *segReturn,
|
||||
MVFF mvff, Size size, Bool withReservoirPermit)
|
||||
static Res MVFFExtend(Range rangeReturn, MVFF mvff, Size size,
|
||||
Bool withReservoirPermit)
|
||||
{
|
||||
Pool pool;
|
||||
Arena arena;
|
||||
Size segSize;
|
||||
Seg seg;
|
||||
Res res;
|
||||
Size allocSize;
|
||||
Align align;
|
||||
Addr base, limit;
|
||||
RangeStruct range, coalescedRange;
|
||||
Addr base;
|
||||
Res res;
|
||||
|
||||
AVERT(MVFF, mvff);
|
||||
AVER(size > 0);
|
||||
|
|
@ -239,85 +218,67 @@ static Res MVFFAddSeg(Seg *segReturn,
|
|||
/* Use extendBy unless it's too small (see */
|
||||
/* <design/poolmvff/#design.seg-size>). */
|
||||
if (size <= mvff->extendBy)
|
||||
segSize = mvff->extendBy;
|
||||
allocSize = mvff->extendBy;
|
||||
else
|
||||
segSize = size;
|
||||
allocSize = size;
|
||||
|
||||
segSize = SizeAlignUp(segSize, align);
|
||||
allocSize = SizeAlignUp(allocSize, align);
|
||||
|
||||
res = SegAlloc(&seg, SegClassGet(), mvff->segPref, segSize, pool,
|
||||
withReservoirPermit, argsNone);
|
||||
res = ArenaAlloc(&base, MVFFSegPref(mvff), allocSize, pool, withReservoirPermit);
|
||||
if (res != ResOK) {
|
||||
/* try again for a seg just large enough for object */
|
||||
/* try again with a range just large enough for object */
|
||||
/* see <design/poolmvff/#design.seg-fail> */
|
||||
segSize = SizeAlignUp(size, align);
|
||||
res = SegAlloc(&seg, SegClassGet(), mvff->segPref, segSize, pool,
|
||||
withReservoirPermit, argsNone);
|
||||
if (res != ResOK) {
|
||||
allocSize = SizeAlignUp(size, align);
|
||||
res = ArenaAlloc(&base, MVFFSegPref(mvff), allocSize, pool,
|
||||
withReservoirPermit);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
mvff->total += segSize;
|
||||
base = SegBase(seg);
|
||||
limit = AddrAdd(base, segSize);
|
||||
DebugPoolFreeSplat(pool, base, limit);
|
||||
res = MVFFAddToFreeList(&base, &limit, mvff);
|
||||
RangeInitSize(&range, base, allocSize);
|
||||
res = LandInsert(&coalescedRange, MVFFTotalCBS(mvff), &range);
|
||||
if (res != ResOK) {
|
||||
/* Can't record this memory, so return it to the arena and fail. */
|
||||
ArenaFree(base, allocSize, pool);
|
||||
return res;
|
||||
}
|
||||
|
||||
DebugPoolFreeSplat(pool, RangeBase(&range), RangeLimit(&range));
|
||||
res = LandInsert(rangeReturn, MVFFFailover(mvff), &range);
|
||||
AVER(res == ResOK);
|
||||
AVER(base <= SegBase(seg));
|
||||
if (mvff->minSegSize > segSize) mvff->minSegSize = segSize;
|
||||
|
||||
/* Don't call MVFFFreeSegs; that would be silly. */
|
||||
/* Don't call MVFFReduce; that would be silly. */
|
||||
|
||||
*segReturn = seg;
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* 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, MVFFFailover(mvff), size, findDelete);
|
||||
|
||||
return foundBlock;
|
||||
}
|
||||
|
|
@ -330,7 +291,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 +304,23 @@ 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;
|
||||
RangeStruct addRange;
|
||||
|
||||
res = MVFFAddSeg(&seg, mvff, size, withReservoirPermit);
|
||||
res = MVFFExtend(&addRange, 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);
|
||||
|
||||
/* We also know that the found range is no larger than the segment. */
|
||||
AVER(SegSize(seg) >= AddrOffset(base, limit));
|
||||
AVER(foundBlock && RangesOverlap(&range, &addRange));
|
||||
}
|
||||
AVER(foundBlock);
|
||||
AVER(AddrOffset(base, limit) == size);
|
||||
AVER(RangeSize(&range) == size);
|
||||
|
||||
*aReturn = base;
|
||||
*aReturn = RangeBase(&range);
|
||||
|
||||
return ResOK;
|
||||
}
|
||||
|
|
@ -376,7 +331,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, coalescedRange;
|
||||
MVFF mvff;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
|
|
@ -387,46 +342,17 @@ 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);
|
||||
|
||||
res = MVFFAddToFreeList(&base, &limit, mvff);
|
||||
RangeInitSize(&range, old, SizeAlignUp(size, PoolAlignment(pool)));
|
||||
res = LandInsert(&coalescedRange, MVFFFailover(mvff), &range);
|
||||
AVER(res == ResOK);
|
||||
if (res == ResOK)
|
||||
MVFFFreeSegs(mvff, base, limit);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* MVFFFindLargest -- call CBSFindLargest and then fall back to
|
||||
* FreelistFindLargest if no block in the CBS was big enough. */
|
||||
|
||||
static Bool MVFFFindLargest(Range range, Range oldRange, MVFF mvff,
|
||||
Size size, FindDelete findDelete)
|
||||
{
|
||||
AVER(range != NULL);
|
||||
AVER(oldRange != NULL);
|
||||
AVERT(MVFF, mvff);
|
||||
AVER(size > 0);
|
||||
AVERT(FindDelete, findDelete);
|
||||
|
||||
FreelistFlushToCBS(FreelistOfMVFF(mvff), CBSOfMVFF(mvff));
|
||||
|
||||
if (CBSFindLargest(range, oldRange, CBSOfMVFF(mvff), size, findDelete))
|
||||
return TRUE;
|
||||
|
||||
if (FreelistFindLargest(range, oldRange, FreelistOfMVFF(mvff),
|
||||
size, findDelete))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
MVFFReduce(mvff);
|
||||
}
|
||||
|
||||
|
||||
/* MVFFBufferFill -- Fill the buffer
|
||||
*
|
||||
* Fill it with the largest block we can find.
|
||||
* Fill it with the largest block we can find. This is worst-fit
|
||||
* allocation policy; see <design/poolmvff/#over.buffer>.
|
||||
*/
|
||||
static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn,
|
||||
Pool pool, Buffer buffer, Size size,
|
||||
|
|
@ -434,9 +360,8 @@ static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn,
|
|||
{
|
||||
Res res;
|
||||
MVFF mvff;
|
||||
RangeStruct range, oldRange;
|
||||
RangeStruct range, oldRange, newRange;
|
||||
Bool found;
|
||||
Seg seg = NULL;
|
||||
AVER(baseReturn != NULL);
|
||||
AVER(limitReturn != NULL);
|
||||
AVERT(Pool, pool);
|
||||
|
|
@ -447,18 +372,20 @@ 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, MVFFFailover(mvff), size,
|
||||
FindDeleteENTIRE);
|
||||
if (!found) {
|
||||
/* Add a new segment to the free list and try again. */
|
||||
res = MVFFAddSeg(&seg, mvff, size, withReservoirPermit);
|
||||
/* Add a new range to the free lists and try again. */
|
||||
res = MVFFExtend(&newRange, mvff, size, withReservoirPermit);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
found = MVFFFindLargest(&range, &oldRange, mvff, size, FindDeleteENTIRE);
|
||||
found = LandFindLargest(&range, &oldRange, MVFFFailover(mvff), size,
|
||||
FindDeleteENTIRE);
|
||||
AVER(found && RangesOverlap(&range, &newRange));
|
||||
}
|
||||
AVER(found);
|
||||
|
||||
AVER(RangesEqual(&range, &oldRange));
|
||||
AVER(RangeSize(&range) >= size);
|
||||
mvff->free -= RangeSize(&range);
|
||||
|
||||
*baseReturn = RangeBase(&range);
|
||||
*limitReturn = RangeLimit(&range);
|
||||
|
|
@ -473,23 +400,21 @@ static void MVFFBufferEmpty(Pool pool, Buffer buffer,
|
|||
{
|
||||
Res res;
|
||||
MVFF mvff;
|
||||
RangeStruct range, coalescedRange;
|
||||
|
||||
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 = LandInsert(&coalescedRange, MVFFFailover(mvff), &range);
|
||||
AVER(res == ResOK);
|
||||
if (res == ResOK)
|
||||
MVFFFreeSegs(mvff, base, limit);
|
||||
|
||||
return;
|
||||
MVFFReduce(mvff);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -535,10 +460,10 @@ static Res MVFFInit(Pool pool, ArgList args)
|
|||
Bool slotHigh = MVFF_SLOT_HIGH_DEFAULT;
|
||||
Bool arenaHigh = MVFF_ARENA_HIGH_DEFAULT;
|
||||
Bool firstFit = MVFF_FIRST_FIT_DEFAULT;
|
||||
double spare = MVFF_SPARE_DEFAULT;
|
||||
MVFF mvff;
|
||||
Arena arena;
|
||||
Res res;
|
||||
void *p;
|
||||
ArgStruct arg;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
|
|
@ -558,6 +483,9 @@ static Res MVFFInit(Pool pool, ArgList args)
|
|||
if (ArgPick(&arg, args, MPS_KEY_ALIGN))
|
||||
align = arg.val.align;
|
||||
|
||||
if (ArgPick(&arg, args, MPS_KEY_SPARE))
|
||||
spare = arg.val.d;
|
||||
|
||||
if (ArgPick(&arg, args, MPS_KEY_MVFF_SLOT_HIGH))
|
||||
slotHigh = arg.val.b;
|
||||
|
||||
|
|
@ -570,6 +498,8 @@ static Res MVFFInit(Pool pool, ArgList args)
|
|||
AVER(extendBy > 0); /* .arg.check */
|
||||
AVER(avgSize > 0); /* .arg.check */
|
||||
AVER(avgSize <= extendBy); /* .arg.check */
|
||||
AVER(spare >= 0.0); /* .arg.check */
|
||||
AVER(spare <= 1.0); /* .arg.check */
|
||||
AVER(SizeIsAligned(align, MPS_PF_ALIGN));
|
||||
AVERT(Bool, slotHigh);
|
||||
AVERT(Bool, arenaHigh);
|
||||
|
|
@ -579,33 +509,56 @@ static Res MVFFInit(Pool pool, ArgList args)
|
|||
|
||||
mvff->extendBy = extendBy;
|
||||
if (extendBy < ArenaAlign(arena))
|
||||
mvff->minSegSize = ArenaAlign(arena);
|
||||
else
|
||||
mvff->minSegSize = extendBy;
|
||||
mvff->extendBy = ArenaAlign(arena);
|
||||
mvff->avgSize = avgSize;
|
||||
pool->alignment = align;
|
||||
mvff->slotHigh = slotHigh;
|
||||
mvff->firstFit = firstFit;
|
||||
mvff->spare = spare;
|
||||
|
||||
res = ControlAlloc(&p, arena, sizeof(SegPrefStruct), FALSE);
|
||||
SegPrefInit(MVFFSegPref(mvff));
|
||||
SegPrefExpress(MVFFSegPref(mvff), arenaHigh ? SegPrefHigh : SegPrefLow, NULL);
|
||||
|
||||
/* An MFS pool is explicitly initialised for the two CBSs partly to
|
||||
* share space, but mostly to avoid a call to PoolCreate, so that
|
||||
* MVFF can be used during arena bootstrap as the control pool. */
|
||||
|
||||
MPS_ARGS_BEGIN(piArgs) {
|
||||
MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSFastBlockStruct));
|
||||
res = PoolInit(MVFFBlockPool(mvff), arena, PoolClassMFS(), piArgs);
|
||||
} MPS_ARGS_END(piArgs);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
goto failBlockPoolInit;
|
||||
|
||||
mvff->segPref = (SegPref)p;
|
||||
SegPrefInit(mvff->segPref);
|
||||
SegPrefExpress(mvff->segPref, arenaHigh ? SegPrefHigh : SegPrefLow, NULL);
|
||||
|
||||
mvff->total = 0;
|
||||
mvff->free = 0;
|
||||
|
||||
res = FreelistInit(FreelistOfMVFF(mvff), align);
|
||||
MPS_ARGS_BEGIN(liArgs) {
|
||||
MPS_ARGS_ADD(liArgs, CBSBlockPool, MVFFBlockPool(mvff));
|
||||
res = LandInit(MVFFTotalCBS(mvff), CBSFastLandClassGet(), arena, align,
|
||||
mvff, liArgs);
|
||||
} MPS_ARGS_END(liArgs);
|
||||
if (res != ResOK)
|
||||
goto failInit;
|
||||
goto failTotalCBSInit;
|
||||
|
||||
res = CBSInit(CBSOfMVFF(mvff), arena, (void *)mvff, align,
|
||||
/* fastFind */ TRUE, /* zoned */ FALSE, args);
|
||||
MPS_ARGS_BEGIN(liArgs) {
|
||||
MPS_ARGS_ADD(liArgs, CBSBlockPool, MVFFBlockPool(mvff));
|
||||
res = LandInit(MVFFFreeCBS(mvff), CBSFastLandClassGet(), arena, align,
|
||||
mvff, liArgs);
|
||||
} MPS_ARGS_END(liArgs);
|
||||
if (res != ResOK)
|
||||
goto failInit;
|
||||
goto failFreeCBSInit;
|
||||
|
||||
res = LandInit(MVFFFreelist(mvff), FreelistLandClassGet(), arena, align,
|
||||
mvff, mps_args_none);
|
||||
if (res != ResOK)
|
||||
goto failFreelistInit;
|
||||
|
||||
MPS_ARGS_BEGIN(foArgs) {
|
||||
MPS_ARGS_ADD(foArgs, FailoverPrimary, MVFFFreeCBS(mvff));
|
||||
MPS_ARGS_ADD(foArgs, FailoverSecondary, MVFFFreelist(mvff));
|
||||
res = LandInit(MVFFFailover(mvff), FailoverLandClassGet(), arena, align,
|
||||
mvff, foArgs);
|
||||
} MPS_ARGS_END(foArgs);
|
||||
if (res != ResOK)
|
||||
goto failFailoverInit;
|
||||
|
||||
mvff->sig = MVFFSig;
|
||||
AVERT(MVFF, mvff);
|
||||
|
|
@ -613,43 +566,57 @@ static Res MVFFInit(Pool pool, ArgList args)
|
|||
BOOLOF(slotHigh), BOOLOF(arenaHigh), BOOLOF(firstFit));
|
||||
return ResOK;
|
||||
|
||||
failInit:
|
||||
ControlFree(arena, p, sizeof(SegPrefStruct));
|
||||
failFailoverInit:
|
||||
LandFinish(MVFFFreelist(mvff));
|
||||
failFreelistInit:
|
||||
LandFinish(MVFFFreeCBS(mvff));
|
||||
failFreeCBSInit:
|
||||
LandFinish(MVFFTotalCBS(mvff));
|
||||
failTotalCBSInit:
|
||||
PoolFinish(MVFFBlockPool(mvff));
|
||||
failBlockPoolInit:
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* MVFFFinish -- finish method for MVFF */
|
||||
|
||||
static Bool mvffFinishVisitor(Bool *deleteReturn, Land land, Range range,
|
||||
void *closureP, Size closureS)
|
||||
{
|
||||
Pool pool;
|
||||
AVER(deleteReturn != NULL);
|
||||
AVERT(Land, land);
|
||||
AVERT(Range, range);
|
||||
AVER(closureP != NULL);
|
||||
pool = closureP;
|
||||
AVERT(Pool, pool);
|
||||
UNUSED(closureS);
|
||||
|
||||
ArenaFree(RangeBase(range), RangeSize(range), pool);
|
||||
*deleteReturn = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void MVFFFinish(Pool pool)
|
||||
{
|
||||
MVFF mvff;
|
||||
Arena arena;
|
||||
Seg seg;
|
||||
Ring ring, node, nextNode;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
mvff = Pool2MVFF(pool);
|
||||
AVERT(MVFF, mvff);
|
||||
|
||||
ring = PoolSegRing(pool);
|
||||
RING_FOR(node, ring, nextNode) {
|
||||
seg = SegOfPoolRing(node);
|
||||
AVER(SegPool(seg) == pool);
|
||||
SegFree(seg);
|
||||
}
|
||||
|
||||
/* Could maintain mvff->total here and check it falls to zero, */
|
||||
/* but that would just make the function slow. If only we had */
|
||||
/* a way to do operations only if AVERs are turned on. */
|
||||
|
||||
arena = PoolArena(pool);
|
||||
ControlFree(arena, mvff->segPref, sizeof(SegPrefStruct));
|
||||
|
||||
CBSFinish(CBSOfMVFF(mvff));
|
||||
FreelistFinish(FreelistOfMVFF(mvff));
|
||||
|
||||
mvff->sig = SigInvalid;
|
||||
|
||||
LandIterate(MVFFTotalCBS(mvff), mvffFinishVisitor, pool, 0);
|
||||
|
||||
/* TODO: would like to check that LandSize(MVFFTotalCBS(mvff)) == 0
|
||||
* now, but CBS doesn't support deletion while iterating. */
|
||||
|
||||
LandFinish(MVFFFailover(mvff));
|
||||
LandFinish(MVFFFreelist(mvff));
|
||||
LandFinish(MVFFFreeCBS(mvff));
|
||||
LandFinish(MVFFTotalCBS(mvff));
|
||||
PoolFinish(MVFFBlockPool(mvff));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -685,22 +652,26 @@ static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream)
|
|||
(WriteFP)pool, (WriteFU)pool->serial,
|
||||
" extendBy $W\n", (WriteFW)mvff->extendBy,
|
||||
" avgSize $W\n", (WriteFW)mvff->avgSize,
|
||||
" total $U\n", (WriteFU)mvff->total,
|
||||
" free $U\n", (WriteFU)mvff->free,
|
||||
" firstFit $U\n", (WriteFU)mvff->firstFit,
|
||||
" slotHigh $U\n", (WriteFU)mvff->slotHigh,
|
||||
NULL);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
if (res != ResOK) return res;
|
||||
|
||||
res = CBSDescribe(CBSOfMVFF(mvff), stream);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
/* TODO: SegPrefDescribe(MVFFSegPref(mvff), stream); */
|
||||
|
||||
res = FreelistDescribe(FreelistOfMVFF(mvff), stream);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
res = PoolDescribe(MVFFBlockPool(mvff), stream);
|
||||
if (res != ResOK) return res;
|
||||
|
||||
res = LandDescribe(MVFFTotalCBS(mvff), stream);
|
||||
if (res != ResOK) return res;
|
||||
|
||||
res = LandDescribe(MVFFFreeCBS(mvff), stream);
|
||||
if (res != ResOK) return res;
|
||||
|
||||
res = LandDescribe(MVFFFreelist(mvff), stream);
|
||||
if (res != ResOK) return res;
|
||||
|
||||
res = WriteF(stream, "}\n", NULL);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -770,7 +741,7 @@ size_t mps_mvff_free_size(mps_pool_t mps_pool)
|
|||
mvff = Pool2MVFF(pool);
|
||||
AVERT(MVFF, mvff);
|
||||
|
||||
return (size_t)mvff->free;
|
||||
return (size_t)LandSize(MVFFFailover(mvff));
|
||||
}
|
||||
|
||||
/* Total owned bytes. See <design/poolmvff/#design.arena-enter> */
|
||||
|
|
@ -785,7 +756,7 @@ size_t mps_mvff_size(mps_pool_t mps_pool)
|
|||
mvff = Pool2MVFF(pool);
|
||||
AVERT(MVFF, mvff);
|
||||
|
||||
return (size_t)mvff->total;
|
||||
return (size_t)LandSize(MVFFTotalCBS(mvff));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -797,16 +768,19 @@ static Bool MVFFCheck(MVFF mvff)
|
|||
CHECKS(MVFF, mvff);
|
||||
CHECKD(Pool, MVFF2Pool(mvff));
|
||||
CHECKL(IsSubclassPoly(MVFF2Pool(mvff)->class, MVFFPoolClassGet()));
|
||||
CHECKD(SegPref, mvff->segPref);
|
||||
CHECKL(mvff->extendBy > 0); /* see .arg.check */
|
||||
CHECKL(mvff->minSegSize >= ArenaAlign(PoolArena(MVFF2Pool(mvff))));
|
||||
CHECKD(SegPref, MVFFSegPref(mvff));
|
||||
CHECKL(mvff->extendBy >= ArenaAlign(PoolArena(MVFF2Pool(mvff))));
|
||||
CHECKL(mvff->avgSize > 0); /* see .arg.check */
|
||||
CHECKL(mvff->avgSize <= mvff->extendBy); /* see .arg.check */
|
||||
CHECKL(mvff->total >= mvff->free);
|
||||
CHECKL(SizeIsAligned(mvff->free, PoolAlignment(MVFF2Pool(mvff))));
|
||||
CHECKL(SizeIsAligned(mvff->total, ArenaAlign(PoolArena(MVFF2Pool(mvff)))));
|
||||
CHECKD(CBS, CBSOfMVFF(mvff));
|
||||
CHECKD(Freelist, FreelistOfMVFF(mvff));
|
||||
CHECKL(mvff->spare >= 0.0); /* see .arg.check */
|
||||
CHECKL(mvff->spare <= 1.0); /* see .arg.check */
|
||||
CHECKD(Land, MVFFTotalCBS(mvff));
|
||||
CHECKD(Land, MVFFFreeCBS(mvff));
|
||||
CHECKD(Land, MVFFFreelist(mvff));
|
||||
CHECKD(Land, MVFFFailover(mvff));
|
||||
CHECKL(LandSize(MVFFTotalCBS(mvff)) >= LandSize(MVFFFailover(mvff)));
|
||||
CHECKL(SizeIsAligned(LandSize(MVFFFailover(mvff)), PoolAlignment(MVFF2Pool(mvff))));
|
||||
CHECKL(SizeIsAligned(LandSize(MVFFTotalCBS(mvff)), ArenaAlign(PoolArena(MVFF2Pool(mvff)))));
|
||||
CHECKL(BoolCheck(mvff->slotHigh));
|
||||
CHECKL(BoolCheck(mvff->firstFit));
|
||||
return TRUE;
|
||||
|
|
@ -815,17 +789,15 @@ 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);
|
||||
|
||||
return CBSOfMVFF(mvff);
|
||||
return MVFFFreeCBS(mvff);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ extern PoolClass AMSTPoolClassGet(void);
|
|||
|
||||
typedef struct AMSTStruct {
|
||||
AMSStruct amsStruct; /* generic AMS structure */
|
||||
Chain chain; /* chain to use */
|
||||
Bool failSegs; /* fail seg splits & merges when true */
|
||||
Count splits; /* count of successful segment splits */
|
||||
Count merges; /* count of successful segment merges */
|
||||
|
|
@ -335,25 +334,30 @@ static Res AMSTInit(Pool pool, ArgList args)
|
|||
Format format;
|
||||
Chain chain;
|
||||
Res res;
|
||||
static GenParamStruct genParam = { 1024, 0.2 };
|
||||
unsigned gen = AMS_GEN_DEFAULT;
|
||||
ArgStruct arg;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
|
||||
AVERT(ArgList, args);
|
||||
|
||||
if (ArgPick(&arg, args, MPS_KEY_CHAIN))
|
||||
chain = arg.val.chain;
|
||||
else {
|
||||
chain = ArenaGlobals(PoolArena(pool))->defaultChain;
|
||||
gen = 1; /* avoid the nursery of the default chain by default */
|
||||
}
|
||||
if (ArgPick(&arg, args, MPS_KEY_GEN))
|
||||
gen = arg.val.u;
|
||||
ArgRequire(&arg, args, MPS_KEY_FORMAT);
|
||||
format = arg.val.format;
|
||||
|
||||
res = ChainCreate(&chain, pool->arena, 1, &genParam);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
res = AMSInitInternal(Pool2AMS(pool), format, chain, 0, FALSE);
|
||||
|
||||
res = AMSInitInternal(Pool2AMS(pool), format, chain, gen, FALSE);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
amst = Pool2AMST(pool);
|
||||
ams = Pool2AMS(pool);
|
||||
ams->segSize = AMSTSegSizePolicy;
|
||||
ams->segClass = AMSTSegClassGet;
|
||||
amst->chain = chain;
|
||||
amst->failSegs = TRUE;
|
||||
amst->splits = 0;
|
||||
amst->merges = 0;
|
||||
|
|
@ -388,7 +392,6 @@ static void AMSTFinish(Pool pool)
|
|||
|
||||
AMSFinish(pool);
|
||||
amst->sig = SigInvalid;
|
||||
ChainDestroy(amst->chain);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -399,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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -433,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;
|
||||
|
|
@ -461,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;
|
||||
PoolGenEmpty(&ams->pgen, AddrOffset(base, limit), FALSE);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -471,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;
|
||||
|
|
@ -499,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;
|
||||
PoolGenFill(&ams->pgen, AddrOffset(base, limit), FALSE);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -526,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;
|
||||
|
|
@ -537,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 */
|
||||
|
|
@ -558,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -576,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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -758,14 +766,19 @@ static void *test(void *arg, size_t s)
|
|||
mps_ap_t busy_ap;
|
||||
mps_addr_t busy_init;
|
||||
const char *indent = " ";
|
||||
mps_chain_t chain;
|
||||
static mps_gen_param_s genParam = {1024, 0.2};
|
||||
|
||||
arena = (mps_arena_t)arg;
|
||||
(void)s; /* unused */
|
||||
|
||||
die(mps_fmt_create_A(&format, arena, dylan_fmt_A()), "fmt_create");
|
||||
die(mps_chain_create(&chain, arena, 1, &genParam), "chain_create");
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_GEN, 0);
|
||||
die(mps_pool_create_k(&pool, arena, mps_class_amst(), args),
|
||||
"pool_create(amst)");
|
||||
} MPS_ARGS_END(args);
|
||||
|
|
@ -843,6 +856,7 @@ static void *test(void *arg, size_t s)
|
|||
mps_root_destroy(exactRoot);
|
||||
mps_root_destroy(ambigRoot);
|
||||
mps_pool_destroy(pool);
|
||||
mps_chain_destroy(chain);
|
||||
mps_fmt_destroy(format);
|
||||
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -945,13 +945,12 @@ Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn,
|
|||
|
||||
/* SplayTreeFirst, SplayTreeNext -- iterators
|
||||
*
|
||||
* SplayTreeFirst receives a key that must precede all
|
||||
* nodes in the tree. It returns TreeEMPTY if the tree is empty.
|
||||
* Otherwise, it splays the tree to the first node, and returns the
|
||||
* new root.
|
||||
* SplayTreeFirst returns TreeEMPTY if the tree is empty. Otherwise,
|
||||
* 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.
|
||||
|
|
@ -1006,7 +1005,7 @@ Tree SplayTreeNext(SplayTree splay, TreeKey oldKey) {
|
|||
*/
|
||||
|
||||
static Res SplayNodeDescribe(Tree node, mps_lib_FILE *stream,
|
||||
SplayNodeDescribeMethod nodeDescribe) {
|
||||
TreeDescribeMethod nodeDescribe) {
|
||||
Res res;
|
||||
|
||||
#if defined(AVER_AND_CHECK)
|
||||
|
|
@ -1318,13 +1317,27 @@ void SplayNodeRefresh(SplayTree splay, Tree node)
|
|||
}
|
||||
|
||||
|
||||
/* SplayNodeUpdate -- update the client property without splaying */
|
||||
|
||||
void SplayNodeUpdate(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 defined(AVER_AND_CHECK)
|
||||
|
|
|
|||
|
|
@ -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 SplayNodeUpdate(SplayTree splay, Tree node);
|
||||
|
||||
extern Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream,
|
||||
SplayNodeDescribeMethod nodeDescribe);
|
||||
TreeDescribeMethod nodeDescribe);
|
||||
|
||||
extern void SplayDebugUpdate(SplayTree splay, Tree tree);
|
||||
|
||||
|
|
|
|||
|
|
@ -634,33 +634,6 @@ static Res traceFlip(Trace trace)
|
|||
return res;
|
||||
}
|
||||
|
||||
/* traceCopySizes -- preserve size information for later use
|
||||
*
|
||||
* A PoolGen's newSize is important information that we want to emit in
|
||||
* a diagnostic message at TraceStart. In order to do that we must copy
|
||||
* the information before Whiten changes it. This function does that.
|
||||
*/
|
||||
|
||||
static void traceCopySizes(Trace trace)
|
||||
{
|
||||
Ring node, nextNode;
|
||||
Index i;
|
||||
Arena arena = trace->arena;
|
||||
|
||||
RING_FOR(node, &arena->chainRing, nextNode) {
|
||||
Chain chain = RING_ELT(Chain, chainRing, node);
|
||||
|
||||
for(i = 0; i < chain->genCount; ++i) {
|
||||
Ring n, nn;
|
||||
GenDesc desc = &chain->gens[i];
|
||||
RING_FOR(n, &desc->locusRing, nn) {
|
||||
PoolGen gen = RING_ELT(PoolGen, genRing, n);
|
||||
gen->newSizeAtCreate = gen->newSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* TraceCreate -- create a Trace object
|
||||
*
|
||||
|
|
@ -677,6 +650,17 @@ static void traceCopySizes(Trace trace)
|
|||
* This code is written to be adaptable to allocating Trace objects
|
||||
* dynamically. */
|
||||
|
||||
static void TraceCreatePoolGen(GenDesc gen)
|
||||
{
|
||||
Ring n, nn;
|
||||
RING_FOR(n, &gen->locusRing, nn) {
|
||||
PoolGen pgen = RING_ELT(PoolGen, genRing, n);
|
||||
EVENT11(TraceCreatePoolGen, gen, gen->capacity, gen->mortality, gen->zones,
|
||||
pgen->pool, pgen->totalSize, pgen->freeSize, pgen->newSize,
|
||||
pgen->oldSize, pgen->newDeferredSize, pgen->oldDeferredSize);
|
||||
}
|
||||
}
|
||||
|
||||
Res TraceCreate(Trace *traceReturn, Arena arena, int why)
|
||||
{
|
||||
TraceId ti;
|
||||
|
|
@ -747,7 +731,24 @@ Res TraceCreate(Trace *traceReturn, Arena arena, int why)
|
|||
/* .. _request.dylan.160098: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/160098 */
|
||||
ShieldSuspend(arena);
|
||||
|
||||
traceCopySizes(trace);
|
||||
STATISTIC_STAT ({
|
||||
/* Iterate over all chains, all GenDescs within a chain, and all
|
||||
* PoolGens within a GenDesc. */
|
||||
Ring node;
|
||||
Ring nextNode;
|
||||
|
||||
RING_FOR(node, &arena->chainRing, nextNode) {
|
||||
Chain chain = RING_ELT(Chain, chainRing, node);
|
||||
Index i;
|
||||
for (i = 0; i < chain->genCount; ++i) {
|
||||
GenDesc gen = &chain->gens[i];
|
||||
TraceCreatePoolGen(gen);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now do topgen GenDesc, and all PoolGens within it. */
|
||||
TraceCreatePoolGen(&arena->topGen);
|
||||
});
|
||||
|
||||
*traceReturn = trace;
|
||||
return ResOK;
|
||||
|
|
@ -1564,9 +1565,9 @@ double TraceWorkFactor = 0.25;
|
|||
*
|
||||
* TraceStart should be passed a trace with state TraceINIT, i.e.,
|
||||
* recently returned from TraceCreate, with some condemned segments
|
||||
* added. mortality is the fraction of the condemned set expected to
|
||||
* survive. finishingTime is relative to the current polling clock, see
|
||||
* <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
|
||||
|
|
@ -1588,19 +1589,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);
|
||||
EVENT11(TraceStartPoolGen, chain, BOOLOF(top), i, desc,
|
||||
desc->capacity, desc->mortality, desc->zones,
|
||||
gen->pool, gen->nr, 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
|
||||
|
|
@ -1665,26 +1653,6 @@ Res TraceStart(Trace trace, double mortality, double finishingTime)
|
|||
} while (SegNext(&seg, arena, seg));
|
||||
}
|
||||
|
||||
STATISTIC_STAT ({
|
||||
/* @@ */
|
||||
/* Iterate over all chains, all GenDescs within a chain, */
|
||||
/* (and all PoolGens within a GenDesc). */
|
||||
Ring node;
|
||||
Ring nextNode;
|
||||
Index i;
|
||||
|
||||
RING_FOR(node, &arena->chainRing, nextNode) {
|
||||
Chain chain = RING_ELT(Chain, chainRing, node);
|
||||
for(i = 0; i < chain->genCount; ++i) {
|
||||
GenDesc 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -210,25 +210,25 @@ Res ChunkInit(Chunk chunk, Arena arena,
|
|||
|
||||
/* Add the chunk's free address space to the arena's freeCBS, so that
|
||||
we can allocate from it. */
|
||||
if (arena->hasFreeCBS) {
|
||||
res = ArenaFreeCBSInsert(arena,
|
||||
PageIndexBase(chunk, chunk->allocBase),
|
||||
chunk->limit);
|
||||
if (arena->hasFreeLand) {
|
||||
res = ArenaFreeLandInsert(arena,
|
||||
PageIndexBase(chunk, chunk->allocBase),
|
||||
chunk->limit);
|
||||
if (res != ResOK)
|
||||
goto failCBSInsert;
|
||||
goto failLandInsert;
|
||||
}
|
||||
|
||||
chunk->sig = ChunkSig;
|
||||
AVERT(Chunk, chunk);
|
||||
|
||||
/* As part of the bootstrap, the first created chunk becomes the primary
|
||||
chunk. This step allows AreaFreeCBSInsert to allocate pages. */
|
||||
chunk. This step allows AreaFreeLandInsert to allocate pages. */
|
||||
if (arena->primary == NULL)
|
||||
arena->primary = chunk;
|
||||
|
||||
return ResOK;
|
||||
|
||||
failCBSInsert:
|
||||
failLandInsert:
|
||||
(arena->class->chunkFinish)(chunk);
|
||||
/* .no-clean: No clean-ups needed past this point for boot, as we will
|
||||
discard the chunk. */
|
||||
|
|
@ -248,10 +248,10 @@ void ChunkFinish(Chunk chunk)
|
|||
chunk->sig = SigInvalid;
|
||||
RingRemove(&chunk->chunkRing);
|
||||
|
||||
if (ChunkArena(chunk)->hasFreeCBS)
|
||||
ArenaFreeCBSDelete(ChunkArena(chunk),
|
||||
PageIndexBase(chunk, chunk->allocBase),
|
||||
chunk->limit);
|
||||
if (ChunkArena(chunk)->hasFreeLand)
|
||||
ArenaFreeLandDelete(ChunkArena(chunk),
|
||||
PageIndexBase(chunk, chunk->allocBase),
|
||||
chunk->limit);
|
||||
|
||||
if (chunk->arena->primary == chunk)
|
||||
chunk->arena->primary = NULL;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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,110 @@ 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 support visitors setting
|
||||
``deleteReturn`` to ``TRUE`` when iterating over ranges with
|
||||
``LandIterate()``.
|
||||
|
||||
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 +171,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 +195,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 +209,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
|
||||
|
|
@ -330,7 +233,6 @@ the size of that area. [Four words per two grains.] The CBS structure
|
|||
is thus suitable only for managing large enough ranges.
|
||||
|
||||
|
||||
|
||||
Document History
|
||||
----------------
|
||||
|
||||
|
|
@ -359,6 +261,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/
|
||||
|
||||
|
|
|
|||
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,13 +45,14 @@ 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 allocator
|
||||
check_ Design of checking in MPS
|
||||
class-interface_ Design of the pool class interface
|
||||
collection_ The collection framework
|
||||
config_ The design of MPS configuration
|
||||
critical-path_ The critical path through the MPS
|
||||
diag_ The design of MPS diagnostic feedback
|
||||
failover_ Fail-over allocator
|
||||
finalize_ Finalization
|
||||
fix_ The Design of the Generic Fix Function
|
||||
freelist_ Free list allocator
|
||||
|
|
@ -60,6 +61,7 @@ guide.impl.c.format_ Coding standard: conventions for the general format of C
|
|||
interface-c_ The design of the Memory Pool System interface to C
|
||||
io_ The design of the MPS I/O subsystem
|
||||
keyword-arguments_ The design of the MPS mechanism for passing arguments by keyword.
|
||||
land_ Lands (collections of address ranges)
|
||||
lib_ The design of the Memory Pool System library interface
|
||||
lock_ The design of the lock module
|
||||
locus_ The design for the locus manager
|
||||
|
|
@ -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
|
||||
|
|
|
|||
336
mps/design/land.txt
Normal file
336
mps/design/land.txt
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
.. 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)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS);``
|
||||
|
||||
_`.type.visitor`: Type ``LandVisitor`` is a callback function that may
|
||||
be passed to ``LandIterate()``. It is called for every isolated
|
||||
contiguous range in address order. The function must return a ``Bool``
|
||||
indicating whether to continue with the iteration. It may additionally
|
||||
update ``*deleteReturn`` to ``TRUE`` if the range must be deleted from
|
||||
the land, or ``FALSE`` if the range must be kept. (The default is to
|
||||
keep the range, and not all land classes support deletion.)
|
||||
|
||||
|
||||
Generic functions
|
||||
.................
|
||||
|
||||
``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.
|
||||
|
||||
``void LandIterate(Land land, LandIterateMethod iterate, void *closureP, Size closureS)``
|
||||
|
||||
_`.function.iterate`: ``LandIterate()`` is the function used to
|
||||
iterate all isolated contiguous ranges in a land. It receives a
|
||||
pointer, ``Size`` closure pair to pass on to the iterator method, and
|
||||
an iterator method to invoke on every range. If the iterator method
|
||||
returns ``FALSE``, then the iteration is terminated.
|
||||
|
||||
``Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)``
|
||||
|
||||
_`.function.find.first`: Locate the first block (in address order)
|
||||
within the land of at least the specified size, update ``rangeReturn``
|
||||
to describe that range, and return ``TRUE``. If there is no such
|
||||
block, it returns ``FALSE``.
|
||||
|
||||
In addition, optionally delete the top, bottom, or all of the found
|
||||
range, depending on the ``findDelete`` argument. This saves a separate
|
||||
call to ``LandDelete()``, and uses the knowledge of exactly where we
|
||||
found the range. The value of ``findDelete`` must come from this
|
||||
enumeration::
|
||||
|
||||
enum {
|
||||
FindDeleteNONE, /* don't delete after finding */
|
||||
FindDeleteLOW, /* delete size bytes from low end of block */
|
||||
FindDeleteHIGH, /* delete size bytes from high end of block */
|
||||
FindDeleteENTIRE /* delete entire range */
|
||||
};
|
||||
|
||||
The original contiguous isolated range in which the range was found is
|
||||
returned via the ``oldRangeReturn`` argument. (If ``findDelete`` is
|
||||
``FindDeleteNONE`` or ``FindDeleteENTIRE``, then this will be
|
||||
identical to the range returned via the ``rangeReturn`` argument.)
|
||||
|
||||
``Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)``
|
||||
|
||||
_`.function.find.last`: Like ``LandFindFirst()``, except that it
|
||||
finds the last block in address order.
|
||||
|
||||
``Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)``
|
||||
|
||||
_`.function.find.largest`: Locate the largest block within the
|
||||
land, and if that block is at least as big as ``size``, return its
|
||||
range via the ``rangeReturn`` argument, and return ``TRUE``. If there
|
||||
are no blocks in the land at least as large as ``size``, return
|
||||
``FALSE``. Pass 0 for ``size`` if you want the largest block
|
||||
unconditionally.
|
||||
|
||||
Like ``LandFindFirst()``, optionally delete the range (specifying
|
||||
``FindDeleteLOW`` or ``FindDeleteHIGH`` has the same effect as
|
||||
``FindDeleteENTIRE``), and return the original contiguous isolated
|
||||
range in which the range was found via the ``oldRangeReturn``
|
||||
argument.
|
||||
|
||||
``Res LandFindInZones(Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high)``
|
||||
|
||||
_`.function.find.zones`: Locate a block at least as big as ``size``
|
||||
that lies entirely within the ``zoneSet``, return its range via the
|
||||
``rangeReturn`` argument, and return ``ResOK``. (The first such block,
|
||||
if ``high`` is ``FALSE``, or the last, if ``high`` is ``TRUE``.) If
|
||||
there is no such block, , return ``ResFAIL``.
|
||||
|
||||
Delete the range as for ``LandFindFirst()`` and ``LastFindLast()``
|
||||
(with the effect of ``FindDeleteLOW`` if ``high`` is ``FALSE`` and the
|
||||
effect of ``FindDeleteHIGH`` if ``high`` is ``TRUE``), and return the
|
||||
original contiguous isolated range in which the range was found via
|
||||
the ``oldRangeReturn`` argument.
|
||||
|
||||
_`.function.find.zones.fail`: It's possible that the range can't be
|
||||
deleted from the land because that would require allocation, in which
|
||||
case the result code will indicate the cause of the failure.
|
||||
|
||||
``Res LandDescribe(Land land, mps_lib_FILE *stream)``
|
||||
|
||||
_`.function.describe`: ``LandDescribe()`` prints a textual
|
||||
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.**
|
||||
|
|
@ -44,9 +44,6 @@ BufferClass. This is appropriate since these buffers don't attach to
|
|||
segments, and hence don't constrain buffered regions to lie within
|
||||
segment boundaries.
|
||||
|
||||
_`.over.segments`: The pool uses the simplest segment class
|
||||
(SegClass). There's no need for anything more complex.
|
||||
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
|
@ -89,14 +86,10 @@ _`.method.init.epdr`: To simulate the EPDR pool, specify ``extendBy``,
|
|||
_`.method.init.other`: The performance characteristics of other
|
||||
combinations are unknown.
|
||||
|
||||
_`.method.finish`: The ``PoolFinish()`` method,
|
||||
|
||||
_`.method.alloc`: ``PoolAlloc()`` and ``PoolFree()`` methods are
|
||||
supported, implementing the policy set by the pool params (see
|
||||
`.method.init`_).
|
||||
|
||||
_`.method.describe`: The usual describe method.
|
||||
|
||||
_`.method.buffer`: The buffer methods implement a worst-fit fill
|
||||
strategy.
|
||||
|
||||
|
|
@ -119,23 +112,29 @@ the pool class, to be used in pool creation.
|
|||
Implementation
|
||||
--------------
|
||||
|
||||
_`.impl.alloc_list`: The pool stores the address ranges that it has
|
||||
acquired from the arena in a CBS (see design.mps.cbs_).
|
||||
|
||||
_`.impl.free-list`: The pool stores its free list in a CBS (see
|
||||
//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
|
||||
-------
|
||||
|
||||
_`.design.seg-size`: When adding a segment, we use extendBy as the
|
||||
segment size unless the object won't fit, in which case we use the
|
||||
object size (in both cases we align up).
|
||||
_`.design.acquire-size`: When acquiring memory from the arena, we use
|
||||
``extendBy`` as the unit of allocation unless the object won't fit, in
|
||||
which case we use the object size (in both cases we align up to the
|
||||
arena alignment).
|
||||
|
||||
_`.design.seg-fail`: If allocating a segment fails, we try again with
|
||||
a segment size just large enough for the object we're allocating. This
|
||||
is in response to request.mps.170186_.
|
||||
_`.design.acquire-fail`: If allocating ``extendBy``, we try again with
|
||||
an aligned size just large enough for the object we're allocating.
|
||||
This is in response to request.mps.170186_.
|
||||
|
||||
.. _request.mps.170186: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/mps/170186
|
||||
|
||||
|
|
@ -160,6 +159,9 @@ Document History
|
|||
- 2013-06-04 GDR_ The CBS module no longer maintains its own emergency
|
||||
list, so MVFF handles the fail-over from its CBS to a Freelist.
|
||||
|
||||
- 2014-04-15 GDR_ The address ranges acquired from the arena are now
|
||||
stored in a CBS; segments are no longer used for this purpose.
|
||||
|
||||
.. _RB: http://www.ravenbrook.com/consultants/rb/
|
||||
.. _GDR: http://www.ravenbrook.com/consultants/gdr/
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
_`.def.node`: A *node* is used in the typical data structure sense to
|
||||
mean an element of a tree (see also `.type.splay.node`_).
|
||||
|
||||
_`.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
|
||||
|
|
@ -443,16 +461,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 +480,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 +499,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 +521,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 +535,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 +574,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 +582,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 +604,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 +613,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 +624,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 +643,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 +653,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 +681,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 +761,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 +821,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 +908,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
|
||||
|
|
|
|||
|
|
@ -175,46 +175,113 @@ new segment occupies.
|
|||
|
||||
Note that this zoneset can never shrink.
|
||||
|
||||
|
||||
Parameters
|
||||
..........
|
||||
|
||||
_`.param.intro`: A generation has two parameters, *capacity* and
|
||||
*mortality*, specified by the client program.
|
||||
|
||||
_`.param.capacity`: The *capacity* of a generation is the amount of
|
||||
*new* allocation in that generation (that is, allocation since the
|
||||
last time the generation was condemned) that will cause the generation
|
||||
to be collected by ``TracePoll()``.
|
||||
|
||||
_`.param.capacity.misnamed`: The name *capacity* is unfortunate since
|
||||
it suggests that the total amount of memory in the generation will not
|
||||
exceed this value. But that will only be the case for pool classes
|
||||
that always promote survivors to another generation. When there is
|
||||
*old* allocation in the generation (that is, prior to the last time
|
||||
the generation was condemned), as there is in the case of non-moving
|
||||
pool classes, the size of a generation is unrelated to its capacity.
|
||||
|
||||
_`.param.mortality`: The *mortality* of a generation is the proportion
|
||||
(between 0 and 1) of memory in the generation that is expected to be
|
||||
dead when the generation is collected. It is used in ``TraceStart()``
|
||||
to estimate the amount of data that will have to be scanned in order
|
||||
to complete the trace.
|
||||
|
||||
|
||||
Accounting
|
||||
..........
|
||||
|
||||
- ``gen[N].mortality``
|
||||
_`.accounting.intro`: Pool generations maintain the sizes of various
|
||||
categories of data allocated in that generation for that pool. This
|
||||
accounting information is reported via the event system, but also used
|
||||
in two places:
|
||||
|
||||
- Specified by the client.
|
||||
- TODO: fill in how this is used.
|
||||
_`.accounting.poll`: ``ChainDeferral()`` uses the *new size* of each
|
||||
generation to determine which generations in the chain are over
|
||||
capacity and so might need to be collected via ``TracePoll()``.
|
||||
|
||||
- ``gen[N].capacity``
|
||||
_`.accounting.condemn`: ``ChainCondemnAuto()`` uses the *new size* of
|
||||
each generation to determine which generations in the chain will be
|
||||
collected; it also uses the *total size* of the generation to compute
|
||||
the mortality.
|
||||
|
||||
- Specified by the client.
|
||||
- TODO: fill in how this is used.
|
||||
_`.accounting.check`: Computing the new size for a pool generation is
|
||||
far from straightforward: see job003772_ for some (former) errors in
|
||||
this code. In order to assist with checking that this has been
|
||||
computed correctly, the locus module uses a double-entry book-keeping
|
||||
system to account for every byte in each pool generation. This uses
|
||||
six accounts:
|
||||
|
||||
- ``amcSeg->new``
|
||||
.. _job003772: http://www.ravenbrook.com/project/mps/issue/job003772/
|
||||
|
||||
- TODO: fill this in
|
||||
_`.account.total`: Memory acquired from the arena.
|
||||
|
||||
- ``pgen->totalSize``:
|
||||
_`.account.total.negated`: From the point of view of the double-entry
|
||||
system, the *total* should be negative as it is owing to the arena,
|
||||
but it is inconvenient to represent negative sizes, and so the
|
||||
positive value is stored instead.
|
||||
|
||||
- incremented by ``AMCBufferFill()``;
|
||||
- decremented by ``amcReclaimNailed()`` and ``AMCReclaim()``;
|
||||
- added up by ``GenDescTotalSize(gen)``.
|
||||
_`.account.total.negated.justification`: We don't have a type for
|
||||
signed sizes; but if we represented it in two's complement using the
|
||||
unsigned ``Size`` type then Clang's unsigned integer overflow detector
|
||||
would complain.
|
||||
|
||||
- ``pgen->newSize``:
|
||||
_`.account.free`: Memory that is not in use (free or lost to
|
||||
fragmentation).
|
||||
|
||||
- incremented by ``AMCBufferFill()`` (*when not ramping*) and ``AMCRampEnd()``;
|
||||
- decremented by ``AMCWhiten()``,
|
||||
- added up by ``GenDescNewSize(gen)``.
|
||||
_`.account.new`: Memory in use by the client program, allocated
|
||||
since the last time the generation was condemned.
|
||||
|
||||
- ``gen[N].proflow``:
|
||||
_`.account.old`: Memory in use by the client program, allocated
|
||||
prior to the last time the generation was condemned.
|
||||
|
||||
- set to 1.0 by ``ChainCreate()``;
|
||||
- ``arena->topGen.proflow`` set to 0.0 by ``LocusInit(arena)``;
|
||||
- *The value of this field is never used*.
|
||||
_`.account.newDeferred`: Memory in use by the client program,
|
||||
allocated since the last time the generation was condemned, but which
|
||||
should not cause collections via ``TracePoll()``. (Due to ramping; see
|
||||
below.)
|
||||
|
||||
_`.account.oldDeferred`: Memory in use by the client program,
|
||||
allocated prior to the last time the generation was condemned, but
|
||||
which should not cause collections via ``TracePoll()``. (Due to
|
||||
ramping; see below.)
|
||||
|
||||
- ``pgen->newSizeAtCreate``:
|
||||
_`.accounting.op`: The following operations are provided:
|
||||
|
||||
_`.accounting.op.alloc`: Allocate a segment in a pool generation.
|
||||
Debit *total*, credit *free*. (But see `.account.total.negated`_.)
|
||||
|
||||
_`.accounting.op.free`: Free a segment. Debit *free*, credit *total*.
|
||||
(But see `.account.total.negated`_.)
|
||||
|
||||
_`.accounting.op.fill`: Allocate memory, for example by filling a
|
||||
buffer. Debit *free*, credit *new* or *newDeferred*.
|
||||
|
||||
_`.accounting.op.empty`: Deallocate memory, for example by emptying
|
||||
the unused portion of a buffer. Debit *new* or *newDeferred*, credit
|
||||
*free*.
|
||||
|
||||
_`.accounting.op.age`: Condemn memory. Debit *new* or *newDeferred*,
|
||||
credit *old* or *oldDeferred*.
|
||||
|
||||
_`.accounting.op.reclaim`: Reclaim dead memory. Debit *old* or
|
||||
*oldDeferred*, credit *free*.
|
||||
|
||||
_`.accounting.op.undefer`: Stop deferring the accounting of memory. Debit *oldDeferred*, credit *old*. Debit *newDeferred*, credit *new*.
|
||||
|
||||
- set by ``traceCopySizes()`` (that is its purpose);
|
||||
- output in the ``TraceStartPoolGen`` telemetry event.
|
||||
|
||||
Ramps
|
||||
.....
|
||||
|
|
@ -289,29 +356,31 @@ Reclaiming any AMC segment:
|
|||
Now, some deductions:
|
||||
|
||||
#. When OUTSIDE, the count is always zero, because (a) it starts that
|
||||
way, and the only ways to go OUTSIDE are (b) by leaving an outermost
|
||||
ramp (count goes to zero) or (c) by reclaiming when the count is zero.
|
||||
way, and the only ways to go OUTSIDE are (b) by leaving an
|
||||
outermost ramp (count goes to zero) or (c) by reclaiming when the
|
||||
count is zero.
|
||||
|
||||
#. When BEGIN, the count is never zero (consider the transitions to
|
||||
BEGIN and the transition to zero).
|
||||
BEGIN and the transition to zero).
|
||||
|
||||
#. When RAMPING, the count is never zero (again consider transitions to
|
||||
RAMPING and the transition to zero).
|
||||
#. When RAMPING, the count is never zero (again consider transitions
|
||||
to RAMPING and the transition to zero).
|
||||
|
||||
#. When FINISH, the count can be anything (the transition to FINISH has
|
||||
zero count, but the Enter transition when FINISH can change that and
|
||||
then it can increment to any value).
|
||||
#. When FINISH, the count can be anything (the transition to FINISH
|
||||
has zero count, but the Enter transition when FINISH can change
|
||||
that and then it can increment to any value).
|
||||
|
||||
#. When COLLECTING, the count can be anything (from the previous fact,
|
||||
and the transition to COLLECTING).
|
||||
and the transition to COLLECTING).
|
||||
|
||||
#. *This is a bug!!* The ramp generation is not always reset (to forward
|
||||
to the after-ramp generation). If we get into FINISH and then see
|
||||
another ramp before the next condemnation of the ramp generation, we
|
||||
will Enter followed by Leave. The Enter will keep us in FINISH, and
|
||||
the Leave will take us back to OUTSIDE, skipping the transition to the
|
||||
COLLECTING state which is what resets the ramp generation forwarding
|
||||
buffer. [TODO: check whether I made an issue and/or fixed it; NB 2013-06-04]
|
||||
#. *This is a bug!!* The ramp generation is not always reset (to
|
||||
forward to the after-ramp generation). If we get into FINISH and
|
||||
then see another ramp before the next condemnation of the ramp
|
||||
generation, we will Enter followed by Leave. The Enter will keep us
|
||||
in FINISH, and the Leave will take us back to OUTSIDE, skipping the
|
||||
transition to the COLLECTING state which is what resets the ramp
|
||||
generation forwarding buffer. [TODO: check whether I made an issue
|
||||
and/or fixed it; NB 2013-06-04]
|
||||
|
||||
The simplest change to fix this is to change the behaviour of the Leave
|
||||
transition, which should only take us OUTSIDE if we are in BEGIN or
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -132,6 +132,12 @@ MVFF interface
|
|||
The minimum alignment supported by pools of this class is
|
||||
``sizeof(void *)``.
|
||||
|
||||
* :c:macro:`MPS_KEY_SPARE` (type :c:type:`double`, default 0.75)
|
||||
is the maximum proportion of memory that the pool will keep
|
||||
spare for future allocations. If the proportion of memory that's
|
||||
free exceeds this, then the pool will return some of it to the
|
||||
arena for use by other pools.
|
||||
|
||||
* :c:macro:`MPS_KEY_MVFF_ARENA_HIGH` (type :c:type:`mps_bool_t`,
|
||||
default false) determines whether new segments are acquired at high
|
||||
addresses (if true), or at low addresses (if false).
|
||||
|
|
|
|||
|
|
@ -54,6 +54,10 @@ Interface changes
|
|||
the value ``FALSE`` is appropriate only when you know that all
|
||||
references are exact. See :ref:`pool-ams`.
|
||||
|
||||
#. The :ref:`pool-mvff` pool class takes a new keyword argument
|
||||
:c:macro:`MPS_KEY_SPARE`. This specifies the maximum proportion of
|
||||
memory that the pool will keep spare for future allocations.
|
||||
|
||||
|
||||
Other changes
|
||||
.............
|
||||
|
|
@ -90,6 +94,9 @@ Other changes
|
|||
|
||||
.. _job003771: https://www.ravenbrook.com/project/mps/issue/job003771/
|
||||
|
||||
#. The :ref:`pool-mvt` and :ref:`pool-mvff` pool classes are now
|
||||
around 25% faster (in our benchmarks) than they were in release
|
||||
1.113.0.
|
||||
|
||||
|
||||
.. _release-notes-1.113:
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ now :c:macro:`MPS_KEY_ARGS_END`.
|
|||
:c:macro:`MPS_KEY_MVT_RESERVE_DEPTH` :c:type:`mps_count_t` ``count`` :c:func:`mps_class_mvt`
|
||||
:c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` ``mps_pool_debug_options_s *`` ``pool_debug_options`` :c:func:`mps_class_ams_debug`, :c:func:`mps_class_mv_debug`, :c:func:`mps_class_mvff_debug`
|
||||
:c:macro:`MPS_KEY_RANK` :c:type:`mps_rank_t` ``rank`` :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_snc`
|
||||
:c:macro:`MPS_KEY_SPARE` :c:type:`double` ``d`` :c:func:`mps_class_mvff`
|
||||
:c:macro:`MPS_KEY_VMW3_TOP_DOWN` :c:type:`mps_bool_t` ``b`` :c:func:`mps_arena_class_vm`
|
||||
======================================== ====================================================== ==========================================================
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ bttest =N interactive
|
|||
djbench =N benchmark
|
||||
exposet0 =P
|
||||
expt825
|
||||
fbmtest
|
||||
finalcv =P
|
||||
finaltest =P
|
||||
fotest
|
||||
gcbench =N benchmark
|
||||
landtest
|
||||
locbwcss
|
||||
lockcov
|
||||
lockut =T
|
||||
|
|
|
|||
Loading…
Reference in a new issue