diff --git a/mps/code/arena.c b/mps/code/arena.c index 6c1990ee880..89abc7e0e7c 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -494,7 +494,7 @@ Res ArenaDescribe(Arena arena, mps_lib_FILE *stream) } -/* ArenaDescribeTractsInChunk -- describe all the tracts in a chunk */ +/* ArenaDescribeTractsInChunk -- describe the tracts in a chunk */ static Bool arenaDescribeTractsInChunk(Tree tree, void *closureP, Size closureS) { @@ -509,20 +509,30 @@ static Bool arenaDescribeTractsInChunk(Tree tree, void *closureP, Size closureS) if (stream == NULL) return ResFAIL; UNUSED(closureS); + res = WriteF(stream, "Chunk [$P, $P) ($U) {\n", + (WriteFP)chunk->base, (WriteFP)chunk->limit, + (WriteFU)chunk->serial, + NULL); + if (res != ResOK) return res; + TRACT_TRACT_FOR(tract, addr, ChunkArena(chunk), PageTract(ChunkPage(chunk, chunk->allocBase)), chunk->limit) { res = WriteF(stream, - "[$P, $P) $U $P ($S)\n", + " [$P, $P) $P $U ($S)\n", (WriteFP)TractBase(tract), (WriteFP)TractLimit(tract), - (WriteFW)ArenaAlign(ChunkArena(chunk)), (WriteFP)TractPool(tract), + (WriteFU)(TractPool(tract)->serial), (WriteFS)(TractPool(tract)->class->name), NULL); if (res != ResOK) return res; } - return ResOK; + + res = WriteF(stream, "} Chunk [$P, $P)\n", + (WriteFP)chunk->base, (WriteFP)chunk->limit, + NULL); + return res; } @@ -599,17 +609,7 @@ Res ControlDescribe(Arena arena, mps_lib_FILE *stream) } -/* ArenaChunkInsert -- insert chunk into arena's chunk tree - * - * Note that there's no corresponding ArenaChunkDelete. That's because - * we don't have a function that deletes an item from a balanced tree - * efficiently. Instead, deletions from the chunk tree are carried out - * by calling TreeToVine, iterating over the vine (where deletion is - * straightforward) and then calling TreeBalance. This is efficient - * when deleting all the chunks at a time in ArenaFinish, and - * acceptable in VMCompact when multiple chunks may be deleted from - * the tree. - */ +/* ArenaChunkInsert -- insert chunk into arena's chunk tree */ void ArenaChunkInsert(Arena arena, Tree tree) { Bool inserted; @@ -689,13 +689,12 @@ static Res arenaAllocPage(Addr *baseReturn, Arena arena, Pool pool) /* Favour the primary chunk, because pages allocated this way aren't currently freed, and we don't want to prevent chunks being destroyed. */ /* TODO: Consider how the ArenaCBSBlockPool might free pages. */ - if (arenaAllocPageInChunk(&arena->primary->chunkTree, &closure, 0) == FALSE) + if (!arenaAllocPageInChunk(&arena->primary->chunkTree, &closure, 0)) goto found; closure.avoid = arena->primary; - if (TreeTraverse(ArenaChunkTree(arena), ChunkCompare, ChunkKey, - arenaAllocPageInChunk, &closure, 0) - == FALSE) + if (!TreeTraverse(ArenaChunkTree(arena), ChunkCompare, ChunkKey, + arenaAllocPageInChunk, &closure, 0)) goto found; return ResRESOURCE; diff --git a/mps/code/arenacl.c b/mps/code/arenacl.c index 7d8c3143f3d..0129c63fea9 100644 --- a/mps/code/arenacl.c +++ b/mps/code/arenacl.c @@ -295,7 +295,8 @@ static void ClientArenaFinish(Arena arena) clientArena = Arena2ClientArena(arena); AVERT(ClientArena, clientArena); - /* destroy all chunks, including the primary */ + /* Destroy all chunks, including the primary. See + * */ arena->primary = NULL; TREE_DESTROY(treeref, tree, next, arena->chunkTree) { clientChunkDestroy(ChunkOfTree(tree)); diff --git a/mps/code/arenavm.c b/mps/code/arenavm.c index 210c943d047..538f50b134a 100644 --- a/mps/code/arenavm.c +++ b/mps/code/arenavm.c @@ -597,12 +597,13 @@ static void VMArenaFinish(Arena arena) EVENT1(ArenaDestroy, vmArena); - /* destroy all chunks, including the primary */ + /* Destroy all chunks, including the primary. See + * */ arena->primary = NULL; TREE_DESTROY(treeref, tree, next, arena->chunkTree) { vmChunkDestroy(ChunkOfTree(tree)); } - + /* Destroying the chunks should have purged and removed all spare pages. */ RingFinish(&vmArena->spareRing); @@ -1095,11 +1096,9 @@ static void VMCompact(Arena arena, Trace trace) vmem1 = VMArenaReserved(arena); - /* Destroy all the chunks that are completely free. Be very careful - * about the order of operations on the tree because vmChunkDestroy - * unmaps the memory that the tree node resides in, so the next tree - * node has to be looked up first. TODO: add hysteresis here. See - * job003815. */ + /* Destroy chunks that are completely free, but not the primary + * chunk. See + * TODO: add hysteresis here. See job003815. */ tree = &arena->chunkTree; TreeToVine(tree); while (*tree != TreeEMPTY) { diff --git a/mps/code/splay.c b/mps/code/splay.c index 41cc3069412..fe677a3c866 100644 --- a/mps/code/splay.c +++ b/mps/code/splay.c @@ -694,7 +694,7 @@ static Compare SplaySplay(SplayTree splay, TreeKey key, TreeCompare compare) SplayStateStruct stateStruct; #ifdef SPLAY_DEBUG - Count count = TreeDebugCount(SplayTreeRoot(splay), splay->compare, splay->nodeKey); + Count count = SplayDebugCount(splay); #endif /* Short-circuit common cases. Splay trees often bring recently @@ -714,7 +714,7 @@ static Compare SplaySplay(SplayTree splay, TreeKey key, TreeCompare compare) SplayTreeSetRoot(splay, stateStruct.middle); #ifdef SPLAY_DEBUG - AVER(count == TreeDebugCount(SplayTreeRoot(splay), splay->compare, splay->nodeKey)); + AVER(count == SplayDebugCount(splay)); #endif return cmp; @@ -909,7 +909,7 @@ Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, Bool found; Compare cmp; #ifdef SPLAY_DEBUG - Count count = TreeDebugCount(SplayTreeRoot(splay), splay->compare, splay->nodeKey); + Count count = SplayDebugCount(splay); #endif @@ -951,7 +951,7 @@ Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, SplayTreeSetRoot(splay, stateStruct.middle); #ifdef SPLAY_DEBUG - AVER(count == TreeDebugCount(SplayTreeRoot(splay), splay->compare, splay->nodeKey)); + AVER(count == SplayDebugCount(splay)); #endif return found; @@ -970,9 +970,8 @@ Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, * unmodified. * * IMPORTANT: Iterating over the tree using these functions will leave - * the tree totally unbalanced, throwing away optimisations of the - * tree shape caused by previous splays. Consider using - * SplayTreeTraverse instead. + * the tree totally unbalanced, throwing away optimisations of the tree + * shape caused by previous splays. Consider using TreeTraverse instead. */ Tree SplayTreeFirst(SplayTree splay) { @@ -1013,16 +1012,6 @@ Tree SplayTreeNext(SplayTree splay, TreeKey oldKey) { } -/* SplayTreeTraverse -- iterate over splay tree without splaying it */ - -Bool SplayTreeTraverse(SplayTree splay, TreeVisitor visitor, - void *closureP, Size closureS) -{ - return TreeTraverse(splay->root, splay->compare, splay->nodeKey, - visitor, closureP, closureS); -} - - /* SplayNodeDescribe -- Describe a node in the splay tree * * Note that this breaks the restriction of .note.stack. diff --git a/mps/code/splay.h b/mps/code/splay.h index 9bfb27c0ed1..8aa335e88a1 100644 --- a/mps/code/splay.h +++ b/mps/code/splay.h @@ -55,8 +55,6 @@ extern Bool SplayTreeNeighbours(Tree *leftReturn, extern Tree SplayTreeFirst(SplayTree splay); extern Tree SplayTreeNext(SplayTree splay, TreeKey oldKey); -extern Bool SplayTreeTraverse(SplayTree splay, TreeVisitor visitor, - void *closureP, Size closureS); typedef Bool (*SplayFindMethod)(Tree *nodeReturn, SplayTree splay, SplayTestNodeMethod testNode, @@ -79,6 +77,7 @@ extern Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, extern void SplayDebugUpdate(SplayTree splay, Tree tree); extern Count SplayDebugCount(SplayTree splay); + #endif /* splay_h */ diff --git a/mps/code/tree.c b/mps/code/tree.c index 0e1bb372f65..dbf112f31bc 100644 --- a/mps/code/tree.c +++ b/mps/code/tree.c @@ -119,11 +119,11 @@ Compare TreeFind(Tree *treeReturn, Tree root, TreeKey key, TreeCompare compare) /* TreeFindNext -- search for node containing key, or next node * - * If there is a node that is greater than key, set treeReturn to that + * If there is a node that is greater than key, set *treeReturn to that * node and return TRUE. * * Otherwise, key is greater than all nodes in the tree, so leave - * treeReturn unchanged and return FALSE. + * *treeReturn unchanged and return FALSE. */ Bool TreeFindNext(Tree *treeReturn, Tree root, TreeKey key, TreeCompare compare) diff --git a/mps/code/tree.h b/mps/code/tree.h index 6c5ea33320e..6d2fde3ea6f 100644 --- a/mps/code/tree.h +++ b/mps/code/tree.h @@ -136,7 +136,7 @@ extern void TreeBalance(Tree *treeIO); /* TREE_DESTROY -- iterate over a tree while destroying it. * - * root is a lvalue storing the root of the tree. + * root is an lvalue storing the root of the tree. * treeref is a variable of type Tree*. * tree and next are variables of type Tree. * In the body of the loop, tree is the current node. @@ -145,7 +145,7 @@ extern void TreeBalance(Tree *treeIO); for ((treeref = &(root), TreeToVine(treeref), next = TreeEMPTY); \ (tree = *treeref) != TreeEMPTY \ ? (next = tree->right, TRUE) \ - : (TreeBalance(treeref), FALSE); \ + : FALSE; \ *treeref = next) diff --git a/mps/design/arena.txt b/mps/design/arena.txt index 6c48d2ad111..28dd6334065 100644 --- a/mps/design/arena.txt +++ b/mps/design/arena.txt @@ -220,6 +220,49 @@ implementations of those methods which must be overridden. Instead each abstract method is initialized to ``NULL``. +Chunks +...... + +_`.chunk`: Each contiguous region of address space managed by the MPS +is represented by a *chunk*. + +_`.chunk.tracts`: A chunk contains a table of tracts. See `.tract`_. + +_`.chunk.lookup`: Looking of the chunk of an address is the first +step in the second-stage fix operation, and so on the critical path. +See `design.mps.critical-path`_. + +.. _design.mps.critical-path: critical-path + +_`.chunk.tree`: For efficient lookup, chunks are stored in a balanced +tree; ``arena->chunkTree`` points to the root of the tree. Operations +on this tree must ensure that the tree remains balanced, otherwise +performance degrades badly with many chunks. + +_`.chunk.insert`: New chunks are inserted into the tree by calling +``ArenaChunkInsert()``. This calls ``TreeInsert()``, followed by +``TreeBalance()`` to ensure that the tree is balanced. + +_`.chunk.delete`: There is no corresponding function +``ArenaChunkDelete()``. Instead, deletions from the chunk tree are +carried out by calling ``TreeToVine()``, iterating over the vine +(where deletion is straightforward, with care) and then calling +``TreeBalance()`` if any chunks might remain. The macro +``TREE_DESTROY()`` assists with the common case. + +_`.chunk.delete.justify`: This is because we don't have a function +that deletes an item from a balanced tree efficiently, and because all +functions that delete chunks do so in a loop that may delete multiple +chunks. The procedure is efficient when deleting all the chunks in +``ArenaFinish()``, and has acceptable performance in ``VMCompact()`` +where multiple chunks may be deleted from the tree. + +_`.chunk.delete.tricky`: Deleting chunks from the chunk tree is tricky +in the virtual memory arena because ``vmChunkDestroy()`` unmaps the +memory containing the chunk, which includes the tree node. So the next +chunk must be looked up before deleting the current chunk. + + Tracts ...... @@ -233,11 +276,11 @@ associating their own data with each allocation grain. _`.tract.structure`: The tract structure definition looks like this:: typedef struct TractStruct { /* Tract structure */ - PagePoolUnion pool; /* MUST BE FIRST (design.mps.arena.tract.field.pool) */ - void *p; /* pointer for use of owning pool */ - Addr base; /* Base address of the tract */ - TraceSet white : TRACE_MAX; /* traces for which tract is white */ - unsigned int hasSeg : 1; /* does tract have a seg in p? */ + PagePoolUnion pool; /* MUST BE FIRST ( pool) */ + void *p; /* pointer for use of owning pool */ + Addr base; /* Base address of the tract */ + TraceSet white : TraceLIMIT; /* traces for which tract is white */ + unsigned hasSeg : 1; /* does tract have a seg in p? See .bool */ } TractStruct; _`.tract.field.pool`: The pool.pool field indicates to which pool the tract @@ -263,9 +306,11 @@ use it for any purpose. _`.tract.field.hasSeg`: The ``hasSeg`` bit-field is a Boolean which indicates whether the ``p`` field is being used by the segment module. If this field is ``TRUE``, then the value of ``p`` is a ``Seg``. -``hasSeg`` is typed as an ``unsigned int``, rather than a ``Bool``. -This ensures that there won't be sign conversion problems when -converting the bit-field value. +``hasSeg`` has type ``unsigned:1`` rather than ``Bool:1`` to avoid +sign conversion problems when converting the bit-field value. See +`design.mps.type.bool.bitfield`_. + +.. _design.mps.type.bool.bitfield: type#bool.bitfield _`.tract.field.base`: The base field contains the base address of the memory represented by the tract. diff --git a/mps/design/locus.txt b/mps/design/locus.txt index 1e70de035bd..501578ee724 100644 --- a/mps/design/locus.txt +++ b/mps/design/locus.txt @@ -511,7 +511,10 @@ requested (to allow for large objects). _`.arch.chunk`: Arenas may allocate more address space in additional chunks, which may be disjoint from the existing chunks. Inter-chunk space will be represented by dummy regions. There are also sentinel -regions at both ends of the address space. +regions at both ends of the address space. See +`design.mps.arena.chunk`_. + +.. _design.mps.arena.chunk: arena#chunk Overview of strategy diff --git a/mps/design/seg.txt b/mps/design/seg.txt index 87ff87575f6..55634a92092 100644 --- a/mps/design/seg.txt +++ b/mps/design/seg.txt @@ -63,21 +63,20 @@ Data Structure The implementations are as follows:: typedef struct SegStruct { /* segment structure */ - Sig sig; /* impl.h.misc.sig */ + Sig sig; /* */ SegClass class; /* segment class structure */ Tract firstTract; /* first tract of segment */ RingStruct poolRing; /* link in list of segs in pool */ Addr limit; /* limit of segment */ - unsigned depth : SHIELD_DEPTH_WIDTH; /* see impl.c.shield.def.depth */ - AccessSet pm : AccessMAX; /* protection mode, impl.c.shield */ - AccessSet sm : AccessMAX; /* shield mode, impl.c.shield */ - TraceSet grey : TRACE_MAX; /* traces for which seg is grey */ - TraceSet white : TRACE_MAX; /* traces for which seg is white */ - TraceSet nailed : TRACE_MAX; /* traces for which seg has nailed objects */ - RankSet rankSet : RankMAX; /* ranks of references in this seg */ + unsigned depth : ShieldDepthWIDTH; /* see */ + AccessSet pm : AccessLIMIT; /* protection mode, */ + AccessSet sm : AccessLIMIT; /* shield mode, */ + TraceSet grey : TraceLIMIT; /* traces for which seg is grey */ + TraceSet white : TraceLIMIT; /* traces for which seg is white */ + TraceSet nailed : TraceLIMIT; /* traces for which seg has nailed objects */ + RankSet rankSet : RankLIMIT; /* ranks of references in this seg */ } SegStruct; - typedef struct GCSegStruct { /* GC segment structure */ SegStruct segStruct; /* superclass fields must come first */ RingStruct greyRing; /* link in list of grey segs */ diff --git a/mps/design/type.txt b/mps/design/type.txt index baee04ef3d2..f60ac1d7ba3 100644 --- a/mps/design/type.txt +++ b/mps/design/type.txt @@ -564,9 +564,11 @@ space as the client data. ``typedef unsigned TraceId`` _`.traceid`: A ``TraceId`` is an unsigned integer which is less than -``TRACE_MAX``. Each running trace has a different ``TraceId`` which is -used to index into tables and bitfields used to remember the state of -that trace. +``TraceLIMIT``. Each running trace has a different ``TraceId`` which +is used to index into the tables and bitfields that record the state +of that trace. See `design.mps.trace.instance.limit`_. + +.. _design.mps.trace.instance.limit: trace#instance.limit ``typedef unsigned TraceSet``