mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-05 07:01:11 -08:00
Improvements following review.
Copied from Perforce Change: 186227 ServerID: perforce.ravenbrook.com
This commit is contained in:
parent
8acf4952a4
commit
a0e076be57
11 changed files with 107 additions and 71 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
* <design/arena/#chunk.delete> */
|
||||
arena->primary = NULL;
|
||||
TREE_DESTROY(treeref, tree, next, arena->chunkTree) {
|
||||
clientChunkDestroy(ChunkOfTree(tree));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
* <design/arena/#chunk.delete> */
|
||||
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 <design/arena/#chunk.delete>
|
||||
* TODO: add hysteresis here. See job003815. */
|
||||
tree = &arena->chunkTree;
|
||||
TreeToVine(tree);
|
||||
while (*tree != TreeEMPTY) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 (<design/arena/#tract.field> pool) */
|
||||
void *p; /* pointer for use of owning pool */
|
||||
Addr base; /* Base address of the tract */
|
||||
TraceSet white : TraceLIMIT; /* traces for which tract is white */
|
||||
unsigned hasSeg : 1; /* does tract have a seg in p? See .bool */
|
||||
} 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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -63,21 +63,20 @@ Data Structure
|
|||
The implementations are as follows::
|
||||
|
||||
typedef struct SegStruct { /* segment structure */
|
||||
Sig sig; /* impl.h.misc.sig */
|
||||
Sig sig; /* <code/misc.h#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 <code/shield.c#def.depth> */
|
||||
AccessSet pm : AccessLIMIT; /* protection mode, <code/shield.c> */
|
||||
AccessSet sm : AccessLIMIT; /* shield mode, <code/shield.c> */
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -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``
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue