diff --git a/mps/code/arena.c b/mps/code/arena.c
index 3d843cc7bde..e5d2901683e 100644
--- a/mps/code/arena.c
+++ b/mps/code/arena.c
@@ -243,10 +243,11 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args)
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 Land is used. */
+ /* Initialise a pool to hold the CBS blocks for the arena's free
+ * land. This pool can't be allowed to extend itself using
+ * ArenaAlloc because it is used to implement ArenaAlloc, so
+ * MFSExtendSelf is set to FALSE. Failures to extend are handled
+ * where the free land is used: see arenaFreeLandInsertExtend. */
MPS_ARGS_BEGIN(piArgs) {
MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSZonedBlockStruct));
@@ -258,18 +259,6 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args)
if (res != ResOK)
goto failMFSInit;
- /* Initialise the freeLand. */
- MPS_ARGS_BEGIN(liArgs) {
- MPS_ARGS_ADD(liArgs, CBSBlockPool, ArenaCBSBlockPool(arena));
- res = LandInit(ArenaFreeLand(arena), CBSZonedLandClassGet(), arena,
- ArenaGrainSize(arena), arena, liArgs);
- } MPS_ARGS_END(liArgs);
- AVER(res == ResOK); /* no allocation, no failure expected */
- if (res != ResOK)
- 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, */
res = ReservoirInit(&arena->reservoirStruct, arena);
if (res != ResOK)
@@ -279,8 +268,6 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args)
return ResOK;
failReservoirInit:
- LandFinish(ArenaFreeLand(arena));
-failLandInit:
PoolFinish(ArenaCBSBlockPool(arena));
failMFSInit:
GlobalsFinish(ArenaGlobals(arena));
@@ -316,16 +303,33 @@ static Res arenaFreeLandInit(Arena arena)
AVER(!arena->hasFreeLand);
AVER(arena->primary != NULL);
- /* With the primary chunk initialised we can add page memory to the freeLand
- * that describes the free address space in the primary chunk. */
+ /* Initialise the free land. */
+ MPS_ARGS_BEGIN(liArgs) {
+ MPS_ARGS_ADD(liArgs, CBSBlockPool, ArenaCBSBlockPool(arena));
+ res = LandInit(ArenaFreeLand(arena), CBSZonedLandClassGet(), arena,
+ ArenaGrainSize(arena), arena, liArgs);
+ } MPS_ARGS_END(liArgs);
+ AVER(res == ResOK); /* no allocation, no failure expected */
+ if (res != ResOK)
+ goto failLandInit;
+
+ /* With the primary chunk initialised we can add page memory to the
+ * free land that describes the free address space in the primary
+ * chunk. */
res = ArenaFreeLandInsert(arena,
PageIndexBase(arena->primary,
arena->primary->allocBase),
arena->primary->limit);
if (res != ResOK)
- return res;
+ goto failFreeLandInsert;
+
arena->hasFreeLand = TRUE;
return ResOK;
+
+failFreeLandInsert:
+ LandFinish(ArenaFreeLand(arena));
+failLandInit:
+ return res;
}
Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args)
@@ -441,16 +445,16 @@ static void arenaMFSPageFreeVisitor(Pool pool, Addr base, Size size,
static void arenaFreeLandFinish(Arena arena)
{
- /* We must tear down the freeLand before the chunks, because pages
- * containing CBS blocks might be allocated in those chunks. */
+ AVERT(Arena, 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 freeLand. */
+ * that would use the free land. */
MFSFinishTracts(ArenaCBSBlockPool(arena), arenaMFSPageFreeVisitor,
UNUSED_POINTER, UNUSED_SIZE);
+
+ arena->hasFreeLand = FALSE;
+ LandFinish(ArenaFreeLand(arena));
}
void ArenaDestroy(Arena arena)
@@ -464,6 +468,8 @@ void ArenaDestroy(Arena arena)
ControlFinish(arena);
+ /* We must tear down the free land before the chunks, because pages
+ * containing CBS blocks might be allocated in those chunks. */
arenaFreeLandFinish(arena);
/* Call class-specific finishing. This will call ArenaFinish. */
@@ -854,7 +860,7 @@ static Res arenaExtendCBSBlockPool(Range pageRangeReturn, Arena arena)
return res;
MFSExtend(ArenaCBSBlockPool(arena), pageBase, ArenaGrainSize(arena));
- RangeInit(pageRangeReturn, pageBase, AddrAdd(pageBase, ArenaGrainSize(arena)));
+ RangeInitSize(pageRangeReturn, pageBase, ArenaGrainSize(arena));
return ResOK;
}
@@ -874,15 +880,19 @@ static void arenaExcludePage(Arena arena, Range pageRange)
}
-/* arenaLandInsert -- add range to arena's land, maybe extending block pool
+/* arenaFreeLandInsertExtend -- add range to arena's free land, maybe
+ * extending block pool
*
- * 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.
+ * The arena's free land can't get memory for its block pool in the
+ * usual way (via ArenaAlloc), because it is the mechanism behind
+ * ArenaAlloc! So we extend the block pool via a back door (see
+ * arenaExtendCBSBlockPool).
*
* Only fails if it can't get a page for the block pool.
*/
-static Res arenaLandInsert(Range rangeReturn, Arena arena, Range range)
+static Res arenaFreeLandInsertExtend(Range rangeReturn, Arena arena,
+ Range range)
{
Res res;
@@ -908,16 +918,18 @@ static Res arenaLandInsert(Range rangeReturn, Arena arena, Range range)
}
-/* ArenaFreeLandInsert -- add range to arena's land, maybe stealing memory
+/* arenaFreeLandInsertSteal -- add range to arena's free land, maybe
+ * stealing memory
*
- * 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.
+ * See arenaFreeLandInsertExtend. 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 arenaLandInsertSteal(Range rangeReturn, Arena arena, Range rangeIO)
+static void arenaFreeLandInsertSteal(Range rangeReturn, Arena arena,
+ Range rangeIO)
{
Res res;
@@ -925,7 +937,7 @@ static void arenaLandInsertSteal(Range rangeReturn, Arena arena, Range rangeIO)
AVERT(Arena, arena);
AVERT(Range, rangeIO);
- res = arenaLandInsert(rangeReturn, arena, rangeIO);
+ res = arenaFreeLandInsertExtend(rangeReturn, arena, rangeIO);
if (res != ResOK) {
Addr pageBase;
@@ -954,7 +966,8 @@ static void arenaLandInsertSteal(Range rangeReturn, Arena arena, Range rangeIO)
}
-/* ArenaFreeLandInsert -- add range to arena's land, maybe extending block pool
+/* ArenaFreeLandInsert -- add range to arena's free 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
@@ -969,7 +982,7 @@ Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit)
AVERT(Arena, arena);
RangeInit(&range, base, limit);
- res = arenaLandInsert(&oldRange, arena, &range);
+ res = arenaFreeLandInsertExtend(&oldRange, arena, &range);
if (res != ResOK)
return res;
@@ -984,7 +997,8 @@ Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit)
}
-/* ArenaFreeLandDelete -- remove range from arena's land, maybe extending block pool
+/* ArenaFreeLandDelete -- remove range from arena's free land, maybe
+ * extending block pool
*
* This is called from ChunkFinish in order to remove address space from
* the arena.
@@ -1076,7 +1090,7 @@ static Res arenaAllocFromLand(Tract *tractReturn, ZoneSet zones, Bool high,
failMark:
{
- Res insertRes = arenaLandInsert(&oldRange, arena, &range);
+ Res insertRes = arenaFreeLandInsertExtend(&oldRange, arena, &range);
AVER(insertRes == ResOK); /* We only just deleted it. */
/* If the insert does fail, we lose some address space permanently. */
}
@@ -1288,7 +1302,7 @@ void ArenaFree(Addr base, Size size, Pool pool)
RangeInit(&range, base, limit);
- arenaLandInsertSteal(&oldRange, arena, &range); /* may update range */
+ arenaFreeLandInsertSteal(&oldRange, arena, &range); /* may update range */
(*arena->class->free)(RangeBase(&range), RangeSize(&range), pool);
diff --git a/mps/code/freelist.c b/mps/code/freelist.c
index a9e482550f3..6eddc3dff83 100644
--- a/mps/code/freelist.c
+++ b/mps/code/freelist.c
@@ -1,7 +1,7 @@
/* freelist.c: FREE LIST ALLOCATOR IMPLEMENTATION
*
* $Id$
- * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2013-2015 Ravenbrook Limited. See end of file for license.
*
* .sources: .
*/
@@ -18,11 +18,11 @@ SRCID(freelist, "$Id$");
typedef union FreelistBlockUnion {
- struct {
+ struct FreelistBlockSmall {
FreelistBlock next; /* tagged with low bit 1 */
/* limit is (char *)this + freelistAlignment(fl) */
} small;
- struct {
+ struct FreelistBlockLarge {
FreelistBlock next; /* not tagged (low bit 0) */
Addr limit;
} large;
@@ -101,6 +101,9 @@ static Bool FreelistBlockCheck(FreelistBlock block)
CHECKL(freelistBlockNext(block) == freelistEND
|| block < freelistBlockNext(block));
CHECKL(freelistBlockIsSmall(block) || (Addr)block < block->large.limit);
+ /* Would like to CHECKL(!freelistBlockIsSmall(block) ||
+ * freelistBlockSize(fl, block) == freelistAlignment(fl)) but we
+ * don't have 'fl' here. This is checked in freelistBlockSetLimit. */
return TRUE;
}
@@ -139,6 +142,7 @@ static void freelistBlockSetLimit(Freelist fl, FreelistBlock block, Addr limit)
} else {
AVER(size >= sizeof(block->small));
block->small.next = freelistTagSet(block->small.next);
+ AVER(freelistBlockSize(fl, block) == freelistAlignment(fl));
}
AVER(freelistBlockLimit(fl, block) == limit);
}
@@ -170,6 +174,9 @@ Bool FreelistCheck(Freelist fl)
CHECKS(Freelist, fl);
land = FreelistLand(fl);
CHECKD(Land, land);
+ CHECKL(AlignCheck(FreelistMinimumAlignment));
+ CHECKL(sizeof(struct FreelistBlockSmall) < sizeof(struct FreelistBlockLarge));
+ CHECKL(sizeof(struct FreelistBlockSmall) <= freelistAlignment(fl));
/* See */
CHECKL(AlignIsAligned(freelistAlignment(fl), FreelistMinimumAlignment));
CHECKL((fl->list == freelistEND) == (fl->listSize == 0));
@@ -236,12 +243,14 @@ static Size freelistSize(Land land)
* Otherwise, if next is freelistEND, make prev the last block in the list.
* Otherwise, make next follow prev in the list.
* Update the count of blocks by 'delta'.
-
+ *
* It is tempting to try to simplify this code by putting a
* FreelistBlockUnion into the FreelistStruct and so avoiding the
* special case on prev. But the problem with that idea is that we
* can't guarantee that such a sentinel would respect the isolated
- * range invariant, and so it would still have to be special-cases.
+ * range invariant (it would have to be at a lower address than the
+ * first block in the free list, which the MPS has no mechanism to
+ * enforce), and so it would still have to be special-cased.
*/
static void freelistBlockSetPrevNext(Freelist fl, FreelistBlock prev,
@@ -781,6 +790,7 @@ static Res freelistDescribe(Land land, mps_lib_FILE *stream, Count depth)
res = WriteF(stream, depth,
"Freelist $P {\n", (WriteFP)fl,
" listSize = $U\n", (WriteFU)fl->listSize,
+ " size = $U\n", (WriteFU)fl->size,
NULL);
b = LandIterate(land, freelistDescribeVisitor, stream, depth + 2);
@@ -815,7 +825,7 @@ DEFINE_LAND_CLASS(FreelistLandClass, class)
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2013-2014 Ravenbrook Limited .
+ * Copyright (C) 2013-2015 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt
index 629ffee08fe..34c2eedd4cd 100644
--- a/mps/design/cbs.txt
+++ b/mps/design/cbs.txt
@@ -76,14 +76,14 @@ class, a subclass of ``LandClass`` suitable for passing to
``LandClass CBSFastLandClassGet(void)``
-_`.function.class`: Returns a subclass of ``CBSLandClass`` that
+_`.function.class.fast`: 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.
``LandClass CBSZonedLandClassGet(void)``
-_`.function.class`: Returns a subclass of ``CBSFastLandClass`` that
+_`.function.class.zoned`: 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.
diff --git a/mps/design/exec-env.txt b/mps/design/exec-env.txt
index 5a7a4c5c76d..65de1dd228f 100644
--- a/mps/design/exec-env.txt
+++ b/mps/design/exec-env.txt
@@ -74,8 +74,8 @@ available, only language facilities.
_`.int.free.lib`: We assume that the headers ````,
````, ```` and ```` are available in the
freestanding environment, because they define only language features
-and not library calls. We assume that we may not make use of any other
-definitions if freestanding parts of the system.
+and not library calls. We assume that we may not make use of
+definitions in any other headers in freestanding parts of the system.
_`.int.free.term`: We may not terminate the program in a freestanding
environment, and therefore we may not call :c:func:`abort`. We can't
@@ -149,7 +149,7 @@ Document History
Copyright and License
---------------------
-Copyright © 1996-2014 Ravenbrook Limited. All rights reserved.
+Copyright © 1996-2015 Ravenbrook Limited. All rights reserved.
. This is an open source license. Contact
Ravenbrook for commercial licensing options.
diff --git a/mps/design/land.txt b/mps/design/land.txt
index f99ff95baba..df16fc922b9 100644
--- a/mps/design/land.txt
+++ b/mps/design/land.txt
@@ -86,7 +86,7 @@ indicating whether to continue with the iteration.
``typedef Bool (*LandDeleteVisitor)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS)``
-_`.type.visitor`: Type ``LandDeleteVisitor`` is a callback function that may
+_`.type.deletevisitor`: Type ``LandDeleteVisitor`` is a callback function that may
be passed to ``LandIterateAndDelete()``. It is called for every isolated
contiguous range in address order. The function must return a ``Bool``
indicating whether to continue with the iteration. It may additionally
@@ -187,6 +187,16 @@ _`.function.iterate.and.delete`: As ``LandIterate()``, but the visitor
function additionally returns a Boolean indicating whether the range
should be deleted from the land.
+_`.function.iterate.and.delete.justify`: The reason for having both
+``LandIterate()`` and ``LandIterateAndDelete()`` is that it may be
+possible to use a more efficient algorithm, or to preserve more
+properties of the data structure, when it is known that the land willl
+not be modified during the iteration. For example, in the CBS
+implementation, ``LandIterate()`` uses ``TreeTraverse()`` which
+preserves the tree structure, whereas ``LandIterateAndDelete()`` uses
+``TreeTraverseAndDelete()`` which flattens the tree structure, losing
+information about recently accessed nodes.
+
``Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)``
_`.function.find.first`: Locate the first block (in address order)
@@ -311,7 +321,7 @@ Document History
Copyright and License
---------------------
-Copyright © 2014 Ravenbrook Limited. All rights reserved.
+Copyright © 2014-2015 Ravenbrook Limited. All rights reserved.
. This is an open source license. Contact
Ravenbrook for commercial licensing options.
diff --git a/mps/manual/source/pool/amc.rst b/mps/manual/source/pool/amc.rst
index 8c74d7119a6..a21594202c6 100644
--- a/mps/manual/source/pool/amc.rst
+++ b/mps/manual/source/pool/amc.rst
@@ -128,8 +128,10 @@ AMC interface
pointers` keep objects alive.
* :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`,
- default 4096) is the default :term:`size` of block that the pool
- will request from the :term:`arena`.
+ default 4096) is the minimum :term:`size` of the memory segments
+ that the pool requests from the :term:`arena`. Larger segments
+ reduce the per-segment overhead, but increase
+ :term:`fragmentation` and :term:`retention`.
For example::
diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst
index b3037c28920..b7f580c7286 100644
--- a/mps/manual/source/release.rst
+++ b/mps/manual/source/release.rst
@@ -12,6 +12,11 @@ Release 1.115.0
New features
............
+#. When creating an :ref:`pool-amc` pool, :c:func:`mps_pool_create_k`
+ accepts the new keyword argument :c:macro:`MPS_KEY_EXTEND_BY`,
+ specifying the minimum size of the memory segments that the pool
+ requests from the :term:`arena`.
+
#. The function :c:func:`mps_arena_create_k` accepts two new
:term:`keyword arguments`. :c:macro:`MPS_KEY_ARENA_COMMIT_LIMIT`
sets the :term:`commit limit` for the arena, and
@@ -49,6 +54,30 @@ Other changes
.. _job001887: https://www.ravenbrook.com/project/mps/issue/job001887/
+#. :ref:`pool-amc` pools now assert that exact references into the
+ pool are aligned to the pool's alignment. See job002175_.
+
+ .. _job002175: https://www.ravenbrook.com/project/mps/issue/job002175/
+
+#. Internal calculation of the address space available to the MPS no
+ longer takes time proportional to the number of times the arena has
+ been extended, speeding up allocation when memory is tight. See
+ job003814_.
+
+ .. _job003814: https://www.ravenbrook.com/project/mps/issue/job003814/
+
+#. Setting :c:macro:`MPS_KEY_SPARE` for a :ref:`pool-mvff` pool now
+ works. See job003870_.
+
+ .. _job003870: https://www.ravenbrook.com/project/mps/issue/job003870/
+
+#. When the arena is out of memory and cannot be extended without
+ hitting the :term:`commit limit`, the MPS now returns
+ :c:macro:`MPS_RES_COMMIT_LIMIT` rather than substituting
+ :c:macro:`MPS_RES_RESOURCE`. See job003899_.
+
+ .. _job003899: https://www.ravenbrook.com/project/mps/issue/job003899/
+
.. _release-notes-1.114:
@@ -180,8 +209,8 @@ Other changes
#. Allocation into :ref:`pool-awl` pools again reliably provokes
garbage collections of the generation that the pool belongs to. (In
- release 1.113.0, the generation would only be collected if a pool
- of some other class allocated into it.) See job003772_.
+ version 1.113, the generation would only be collected if a pool of
+ some other class allocated into it.) See job003772_.
.. _job003772: https://www.ravenbrook.com/project/mps/issue/job003772/
@@ -193,13 +222,21 @@ Other changes
.. _job003773: https://www.ravenbrook.com/project/mps/issue/job003773/
#. 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.
+ around 25% faster (in our benchmarks) than they were in version
+ 1.113.
#. The default assertion handler in the default :term:`plinth` now
flushes the telemetry stream before aborting. See
:c:func:`mps_lib_assert_fail`.
+#. Garbage collection performance is substantially improved in the
+ situation where the arena has been extended many times. Critical
+ operations now take time logarithmic in the number of times the
+ arena has been extended (rather than linear, as in version 1.113
+ and earlier). See job003554_.
+
+ .. _job003554: https://www.ravenbrook.com/project/mps/issue/job003554/
+
.. _release-notes-1.113:
diff --git a/mps/test/README b/mps/test/README
index 18bc2ed675e..90edaf8857f 100644
--- a/mps/test/README
+++ b/mps/test/README
@@ -13,7 +13,7 @@ From the test directory::
PLATFORM=lii6ll # substitute your platform
CODE=../code # code directory of the branch you are testing
- make -C $CODE -f $PLATFORM.gmk VARIETY=cool $PLATFORM/cool/mps.o
+ make -B -C $CODE -f $PLATFORM.gmk VARIETY=cool $PLATFORM/cool/mps.o
alias qa="perl test/qa -i $CODE -l $CODE/$PLATFORM/cool/mps.o"
qa clib
qa run function/5.c