1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-28 07:50:48 -08:00

Catch-up merge from master into branch/2023-05-31/hash-arrays

This commit is contained in:
Richard Brooksby 2023-06-17 11:30:34 +01:00
commit 4823617e1b
23 changed files with 1200 additions and 127 deletions

240
mps/code/addrobj.c Normal file
View file

@ -0,0 +1,240 @@
/* addrobj.c: BASE ADDRESS FROM INTERIOR POINTER TEST
*
* Copyright (c) 2023 Ravenbrook Limited. See end of file for license.
*
* .overview This test is for mps_addr_object(). Its intention is to
* verify that the function returns the appropriate base pointer to an
* object when provided with an interior pointer. It also tests that the
* function fails appropriately when the provided with a pointer to
* unmanaged memory, or to an object in a pool that doesn't support this
* feature.
*
* .limitations Objects that have been moved should cause the function to
* fail with MPS_RES_FAIL, however this is not tested. It could be tested if
* a testbench deliberately created a forwarding object, however this might
* confuse a pool that does automatic garbage collection such as AMC or AMCZ,
* so any such test would need to be designed to handle that.
* This test only examines behaviour in AMCZ and MVFF pools, i.e. A pool (AMCZ)
* which currently implements mps_addr_object() and one (MVFF) that doesn't.
*/
#include "mps.h"
#include "testlib.h"
#include "fmtdy.h"
#include "fmtdytst.h"
#include "mpsavm.h"
#include "mpscamc.h"
#include "mpscmvff.h"
#include "stdio.h"
#include <stdlib.h>
/* Define an object size to allocate. The size chosen doesn't matter much, except that this testbench assumes
that the object is large enough that a pointer could point to the interior of the object, without also
pointing to the base pointer of the object at the same time. For char pointers, this is probably 2 bytes.
Since we are using the Dylan library, we define the size of the object in terms of Dylan slots. See
fmtdytst.c for details of the Dylan object structure.*/
#define N_SLOT_TESTOBJ 100
static void test_main(void)
{
mps_arena_t arena;
mps_pool_t amcz_pool, mvff_pool;
mps_ap_t obj_ap;
mps_fmt_t obj_fmt;
mps_root_t testobj_root;
mps_res_t res;
/* In another testbench (extcon.c) we observed unreliable failures to do with registering the cold end
of the stack. See GitHub issue #210
<https://github.com/Ravenbrook/mps/issues/210>. For now, we
declare this as a separate root. */
static mps_addr_t testobj;
mps_addr_t out, in;
/* Create arena */
die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "mps_arena_create_k");
/* INTRO TO TESTS: There are several tests. They test the expected "normal" operation of the
function, using an interior pointer, also corner cases where the interior pointer equals the
base pointer, where it equals the limit pointer. We also test asking about an address in unmanaged
memory, and about an address in a pool which currently does not support mps_addr_object. If you write
more tests, describe them here.*/
/* TEST 1: Test using an interior pointer in an object in an AMCZ pool.
At the time of writing this test, the AMCZ pool is the only pool where
there exists a requirement to provide base addresses from interior pointers.
Currently, the AMCZ pool (and by extension, the AMC pool which shares the same
module as AMCZ) is the only pool for which mps_addr_object is implemented */
/* Use the dylan format for convenience */
die(dylan_fmt(&obj_fmt, arena), "dylan_fmt");
/* Create the pool */
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt);
die(mps_pool_create_k(&amcz_pool, arena, mps_class_amcz(), args), "mps_pool_create_k amcz");
} MPS_ARGS_END(args);
/* Create an area of ambiguous pointers to keep the object alive and in place, in this case
the area only contains room for a single reference since we are only using one object to test */
die(mps_root_create_area(&testobj_root, arena,
mps_rank_ambig(), (mps_rm_t)0,
&testobj, &testobj+1,
mps_scan_area, NULL),
"mps_root_create_area");
/* Create the allocation point */
die(mps_ap_create_k(&obj_ap, amcz_pool, mps_args_none), "mps_ap_create_k");
/* Make a Dylan object, size = (N_SLOT_TESTOBJ+2) * sizeof(mps_word_t).
(See fmtdytst.c for size calculation) */
{
/* Because make_dylan_vector returns its pointer-to-object as an mps_word_t rather than an
mps_addr_t, and commits the object, we need to somehow safely allocate our object without
type punning and without risking that our object be destroyed.
Rather than redefine our reference table with type mps_word_t, which hides the intention of the table,
park the arena to disable garbage collection. Allocate our dylan object on the (unregistered) stack
storing its address in an mps_word_t. Then store this mps_word_t as an mps_addr_t in our reference
table, and release the arena since our object is now safely pinned.
Another approach would be to create another static registered root for ambiguous references of type
mps_word_t and then copy to the mps_addr_t root, which would avoid needing to park the arena.
*/
mps_word_t p_word;
mps_arena_park(arena);
die(make_dylan_vector(&p_word, obj_ap, N_SLOT_TESTOBJ), "make_dylan_vector");
/* If we hadn't parked the arena, our vector might have been GC'd here */
testobj = (mps_addr_t)p_word;
mps_arena_release(arena);
}
/* Construct a pointer to roughly halfway inside the object */
in = (mps_addr_t)((char *)testobj + (N_SLOT_TESTOBJ/2) * sizeof(mps_word_t));
/* Ensure that this is an interior pointer, and not the base pointer,
since we want to make sure we are testing with a true interior pointer and not
one that also happens to be the base pointer. This Insist is intended to protect
against the testbench losing its ability to test "true" interior pointers (i.e. ones
which don't match the base pointer) if the test object sizes were changed to be very
small. Note that we don't currently consider the "limit" of the object as a corner case
(so we don't Insist(in != limit) ) but we do consider limit+1, i.e. the pointer to the
next object to be a corner case. This test could be updated to consider in == limit as a
corner case. */
Insist(in > testobj);
/* Do Test */
res = mps_addr_object(&out, arena, in);
Insist(out == testobj);
Insist(res == MPS_RES_OK);
printf("Interior pointer input: passed\n");
/* TEST 2: Test using the base pointer itself as an input*/
in = testobj;
/* Do Test */
res = mps_addr_object(&out, arena, in);
Insist(out == testobj);
Insist(res == MPS_RES_OK);
printf("Base pointer input: passed\n");
/* TEST 3: Test using a pointer one-off-the-end of the object*/
in = (mps_addr_t)((char *)testobj + (N_SLOT_TESTOBJ + 2) * sizeof(mps_word_t));
/* Do Test */
res = mps_addr_object(&out, arena, in);
Insist(res == MPS_RES_FAIL);
printf("Pointer to next object input: passed\n");
/* Clean up from above tests */
mps_root_destroy(testobj_root);
mps_ap_destroy(obj_ap);
mps_pool_destroy(amcz_pool);
mps_fmt_destroy(obj_fmt);
/* TEST 4: Test using a pointer in unmanaged memory */
/* Use malloc to allocate non-mps-managed memory on the heap */
in = malloc(sizeof(mps_word_t));
Insist(NULL != in);
/* Do the test */
res = mps_addr_object(&out, arena, in);
/* Expect MPS to fail to find a base pointer for addresses not in managed memory */
Insist(res == MPS_RES_FAIL);
printf("Pointer to unmanaged memory input: passed\n");
/* clean up from this test */
if (NULL != in)
free(in);
/* TEST 5: Test using a pointer in a pool which currently doesn't implement mps_addr_object */
/* Create mvff pool for which mps_addr_object is not implemented */
die(mps_pool_create_k(&mvff_pool, arena, mps_class_mvff(), mps_args_none), "mps_pool_create_k mvff");
/* allocate an object (just some memory) in this pool */
die(mps_alloc(&in, mvff_pool, sizeof(mps_word_t)), "mps_alloc");
/* Do the test */
res = mps_addr_object(&out, arena, in);
Insist(res == MPS_RES_UNIMPL);
printf("Pointer to object in pool where mps_addr_object not implemented: passed\n");
/* If more tests are added here, briefly describe them above under "INTRO TO TESTS" comment */
/* Final clean up */
mps_free(mvff_pool, in, sizeof(mps_word_t));
mps_pool_destroy(mvff_pool);
mps_arena_destroy(arena);
}
int main(int argc, char *argv[])
{
testlib_init(argc, argv);
test_main();
printf("%s: Conculsion, failed to find any defects.\n", argv[0]);
return 0;
}
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2022-2023 Ravenbrook Limited <https://www.ravenbrook.com/>.
*
* 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.
*
* 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 AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR 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.
*/

View file

@ -1364,6 +1364,22 @@ Bool ArenaHasAddr(Arena arena, Addr addr)
return TractOfAddr(&tract, arena, addr);
}
/* ArenaAddrObject -- return base pointer of managed object */
Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr)
{
Tract tract;
AVER(pReturn != NULL);
AVERT(Arena, arena);
if (!TractOfAddr(&tract, arena, addr)) {
/* address does not belong to the arena */
return ResFAIL;
}
return PoolAddrObject(pReturn, TractPool(tract), addr);
}
/* C. COPYRIGHT AND LICENSE
*

View file

@ -428,6 +428,10 @@ static Bool vmChunkDestroy(Tree tree, void *closure)
{
Chunk chunk;
VMChunk vmChunk;
Arena arena;
Addr base;
Size size;
VMArena vmArena;
AVERT(Tree, tree);
AVER(closure == UNUSED_POINTER);
@ -437,8 +441,14 @@ static Bool vmChunkDestroy(Tree tree, void *closure)
AVERT(Chunk, chunk);
vmChunk = Chunk2VMChunk(chunk);
AVERT(VMChunk, vmChunk);
arena = ChunkArena(chunk);
vmArena = MustBeA(VMArena, arena);
base = chunk->base;
size = ChunkSize(chunk);
(void)vmArenaUnmapSpare(ChunkArena(chunk), ChunkSize(chunk), chunk);
(*vmArena->contracted)(arena, base, size);
(void)vmArenaUnmapSpare(arena, size, chunk);
SparseArrayFinish(&vmChunk->pages);
@ -778,6 +788,7 @@ static void VMArenaDestroy(Arena arena)
* <design/arena#.chunk.delete> */
arena->primary = NULL;
TreeTraverseAndDelete(&arena->chunkTree, vmChunkDestroy, UNUSED_POINTER);
AVER(arena->chunkTree == TreeEMPTY);
/* Must wait until the chunks are destroyed, since vmChunkDestroy
calls vmArenaUnmapSpare which uses the spare land. */
@ -1223,7 +1234,6 @@ static Bool vmChunkCompact(Tree tree, void *closure)
{
Chunk chunk;
Arena arena = closure;
VMArena vmArena = MustBeA(VMArena, arena);
AVERT(Tree, tree);
@ -1232,11 +1242,6 @@ static Bool vmChunkCompact(Tree tree, void *closure)
if(chunk != arena->primary
&& BTIsResRange(chunk->allocTable, 0, chunk->pages))
{
Addr base = chunk->base;
Size size = ChunkSize(chunk);
/* Callback before destroying the chunk, as the arena is (briefly)
invalid afterwards. See job003893. */
(*vmArena->contracted)(arena, base, size);
vmChunkDestroy(tree, UNUSED_POINTER);
return TRUE;
} else {

View file

@ -253,6 +253,7 @@ LIB_TARGETS=mps.a mpsplan.a
TEST_TARGETS=\
abqtest \
addrobj \
airtest \
amcss \
amcsshe \
@ -267,6 +268,7 @@ TEST_TARGETS=\
btcv \
bttest \
djbench \
extcon \
finalcv \
finaltest \
forktest \
@ -445,6 +447,9 @@ ifdef VARIETY
$(PFM)/$(VARIETY)/abqtest: $(PFM)/$(VARIETY)/abqtest.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
$(PFM)/$(VARIETY)/addrobj: $(PFM)/$(VARIETY)/addrobj.o \
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
$(PFM)/$(VARIETY)/airtest: $(PFM)/$(VARIETY)/airtest.o \
$(FMTSCMOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
@ -487,6 +492,9 @@ $(PFM)/$(VARIETY)/bttest: $(PFM)/$(VARIETY)/bttest.o \
$(PFM)/$(VARIETY)/djbench: $(PFM)/$(VARIETY)/djbench.o \
$(TESTLIBOBJ) $(TESTTHROBJ)
$(PFM)/$(VARIETY)/extcon: $(PFM)/$(VARIETY)/extcon.o \
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
$(PFM)/$(VARIETY)/finalcv: $(PFM)/$(VARIETY)/finalcv.o \
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a

View file

@ -177,6 +177,9 @@ $(PFM)\cool\mps.lib: $(MPMOBJ)
$(PFM)\$(VARIETY)\abqtest.exe: $(PFM)\$(VARIETY)\abqtest.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
$(PFM)\$(VARIETY)\addrobj.exe: $(PFM)\$(VARIETY)\addrobj.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
$(PFM)\$(VARIETY)\airtest.exe: $(PFM)\$(VARIETY)\airtest.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTSCHEMEOBJ) $(TESTLIBOBJ)
@ -225,6 +228,9 @@ $(PFM)\$(VARIETY)\cvmicv.exe: $(PFM)\$(VARIETY)\cvmicv.obj \
$(PFM)\$(VARIETY)\djbench.exe: $(PFM)\$(VARIETY)\djbench.obj \
$(TESTLIBOBJ) $(TESTTHROBJ)
$(PFM)\$(VARIETY)\extcon.exe: $(PFM)\$(VARIETY)\extcon.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
$(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)

View file

@ -59,6 +59,7 @@ LIB_TARGETS=mps.lib
TEST_TARGETS=\
abqtest.exe \
addrobj.exe \
airtest.exe \
amcss.exe \
amcsshe.exe \
@ -73,6 +74,7 @@ TEST_TARGETS=\
btcv.exe \
bttest.exe \
djbench.exe \
extcon.exe \
finalcv.exe \
finaltest.exe \
fotest.exe \

306
mps/code/extcon.c Normal file
View file

@ -0,0 +1,306 @@
/* extcon.c: ARENA EXTENDED AND CONTRACTED CALLBACK TEST
*
* $Id$
* Copyright (c) 2022-2023 Ravenbrook Limited. See end of file for license.
*
* .overview: This test case allocates a bunch of large objects, of a size
* similar to the size of the arena, to force the arena to extend. It then
* discards the base pointers to those objects, and forces a collection.
*
* .limitations: This test checks that the EXTENDED and CONTRACTED
* callbacks were called at least once, and that they are called the
* same number of times. It does not check that the extensions and
* contractions themselves were performed correctly, nor does it check
* that an appropriate number of extensions and contractions took
* place, nor does it check that they took place at sensible times.
*
* .dylan: This test uses Dylan format objects in common with most
* other tests for convenience and brevity.
*/
#include "mps.h"
#include "testlib.h"
#include "fmtdy.h"
#include "fmtdytst.h"
#include "mpsavm.h"
#include "mpscamc.h"
#include <stdio.h>
#include <stdlib.h>
/* Number of test objects to allocate */
#define N_TESTOBJ 100
/* The number of slots determines the size of each object */
#define N_SLOT_TESTOBJ 10000
/* The initial arena size is requested to be bigger the test object by
this many bytes */
#define SIZEDIFF 10
/* Set alignment to mps_word_ts */
#define ALIGNMENT sizeof(mps_word_t)
/* Align size upwards to the next multiple of the word size. */
#define ALIGN_WORD(size) \
(((size) + ALIGNMENT - 1) & ~(ALIGNMENT - 1))
/* Global objects*/
static mps_arena_t arena; /* the arena */
static mps_pool_t obj_pool; /* pool for test objects */
static mps_ap_t obj_ap; /* allocation point used to allocate objects */
/* Count of number of arena contractions and extensions */
static int n_contract = 0;
static int n_extend = 0;
/* Callback functions for arena extension and contraction */
static void arena_extended_cb(mps_arena_t arena_in, mps_addr_t addr, size_t size)
{
testlib_unused(arena_in);
testlib_unused(addr);
testlib_unused(size);
printf("Arena extended by %"PRIuLONGEST" bytes\n", (ulongest_t)size);
n_extend++;
}
static void arena_contracted_cb(mps_arena_t arena_in, mps_addr_t addr, size_t size)
{
testlib_unused(arena_in);
testlib_unused(addr);
testlib_unused(size);
printf("Arena contracted by %"PRIuLONGEST" bytes\n", (ulongest_t)size);
n_contract++;
}
/* Messages for testbench debugging */
static void print_messages(void)
{
mps_message_type_t type;
while (mps_message_queue_type(&type, arena)) {
mps_message_t message;
cdie(mps_message_get(&message, arena, type),
"get");
switch(type) {
case mps_message_type_gc_start():
printf("GC start at %"PRIuLONGEST": %s\n",
(ulongest_t)mps_message_clock(arena, message),
mps_message_gc_start_why(arena, message));
break;
case mps_message_type_gc():
printf("GC end at %"PRIuLONGEST" "
"condemned %"PRIuLONGEST" "
"not condemned %"PRIuLONGEST" "
"live %"PRIuLONGEST"\n",
(ulongest_t)mps_message_clock(arena, message),
(ulongest_t)mps_message_gc_condemned_size(arena, message),
(ulongest_t)mps_message_gc_not_condemned_size(arena, message),
(ulongest_t)mps_message_gc_live_size(arena, message));
break;
default:
cdie(0, "message type");
break;
}
mps_message_discard(arena, message);
}
}
/* Disabling inlining is necessary (but perhaps not sufficient) if using stack roots.
See comment below with link to GitHub issue*/
ATTRIBUTE_NOINLINE
static void test_main(void *cold_stack_end)
{
mps_fmt_t obj_fmt;
mps_thr_t thread;
mps_root_t stack_root, testobj_root;
size_t arena_size, obj_size;
int i;
/* In the original version of extcon this was a stack root, but we
observed unreliable failures to do with registering the cold end
of the stack. See GitHub issue #210
<https://github.com/Ravenbrook/mps/issues/210>. For now, we
declare this as a separate root. */
static mps_word_t testobj[N_TESTOBJ];
/* The testobj array must be below (on all current Posix platforms)
the cold end of the stack in order for the MPS to scan it. We
have observed a Heisenbug where GCC will inline test_main into
main and lose this condition if the expression below is removed.
This is a problem we are analysing in GitHub issue #210
<https://github.com/Ravenbrook/mps/issues/210>. For now, we
disable this Insist to allow the test to run with a static
testobj array. */
#if 0
Insist((void *)&testobj[N_TESTOBJ] <= cold_stack_end);
if ((void *)&testobj[N_TESTOBJ] > cold_stack_end)
printf("Cold stack marker invalid!\n");
else
printf("Cold stack marker probably valid.\n");
#endif
/* Make initial arena size slightly bigger than the test object size to force an extension as early as possible */
/* See definition of make_dylan_vector() in fmtdytst.c for calculation of vector size */
obj_size = ALIGN_WORD((N_SLOT_TESTOBJ + 2) * sizeof(mps_word_t));
arena_size = ALIGN_WORD(obj_size + SIZEDIFF);
/* Create arena and register callbacks */
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arena_size);
MPS_ARGS_ADD(args, MPS_KEY_ARENA_EXTENDED, (mps_fun_t)&arena_extended_cb);
MPS_ARGS_ADD(args, MPS_KEY_ARENA_CONTRACTED, (mps_fun_t)&arena_contracted_cb);
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "mps_arena_create_k");
} MPS_ARGS_END(args);
printf("Initial reservation %"PRIuLONGEST".\n", (ulongest_t)mps_arena_reserved(arena));
die(dylan_fmt(&obj_fmt, arena), "dylan_fmt()");
/* Create new pool */
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt);
die(mps_pool_create_k(&obj_pool, arena, mps_class_amcz(), args),
"mps_pool_create_k");
} MPS_ARGS_END(args);
/* Register thread */
die(mps_thread_reg(&thread, arena), "Thread reg");
/* Register stack roots */
/* Since this testbench is currently not using a stack root, #IF 0 this out */
testlib_unused(cold_stack_end);
testlib_unused(stack_root);
#if 0
die(mps_root_create_thread(&stack_root, arena, thread, cold_stack_end), "Create Stack root");
#endif
/* Register ambiguous array of object roots. */
die(mps_root_create_area(&testobj_root, arena,
mps_rank_ambig(), (mps_rm_t)0,
&testobj[0], &testobj[N_TESTOBJ],
mps_scan_area, NULL),
"root_create_area(testobj)");
/* Create allocation point */
die(mps_ap_create_k(&obj_ap, obj_pool, mps_args_none), "Create Allocation point");
mps_message_type_enable(arena, mps_message_type_gc_start());
mps_message_type_enable(arena, mps_message_type_gc());
/* Allocate objects and force arena extension */
for (i = 0; i < N_TESTOBJ; i++) {
die(make_dylan_vector(&testobj[i], obj_ap, N_SLOT_TESTOBJ), "make_dylan_vector");
printf("Object %d committed. "
"Arena reserved: %"PRIuLONGEST".\n",
i,
(ulongest_t)mps_arena_reserved(arena));
print_messages();
}
/* overwrite all the references to the objects*/
for (i = 0; i < N_TESTOBJ; i++) {
/* bonus test of mps_addr_object */
#if 0 /* Comment this out until mps_addr_object becomes available. */
mps_addr_t out;
Insist(N_TESTOBJ <= N_INT_TESTOBJ);
/* use "i" to as a convenient way to generate different interior pointers
To guarantee the i index will give us an interior pointer the number of test
objects must be <= the number of integers in each object */
Insist(N_TESTOBJ <= N_INT_TESTOBJ);
die(mps_addr_object(&out, arena, &(testobj[i])->int_array[i]), "Address object");
Insist(out == testobj[i]);
/* end piggy back testbench */
#endif
/* now overwrite the ref */
testobj[i] = (mps_word_t)NULL;
print_messages();
}
/* Collect */
mps_arena_collect(arena);
print_messages();
/* Clean up */
mps_root_destroy(testobj_root);
/* mps_root_destroy(stack_root);*/ /*commented out while not using stack root */
mps_thread_dereg(thread);
mps_ap_destroy(obj_ap);
mps_pool_destroy(obj_pool);
mps_fmt_destroy(obj_fmt);
mps_arena_destroy(arena);
/* Destroying the arena should cause contraction callbacks on all
remaining chunks, even if they had contents. */
Insist(n_extend == n_contract);
printf("Arena extended %d times\n", n_extend);
printf("Arena contracted %d times\n", n_contract);
/* comment out some diagnostics for investigating issue #210 mentioned above */
#if 0
printf("&testobj[N_TESTOBJ] = %p\n", (void *)&testobj[N_TESTOBJ]);
printf("cold_stack_end = %p\n", cold_stack_end);
#endif
if (n_extend == 0)
printf("No callbacks received upon arena extended!\n");
if (n_contract == 0)
printf("No callbacks received upon arena contracted!\n");
if (n_contract == 0 || n_extend == 0)
exit(EXIT_FAILURE);
}
int main(int argc, char* argv[])
{
void *stack_marker = &stack_marker;
testlib_init(argc, argv);
test_main(stack_marker);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
return 0;
}
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2022-2023 Ravenbrook Limited <https://www.ravenbrook.com/>.
*
* 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.
*
* 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 AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR 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.
*/

View file

@ -236,6 +236,7 @@ extern Res PoolTraceBegin(Pool pool, Trace trace);
extern void PoolFreeWalk(Pool pool, FreeBlockVisitor f, void *p);
extern Size PoolTotalSize(Pool pool);
extern Size PoolFreeSize(Pool pool);
extern Res PoolAddrObject(Addr *pReturn, Pool pool, Addr addr);
extern Res PoolAbsInit(Pool pool, Arena arena, PoolClass klass, ArgList arg);
extern void PoolAbsFinish(Inst inst);
@ -267,6 +268,7 @@ extern void PoolTrivFreeWalk(Pool pool, FreeBlockVisitor f, void *p);
extern PoolDebugMixin PoolNoDebugMixin(Pool pool);
extern BufferClass PoolNoBufferClass(void);
extern Size PoolNoSize(Pool pool);
extern Res PoolTrivAddrObject(Addr *pReturn, Pool pool, Addr addr);
/* See .critical.macros. */
#define PoolFreeMacro(pool, old, size) Method(Pool, pool, free)(pool, old, size)
@ -536,6 +538,7 @@ extern Res ArenaStartCollect(Globals globals, TraceStartWhy why);
extern Res ArenaCollect(Globals globals, TraceStartWhy why);
extern Bool ArenaBusy(Arena arena);
extern Bool ArenaHasAddr(Arena arena, Addr addr);
extern Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr);
extern void ArenaChunkInsert(Arena arena, Chunk chunk);
extern void ArenaChunkRemoved(Arena arena, Chunk chunk);
extern void ArenaAccumulateTime(Arena arena, Clock start, Clock now);

View file

@ -60,6 +60,7 @@ typedef struct mps_pool_class_s {
PoolRampEndMethod rampEnd; /* end a ramp pattern */
PoolFramePushMethod framePush; /* push an allocation frame */
PoolFramePopMethod framePop; /* pop an allocation frame */
PoolAddrObjectMethod addrObject; /* return object's base pointer */
PoolFreeWalkMethod freewalk; /* walk over free blocks */
PoolBufferClassMethod bufferClass; /* default BufferClass of pool */
PoolDebugMixinMethod debugMixin; /* find the debug mixin, if any */

View file

@ -214,6 +214,7 @@ typedef Res (*PoolFramePushMethod)(AllocFrame *frameReturn,
Pool pool, Buffer buf);
typedef Res (*PoolFramePopMethod)(Pool pool, Buffer buf,
AllocFrame frame);
typedef Res (*PoolAddrObjectMethod)(Addr *pReturn, Pool pool, Addr addr);
typedef void (*PoolFreeWalkMethod)(Pool pool, FreeBlockVisitor f, void *p);
typedef BufferClass (*PoolBufferClassMethod)(void);
typedef PoolDebugMixin (*PoolDebugMixinMethod)(Pool pool);

View file

@ -120,6 +120,13 @@ typedef mps_addr_t (*mps_fmt_isfwd_t)(mps_addr_t);
typedef void (*mps_fmt_pad_t)(mps_addr_t, size_t);
typedef mps_addr_t (*mps_fmt_class_t)(mps_addr_t);
/* Callbacks indicating that the arena has extended or contracted.
* These are used to register chunks with RtlInstallFunctionTableCallback
* <https://docs.microsoft.com/en-gb/windows/win32/api/winnt/nf-winnt-rtlinstallfunctiontablecallback>
* so that the client can unwind the stack through functions in the arena.
*/
typedef void (*mps_arena_extended_t)(mps_arena_t, void *, size_t);
typedef void (*mps_arena_contracted_t)(mps_arena_t, void *, size_t);
/* Keyword argument lists */
@ -171,6 +178,12 @@ extern const struct mps_key_s _mps_key_ARENA_SIZE;
extern const struct mps_key_s _mps_key_ARENA_ZONED;
#define MPS_KEY_ARENA_ZONED (&_mps_key_ARENA_ZONED)
#define MPS_KEY_ARENA_ZONED_FIELD b
extern const struct mps_key_s _mps_key_arena_extended;
#define MPS_KEY_ARENA_EXTENDED (&_mps_key_arena_extended)
#define MPS_KEY_ARENA_EXTENDED_FIELD fun
extern const struct mps_key_s _mps_key_arena_contracted;
#define MPS_KEY_ARENA_CONTRACTED (&_mps_key_arena_contracted)
#define MPS_KEY_ARENA_CONTRACTED_FIELD fun
extern const struct mps_key_s _mps_key_FORMAT;
#define MPS_KEY_FORMAT (&_mps_key_FORMAT)
#define MPS_KEY_FORMAT_FIELD format
@ -835,6 +848,8 @@ extern mps_res_t _mps_fix2(mps_ss_t, mps_addr_t *);
(ss)->_ufs = _mps_ufs; \
MPS_END
/* Misc interface */
extern mps_res_t mps_addr_object(mps_addr_t *p_o, mps_arena_t arena, mps_addr_t addr);
#endif /* mps_h */

View file

@ -73,6 +73,7 @@
buildPhases = (
);
dependencies = (
319F7A192A30D2F000E5B418 /* PBXTargetDependency */,
3104AFF6156D37BC000A585A /* PBXTargetDependency */,
3114A644156E94FB001E0AA3 /* PBXTargetDependency */,
22FACEF1188809B5000FDBC1 /* PBXTargetDependency */,
@ -89,8 +90,6 @@
3114A677156E961C001E0AA3 /* PBXTargetDependency */,
3114A612156E943B001E0AA3 /* PBXTargetDependency */,
22B2BC3D18B643B300C33E63 /* PBXTargetDependency */,
2291A5E6175CB207001D4920 /* PBXTargetDependency */,
2291A5E8175CB20E001D4920 /* PBXTargetDependency */,
3114A5CC156E932C001E0AA3 /* PBXTargetDependency */,
3114A5EA156E93C4001E0AA3 /* PBXTargetDependency */,
22EA3F4820D2B23F0065F5B6 /* PBXTargetDependency */,
@ -158,16 +157,6 @@
2291A5B5175CAB2F001D4920 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
2291A5B7175CAB2F001D4920 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
2291A5BE175CAB4E001D4920 /* awlutth.c in Sources */ = {isa = PBXBuildFile; fileRef = 2291A5A9175CAA9B001D4920 /* awlutth.c */; };
2291A5C5175CAFCA001D4920 /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; };
2291A5C6175CAFCA001D4920 /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; };
2291A5C7175CAFCA001D4920 /* fmtno.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CACC156BE4C200753214 /* fmtno.c */; };
2291A5C8175CAFCA001D4920 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
2291A5CB175CAFCA001D4920 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
2291A5D8175CB05F001D4920 /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; };
2291A5D9175CB05F001D4920 /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; };
2291A5DA175CB05F001D4920 /* fmtno.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CACC156BE4C200753214 /* fmtno.c */; };
2291A5DB175CB05F001D4920 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
2291A5DD175CB05F001D4920 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
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 */; };
@ -301,6 +290,12 @@
3124CAFC156BE82900753214 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
3150AE53156ABA2500A6E22A /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
318DA8D31892B27E0089718C /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
319F7A082A30D08500E5B418 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
319F7A0A2A30D08500E5B418 /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; };
319F7A0B2A30D08500E5B418 /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; };
319F7A0C2A30D08500E5B418 /* fmtno.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CACC156BE4C200753214 /* fmtno.c */; };
319F7A0E2A30D08500E5B418 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
319F7A172A30D11400E5B418 /* addrobj.c in Sources */ = {isa = PBXBuildFile; fileRef = 319F7A152A30D11400E5B418 /* addrobj.c */; };
31A47BA4156C1E130039B1C2 /* mps.c in Sources */ = {isa = PBXBuildFile; fileRef = 31A47BA3156C1E130039B1C2 /* mps.c */; };
31D60007156D3C6200337B26 /* segsmss.c in Sources */ = {isa = PBXBuildFile; fileRef = 31D60006156D3C5F00337B26 /* segsmss.c */; };
31D60008156D3C7400337B26 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
@ -468,20 +463,6 @@
remoteGlobalIDString = 2291A5AC175CAB2F001D4920;
remoteInfo = awlutth;
};
2291A5C3175CAFCA001D4920 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 31EEABFA156AAF9D00714D05;
remoteInfo = mps;
};
2291A5D5175CB05F001D4920 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 31EEABFA156AAF9D00714D05;
remoteInfo = mps;
};
229E228719EAB10D00E21417 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
@ -923,6 +904,20 @@
remoteGlobalIDString = 31108A3A1C6B90E900E728EA;
remoteInfo = tagtest;
};
319F7A062A30D08500E5B418 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 31EEABFA156AAF9D00714D05;
remoteInfo = mps;
};
319F7A182A30D2F000E5B418 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 319F7A042A30D08500E5B418;
remoteInfo = addrobj;
};
31A47BA9156C210D0039B1C2 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
@ -1092,24 +1087,6 @@
);
runOnlyForDeploymentPostprocessing = 1;
};
2291A5CC175CAFCA001D4920 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
2291A5DE175CB05F001D4920 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
22B2BC3118B6434F00C33E63 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@ -1407,6 +1384,15 @@
);
runOnlyForDeploymentPostprocessing = 1;
};
319F7A0F2A30D08500E5B418 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
31D6000B156D3CB200337B26 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@ -1729,6 +1715,8 @@
31942AA91C8EC446001AAF32 /* sp.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = sp.txt; path = ../design/sp.txt; sourceTree = "<group>"; };
31942AAB1C8EC446001AAF32 /* stack-scan.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "stack-scan.txt"; path = "../design/stack-scan.txt"; sourceTree = "<group>"; };
31942AB01C8EC446001AAF32 /* testthr.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = testthr.txt; path = ../design/testthr.txt; sourceTree = "<group>"; };
319F7A142A30D08500E5B418 /* addrobj */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = addrobj; sourceTree = BUILT_PRODUCTS_DIR; };
319F7A152A30D11400E5B418 /* addrobj.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = addrobj.c; sourceTree = "<group>"; };
31A47BA3156C1E130039B1C2 /* mps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mps.c; sourceTree = "<group>"; };
31C83ADD1786281C0031A0DB /* protxc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = protxc.h; sourceTree = "<group>"; };
31CD33BB173A9F1500524741 /* mpscams.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mpscams.h; sourceTree = "<group>"; };
@ -1856,22 +1844,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
2291A5CA175CAFCA001D4920 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2291A5CB175CAFCA001D4920 /* libmps.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2291A5DC175CB05F001D4920 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2291A5DD175CB05F001D4920 /* libmps.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
22B2BC3018B6434F00C33E63 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -2134,6 +2106,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
319F7A0D2A30D08500E5B418 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
319F7A0E2A30D08500E5B418 /* libmps.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
31D6000A156D3CB200337B26 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -2340,6 +2320,7 @@
3124CAB3156BE1B700753214 /* Tests */ = {
isa = PBXGroup;
children = (
319F7A152A30D11400E5B418 /* addrobj.c */,
3114A63D156E94EA001E0AA3 /* abqtest.c */,
22FACED1188807FF000FDBC1 /* airtest.c */,
3124CAF5156BE81100753214 /* amcss.c */,
@ -2485,6 +2466,7 @@
223E796519EAB00B00DC26A6 /* sncss */,
22EA3F4520D2B0D90065F5B6 /* forktest */,
2265D71D20E53F9C003019E8 /* mpseventpy */,
319F7A142A30D08500E5B418 /* addrobj */,
);
name = Products;
sourceTree = "<group>";
@ -3365,6 +3347,24 @@
productReference = 318DA8CD1892B0F30089718C /* djbench */;
productType = "com.apple.product-type.tool";
};
319F7A042A30D08500E5B418 /* addrobj */ = {
isa = PBXNativeTarget;
buildConfigurationList = 319F7A102A30D08500E5B418 /* Build configuration list for PBXNativeTarget "addrobj" */;
buildPhases = (
319F7A072A30D08500E5B418 /* Sources */,
319F7A0D2A30D08500E5B418 /* Frameworks */,
319F7A0F2A30D08500E5B418 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
319F7A052A30D08500E5B418 /* PBXTargetDependency */,
);
name = addrobj;
productName = finalcv;
productReference = 319F7A142A30D08500E5B418 /* addrobj */;
productType = "com.apple.product-type.tool";
};
31D6000C156D3CB200337B26 /* awluthe */ = {
isa = PBXNativeTarget;
buildConfigurationList = 31D60014156D3CB200337B26 /* Build configuration list for PBXNativeTarget "awluthe" */;
@ -3555,6 +3555,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = 31EEABD8156AAE9E00714D05;
@ -3617,6 +3618,7 @@
31FCAE0917692403008C034C /* scheme */,
22B2BC2C18B6434F00C33E63 /* scheme-advanced */,
31108A3A1C6B90E900E728EA /* tagtest */,
319F7A042A30D08500E5B418 /* addrobj */,
);
};
/* End PBXProject section */
@ -4090,6 +4092,18 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
319F7A072A30D08500E5B418 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
319F7A172A30D11400E5B418 /* addrobj.c in Sources */,
319F7A082A30D08500E5B418 /* testlib.c in Sources */,
319F7A0A2A30D08500E5B418 /* fmtdy.c in Sources */,
319F7A0B2A30D08500E5B418 /* fmtdytst.c in Sources */,
319F7A0C2A30D08500E5B418 /* fmtno.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
31D60009156D3CB200337B26 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -4287,16 +4301,6 @@
target = 2291A5AC175CAB2F001D4920 /* awlutth */;
targetProxy = 2291A5BF175CAB5F001D4920 /* PBXContainerItemProxy */;
};
2291A5C2175CAFCA001D4920 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 31EEABFA156AAF9D00714D05 /* mps */;
targetProxy = 2291A5C3175CAFCA001D4920 /* PBXContainerItemProxy */;
};
2291A5D4175CB05F001D4920 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 31EEABFA156AAF9D00714D05 /* mps */;
targetProxy = 2291A5D5175CB05F001D4920 /* PBXContainerItemProxy */;
};
229E228819EAB10D00E21417 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 223E795819EAB00B00DC26A6 /* sncss */;
@ -4612,6 +4616,16 @@
target = 31108A3A1C6B90E900E728EA /* tagtest */;
targetProxy = 314CB6EA1C6D272A0073CA42 /* PBXContainerItemProxy */;
};
319F7A052A30D08500E5B418 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 31EEABFA156AAF9D00714D05 /* mps */;
targetProxy = 319F7A062A30D08500E5B418 /* PBXContainerItemProxy */;
};
319F7A192A30D2F000E5B418 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 319F7A042A30D08500E5B418 /* addrobj */;
targetProxy = 319F7A182A30D2F000E5B418 /* PBXContainerItemProxy */;
};
31A47BAA156C210D0039B1C2 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 31EEABFA156AAF9D00714D05 /* mps */;
@ -4891,34 +4905,6 @@
};
name = Release;
};
2291A5CE175CAFCA001D4920 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
2291A5CF175CAFCA001D4920 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
2291A5E0175CB05F001D4920 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
2291A5E1175CB05F001D4920 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
22B2BC3318B6434F00C33E63 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -5633,20 +5619,6 @@
};
name = RASH;
};
318DA8E51892C0D00089718C /* RASH */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = RASH;
};
318DA8E61892C0D00089718C /* RASH */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = RASH;
};
318DA8E71892C0D00089718C /* RASH */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -5816,6 +5788,27 @@
};
name = RASH;
};
319F7A112A30D08500E5B418 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
319F7A122A30D08500E5B418 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
319F7A132A30D08500E5B418 /* RASH */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = RASH;
};
31D60015156D3CB200337B26 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -6553,6 +6546,16 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
319F7A102A30D08500E5B418 /* Build configuration list for PBXNativeTarget "addrobj" */ = {
isa = XCConfigurationList;
buildConfigurations = (
319F7A112A30D08500E5B418 /* Debug */,
319F7A122A30D08500E5B418 /* Release */,
319F7A132A30D08500E5B418 /* RASH */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
31D60014156D3CB200337B26 /* Build configuration list for PBXNativeTarget "awluthe" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "319F7A042A30D08500E5B418"
BuildableName = "addrobj"
BlueprintName = "addrobj"
ReferencedContainer = "container:mps.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
viewDebuggingEnabled = "No">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "319F7A042A30D08500E5B418"
BuildableName = "addrobj"
BlueprintName = "addrobj"
ReferencedContainer = "container:mps.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "319F7A042A30D08500E5B418"
BuildableName = "addrobj"
BlueprintName = "addrobj"
ReferencedContainer = "container:mps.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3114A5D5156E93A0001E0AA3"
BuildableName = "finaltest"
BlueprintName = "finaltest"
ReferencedContainer = "container:mps.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
viewDebuggingEnabled = "No">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3114A5D5156E93A0001E0AA3"
BuildableName = "finaltest"
BlueprintName = "finaltest"
ReferencedContainer = "container:mps.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3114A5D5156E93A0001E0AA3"
BuildableName = "finaltest"
BlueprintName = "finaltest"
ReferencedContainer = "container:mps.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -448,6 +448,37 @@ mps_bool_t mps_addr_pool(mps_pool_t *mps_pool_o,
}
/* mps_addr_object -- find base pointer of a managed object */
mps_res_t mps_addr_object(mps_addr_t *p_o, mps_arena_t arena, mps_addr_t addr)
{
Res res;
Addr p;
AVER(p_o != NULL);
/* This function cannot be called while walking the heap, unlike
* mps_arena_has_addr(). This is because it is designed to be called
* with an active mutator, so takes the arena lock. This is in order
* that it sees a consistent view of MPS structures and the heap,
* and can peek behind the barrier.
*/
ArenaEnter(arena);
AVERT(Arena, arena);
res = ArenaAddrObject(&p, arena, (Addr)addr);
ArenaLeave(arena);
/* We require the object to be ambiguously referenced (hence pinned)
* so that p doesn't become invalid before it is written to *p_o.
* (We can't simply put this write before the ArenaLeave(), because
* p_o could point to MPS-managed memory that is behind a barrier.)
*/
if (res == ResOK)
*p_o = (mps_addr_t)p;
return res;
}
/* mps_addr_fmt -- what format might this address have?
*
* .per-pool: There's no reason why all objects in a pool should have

View file

@ -57,6 +57,7 @@ Bool PoolClassCheck(PoolClass klass)
CHECKL(FUNCHECK(klass->debugMixin));
CHECKL(FUNCHECK(klass->totalSize));
CHECKL(FUNCHECK(klass->freeSize));
CHECKL(FUNCHECK(klass->addrObject));
/* Check that pool classes overide sets of related methods. */
CHECKL((klass->init == PoolAbsInit) ==
@ -303,6 +304,21 @@ Size PoolFreeSize(Pool pool)
}
/* PoolAddrObject -- return base pointer from interior pointer
*
* Note: addr is not necessarily inside the pool, even though
* mps_addr_object dispatches via the tract table. This allows this
* function to be used more generally internally. The pool should
* check (it has to anyway).
*/
Res PoolAddrObject(Addr *pReturn, Pool pool, Addr addr)
{
AVER(pReturn != NULL);
AVERT(Pool, pool);
return Method(Pool, pool, addrObject)(pReturn, pool, addr);
}
/* PoolDescribe -- describe a pool */
Res PoolDescribe(Pool pool, mps_lib_FILE *stream, Count depth)

View file

@ -173,6 +173,7 @@ DEFINE_CLASS(Pool, AbstractPool, klass)
klass->debugMixin = PoolNoDebugMixin;
klass->totalSize = PoolNoSize;
klass->freeSize = PoolNoSize;
klass->addrObject = PoolTrivAddrObject;
klass->sig = PoolClassSig;
AVERT(PoolClass, klass);
}
@ -476,6 +477,16 @@ Size PoolNoSize(Pool pool)
}
Res PoolTrivAddrObject(Addr *pReturn, Pool pool, Addr addr)
{
AVERT(Pool, pool);
AVER(pReturn != NULL);
UNUSED(addr);
return ResUNIMPL;
}
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.

View file

@ -1892,6 +1892,90 @@ static void amcWalkAll(Pool pool, FormattedObjectsVisitor f, void *p, size_t s)
}
}
/* AMCAddrObject -- return base pointer from interior pointer
*
* amcAddrObjectSearch implements the scan for an object containing
* the interior pointer by skipping using format methods.
*
* AMCAddrObject locates the segment containing the interior pointer
* and wraps amcAddrObjectSearch in the necessary shield operations to
* give it access.
*/
static Res amcAddrObjectSearch(Addr *pReturn,
Pool pool,
Addr objBase,
Addr searchLimit,
Addr addr)
{
Format format;
Size hdrSize;
AVER(pReturn != NULL);
AVERT(Pool, pool);
AVER(objBase <= searchLimit);
format = pool->format;
hdrSize = format->headerSize;
while (objBase < searchLimit) {
Addr objRef = AddrAdd(objBase, hdrSize);
Addr objLimit = AddrSub((*format->skip)(objRef), hdrSize);
AVER(objBase < objLimit);
if (addr < objLimit) {
AVER(objBase <= addr);
AVER(addr < objLimit);
/* Don't return base pointer if object is moved */
if (NULL == (*format->isMoved)(objRef)) {
*pReturn = objRef;
return ResOK;
}
break;
}
objBase = objLimit;
}
return ResFAIL;
}
static Res AMCAddrObject(Addr *pReturn, Pool pool, Addr addr)
{
Res res;
Arena arena;
Addr base, limit;
Buffer buffer;
Seg seg;
AVER(pReturn != NULL);
AVERT(Pool, pool);
arena = PoolArena(pool);
if (!SegOfAddr(&seg, arena, addr) || SegPool(seg) != pool)
return ResFAIL;
base = SegBase(seg);
if (SegBuffer(&buffer, seg))
/* We use BufferGetInit here (and not BufferScanLimit) because we
* want to be able to find objects that have been allocated and
* committed since the last flip. These objects lie between the
* addresses returned by BufferScanLimit (which returns the value
* of init at the last flip) and BufferGetInit.
*
* Strictly speaking we only need a limit that is at least the
* maximum of the objects on the segments. This is because addr
* *must* point inside a live object and we stop skipping once we
* have found it. The init pointer serves this purpose.
*/
limit = BufferGetInit(buffer);
else
limit = SegLimit(seg);
ShieldExpose(arena, seg);
res = amcAddrObjectSearch(pReturn, pool, base, limit, addr);
ShieldCover(arena, seg);
return res;
}
/* AMCTotalSize -- total memory allocated from the arena */
@ -2007,6 +2091,7 @@ DEFINE_CLASS(Pool, AMCZPool, klass)
klass->bufferClass = amcBufClassGet;
klass->totalSize = AMCTotalSize;
klass->freeSize = AMCFreeSize;
klass->addrObject = AMCAddrObject;
AVERT(PoolClass, klass);
}

View file

@ -50,7 +50,7 @@
#include <signal.h> /* sig_atomic_t */
#include <sys/mman.h> /* see .feature.li in config.h */
#include <sys/types.h> /* mmap, munmap */
#include <unistd.h> /* getpagesize */
#include <unistd.h> /* sysconf, _SC_PAGESIZE */
SRCID(vmix, "$Id$");
@ -59,10 +59,11 @@ SRCID(vmix, "$Id$");
Size PageSize(void)
{
int pageSize;
long pageSize;
/* Find out the operating system page size */
pageSize = getpagesize();
/* Find out the operating system page size
(see design.mps.vm.impl.ix.page.size) */
pageSize = sysconf(_SC_PAGESIZE);
/* Check the page size will fit in a Size. */
AVER((unsigned long)pageSize <= (unsigned long)(Size)-1);

View file

@ -269,7 +269,13 @@ Unix implementation
_`.impl.ix`: In ``vmix.c``.
_`.impl.ix.page.size`: The page size is given by ``getpagesize()``.
_`.impl.ix.page.size`: The page size is given by
``sysconf(_SC_PAGESIZE)``. We avoid ``getpagesize()``, which is a
legacy function in Posix:
Applications should use the sysconf() function instead.
— `The Single UNIX ® Specification, Version 2 <https://pubs.opengroup.org/onlinepubs/7908799/xsh/getpagesize.html>`__
_`.impl.ix.param`: Decodes no keyword arguments.

View file

@ -53,6 +53,18 @@ New features
address-based hash tables using :term:`location dependency`. See
:ref:`pool-amc-hash-arrays`.
#. The new function :c:func:`mps_addr_object` allows clients to
discover the base pointer of an object from a pointer to anywhere
inside the object. This is intended to support stack tracing and
debugging for client programs that allocate their code on the
heap.
#. A :term:`virtual memory arena` can now be configured to call
functions when it acquires a new chunk of :term:`address space`,
and when it returns a chunk of address space to the operation
system. This is intended to support dynamic function tables in
Windows. See :ref:`topic-arena-extension`.
Interface changes
.................

View file

@ -139,7 +139,7 @@ Client arenas
* :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`) is its
size.
It also accepts three optional keyword arguments:
It also accepts five optional keyword arguments:
* :c:macro:`MPS_KEY_COMMIT_LIMIT` (type :c:type:`size_t`) is
the maximum amount of memory, in :term:`bytes (1)`, that the MPS
@ -159,6 +159,18 @@ Client arenas
may pause the :term:`client program` for. See
:c:func:`mps_arena_pause_time_set` for details.
* :c:macro:`MPS_KEY_ARENA_EXTENDED` (type :c:type:`mps_fun_t`) is
a function that will be called immediately after the arena is
*extended*: that is, just after it acquires a new chunk of address
space from the operating system. See :ref:`topic-arena-extension`
for details.
* :c:macro:`MPS_KEY_ARENA_CONTRACTED` (type :c:type:`mps_fun_t`)
is a function that will be called immediately before the arena is
*contracted*: that is, just before it finishes with a chunk of
address space and returns it to the operating system. See
:ref:`topic-arena-extension` for details.
For example::
MPS_ARGS_BEGIN(args) {
@ -983,8 +995,8 @@ Arena introspection and debugging
from MPS-managed memory, then it may attempt to re-enter the
MPS, which will fail as the MPS is not re-entrant.
.. |RtlInstallFunctionTableCallback| replace:: ``RtlInstallFunctionTableCallback()``
.. _RtlInstallFunctionTableCallback: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680595(v=vs.85).aspx
.. |RtlInstallFunctionTableCallback| replace:: :c:func:`RtlInstallFunctionTableCallback`
.. _RtlInstallFunctionTableCallback: https://docs.microsoft.com/en-gb/windows/win32/api/winnt/nf-winnt-rtlinstallfunctiontablecallback
If this happens, in order to allow the debugger to finish
decoding the call stack, the only remedy is to put the arena
@ -1040,3 +1052,138 @@ Arena introspection and debugging
:c:func:`mps_addr_pool`, and to find out which :term:`object
format` describes the object at the address, use
:c:func:`mps_addr_fmt`.
.. c:function:: mps_res_t mps_addr_object(mps_addr_t *p_o, mps_arena_t arena, mps_addr_t addr)
Find the :term:`base pointer` of an :term:`object` if provided with an
:term:`interior pointer` to that object, or the object's base pointer,
provided the object exists in a pool that supports this feature.
``p_o`` points to a location that will hold the object's base pointer.
``arena`` is an arena.
``addr`` is an address that might be an interior or base pointer.
Returns MPS_RES_OK if a base pointer to an object into which ``addr``
points was successfully returned.
Returns MPS_RES_FAIL if ``addr`` points to memory not managed by the
``arena`` or if ``addr`` points to the interior of an object which has
been moved by a :term:`moving memory manager`.
Returns MPS_RES_UNIMPL if ``addr`` is found to be managed by a :term:`pool`
which does not currently implement this feature.
:c:func:`mps_addr_object` allows client programs that allocate
code on the heap to implement debugging and stack tracing, in that it provides
a way to unwind a client program's stack by finding the block of code to which the
program counter or function return addresses currently point. It can be called
multiple times as needed to build a complete trace of the client program's stack.
This function does not support debugging in situations where the arena
itself has encountered a runtime error. For cases where the MPS encounters
runtime errors, see :c:func:`mps_arena_postmortem`.
.. note::
This function is intended to assist with debugging fatal
errors in the :term:`client program`. It is not expected to be
needed in normal use, i.e. as part of the regular operation of code in
production, since it is not optimized for performance. If you find yourself
wanting to use this function other than in the use case described, there may
be a better way to meet your requirements: please
:ref:`contact us <contact>`.
If you would like this function to work in a pool in which it's currently
unimplemented, please :ref:`contact us <contact>`.
.. index::
single: arena extension callbacks; introduction
single: extension callbacks; introduction
single: arena contraction callbacks; introduction
single: contraction callbacks; introduction
.. _topic-arena-extension:
Arena extension callbacks
-------------------------
There are situations in which the :term:`client program` needs to be
informed about the chunks of address space that an :term:`arena` is
managing. To support this, the MPS allows the client program to
specify two callback functions when creating a :term:`virtual memory
arena`: one function is called when the arena is *extended* (that is,
when it acquires a new chunk of address space from the operating
system), and the other when the arena is *contracted* (that is, when
it returns a chunk of address space to the operating system).
The use case that this feature is designed to support is debugging of
dynamically generated code in 64-bit Windows. Microsoft's
documentation for |RtlInstallFunctionTableCallback|_ says:
Function tables are used on 64-bit Windows to determine how to
unwind or walk the stack. These tables are usually generated by
the compiler and stored as part of the image. However,
applications must provide the function table for dynamically
generated code.
An application may install a dynamic function table by calling
|RtlInstallFunctionTableCallback|_, passing the region of memory in
which the dynamically generated functions can be found, and may later
delete the table by calling |RtlDeleteFunctionTable|_.
.. |RtlDeleteFunctionTable| replace:: :c:func:`RtlDeleteFunctionTable`
.. _RtlDeleteFunctionTable: https://docs.microsoft.com/en-gb/windows/win32/api/winnt/nf-winnt-rtldeletefunctiontable
So if the client program is storing dynamically generated functions in
MPS-managed memory, then it could define callback functions that
install and delete the function table callback for the dynamically
generated code, like this::
void arena_extended(mps_arena_t arena, void *base, size_t size)
{
RtlInstallFunctionTableCallback(...);
}
void arena_contracted(mps_arena_t arena, void *base, size_t size)
{
RtlDeleteFunctionTable(...);
}
and then pass these two functions using :term:`keyword arguments` to
:c:func:`mps_arena_create_k`::
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_EXTENDED, (mps_fun_t)arena_extended);
MPS_ARGS_ADD(args, MPS_KEY_ARENA_CONTRACTED, (mps_fun_t)arena_contracted);
/* ... other keyword arguments ... */
res = mps_arena_create_k(&arena, mps_arena_class_vm(), args);
} MPS_ARGS_END(args);
The callback functions receive three arguments: ``arena`` (the arena
being extended or contracted), ``base`` (the base address of the chunk
of address space that has just been acquired from, or is about to be
returned to, the operating system), and ``size`` (the size of the
chunk, in bytes). They must not call any function in the MPS, and must
not access any memory managed by the MPS.
.. note::
The extenstion callback is also called immediately after the arena
is created, in other words, the creation of the arena is treated as
a special example of an extension of the arena.
The contraction callback is called on all remaining chunks when
the arena is destroyed. There will be at least one callback.
Every contraction of the arena will match one-to-one with the arena
extensions that have already taken place. After creation, any
contractions performed by the arena will be the same size as the
extensions that have already taken place. Contractions never occur as
amalgamations nor as fractions of previous arena extensions.
Arena extension callbacks are only supported by :term:`virtual
memory arenas`.

View file

@ -2,6 +2,7 @@
Test case Flags Notes
============= ================ ==========================================
abqtest
addrobj
airtest
amcss =P
amcsshe =P
@ -16,6 +17,7 @@ awlutth =T
btcv
bttest =N interactive
djbench =N benchmark
extcon =W TODO: Enable when we can update Xcode project. See GitHub issue #217 <https://github.com/Ravenbrook/mps/issues/217>.
finalcv =P
finaltest =P
forktest =X