diff --git a/.p4ignore b/.p4ignore index ba720759d8..25cd419ae6 100644 --- a/.p4ignore +++ b/.p4ignore @@ -16,3 +16,8 @@ TAGS *.dSYM code/*/*/*.d *.pyc +test/obj +test/test/log +test/test/obj +....gcda +....gcno \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 07a0dd6652..d78b45e190 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,19 @@ # $Id$ # See . language: c +os: + - linux + - osx compiler: - clang - gcc +matrix: + exclude: + - os: osx + compiler: gcc notifications: email: - mps-travis@ravenbrook.com irc: "irc.freenode.net#memorypoolsystem" +script: + - ./configure --prefix=$PWD/prefix && make install && make test diff --git a/Makefile.in b/Makefile.in index 2d55858867..fe33a4d49f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,7 +1,7 @@ # Makefile.in -- source for autoconf Makefile # # $Id$ -# Copyright (C) 2012-2013 Ravenbrook Limited. See end of file for license. +# Copyright (C) 2012-2014 Ravenbrook Limited. See end of file for license. # # YOU DON'T NEED AUTOCONF TO BUILD THE MPS # This is just here for people who want or expect a configure script. @@ -13,11 +13,14 @@ INSTALL=@INSTALL@ INSTALL_DATA=@INSTALL_DATA@ INSTALL_PROGRAM=@INSTALL_PROGRAM@ MAKE=@MAKE@ -MPS_TARGET_NAME=@MPS_TARGET_NAME@ +MPS_OS_NAME=@MPS_OS_NAME@ +MPS_ARCH_NAME=@MPS_ARCH_NAME@ +MPS_BUILD_NAME=@MPS_BUILD_NAME@ +MPS_TARGET_NAME=$(MPS_OS_NAME)$(MPS_ARCH_NAME)$(MPS_BUILD_NAME) EXTRA_TARGETS=@EXTRA_TARGETS@ prefix=$(DESTDIR)@prefix@ TARGET_OPTS=-C code -f $(MPS_TARGET_NAME).gmk EXTRA_TARGETS="$(EXTRA_TARGETS)" -XCODEBUILD=xcodebuild -project code/mps.xcodeproj +XCODEBUILD=xcrun xcodebuild -project code/mps.xcodeproj all: @BUILD_TARGET@ @@ -31,15 +34,15 @@ install-make-build: make-install-dirs build-via-make $(INSTALL_DATA) code/mps*.h $(prefix)/include/ $(INSTALL_DATA) code/$(MPS_TARGET_NAME)/cool/mps.a $(prefix)/lib/libmps-debug.a $(INSTALL_DATA) code/$(MPS_TARGET_NAME)/hot/mps.a $(prefix)/lib/libmps.a - $(INSTALL_PROGRAM) $(addprefix code/$(MPS_TARGET_NAME)/hot/Release/,$(EXTRA_TARGETS)) $(prefix)/bin + $(INSTALL_PROGRAM) $(addprefix code/$(MPS_TARGET_NAME)/hot/,$(EXTRA_TARGETS)) $(prefix)/bin build-via-xcode: - $(XCODEBUILD) -config Release $(XCODEBUILD) -config Debug + $(XCODEBUILD) -config Release clean-xcode-build: - $(XCODEBUILD) -config Release clean $(XCODEBUILD) -config Debug clean + $(XCODEBUILD) -config Release clean install-xcode-build: make-install-dirs build-via-xcode $(INSTALL_DATA) code/mps*.h $(prefix)/include/ @@ -67,12 +70,13 @@ make-install-dirs: install: @INSTALL_TARGET@ -test-make-build: @BUILD_TARGET@ - $(MAKE) $(TARGET_OPTS) VARIETY=cool testrun - $(MAKE) $(TARGET_OPTS) VARIETY=hot testrun +test-make-build: + $(MAKE) $(TARGET_OPTS) testci + $(MAKE) -C code -f anan$(MPS_BUILD_NAME).gmk VARIETY=cool clean testansi + $(MAKE) -C code -f anan$(MPS_BUILD_NAME).gmk VARIETY=cool CFLAGS="-DCONFIG_POLL_NONE" clean testpollnone test-xcode-build: - $(XCODEBUILD) -config Release -target testrun - $(XCODEBUILD) -config Debug -target testrun + $(XCODEBUILD) -config Debug -target testci + $(XCODEBUILD) -config Release -target testci test: @TEST_TARGET@ diff --git a/NEWS b/NEWS new file mode 120000 index 0000000000..6bf8922f3b --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +manual/source/release.rst \ No newline at end of file diff --git a/code/.gdbinit b/code/.gdbinit deleted file mode 100644 index cbdac5f7cc..0000000000 --- a/code/.gdbinit +++ /dev/null @@ -1 +0,0 @@ -handle SIGBUS nostop diff --git a/code/.p4ignore b/code/.p4ignore index 7a9dfb599a..6cb34b80f0 100644 --- a/code/.p4ignore +++ b/code/.p4ignore @@ -1,6 +1,9 @@ # code/.p4ignore -- Perforce files to ignore list # $Id$ # Make output +anangc +ananll +ananmv fri3gc fri6gc lii3gc @@ -9,6 +12,7 @@ lii6ll w3i3mv w3i6mv xci3gc +xci6ll # Visual Studio junk Debug Release diff --git a/code/abq.c b/code/abq.c index 596921dd09..0e9b808a27 100644 --- a/code/abq.c +++ b/code/abq.c @@ -1,7 +1,7 @@ /* abq.c: QUEUE IMPLEMENTATION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: A fixed-length FIFO queue. * @@ -107,7 +107,7 @@ Bool ABQPush(ABQ abq, void *element) if (ABQIsFull(abq)) return FALSE; - mps_lib_memcpy(ABQElement(abq, abq->in), element, abq->elementSize); + (void)mps_lib_memcpy(ABQElement(abq, abq->in), element, abq->elementSize); abq->in = ABQNextIndex(abq, abq->in); AVERT(ABQ, abq); @@ -126,7 +126,7 @@ Bool ABQPop(ABQ abq, void *elementReturn) if (ABQIsEmpty(abq)) return FALSE; - mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize); + (void)mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize); abq->out = ABQNextIndex(abq, abq->out); @@ -146,7 +146,7 @@ Bool ABQPeek(ABQ abq, void *elementReturn) if (ABQIsEmpty(abq)) return FALSE; - mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize); + (void)mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize); /* Identical to pop, but don't increment out */ @@ -156,49 +156,40 @@ Bool ABQPeek(ABQ abq, void *elementReturn) /* ABQDescribe -- Describe an ABQ */ -Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream) +Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream, Count depth) { Res res; Index index; - if (!TESTT(ABQ, abq)) return ResFAIL; - if (stream == NULL) return ResFAIL; - - res = WriteF(stream, - "ABQ $P\n{\n", (WriteFP)abq, - " elements: $U \n", (WriteFU)abq->elements, - " in: $U \n", (WriteFU)abq->in, - " out: $U \n", (WriteFU)abq->out, - " queue: \n", + if (!TESTT(ABQ, abq)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "ABQ $P {\n", (WriteFP)abq, + " elements $U\n", (WriteFU)abq->elements, + " elementSize $W\n", (WriteFW)abq->elementSize, + " in $U\n", (WriteFU)abq->in, + " out $U\n", (WriteFU)abq->out, + " queue:\n", NULL); if(res != ResOK) return res; for (index = abq->out; index != abq->in; ) { - res = (*describeElement)(ABQElement(abq, index), stream); + res = (*describeElement)(ABQElement(abq, index), stream, depth + 2); if(res != ResOK) return res; index = ABQNextIndex(abq, index); } - res = WriteF(stream, "\n", NULL); - if(res != ResOK) - return res; + METER_WRITE(abq->push, stream, depth + 2); + METER_WRITE(abq->pop, stream, depth + 2); + METER_WRITE(abq->peek, stream, depth + 2); + METER_WRITE(abq->delete, stream, depth + 2); - res = METER_WRITE(abq->push, stream); - if(res != ResOK) - return res; - res = METER_WRITE(abq->pop, stream); - if(res != ResOK) - return res; - res = METER_WRITE(abq->peek, stream); - if(res != ResOK) - return res; - res = METER_WRITE(abq->delete, stream); - if(res != ResOK) - return res; - - res = WriteF(stream, "}\n", NULL); + res = WriteF(stream, depth, "} ABQ $P\n", (WriteFP)abq, NULL); if(res != ResOK) return res; @@ -240,13 +231,13 @@ Count ABQDepth(ABQ abq) } -/* ABQIterate -- call 'iterate' for each element in an ABQ */ -void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP, Size closureS) +/* ABQIterate -- call 'visitor' for each element in an ABQ */ +void ABQIterate(ABQ abq, ABQVisitor visitor, void *closureP, Size closureS) { Index copy, index, in; AVERT(ABQ, abq); - AVER(FUNCHECK(iterate)); + AVER(FUNCHECK(visitor)); copy = abq->out; index = abq->out; @@ -256,12 +247,12 @@ void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP, Size closureS void *element = ABQElement(abq, index); Bool delete = FALSE; Bool cont; - cont = (*iterate)(&delete, element, closureP, closureS); + cont = (*visitor)(&delete, element, closureP, closureS); AVERT(Bool, cont); AVERT(Bool, delete); if (!delete) { if (copy != index) - mps_lib_memcpy(ABQElement(abq, copy), element, abq->elementSize); + (void)mps_lib_memcpy(ABQElement(abq, copy), element, abq->elementSize); copy = ABQNextIndex(abq, copy); } index = ABQNextIndex(abq, index); @@ -272,8 +263,8 @@ void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP, Size closureS /* If any elements were deleted, need to copy remainder of queue. */ if (copy != index) { while (index != in) { - mps_lib_memcpy(ABQElement(abq, copy), ABQElement(abq, index), - abq->elementSize); + (void)mps_lib_memcpy(ABQElement(abq, copy), ABQElement(abq, index), + abq->elementSize); copy = ABQNextIndex(abq, copy); index = ABQNextIndex(abq, index); } @@ -311,7 +302,7 @@ static void *ABQElement(ABQ abq, Index index) { /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/abq.h b/code/abq.h index 022fc7e530..85cdfcd575 100644 --- a/code/abq.h +++ b/code/abq.h @@ -1,7 +1,7 @@ /* abq.h: QUEUE INTERFACE * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: A fixed-length FIFO queue. * @@ -23,8 +23,8 @@ /* Prototypes */ typedef struct ABQStruct *ABQ; -typedef Res (*ABQDescribeElement)(void *element, mps_lib_FILE *stream); -typedef Bool (*ABQIterateMethod)(Bool *deleteReturn, void *element, void *closureP, Size closureS); +typedef Res (*ABQDescribeElement)(void *element, mps_lib_FILE *stream, Count depth); +typedef Bool (*ABQVisitor)(Bool *deleteReturn, void *element, void *closureP, Size closureS); extern Res ABQInit(Arena arena, ABQ abq, void *owner, Count elements, Size elementSize); extern Bool ABQCheck(ABQ abq); @@ -32,11 +32,11 @@ extern void ABQFinish(Arena arena, ABQ abq); extern Bool ABQPush(ABQ abq, void *element); extern Bool ABQPop(ABQ abq, void *elementReturn); extern Bool ABQPeek(ABQ abq, void *elementReturn); -extern Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream); +extern Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream, Count depth); extern Bool ABQIsEmpty(ABQ abq); extern Bool ABQIsFull(ABQ abq); extern Count ABQDepth(ABQ abq); -extern void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP, Size closureS); +extern void ABQIterate(ABQ abq, ABQVisitor visitor, void *closureP, Size closureS); /* Types */ @@ -63,7 +63,7 @@ typedef struct ABQStruct /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/abqtest.c b/code/abqtest.c index f8e3a2a4bd..9aad3351cb 100644 --- a/code/abqtest.c +++ b/code/abqtest.c @@ -1,21 +1,22 @@ /* abqtest.c: AVAILABLE BLOCK QUEUE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "abq.h" #include "mps.h" #include "mpsavm.h" +#include "mpscmfs.h" #include "mpstd.h" #include "testlib.h" -#include +#include /* printf */ SRCID(abqtest, "$Id$"); - +static mps_pool_t pool; static ABQStruct abq; /* the ABQ which we will use */ static Size abqSize; /* the size of the current ABQ */ @@ -50,9 +51,12 @@ static TestBlock testBlocks = NULL; static TestBlock CreateTestBlock(unsigned no) { - TestBlock b = malloc(sizeof(TestBlockStruct)); - cdie(b != NULL, "malloc"); + TestBlock b; + mps_addr_t p; + + die(mps_alloc(&p, pool, sizeof(TestBlockStruct)), "alloc"); + b = p; b->next = testBlocks; b->id = no; b->base = 0; @@ -78,7 +82,7 @@ static void DestroyTestBlock(TestBlock b) } } - free(b); + mps_free(pool, b, sizeof(TestBlockStruct)); } typedef struct TestClosureStruct *TestClosure; @@ -92,6 +96,7 @@ static Bool TestDeleteCallback(Bool *deleteReturn, void *element, { TestBlock *a = (TestBlock *)element; TestClosure cl = (TestClosure)closureP; + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); if (*a == cl->b) { *deleteReturn = TRUE; @@ -130,7 +135,7 @@ static void step(void) DestroyTestBlock(a); break; default: - if (!deleted & (pushee > popee)) { + if (!deleted && (pushee > popee)) { TestBlock b; TestClosureStruct cl; deleted = (unsigned)abqRnd (pushee - popee) + popee; @@ -140,28 +145,29 @@ static void step(void) cdie(b != NULL, "found to delete"); cl.b = b; cl.res = ResFAIL; - ABQIterate(&abq, TestDeleteCallback, &cl, 0); + ABQIterate(&abq, TestDeleteCallback, &cl, UNUSED_SIZE); cdie(cl.res == ResOK, "ABQIterate"); } } } - -#define testArenaSIZE (((size_t)4)<<20) - extern int main(int argc, char *argv[]) { mps_arena_t arena; int i; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); abqSize = 0; - die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), + die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "mps_arena_create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, sizeof(TestBlockStruct)); + die(mps_pool_create_k(&pool, arena, mps_class_mfs(), args), "pool_create"); + } MPS_ARGS_END(args); + die(ABQInit((Arena)arena, &abq, NULL, ABQ_SIZE, sizeof(TestBlock)), "ABQInit"); @@ -178,7 +184,7 @@ extern int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/airtest.c b/code/airtest.c new file mode 100644 index 0000000000..c262cb3126 --- /dev/null +++ b/code/airtest.c @@ -0,0 +1,204 @@ +/* airtest.c: AMBIGUOUS INTERIOR REFERENCE TEST + * + * $Id: //info.ravenbrook.com/project/mps/branch/2014-01-15/nailboard/code/fotest.c#1 $ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * .overview: This test case creates a bunch of vectors, registers + * them for finalization, and then discards the base pointers to those + * objects, keeping only ambiguous interior references to the vector + * entries in the stack-allocated table s. + * + * .options: The test has two options: + * + * 'interior' is the value passed as MPS_KEY_INTERIOR when creating + * the AMC pool. If TRUE, interior pointers must keep objects alive, + * and so if any of these objects are finalized, the test fails. If + * FALSE, interior pointers do not keep objects alive, so it is likely + * that all the objects will be finalized. + * + * 'stack' is TRUE if the C stack is registered as a root. (If FALSE, + * we register the table of interior pointers as an ambiguous root.) + * + * .fail.lii6ll: The test case passes on most platforms with + * interior=FALSE and stack=TRUE (that is, all vectors get finalized), + * but fails on lii6ll in variety HOT. Rather than struggle to defeat + * the Clang optimizer, we choose not to test in this configuration. + * In any case, the MPS does not guarantee anything about timely + * finalization (see ). + */ + +#include "mps.h" +#include "mpsavm.h" +#include "mpscamc.h" +#include "mpslib.h" +#include "testlib.h" +#include "fmtscheme.h" + +#define OBJ_LEN (1u << 4) +#define OBJ_COUNT 10 + +static void test_air(int interior, int stack) +{ + size_t n_finalized = 0; + size_t i, j; + obj_t *s[OBJ_COUNT] = {0}; + mps_root_t root = NULL; + if (!stack) { + mps_addr_t *p = (void *)s; + die(mps_root_create_table(&root, scheme_arena, mps_rank_ambig(), 0, p, + OBJ_COUNT), "mps_root_create_table"); + } + mps_message_type_enable(scheme_arena, mps_message_type_finalization()); + for (j = 0; j < OBJ_COUNT; ++j) { + obj_t n = scheme_make_integer(obj_ap, (long)j); + obj_t obj = scheme_make_vector(obj_ap, OBJ_LEN, n); + mps_addr_t ref = obj; + mps_finalize(scheme_arena, &ref); + s[j] = obj->vector.vector; + } + for (i = 1; i < OBJ_LEN; ++i) { + obj_t n = scheme_make_integer(obj_ap, (long)i); + mps_message_t msg; + for (j = 0; j + 1 < OBJ_COUNT; ++j) { + *++s[j] = n; + } + mps_arena_collect(scheme_arena); + mps_arena_release(scheme_arena); + if (mps_message_get(&msg, scheme_arena, mps_message_type_finalization())) { + mps_addr_t ref; + mps_message_finalization_ref(&ref, scheme_arena, msg); + ++ n_finalized; + if (interior) { + obj_t o; + o = ref; + error("wrongly finalized vector %ld at %p", + o->vector.vector[0]->integer.integer, (void *)o); + } + } + } + if (!interior && n_finalized < OBJ_COUNT) { + error("only finalized %"PRIuLONGEST" out of %"PRIuLONGEST" vectors.", + (ulongest_t)n_finalized, (ulongest_t)OBJ_COUNT); + } + if (!stack) { + mps_root_destroy(root); + } +} + +static mps_gen_param_s obj_gen_params[] = { + { 150, 0.85 }, + { 170, 0.45 } +}; + +static void test_main(void *marker, int interior, int stack) +{ + mps_res_t res; + mps_chain_t obj_chain; + mps_fmt_t obj_fmt; + mps_thr_t thread; + mps_root_t reg_root = NULL; + + res = mps_arena_create_k(&scheme_arena, mps_arena_class_vm(), mps_args_none); + if (res != MPS_RES_OK) + error("Couldn't create arena"); + + res = mps_chain_create(&obj_chain, scheme_arena, + sizeof(obj_gen_params) / sizeof(*obj_gen_params), + obj_gen_params); + if (res != MPS_RES_OK) + error("Couldn't create obj chain"); + + scheme_fmt(&obj_fmt); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, obj_chain); + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt); + MPS_ARGS_ADD(args, MPS_KEY_INTERIOR, interior); + die(mps_pool_create_k(&obj_pool, scheme_arena, mps_class_amc(), args), + "mps_pool_create_k"); + } MPS_ARGS_END(args); + + res = mps_ap_create_k(&obj_ap, obj_pool, mps_args_none); + if (res != MPS_RES_OK) + error("Couldn't create obj allocation point"); + + res = mps_thread_reg(&thread, scheme_arena); + if (res != MPS_RES_OK) + error("Couldn't register thread"); + + if (stack) { + res = mps_root_create_reg(®_root, scheme_arena, mps_rank_ambig(), 0, + thread, mps_stack_scan_ambig, marker, 0); + if (res != MPS_RES_OK) + error("Couldn't create root"); + } + + test_air(interior, stack); + + mps_arena_park(scheme_arena); + if (stack) + mps_root_destroy(reg_root); + mps_thread_dereg(thread); + mps_ap_destroy(obj_ap); + mps_pool_destroy(obj_pool); + mps_chain_destroy(obj_chain); + mps_fmt_destroy(obj_fmt); + mps_arena_destroy(scheme_arena); +} + +int main(int argc, char *argv[]) +{ + void *marker = ▮ + + testlib_init(argc, argv); + + test_main(marker, TRUE, TRUE); + test_main(marker, TRUE, FALSE); + /* not test_main(marker, FALSE, TRUE) -- see .fail.lii6ll. */ + test_main(marker, FALSE, FALSE); + + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); + return 0; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (c) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/amcss.c b/code/amcss.c index e3bb32627e..294ce224b1 100644 --- a/code/amcss.c +++ b/code/amcss.c @@ -1,30 +1,28 @@ /* amcss.c: POOL CLASS AMC STRESS TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. */ #include "fmtdy.h" #include "fmtdytst.h" #include "testlib.h" +#include "mpm.h" #include "mpslib.h" #include "mpscamc.h" #include "mpsavm.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif #include "mps.h" #include "mpslib.h" -#include -#include + +#include /* fflush, printf, putchar */ /* These values have been tuned in the hope of getting one dynamic collection. */ #define testArenaSIZE ((size_t)1000*1024) -#define gen1SIZE ((size_t)150) -#define gen2SIZE ((size_t)170) +#define gen1SIZE ((size_t)20) +#define gen2SIZE ((size_t)85) #define avLEN 3 #define exactRootsCOUNT 180 #define ambigRootsCOUNT 50 @@ -46,14 +44,15 @@ static mps_gen_param_s testChain[genCOUNT] = { static mps_ap_t ap; static mps_addr_t exactRoots[exactRootsCOUNT]; static mps_addr_t ambigRoots[ambigRootsCOUNT]; +static size_t scale; /* Overall scale factor. */ +static unsigned long nCollsStart; +static unsigned long nCollsDone; /* report -- report statistics from any messages */ static void report(mps_arena_t arena) { - static int nCollsStart = 0; - static int nCollsDone = 0; mps_message_type_t type; while(mps_message_queue_type(&type, arena)) { @@ -63,7 +62,7 @@ static void report(mps_arena_t arena) if (type == mps_message_type_gc_start()) { nCollsStart += 1; - printf("\n{\n Collection %d started. Because:\n", nCollsStart); + printf("\n{\n Collection %lu started. Because:\n", nCollsStart); printf(" %s\n", mps_message_gc_start_why(arena, message)); printf(" clock: %"PRIuLONGEST"\n", (ulongest_t)mps_message_clock(arena, message)); @@ -75,25 +74,12 @@ static void report(mps_arena_t arena) condemned = mps_message_gc_condemned_size(arena, message); not_condemned = mps_message_gc_not_condemned_size(arena, message); - printf("\n Collection %d finished:\n", nCollsDone); + printf("\n Collection %lu finished:\n", nCollsDone); printf(" live %"PRIuLONGEST"\n", (ulongest_t)live); printf(" condemned %"PRIuLONGEST"\n", (ulongest_t)condemned); printf(" not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); printf(" clock: %"PRIuLONGEST"\n", (ulongest_t)mps_message_clock(arena, message)); printf("}\n"); - - if(condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024) { - /* When condemned size is larger than could happen in a gen 2 - * collection (discounting ramps, natch), guess that was a dynamic - * collection, and reset the commit limit, so it doesn't run out. - * - * GDR 2013-03-12: Fiddling with the commit limit was causing - * the test to fail sometimes (see job003440), so I've commented - * out this feature. - */ - /* die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit"); */ - } - } else { cdie(0, "unknown message type"); break; @@ -101,25 +87,25 @@ static void report(mps_arena_t arena) mps_message_discard(arena, message); } - - return; } /* make -- create one new object */ -static mps_addr_t make(void) +static mps_addr_t make(size_t rootsCount) { - size_t length = rnd() % (2*avLEN); + static unsigned long calls = 0; + size_t length = rnd() % (scale * avLEN); size_t size = (length+2) * sizeof(mps_word_t); mps_addr_t p; mps_res_t res; + ++ calls; do { MPS_RESERVE_BLOCK(res, p, ap, size); if (res) die(res, "MPS_RESERVE_BLOCK"); - res = dylan_init(p, size, exactRoots, exactRootsCOUNT); + res = dylan_init(p, size, exactRoots, rootsCount); if (res) die(res, "dylan_init"); } while(!mps_commit(ap, p, size)); @@ -141,7 +127,8 @@ static void test_stepper(mps_addr_t object, mps_fmt_t fmt, mps_pool_t pool, /* test -- the body of the test */ -static void test(mps_arena_t arena) +static void test(mps_arena_t arena, mps_pool_class_t pool_class, + size_t roots_count) { mps_fmt_t format; mps_chain_t chain; @@ -153,11 +140,12 @@ static void test(mps_arena_t arena) mps_ap_t busy_ap; mps_addr_t busy_init; mps_pool_t pool; + int described = 0; die(dylan_fmt(&format, arena), "fmt_create"); die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - die(mps_pool_create(&pool, arena, mps_class_amc(), format, chain), + die(mps_pool_create(&pool, arena, pool_class, format, chain), "pool_create(amc)"); die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate"); @@ -181,6 +169,8 @@ static void test(mps_arena_t arena) /* create an ap, and leave it busy */ die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy"); + nCollsStart = 0; + nCollsDone = 0; collections = 0; rampSwitch = rampSIZE; die(mps_ap_alloc_pattern_begin(ap, ramp), "pattern begin (ap)"); @@ -188,15 +178,18 @@ static void test(mps_arena_t arena) ramping = 1; objs = 0; while (collections < collectionsCOUNT) { - mps_word_t c; size_t r; - c = mps_collections(arena); - if (collections != c) { - collections = c; - report(arena); + report(arena); + if (collections != nCollsStart) { + if (!described) { + die(ArenaDescribe(arena, mps_lib_get_stdout(), 0), "ArenaDescribe"); + described = TRUE; + } + collections = nCollsStart; - printf("%lu objects (mps_collections says: %lu)\n", objs, c); + printf("%lu objects (nCollsStart=%"PRIuLONGEST")\n", objs, + (ulongest_t)collections); /* test mps_arena_has_addr */ { @@ -268,13 +261,13 @@ static void test(mps_arena_t arena) i = (r >> 1) % exactRootsCOUNT; if (exactRoots[i] != objNULL) cdie(dylan_check(exactRoots[i]), "dying root check"); - exactRoots[i] = make(); + exactRoots[i] = make(roots_count); if (exactRoots[(exactRootsCOUNT-1) - i] != objNULL) dylan_write(exactRoots[(exactRootsCOUNT-1) - i], exactRoots, exactRootsCOUNT); } else { i = (r >> 1) % ambigRootsCOUNT; - ambigRoots[(ambigRootsCOUNT-1) - i] = make(); + ambigRoots[(ambigRootsCOUNT-1) - i] = make(roots_count); /* Create random interior pointers */ ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1); } @@ -285,13 +278,14 @@ static void test(mps_arena_t arena) if (objs % 1024 == 0) { report(arena); putchar('.'); - fflush(stdout); + (void)fflush(stdout); } ++objs; } (void)mps_commit(busy_ap, busy_init, 64); + mps_arena_park(arena); mps_ap_destroy(busy_ap); mps_ap_destroy(ap); mps_root_destroy(exactRoot); @@ -299,27 +293,33 @@ static void test(mps_arena_t arena) mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(format); + mps_arena_release(arena); } int main(int argc, char *argv[]) { + size_t i, grainSize; mps_arena_t arena; mps_thr_t thread; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); - - die(mps_arena_create(&arena, mps_arena_class_vm(), 2*testArenaSIZE), - "arena_create"); + testlib_init(argc, argv); + + scale = (size_t)1 << (rnd() % 6); + for (i = 0; i < genCOUNT; ++i) testChain[i].mps_capacity *= scale; + grainSize = rnd_grain(scale * testArenaSIZE); + printf("Picked scale=%lu grainSize=%lu\n", (unsigned long)scale, (unsigned long)grainSize); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, scale * testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, grainSize); + MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, scale * testArenaSIZE); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create"); + } MPS_ARGS_END(args); mps_message_type_enable(arena, mps_message_type_gc()); mps_message_type_enable(arena, mps_message_type_gc_start()); - /* GDR 2013-03-12: Fiddling with the commit limit was causing - * the test to fail sometimes (see job003440), so I've commented - * out this feature. - */ - /*die(mps_arena_commit_limit_set(arena, testArenaSIZE), "set limit");*/ die(mps_thread_reg(&thread, arena), "thread_reg"); - test(arena); + test(arena, mps_class_amc(), exactRootsCOUNT); + test(arena, mps_class_amcz(), 0); mps_thread_dereg(thread); report(arena); mps_arena_destroy(arena); @@ -331,7 +331,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/amcsshe.c b/code/amcsshe.c index de8eec82a3..bd3ea73d06 100644 --- a/code/amcsshe.c +++ b/code/amcsshe.c @@ -1,7 +1,7 @@ /* amcsshe.c: POOL CLASS AMC STRESS TEST WITH HEADER * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. */ @@ -12,18 +12,15 @@ #include "mpscamc.h" #include "mpsavm.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif #include "mps.h" -#include -#include + +#include /* fflush, printf, putchar */ /* These values have been tuned in the hope of getting one dynamic collection. */ #define headerFACTOR ((float)(20 + headerSIZE) / 20) /* headerFACTOR measures how much larger objects are compared to fmtdy. */ -#define testArenaSIZE ((size_t)(1000*headerFACTOR)*1024) +#define testArenaSIZE ((size_t)(2000*headerFACTOR)*1024) #define gen1SIZE ((size_t)(150*headerFACTOR)) #define gen2SIZE ((size_t)(170*headerFACTOR)) #define avLEN 3 @@ -51,7 +48,7 @@ static mps_addr_t exactRoots[exactRootsCOUNT]; static mps_addr_t ambigRoots[ambigRootsCOUNT]; static mps_addr_t bogusRoots[bogusRootsCOUNT]; -static mps_addr_t make(void) +static mps_addr_t make(size_t roots_count) { size_t length = rnd() % (2*avLEN); size_t size = (length+2) * sizeof(mps_word_t); @@ -63,7 +60,7 @@ static mps_addr_t make(void) if (res) die(res, "MPS_RESERVE_BLOCK"); userP = (mps_addr_t)((char*)p + headerSIZE); - res = dylan_init(userP, size, exactRoots, exactRootsCOUNT); + res = dylan_init(userP, size, exactRoots, roots_count); if (res) die(res, "dylan_init"); ((int*)p)[0] = realHeader; @@ -94,27 +91,15 @@ static void report(mps_arena_t arena) printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); mps_message_discard(arena, message); - - if (condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024) { - /* When condemned size is larger than could happen in a gen 2 - * collection (discounting ramps, natch), guess that was a dynamic - * collection, and reset the commit limit, so it doesn't run out. - * - * GDR 2013-03-07: Fiddling with the commit limit was causing - * the test to fail sometimes (see job003432), so I've commented - * out this feature. - */ - /*die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit");*/ - } } } /* test -- the body of the test */ -static void *test(void *arg, size_t s) +static void *test(mps_arena_t arena, mps_pool_class_t pool_class, + size_t roots_count) { - mps_arena_t arena; mps_fmt_t format; mps_chain_t chain; mps_root_t exactRoot, ambigRoot, bogusRoot; @@ -125,13 +110,10 @@ static void *test(void *arg, size_t s) mps_ap_t busy_ap; mps_addr_t busy_init; - arena = (mps_arena_t)arg; - (void)s; /* unused */ - die(EnsureHeaderFormat(&format, arena), "fmt_create"); die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - die(mps_pool_create(&pool, arena, mps_class_amc(), format, chain), + die(mps_pool_create(&pool, arena, pool_class, format, chain), "pool_create(amc)"); die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate"); @@ -173,7 +155,8 @@ static void *test(void *arg, size_t s) if (collections != c) { collections = c; - printf("\nCollection %lu, %lu objects.\n", c, objs); + printf("\nCollection %"PRIuLONGEST", %lu objects.\n", + (ulongest_t)c, objs); report(arena); for (r = 0; r < exactRootsCOUNT; ++r) { if (exactRoots[r] != objNULL) @@ -219,13 +202,13 @@ static void *test(void *arg, size_t s) i = (r >> 1) % exactRootsCOUNT; if (exactRoots[i] != objNULL) die(HeaderFormatCheck(exactRoots[i]), "wrapper check"); - exactRoots[i] = make(); + exactRoots[i] = make(roots_count); if (exactRoots[(exactRootsCOUNT-1) - i] != objNULL) dylan_write(exactRoots[(exactRootsCOUNT-1) - i], exactRoots, exactRootsCOUNT); } else { i = (r >> 1) % ambigRootsCOUNT; - ambigRoots[(ambigRootsCOUNT-1) - i] = make(); + ambigRoots[(ambigRootsCOUNT-1) - i] = make(roots_count); /* Create random interior pointers */ ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1); } @@ -236,13 +219,14 @@ static void *test(void *arg, size_t s) if (objs % 1024 == 0) { report(arena); putchar('.'); - fflush(stdout); + (void)fflush(stdout); } ++objs; } (void)mps_commit(busy_ap, busy_init, 64); + mps_arena_park(arena); mps_ap_destroy(busy_ap); mps_ap_destroy(ap); mps_root_destroy(exactRoot); @@ -251,6 +235,7 @@ static void *test(void *arg, size_t s) mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(format); + mps_arena_release(arena); return NULL; } @@ -260,21 +245,19 @@ int main(int argc, char *argv[]) { mps_arena_t arena; mps_thr_t thread; - void *r; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); - die(mps_arena_create(&arena, mps_arena_class_vm(), 3*testArenaSIZE), - "arena_create\n"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE)); + MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, testArenaSIZE); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create"); + } MPS_ARGS_END(args); mps_message_type_enable(arena, mps_message_type_gc()); - /* GDR 2013-03-07: Fiddling with the commit limit was causing - * the test to fail sometimes (see job003432), so I've commented - * out this feature. - */ - /*die(mps_arena_commit_limit_set(arena, testArenaSIZE), "set limit");*/ die(mps_thread_reg(&thread, arena), "thread_reg"); - mps_tramp(&r, test, arena, 0); + test(arena, mps_class_amc(), exactRootsCOUNT); + test(arena, mps_class_amcz(), 0); mps_thread_dereg(thread); mps_arena_destroy(arena); @@ -285,7 +268,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/amcssth.c b/code/amcssth.c index e7f8f88233..3eea28df07 100644 --- a/code/amcssth.c +++ b/code/amcssth.c @@ -1,24 +1,37 @@ /* amcssth.c: POOL CLASS AMC STRESS TEST WITH TWO THREADS * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * - * .posix: This is Posix only. + * .mode: This test case has two modes: + * + * .mode.walk: In this mode, the main thread parks the arena half way + * through the test case and runs mps_arena_formatted_objects_walk(). + * This checks that walking works while the other threads continue to + * allocate in the background. + * + * .mode.commit: In this mode, the arena's commit limit is set. This + * checks that the MPS can make progress inside a tight limit in the + * presence of allocation on multiple threads. But this is + * incompatible with .mode.walk: if the arena is parked, then the + * arena has no chance to make progress. */ -#define _POSIX_C_SOURCE 199309L - #include "fmtdy.h" #include "fmtdytst.h" #include "testlib.h" +#include "testthr.h" #include "mpslib.h" #include "mpscamc.h" #include "mpsavm.h" -#include -#include -#include -#include + +#include /* fflush, printf, putchar */ + +enum { + ModeWALK = 0, /* .mode.walk */ + ModeCOMMIT = 1 /* .mode.commit */ +}; /* These values have been tuned in the hope of getting one dynamic collection. */ @@ -43,50 +56,19 @@ static mps_gen_param_s testChain[genCOUNT] = { #define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED)) -static mps_pool_t pool; static mps_addr_t exactRoots[exactRootsCOUNT]; static mps_addr_t ambigRoots[ambigRootsCOUNT]; -/* report - report statistics from any terminated GCs */ - -static void report(mps_arena_t arena) -{ - mps_message_t message; - static int nCollections = 0; - - while (mps_message_get(&message, arena, mps_message_type_gc())) { - size_t live, condemned, not_condemned; - - live = mps_message_gc_live_size(arena, message); - condemned = mps_message_gc_condemned_size(arena, message); - not_condemned = mps_message_gc_not_condemned_size(arena, message); - - printf("\nCollection %d finished:\n", ++nCollections); - printf("live %"PRIuLONGEST"\n", (ulongest_t)live); - printf("condemned %"PRIuLONGEST"\n", (ulongest_t)condemned); - printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); - - mps_message_discard(arena, message); - - if (condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024) - /* When condemned size is larger than could happen in a gen 2 - * collection (discounting ramps, natch), guess that was a dynamic - * collection, and reset the commit limit, so it doesn't run out. */ - die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit"); - } -} - -mps_arena_t arena; -mps_fmt_t format; -mps_chain_t chain; -mps_root_t exactRoot, ambigRoot; -unsigned long objs = 0; +static mps_word_t collections; +static mps_arena_t arena; +static mps_root_t exactRoot, ambigRoot; +static unsigned long objs = 0; /* make -- create one new object */ -static mps_addr_t make(mps_ap_t ap) +static mps_addr_t make(mps_ap_t ap, size_t roots_count) { size_t length = rnd() % (2*avLEN); size_t size = (length+2) * sizeof(mps_word_t); @@ -97,7 +79,7 @@ static mps_addr_t make(mps_ap_t ap) MPS_RESERVE_BLOCK(res, p, ap, size); if (res) die(res, "MPS_RESERVE_BLOCK"); - res = dylan_init(p, size, exactRoots, exactRootsCOUNT); + res = dylan_init(p, size, exactRoots, roots_count); if (res) die(res, "dylan_init"); } while(!mps_commit(ap, p, size)); @@ -108,59 +90,18 @@ static mps_addr_t make(mps_ap_t ap) /* test_stepper -- stepping function for walk */ -static void test_stepper(mps_addr_t object, mps_fmt_t fmt, mps_pool_t pol, +static void test_stepper(mps_addr_t object, mps_fmt_t fmt, mps_pool_t pool, void *p, size_t s) { - testlib_unused(object); testlib_unused(fmt); testlib_unused(pol); + testlib_unused(object); testlib_unused(fmt); testlib_unused(pool); testlib_unused(s); (*(unsigned long *)p)++; } -/* init -- initialize pool and roots */ - -static void init(void) -{ - size_t i; - - die(dylan_fmt(&format, arena), "fmt_create"); - die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - - die(mps_pool_create(&pool, arena, mps_class_amc(), format, chain), - "pool_create(amc)"); - - for(i = 0; i < exactRootsCOUNT; ++i) - exactRoots[i] = objNULL; - for(i = 0; i < ambigRootsCOUNT; ++i) - ambigRoots[i] = rnd_addr(); - - die(mps_root_create_table_masked(&exactRoot, arena, - mps_rank_exact(), (mps_rm_t)0, - &exactRoots[0], exactRootsCOUNT, - (mps_word_t)1), - "root_create_table(exact)"); - die(mps_root_create_table(&ambigRoot, arena, - mps_rank_ambig(), (mps_rm_t)0, - &ambigRoots[0], ambigRootsCOUNT), - "root_create_table(ambig)"); -} - - -/* finish -- finish pool and roots */ - -static void finish(void) -{ - mps_root_destroy(exactRoot); - mps_root_destroy(ambigRoot); - mps_pool_destroy(pool); - mps_chain_destroy(chain); - mps_fmt_destroy(format); -} - - /* churn -- create an object and install into roots */ -static void churn(mps_ap_t ap) +static void churn(mps_ap_t ap, size_t roots_count) { size_t i; size_t r; @@ -171,32 +112,73 @@ static void churn(mps_ap_t ap) i = (r >> 1) % exactRootsCOUNT; if (exactRoots[i] != objNULL) cdie(dylan_check(exactRoots[i]), "dying root check"); - exactRoots[i] = make(ap); + exactRoots[i] = make(ap, roots_count); if (exactRoots[(exactRootsCOUNT-1) - i] != objNULL) dylan_write(exactRoots[(exactRootsCOUNT-1) - i], exactRoots, exactRootsCOUNT); } else { i = (r >> 1) % ambigRootsCOUNT; - ambigRoots[(ambigRootsCOUNT-1) - i] = make(ap); + ambigRoots[(ambigRootsCOUNT-1) - i] = make(ap, roots_count); /* Create random interior pointers */ ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1); } } +typedef struct closure_s { + mps_pool_t pool; + size_t roots_count; +} closure_s, *closure_t; + +static void *kid_thread(void *arg) +{ + void *marker = ▮ + mps_thr_t thread; + mps_root_t reg_root; + mps_ap_t ap; + closure_t cl = arg; + + die(mps_thread_reg(&thread, (mps_arena_t)arena), "thread_reg"); + die(mps_root_create_reg(®_root, arena, mps_rank_ambig(), 0, thread, + mps_stack_scan_ambig, marker, 0), "root_create"); + + die(mps_ap_create(&ap, cl->pool, mps_rank_exact()), "BufferCreate(fooey)"); + while(mps_collections(arena) < collectionsCOUNT) { + churn(ap, cl->roots_count); + } + mps_ap_destroy(ap); + + mps_root_destroy(reg_root); + mps_thread_dereg(thread); + + return NULL; +} + + /* test -- the body of the test */ -static void *test(void *arg, size_t s) +static void test_pool(const char *name, mps_pool_t pool, size_t roots_count, + int mode) { size_t i; - mps_word_t collections, rampSwitch; + mps_word_t rampSwitch; mps_alloc_pattern_t ramp = mps_alloc_pattern_ramp(); int ramping; mps_ap_t ap, busy_ap; mps_addr_t busy_init; + testthr_t kids[10]; + closure_s cl; + int walked = FALSE, ramped = FALSE; - arena = (mps_arena_t)arg; - (void)s; /* unused */ + printf("\n------ mode: %s pool: %s-------\n", + mode == ModeWALK ? "WALK" : "COMMIT", name); + + cl.pool = pool; + cl.roots_count = roots_count; + collections = 0; + + for (i = 0; i < NELEMS(kids); ++i) + testthr_create(&kids[i], kid_thread, &cl); die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate"); die(mps_ap_create(&busy_ap, pool, mps_rank_exact()), "BufferCreate 2"); @@ -204,69 +186,85 @@ static void *test(void *arg, size_t s) /* create an ap, and leave it busy */ die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy"); - collections = 0; rampSwitch = rampSIZE; die(mps_ap_alloc_pattern_begin(ap, ramp), "pattern begin (ap)"); die(mps_ap_alloc_pattern_begin(busy_ap, ramp), "pattern begin (busy_ap)"); ramping = 1; while (collections < collectionsCOUNT) { - unsigned long c; - size_t r; - - c = mps_collections(arena); - - if (collections != c) { - collections = c; - printf("\nCollection %lu started, %lu objects.\n", c, objs); - report(arena); - - for (i = 0; i < exactRootsCOUNT; ++i) - cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]), - "all roots check"); - - if (collections == collectionsCOUNT / 2) { - unsigned long object_count = 0; - mps_arena_park(arena); - mps_arena_formatted_objects_walk(arena, test_stepper, &object_count, 0); - mps_arena_release(arena); - printf("stepped on %lu objects.\n", object_count); - } - if (collections == rampSwitch) { - int begin_ramp = !ramping - || /* Every other time, switch back immediately. */ (collections & 1); - - rampSwitch += rampSIZE; - if (ramping) { - die(mps_ap_alloc_pattern_end(ap, ramp), "pattern end (ap)"); - die(mps_ap_alloc_pattern_end(busy_ap, ramp), "pattern end (busy_ap)"); - ramping = 0; - /* kill half of the roots */ - for(i = 0; i < exactRootsCOUNT; i += 2) { - if (exactRoots[i] != objNULL) { - cdie(dylan_check(exactRoots[i]), "ramp kill check"); - exactRoots[i] = objNULL; + mps_message_type_t type; + + if (mps_message_queue_type(&type, arena)) { + mps_message_t msg; + mps_bool_t b = mps_message_get(&msg, arena, type); + Insist(b); /* we just checked there was one */ + + if (type == mps_message_type_gc()) { + size_t live = mps_message_gc_live_size(arena, msg); + size_t condemned = mps_message_gc_condemned_size(arena, msg); + size_t not_condemned = mps_message_gc_not_condemned_size(arena, msg); + + printf("\nCollection %lu finished:\n", collections++); + printf("live %"PRIuLONGEST"\n", (ulongest_t)live); + printf("condemned %"PRIuLONGEST"\n", (ulongest_t)condemned); + printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); + + } else if (type == mps_message_type_gc_start()) { + printf("\nCollection %lu started, %lu objects, committed=%lu.\n", + (unsigned long)collections, objs, + (unsigned long)mps_arena_committed(arena)); + + for (i = 0; i < exactRootsCOUNT; ++i) + cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]), + "all roots check"); + + if (mode == ModeWALK && collections >= collectionsCOUNT / 2 && !walked) + { + unsigned long count = 0; + mps_arena_park(arena); + mps_arena_formatted_objects_walk(arena, test_stepper, &count, 0); + mps_arena_release(arena); + printf("stepped on %lu objects.\n", count); + walked = TRUE; + } + if (collections >= rampSwitch && !ramped) { + /* Every other time, switch back immediately. */ + int begin_ramp = !ramping || (collections & 1); + + rampSwitch += rampSIZE; + if (ramping) { + die(mps_ap_alloc_pattern_end(ap, ramp), "pattern end (ap)"); + die(mps_ap_alloc_pattern_end(busy_ap, ramp), + "pattern end (busy_ap)"); + ramping = 0; + /* kill half of the roots */ + for(i = 0; i < exactRootsCOUNT; i += 2) { + if (exactRoots[i] != objNULL) { + cdie(dylan_check(exactRoots[i]), "ramp kill check"); + exactRoots[i] = objNULL; + } } } + if (begin_ramp) { + die(mps_ap_alloc_pattern_begin(ap, ramp), + "pattern rebegin (ap)"); + die(mps_ap_alloc_pattern_begin(busy_ap, ramp), + "pattern rebegin (busy_ap)"); + ramping = 1; + } } - if (begin_ramp) { - die(mps_ap_alloc_pattern_begin(ap, ramp), - "pattern rebegin (ap)"); - die(mps_ap_alloc_pattern_begin(busy_ap, ramp), - "pattern rebegin (busy_ap)"); - ramping = 1; - } + ramped = TRUE; } - } - - churn(ap); - r = (size_t)rnd(); - - if (r % initTestFREQ == 0) - *(int*)busy_init = -1; /* check that the buffer is still there */ + mps_message_discard(arena, msg); + } + churn(ap, roots_count); + { + size_t r = (size_t)rnd(); + if (r % initTestFREQ == 0) + *(int*)busy_init = -1; /* check that the buffer is still there */ + } if (objs % 1024 == 0) { - report(arena); putchar('.'); fflush(stdout); } @@ -276,80 +274,76 @@ static void *test(void *arg, size_t s) mps_ap_destroy(busy_ap); mps_ap_destroy(ap); - return NULL; + for (i = 0; i < NELEMS(kids); ++i) + testthr_join(&kids[i], NULL); } - -static void *fooey2(void *arg, size_t s) +static void test_arena(int mode) { - mps_ap_t ap; + size_t i; + mps_fmt_t format; + mps_chain_t chain; + mps_thr_t thread; + mps_root_t reg_root; + mps_pool_t amc_pool, amcz_pool; + void *marker = ▮ - (void)arg; (void)s; /* unused */ - die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate(fooey)"); - while(mps_collections(arena) < collectionsCOUNT) { - churn(ap); - } - mps_ap_destroy(ap); - return NULL; -} + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE)); + if (mode == ModeCOMMIT) + MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, 2 * testArenaSIZE); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create"); + } MPS_ARGS_END(args); + mps_message_type_enable(arena, mps_message_type_gc()); + mps_message_type_enable(arena, mps_message_type_gc_start()); + die(dylan_fmt(&format, arena), "fmt_create"); + die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); -static void *fooey(void* childIsFinishedReturn) -{ - void *r; - mps_thr_t thread; - void *marker = ▮ - mps_root_t reg_root; + for(i = 0; i < exactRootsCOUNT; ++i) + exactRoots[i] = objNULL; + for(i = 0; i < ambigRootsCOUNT; ++i) + ambigRoots[i] = rnd_addr(); - die(mps_thread_reg(&thread, (mps_arena_t)arena), "thread_reg"); + die(mps_root_create_table_masked(&exactRoot, arena, + mps_rank_exact(), (mps_rm_t)0, + &exactRoots[0], exactRootsCOUNT, + (mps_word_t)1), + "root_create_table(exact)"); + die(mps_root_create_table(&ambigRoot, arena, + mps_rank_ambig(), (mps_rm_t)0, + &ambigRoots[0], ambigRootsCOUNT), + "root_create_table(ambig)"); + die(mps_thread_reg(&thread, arena), "thread_reg"); die(mps_root_create_reg(®_root, arena, mps_rank_ambig(), 0, thread, mps_stack_scan_ambig, marker, 0), "root_create"); - mps_tramp(&r, fooey2, NULL, 0); - mps_root_destroy(reg_root); - mps_thread_dereg(thread); - *(int *)childIsFinishedReturn = 1; - return r; -} + die(mps_pool_create(&amc_pool, arena, mps_class_amc(), format, chain), + "pool_create(amc)"); + die(mps_pool_create(&amcz_pool, arena, mps_class_amcz(), format, chain), + "pool_create(amcz)"); -int main(int argc, char *argv[]) -{ - mps_thr_t thread; - mps_root_t reg_root; - void *marker = ▮ - pthread_t kids[10]; - unsigned i; - void *r; - int childIsFinished = 0; - - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + test_pool("AMC", amc_pool, exactRootsCOUNT, mode); + test_pool("AMCZ", amcz_pool, 0, mode); - die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), - "arena_create"); - mps_message_type_enable(arena, mps_message_type_gc()); - init(); - die(mps_thread_reg(&thread, arena), "thread_reg"); - die(mps_root_create_reg(®_root, arena, mps_rank_ambig(), 0, thread, - mps_stack_scan_ambig, marker, 0), "root_create"); - for (i = 0; i < sizeof(kids)/sizeof(kids[0]); ++i) { - int err = pthread_create(&kids[i], NULL, fooey, (void *)&childIsFinished); - if (err != 0) - error("pthread_create returned %d", err); - } - mps_tramp(&r, test, arena, 0); + mps_arena_park(arena); + mps_pool_destroy(amc_pool); + mps_pool_destroy(amcz_pool); mps_root_destroy(reg_root); mps_thread_dereg(thread); - - for (i = 0; i < sizeof(kids)/sizeof(kids[0]); ++i) { - int err = pthread_join(kids[i], NULL); - if (err != 0) - error("pthread_join returned %d", err); - } - - finish(); - report(arena); + mps_root_destroy(exactRoot); + mps_root_destroy(ambigRoot); + mps_chain_destroy(chain); + mps_fmt_destroy(format); mps_arena_destroy(arena); +} + +int main(int argc, char *argv[]) +{ + testlib_init(argc, argv); + test_arena(ModeWALK); + test_arena(ModeCOMMIT); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; @@ -358,21 +352,21 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * 3. Redistributions in any form must be accompanied by information on how * to obtain complete source code for this software and any accompanying * software that uses this software. The source code must either be @@ -383,7 +377,7 @@ int main(int argc, char *argv[]) * include source code for modules or files that typically accompany the * major components of the operating system on which the executable file * runs. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR diff --git a/code/amsss.c b/code/amsss.c index 43d2541f40..223bd205ae 100644 --- a/code/amsss.c +++ b/code/amsss.c @@ -1,7 +1,7 @@ /* amsss.c: POOL CLASS AMS STRESS TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * .design: Adapted from amcss.c, but not counting collections, just @@ -15,14 +15,10 @@ #include "mpscams.h" #include "mpsavm.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif #include "mps.h" -#include -#include -#include -#include +#include "mpm.h" + +#include /* fflush, printf */ #define exactRootsCOUNT 50 @@ -32,7 +28,7 @@ #define totalSizeSTEP 200 * (size_t)1024 /* objNULL needs to be odd so that it's ignored in exactRoots. */ #define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED)) -#define testArenaSIZE ((size_t)16<<20) +#define testArenaSIZE ((size_t)1<<20) #define initTestFREQ 3000 #define splatTestFREQ 6000 static mps_gen_param_s testChain[1] = { { 160, 0.90 } }; @@ -110,9 +106,10 @@ static mps_addr_t make(void) /* test -- the actual stress test */ static mps_pool_debug_option_s freecheckOptions = - { NULL, 0, (const void *)"Dead", 4 }; + { NULL, 0, "Dead", 4 }; -static void *test(void *arg, size_t haveAmbigous) +static void test_pool(mps_pool_class_t pool_class, mps_arg_s args[], + mps_bool_t haveAmbiguous) { mps_pool_t pool; mps_root_t exactRoot, ambigRoot = NULL; @@ -121,14 +118,13 @@ static void *test(void *arg, size_t haveAmbigous) mps_ap_t busy_ap; mps_addr_t busy_init; - pool = (mps_pool_t)arg; - + die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create"); die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate"); die(mps_ap_create(&busy_ap, pool, mps_rank_exact()), "BufferCreate 2"); for(i = 0; i < exactRootsCOUNT; ++i) exactRoots[i] = objNULL; - if (haveAmbigous) + if (haveAmbiguous) for(i = 0; i < ambigRootsCOUNT; ++i) ambigRoots[i] = rnd_addr(); @@ -137,7 +133,7 @@ static void *test(void *arg, size_t haveAmbigous) &exactRoots[0], exactRootsCOUNT, (mps_word_t)1), "root_create_table(exact)"); - if (haveAmbigous) + if (haveAmbiguous) die(mps_root_create_table(&ambigRoot, arena, mps_rank_ambig(), (mps_rm_t)0, &ambigRoots[0], ambigRootsCOUNT), @@ -146,20 +142,22 @@ static void *test(void *arg, size_t haveAmbigous) /* create an ap, and leave it busy */ die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy"); + die(PoolDescribe(pool, mps_lib_get_stdout(), 0), "PoolDescribe"); + objs = 0; totalSize = 0; while(totalSize < totalSizeMAX) { if (totalSize > lastStep + totalSizeSTEP) { lastStep = totalSize; printf("\nSize %"PRIuLONGEST" bytes, %lu objects.\n", (ulongest_t)totalSize, objs); - fflush(stdout); + (void)fflush(stdout); for(i = 0; i < exactRootsCOUNT; ++i) cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]), "all roots check"); } r = (size_t)rnd(); - if (!haveAmbigous || (r & 1)) { + if (!haveAmbiguous || (r & 1)) { i = (r >> 1) % exactRootsCOUNT; if (exactRoots[i] != objNULL) cdie(dylan_check(exactRoots[i]), "dying root check"); @@ -184,7 +182,7 @@ static void *test(void *arg, size_t haveAmbigous) if (objs % 256 == 0) { printf("."); report(); - fflush(stdout); + (void)fflush(stdout); } } @@ -192,82 +190,54 @@ static void *test(void *arg, size_t haveAmbigous) mps_ap_destroy(busy_ap); mps_ap_destroy(ap); mps_root_destroy(exactRoot); - if (haveAmbigous) + if (haveAmbiguous) mps_root_destroy(ambigRoot); - return NULL; + mps_pool_destroy(pool); } int main(int argc, char *argv[]) { + int i; mps_thr_t thread; mps_fmt_t format; mps_chain_t chain; - mps_pool_t pool; - void *r; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE)); + MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, 2 * testArenaSIZE); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create"); + } MPS_ARGS_END(args); - die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), - "arena_create"); mps_message_type_enable(arena, mps_message_type_gc_start()); mps_message_type_enable(arena, mps_message_type_gc()); die(mps_thread_reg(&thread, arena), "thread_reg"); die(mps_fmt_create_A(&format, arena, dylan_fmt_A()), "fmt_create"); die(mps_chain_create(&chain, arena, 1, testChain), "chain_create"); - /* TODO: Add tests using the arena default chain. */ - - printf("\n\n****************************** Testing AMS Debug\n"); - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); - MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); - MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, FALSE); - MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions); - die(mps_pool_create_k(&pool, arena, mps_class_ams_debug(), args), - "pool_create(ams_debug,share)"); - } MPS_ARGS_END(args); - mps_tramp(&r, test, pool, 0); - mps_pool_destroy(pool); - - printf("\n\n****************************** Testing AMS Debug\n"); - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); - MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); - MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, TRUE); - MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions); - die(mps_pool_create_k(&pool, arena, mps_class_ams_debug(), args), - "pool_create(ams_debug,ambig)"); - } MPS_ARGS_END(args); - mps_tramp(&r, test, pool, 1); - mps_pool_destroy(pool); - - printf("\n\n****************************** Testing AMS\n"); - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); - MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); - MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, TRUE); - MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions); - die(mps_pool_create_k(&pool, arena, mps_class_ams(), args), - "pool_create(ams,ambig)"); - } MPS_ARGS_END(args); - mps_tramp(&r, test, pool, 1); - mps_pool_destroy(pool); - - printf("\n\n****************************** Testing AMS\n"); - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); - MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); - MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, FALSE); - MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions); - die(mps_pool_create_k(&pool, arena, mps_class_ams(), args), - "pool_create(ams,share)"); - } MPS_ARGS_END(args); - mps_tramp(&r, test, pool, 0); - mps_pool_destroy(pool); + for (i = 0; i < 8; i++) { + int debug = i % 2; + int ownChain = (i / 2) % 2; + int ambig = (i / 4) % 2; + printf("\n\n*** AMS%s with %sCHAIN and %sSUPPORT_AMBIGUOUS\n", + debug ? " Debug" : "", + ownChain ? "" : "!", + ambig ? "" : "!"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + if (ownChain) + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, ambig); + MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions); + test_pool(debug ? mps_class_ams_debug() : mps_class_ams(), args, ambig); + } MPS_ARGS_END(args); + } + mps_arena_park(arena); mps_chain_destroy(chain); mps_fmt_destroy(format); mps_thread_dereg(thread); @@ -280,7 +250,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/amssshe.c b/code/amssshe.c index ddbda60701..26dc25822e 100644 --- a/code/amssshe.c +++ b/code/amssshe.c @@ -1,7 +1,7 @@ /* amssshe.c: POOL CLASS AMS STRESS TEST WITH HEADERS * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .design: Adapted from amsss.c. */ @@ -13,14 +13,9 @@ #include "mpscams.h" #include "mpsavm.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif #include "mps.h" -#include -#include -#include -#include + +#include /* fflush, printf */ #define exactRootsCOUNT 50 @@ -111,7 +106,7 @@ static void *test(void *arg, size_t s) lastStep = totalSize; printf("\nSize %"PRIuLONGEST" bytes, %lu objects.\n", (ulongest_t)totalSize, objs); - fflush(stdout); + (void)fflush(stdout); for(i = 0; i < exactRootsCOUNT; ++i) cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]), "all roots check"); @@ -139,11 +134,12 @@ static void *test(void *arg, size_t s) ++objs; if (objs % 256 == 0) { printf("."); - fflush(stdout); + (void)fflush(stdout); } } (void)mps_commit(busy_ap, busy_init, 64); + mps_arena_park(arena); mps_ap_destroy(busy_ap); mps_ap_destroy(ap); mps_root_destroy(exactRoot); @@ -151,6 +147,7 @@ static void *test(void *arg, size_t s) mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(format); + mps_arena_release(arena); return NULL; } @@ -162,11 +159,13 @@ int main(int argc, char *argv[]) mps_thr_t thread; void *r; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); - die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), - "arena_create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE)); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create"); + } MPS_ARGS_END(args); die(mps_thread_reg(&thread, arena), "thread_reg"); mps_tramp(&r, test, arena, 0); mps_thread_dereg(thread); @@ -179,7 +178,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/anangc.gmk b/code/anangc.gmk new file mode 100644 index 0000000000..f0a7d2ff51 --- /dev/null +++ b/code/anangc.gmk @@ -0,0 +1,66 @@ +# -*- makefile -*- +# +# anangc.gmk: BUILD FOR ANSI/ANSI/GCC PLATFORM +# +# $Id$ +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + +PFM = anangc + +MPMPF = \ + lockan.c \ + prmcan.c \ + protan.c \ + span.c \ + ssan.c \ + than.c \ + vman.c + +LIBS = -lm -lpthread + +include gc.gmk + +CFLAGSCOMPILER += -DCONFIG_PF_ANSI -DCONFIG_THREAD_SINGLE + +include comm.gmk + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2001-2014 Ravenbrook Limited . +# All rights reserved. This is an open source license. Contact +# Ravenbrook for commercial licensing options. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Redistributions in any form must be accompanied by information on how +# to obtain complete source code for this software and any accompanying +# software that uses this software. The source code must either be +# included in the distribution or be available for no more than the cost +# of distribution plus a nominal fee, and must be freely redistributable +# under reasonable conditions. For an executable file, complete source +# code means the source code for all modules it contains. It does not +# include source code for modules or files that typically accompany the +# major components of the operating system on which the executable file +# runs. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/ananll.gmk b/code/ananll.gmk new file mode 100644 index 0000000000..cc95645f21 --- /dev/null +++ b/code/ananll.gmk @@ -0,0 +1,66 @@ +# -*- makefile -*- +# +# ananll.gmk: BUILD FOR ANSI/ANSI/Clang PLATFORM +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + +PFM = ananll + +MPMPF = \ + lockan.c \ + prmcan.c \ + protan.c \ + span.c \ + ssan.c \ + than.c \ + vman.c + +LIBS = -lm -lpthread + +include ll.gmk + +CFLAGSCOMPILER += -DCONFIG_PF_ANSI -DCONFIG_THREAD_SINGLE + +include comm.gmk + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2001-2014 Ravenbrook Limited . +# All rights reserved. This is an open source license. Contact +# Ravenbrook for commercial licensing options. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Redistributions in any form must be accompanied by information on how +# to obtain complete source code for this software and any accompanying +# software that uses this software. The source code must either be +# included in the distribution or be available for no more than the cost +# of distribution plus a nominal fee, and must be freely redistributable +# under reasonable conditions. For an executable file, complete source +# code means the source code for all modules it contains. It does not +# include source code for modules or files that typically accompany the +# major components of the operating system on which the executable file +# runs. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/ananmv.nmk b/code/ananmv.nmk new file mode 100644 index 0000000000..41d80a0671 --- /dev/null +++ b/code/ananmv.nmk @@ -0,0 +1,62 @@ +# ananmv.nmk: ANSI/ANSI/MICROSOFT VISUAL C/C++ NMAKE FILE -*- makefile -*- +# +# $Id$ +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + +PFM = ananmv + +PFMDEFS = /DCONFIG_PF_ANSI /DCONFIG_THREAD_SINGLE + +MPMPF = \ + [lockan] \ + [prmcan] \ + [protan] \ + [span] \ + [ssan] \ + [than] \ + [vman] + +!INCLUDE commpre.nmk +!INCLUDE mv.nmk +!INCLUDE commpost.nmk + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2001-2014 Ravenbrook Limited . +# All rights reserved. This is an open source license. Contact +# Ravenbrook for commercial licensing options. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Redistributions in any form must be accompanied by information on how +# to obtain complete source code for this software and any accompanying +# software that uses this software. The source code must either be +# included in the distribution or be available for no more than the cost +# of distribution plus a nominal fee, and must be freely redistributable +# under reasonable conditions. For an executable file, complete source +# code means the source code for all modules it contains. It does not +# include source code for modules or files that typically accompany the +# major components of the operating system on which the executable file +# runs. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/apss.c b/code/apss.c index e395b50df2..5192baada4 100644 --- a/code/apss.c +++ b/code/apss.c @@ -1,7 +1,7 @@ /* apss.c: AP MANUAL ALLOC STRESS TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. */ @@ -16,7 +16,7 @@ #include "testlib.h" #include "mpslib.h" -#include +#include /* printf */ #include /* malloc */ @@ -41,36 +41,58 @@ static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size) } +/* check_allocated_size -- check the allocated size of the pool */ + +static void check_allocated_size(mps_pool_t pool, mps_ap_t ap, size_t allocated) +{ + size_t total_size = mps_pool_total_size(pool); + size_t free_size = mps_pool_free_size(pool); + size_t ap_free = (size_t)((char *)ap->limit - (char *)ap->init); + Insist(total_size - free_size == allocated + ap_free); +} + + /* stress -- create a pool of the requested type and allocate in it */ -static mps_res_t stress(mps_class_t class, size_t (*size)(unsigned long i), - mps_arena_t arena, ...) +static mps_res_t stress(mps_arena_t arena, mps_pool_debug_option_s *options, + mps_align_t align, + size_t (*size)(size_t i, mps_align_t align), + const char *name, mps_pool_class_t pool_class, + mps_arg_s args[]) { mps_res_t res = MPS_RES_OK; mps_pool_t pool; mps_ap_t ap; - va_list arg; - unsigned long i, k; + size_t i, k; int *ps[testSetSIZE]; size_t ss[testSetSIZE]; + size_t allocated = 0; /* Total allocated memory */ + size_t debugOverhead = options ? 2 * alignUp(options->fence_size, align) : 0; - va_start(arg, arena); - res = mps_pool_create_v(&pool, arena, class, arg); - va_end(arg); - if (res != MPS_RES_OK) - return res; + printf("stress %s\n", name); + die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create"); die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate"); /* allocate a load of objects */ for (i=0; i= sizeof(ps[i])) *ps[i] = 1; /* Write something, so it gets swap. */ + check_allocated_size(pool, ap, allocated); + } + + /* Check introspection functions */ + for (i = 0; i < NELEMS(ps); ++i) { + mps_pool_t addr_pool = NULL; + Insist(mps_arena_has_addr(arena, ps[i])); + Insist(mps_addr_pool(&addr_pool, arena, ps[i])); + Insist(addr_pool == pool); } mps_pool_check_fenceposts(pool); @@ -78,7 +100,7 @@ static mps_res_t stress(mps_class_t class, size_t (*size)(unsigned long i), for (k=0; k (b)) ? (a) : (b)) - -#define alignUp(w, a) (((w) + (a) - 1) & ~((size_t)(a) - 1)) - - -/* randomSizeAligned -- produce sizes both large and small, - * aligned by platform alignment */ +/* randomSizeAligned -- produce sizes both large and small, aligned to + * align. + */ -static size_t randomSizeAligned(unsigned long i) +static size_t randomSizeAligned(size_t i, mps_align_t align) { size_t maxSize = 2 * 160 * 0x2000; /* Reduce by a factor of 2 every 10 cycles. Total allocation about 40 MB. */ - return alignUp(rnd() % max((maxSize >> (i / 10)), 2) + 1, MPS_PF_ALIGN); + return alignUp(rnd() % max((maxSize >> (i / 10)), 2) + 1, align); } -static mps_pool_debug_option_s bothOptions8 = { - /* .fence_template = */ (const void *)"postpost", - /* .fence_size = */ 8, - /* .free_template = */ (const void *)"DEAD", - /* .free_size = */ 4 -}; - -static mps_pool_debug_option_s bothOptions16 = { - /* .fence_template = */ (const void *)"postpostpostpost", - /* .fence_size = */ 16, - /* .free_template = */ (const void *)"DEAD", +static mps_pool_debug_option_s bothOptions = { + /* .fence_template = */ "post", + /* .fence_size = */ 4, + /* .free_template = */ "DEAD", /* .free_size = */ 4 }; static mps_pool_debug_option_s fenceOptions = { - /* .fence_template = */ (const void *)"\0XXX ''\"\"'' XXX\0", - /* .fence_size = */ 16, + /* .fence_template = */ "123456789abcdef", + /* .fence_size = */ 15, /* .free_template = */ NULL, /* .free_size = */ 0 }; -/* testInArena -- test all the pool classes in the given arena */ -static void testInArena(mps_arena_t arena, mps_pool_debug_option_s *options) +/* test -- create arena using given class and arguments; test all the + * pool classes in this arena + */ + +static void test(mps_arena_class_t arena_class, mps_arg_s arena_args[], + mps_pool_debug_option_s *options) { - mps_res_t res; + mps_arena_t arena; + die(mps_arena_create_k(&arena, arena_class, arena_args), "mps_arena_create"); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = sizeof(void *) << (rnd() % 4); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_SPARE, rnd_double()); + die(stress(arena, NULL, align, randomSizeAligned, "MVFF", + mps_class_mvff(), args), "stress MVFF"); + } MPS_ARGS_END(args); /* IWBN to test MVFFDebug, but the MPS doesn't support debugging APs, */ /* yet (MV Debug works here, because it fakes it through PoolAlloc). */ - printf("MVFF\n"); - res = stress(mps_class_mvff(), randomSizeAligned, arena, - (size_t)65536, (size_t)32, (mps_align_t)MPS_PF_ALIGN, TRUE, TRUE, TRUE); - if (res == MPS_RES_COMMIT_LIMIT) return; - die(res, "stress MVFF"); - - printf("MV debug\n"); - res = stress(mps_class_mv_debug(), randomSizeAligned, arena, - options, (size_t)65536, (size_t)32, (size_t)65536); - if (res == MPS_RES_COMMIT_LIMIT) return; - die(res, "stress MV debug"); - - printf("MV\n"); - res = stress(mps_class_mv(), randomSizeAligned, arena, - (size_t)65536, (size_t)32, (size_t)65536); - if (res == MPS_RES_COMMIT_LIMIT) return; - die(res, "stress MV"); - - printf("MVT\n"); - res = stress(mps_class_mvt(), randomSizeAligned, arena, - (size_t)8, (size_t)32, (size_t)65536, (mps_word_t)4, - (mps_word_t)50); - if (res == MPS_RES_COMMIT_LIMIT) return; - die(res, "stress MVT"); -} - - -int main(int argc, char *argv[]) -{ - mps_arena_t arena; - mps_pool_debug_option_s *bothOptions; - - bothOptions = MPS_PF_ALIGN == 8 ? &bothOptions8 : &bothOptions16; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + MPS_ARGS_BEGIN(args) { + mps_align_t align = (mps_align_t)1 << (rnd() % 6); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + die(stress(arena, NULL, align, randomSizeAligned, "MV", + mps_class_mv(), args), "stress MV"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = (mps_align_t)1 << (rnd() % 6); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, options); + die(stress(arena, options, align, randomSizeAligned, "MV debug", + mps_class_mv_debug(), args), "stress MV debug"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = sizeof(void *) << (rnd() % 4); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + die(stress(arena, NULL, align, randomSizeAligned, "MVT", + mps_class_mvt(), args), "stress MVT"); + } MPS_ARGS_END(args); - die(mps_arena_create(&arena, mps_arena_class_vm(), 2*testArenaSIZE), - "mps_arena_create"); - die(mps_arena_commit_limit_set(arena, testArenaSIZE), "commit limit"); - testInArena(arena, &fenceOptions); mps_arena_destroy(arena); +} - die(mps_arena_create(&arena, mps_arena_class_vmnz(), 2*testArenaSIZE), - "mps_arena_create"); - testInArena(arena, bothOptions); - mps_arena_destroy(arena); - die(mps_arena_create(&arena, mps_arena_class_cl(), - testArenaSIZE, malloc(testArenaSIZE)), - "mps_arena_create"); - testInArena(arena, bothOptions); - mps_arena_destroy(arena); +int main(int argc, char *argv[]) +{ + testlib_init(argc, argv); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 2 * testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(2*testArenaSIZE)); + MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, testArenaSIZE); + test(mps_arena_class_vm(), args, &fenceOptions); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 2 * testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(2*testArenaSIZE)); + test(mps_arena_class_vm(), args, &bothOptions); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_CL_BASE, malloc(testArenaSIZE)); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE)); + test(mps_arena_class_cl(), args, &bothOptions); + } MPS_ARGS_END(args); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; @@ -217,7 +247,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/arena.c b/code/arena.c index f2e94f99fe..f68acd16c1 100644 --- a/code/arena.c +++ b/code/arena.c @@ -1,33 +1,57 @@ /* arena.c: ARENA ALLOCATION FEATURES * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .sources: is the main design document. */ #include "tract.h" #include "poolmv.h" #include "mpm.h" +#include "cbs.h" +#include "bt.h" +#include "poolmfs.h" +#include "mpscmfs.h" + SRCID(arena, "$Id$"); -/* ArenaControlPool -- get the control pool */ +#define ArenaControlPool(arena) MVPool(&(arena)->controlPoolStruct) +#define ArenaCBSBlockPool(arena) MFSPool(&(arena)->freeCBSBlockPoolStruct) +#define ArenaFreeLand(arena) CBSLand(&(arena)->freeLandStruct) + + +/* ArenaGrainSizeCheck -- check that size is a valid arena grain size */ + +Bool ArenaGrainSizeCheck(Size size) +{ + CHECKL(size > 0); + /* */ + CHECKL(SizeIsAligned(size, MPS_PF_ALIGN)); + /* Grain size must be a power of 2 for the tract lookup and the + * zones to work. */ + CHECKL(SizeIsP2(size)); -#define ArenaControlPool(arena) MV2Pool(&(arena)->controlPoolStruct) + return TRUE; +} /* Forward declarations */ static void ArenaTrivCompact(Arena arena, Trace trace); +static void arenaFreePage(Arena arena, Addr base, Pool pool); +static void arenaFreeLandFinish(Arena arena); /* ArenaTrivDescribe -- produce trivial description of an arena */ -static Res ArenaTrivDescribe(Arena arena, mps_lib_FILE *stream) +static Res ArenaTrivDescribe(Arena arena, mps_lib_FILE *stream, Count depth) { - if (!TESTT(Arena, arena)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Arena, arena)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; /* .describe.triv.never-called-from-subclass-method: * This Triv method seems to assume that it will never get called @@ -41,8 +65,8 @@ static Res ArenaTrivDescribe(Arena arena, mps_lib_FILE *stream) * subclass describe method should avoid invoking * ARENA_SUPERCLASS()->describe. RHSK 2007-04-27. */ - return WriteF(stream, - " No class-specific description available.\n", NULL); + return WriteF(stream, depth, + " No class-specific description available.\n", NULL); } @@ -62,15 +86,15 @@ DEFINE_CLASS(AbstractArenaClass, class) class->varargs = ArgTrivVarargs; class->init = NULL; class->finish = NULL; - class->reserved = NULL; class->purgeSpare = ArenaNoPurgeSpare; class->extend = ArenaNoExtend; - class->alloc = NULL; + class->grow = ArenaNoGrow; class->free = NULL; class->chunkInit = NULL; class->chunkFinish = NULL; class->compact = ArenaTrivCompact; class->describe = ArenaTrivDescribe; + class->pagesMarkAllocated = NULL; class->sig = ArenaClassSig; } @@ -79,7 +103,7 @@ DEFINE_CLASS(AbstractArenaClass, class) Bool ArenaClassCheck(ArenaClass class) { - CHECKL(ProtocolClassCheck(&class->protocol)); + CHECKD(ProtocolClass, &class->protocol); CHECKL(class->name != NULL); /* Should be <=6 char C identifier */ CHECKL(class->size >= sizeof(ArenaStruct)); /* Offset of generic Pool within class-specific instance cannot be */ @@ -89,15 +113,15 @@ Bool ArenaClassCheck(ArenaClass class) CHECKL(FUNCHECK(class->varargs)); CHECKL(FUNCHECK(class->init)); CHECKL(FUNCHECK(class->finish)); - CHECKL(FUNCHECK(class->reserved)); CHECKL(FUNCHECK(class->purgeSpare)); CHECKL(FUNCHECK(class->extend)); - CHECKL(FUNCHECK(class->alloc)); + CHECKL(FUNCHECK(class->grow)); CHECKL(FUNCHECK(class->free)); CHECKL(FUNCHECK(class->chunkInit)); CHECKL(FUNCHECK(class->chunkFinish)); CHECKL(FUNCHECK(class->compact)); CHECKL(FUNCHECK(class->describe)); + CHECKL(FUNCHECK(class->pagesMarkAllocated)); CHECKS(ArenaClass, class); return TRUE; } @@ -116,18 +140,21 @@ Bool ArenaCheck(Arena arena) CHECKD(MV, &arena->controlPoolStruct); CHECKD(Reservoir, &arena->reservoirStruct); } - /* Can't check that limit>=size because we may call ArenaCheck */ - /* while the size is being adjusted. */ + /* .reserved.check: Would like to check that arena->committed <= + * arena->reserved, but that isn't always true in the VM arena. + * Memory is committed early on when VMChunkCreate calls vmArenaMap + * (to provide a place for the chunk struct) but is not recorded as + * reserved until ChunkInit calls ArenaChunkInsert. + */ CHECKL(arena->committed <= arena->commitLimit); CHECKL(arena->spareCommitted <= arena->committed); CHECKL(ShiftCheck(arena->zoneShift)); - CHECKL(AlignCheck(arena->alignment)); - /* Tract allocation must be platform-aligned. */ - CHECKL(arena->alignment >= MPS_PF_ALIGN); - /* Stripes can't be smaller than pages. */ - CHECKL(((Size)1 << arena->zoneShift) >= arena->alignment); + CHECKL(ArenaGrainSizeCheck(arena->grainSize)); + + /* Stripes can't be smaller than grains. */ + CHECKL(((Size)1 << arena->zoneShift) >= arena->grainSize); if (arena->lastTract == NULL) { CHECKL(arena->lastTractBase == (Addr)0); @@ -138,51 +165,79 @@ Bool ArenaCheck(Arena arena) if (arena->primary != NULL) { CHECKD(Chunk, arena->primary); } - CHECKL(RingCheck(&arena->chunkRing)); + CHECKD_NOSIG(Ring, &arena->chunkRing); + /* Can't use CHECKD_NOSIG because TreeEMPTY is NULL. */ + CHECKL(TreeCheck(ArenaChunkTree(arena))); + /* TODO: check that the chunkRing and chunkTree have identical members */ /* nothing to check for chunkSerial */ - CHECKD(ChunkCacheEntry, &arena->chunkCache); - - CHECKL(LocusCheck(arena)); + CHECKL(LocusCheck(arena)); + + CHECKL(BoolCheck(arena->hasFreeLand)); + if (arena->hasFreeLand) + CHECKD(Land, ArenaFreeLand(arena)); + + CHECKL(BoolCheck(arena->zoned)); + return TRUE; } /* ArenaInit -- initialize the generic part of the arena * - * .init.caller: Unlike PoolInit, this is called by the class init - * methods, not the generic Create. This is because the class is - * responsible for allocating the descriptor. */ + * .init.caller: ArenaInit is called by class->init (which is called + * by ArenaCreate). The initialization must proceed in this order, as + * opposed to class->init being called by ArenaInit, which would + * correspond to the initialization order for pools and other objects, + * because the memory for the arena structure is not available until + * it has been allocated by the arena class. + */ -Res ArenaInit(Arena arena, ArenaClass class) +Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) { Res res; + Bool zoned = ARENA_DEFAULT_ZONED; + Size commitLimit = ARENA_DEFAULT_COMMIT_LIMIT; + Size spareCommitLimit = ARENA_DEFAULT_SPARE_COMMIT_LIMIT; + mps_arg_s arg; - /* We do not check the arena argument, because it's _supposed_ to */ - /* point to an uninitialized block of memory. */ + AVER(arena != NULL); AVERT(ArenaClass, class); + AVERT(ArenaGrainSize, grainSize); + + if (ArgPick(&arg, args, MPS_KEY_ARENA_ZONED)) + zoned = arg.val.b; + if (ArgPick(&arg, args, MPS_KEY_COMMIT_LIMIT)) + commitLimit = arg.val.size; + if (ArgPick(&arg, args, MPS_KEY_SPARE_COMMIT_LIMIT)) + spareCommitLimit = arg.val.size; arena->class = class; + arena->reserved = (Size)0; arena->committed = (Size)0; - /* commitLimit may be overridden by init (but probably not */ - /* as there's not much point) */ - arena->commitLimit = (Size)-1; + arena->commitLimit = commitLimit; arena->spareCommitted = (Size)0; - arena->spareCommitLimit = ARENA_INIT_SPARE_COMMIT_LIMIT; - /* alignment is usually overridden by init */ - arena->alignment = (Align)1 << ARENA_ZONESHIFT; + arena->spareCommitLimit = spareCommitLimit; + arena->grainSize = grainSize; /* zoneShift is usually overridden by init */ arena->zoneShift = ARENA_ZONESHIFT; arena->poolReady = FALSE; /* */ arena->lastTract = NULL; arena->lastTractBase = NULL; + arena->hasFreeLand = FALSE; + arena->freeZones = ZoneSetUNIV; + arena->zoned = zoned; arena->primary = NULL; RingInit(&arena->chunkRing); + arena->chunkTree = TreeEMPTY; arena->chunkSerial = (Serial)0; - ChunkCacheEntryInit(&arena->chunkCache); - + SplayTreeInit(ArenaSegSplay(arena), + SegCompare, + SegKey, + SplayTrivUpdate); + LocusInit(arena); res = GlobalsInit(ArenaGlobals(arena)); @@ -190,6 +245,23 @@ Res ArenaInit(Arena arena, ArenaClass class) goto failGlobalsInit; arena->sig = ArenaSig; + AVERT(Arena, arena); + + /* 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)); + MPS_ARGS_ADD(piArgs, MPS_KEY_EXTEND_BY, ArenaGrainSize(arena)); + MPS_ARGS_ADD(piArgs, MFSExtendSelf, FALSE); + res = PoolInit(ArenaCBSBlockPool(arena), arena, PoolClassMFS(), piArgs); + } MPS_ARGS_END(piArgs); + AVER(res == ResOK); /* no allocation, no failure expected */ + if (res != ResOK) + goto failMFSInit; /* initialize the reservoir, */ res = ReservoirInit(&arena->reservoirStruct, arena); @@ -200,6 +272,8 @@ Res ArenaInit(Arena arena, ArenaClass class) return ResOK; failReservoirInit: + PoolFinish(ArenaCBSBlockPool(arena)); +failMFSInit: GlobalsFinish(ArenaGlobals(arena)); failGlobalsInit: return res; @@ -214,12 +288,53 @@ Res ArenaInit(Arena arena, ArenaClass class) * platforms, knowing that it has no effect. To do that, the key must * exist on all platforms. */ -ARG_DEFINE_KEY(vmw3_top_down, Bool); +ARG_DEFINE_KEY(VMW3_TOP_DOWN, Bool); /* ArenaCreate -- create the arena and call initializers */ -ARG_DEFINE_KEY(arena_size, Size); +ARG_DEFINE_KEY(ARENA_GRAIN_SIZE, Size); +ARG_DEFINE_KEY(ARENA_SIZE, Size); +ARG_DEFINE_KEY(ARENA_ZONED, Bool); +ARG_DEFINE_KEY(COMMIT_LIMIT, Size); +ARG_DEFINE_KEY(SPARE_COMMIT_LIMIT, Size); + +static Res arenaFreeLandInit(Arena arena) +{ + Res res; + + AVERT(Arena, arena); + AVER(!arena->hasFreeLand); + AVER(arena->primary != NULL); + + /* 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) + goto failFreeLandInsert; + + arena->hasFreeLand = TRUE; + return ResOK; + +failFreeLandInsert: + LandFinish(ArenaFreeLand(arena)); +failLandInit: + return res; +} Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) { @@ -228,7 +343,7 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) AVER(arenaReturn != NULL); AVERT(ArenaClass, class); - AVER(ArgListCheck(args)); + AVERT(ArgList, args); /* We must initialise the event subsystem very early, because event logging will start as soon as anything interesting happens and expect to write @@ -240,12 +355,16 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) if (res != ResOK) goto failInit; - /* arena->alignment must have been set up by *class->init() */ - if (arena->alignment > ((Size)1 << arena->zoneShift)) { + /* Grain size must have been set up by *class->init() */ + if (ArenaGrainSize(arena) > ((Size)1 << arena->zoneShift)) { res = ResMEMORY; /* size was too small */ goto failStripeSize; } + res = arenaFreeLandInit(arena); + if (res != ResOK) + goto failFreeLandInit; + res = ControlInit(arena); if (res != ResOK) goto failControlInit; @@ -261,6 +380,8 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) failGlobalsCompleteCreate: ControlFinish(arena); failControlInit: + arenaFreeLandFinish(arena); +failFreeLandInit: failStripeSize: (*class->finish)(arena); failInit: @@ -276,16 +397,51 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) void ArenaFinish(Arena arena) { + PoolFinish(ArenaCBSBlockPool(arena)); ReservoirFinish(ArenaReservoir(arena)); arena->sig = SigInvalid; GlobalsFinish(ArenaGlobals(arena)); LocusFinish(arena); + SplayTreeFinish(ArenaSegSplay(arena)); RingFinish(&arena->chunkRing); + AVER(ArenaChunkTree(arena) == TreeEMPTY); } /* ArenaDestroy -- destroy the arena */ +static void arenaMFSPageFreeVisitor(Pool pool, Addr base, Size size, + void *closureP, Size closureS) +{ + AVERT(Pool, pool); + AVER(closureP == UNUSED_POINTER); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureP); + UNUSED(closureS); + UNUSED(size); + AVER(size == ArenaGrainSize(PoolArena(pool))); + arenaFreePage(PoolArena(pool), base, pool); +} + +static void arenaFreeLandFinish(Arena arena) +{ + AVERT(Arena, arena); + AVER(arena->hasFreeLand); + + /* We're about to free the memory occupied by the free land, which + contains a CBS. We want to make sure that LandFinish doesn't try + to check the CBS, so nuke it here. TODO: LandReset? */ + arena->freeLandStruct.splayTreeStruct.root = TreeEMPTY; + + /* The CBS block pool can't free its own memory via ArenaFree because + * that would use the free land. */ + MFSFinishExtents(ArenaCBSBlockPool(arena), arenaMFSPageFreeVisitor, + UNUSED_POINTER, UNUSED_SIZE); + + arena->hasFreeLand = FALSE; + LandFinish(ArenaFreeLand(arena)); +} + void ArenaDestroy(Arena arena) { AVERT(Arena, arena); @@ -295,9 +451,12 @@ void ArenaDestroy(Arena arena) /* Empty the reservoir - see */ ReservoirSetLimit(ArenaReservoir(arena), 0); - arena->poolReady = FALSE; 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. */ (*arena->class->finish)(arena); @@ -312,9 +471,10 @@ Res ControlInit(Arena arena) Res res; AVERT(Arena, arena); + AVER(!arena->poolReady); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, CONTROL_EXTEND_BY); - res = PoolInit(&arena->controlPoolStruct.poolStruct, arena, + res = PoolInit(MVPool(&arena->controlPoolStruct), arena, PoolClassMV(), args); } MPS_ARGS_END(args); if (res != ResOK) @@ -329,73 +489,78 @@ Res ControlInit(Arena arena) void ControlFinish(Arena arena) { AVERT(Arena, arena); + AVER(arena->poolReady); arena->poolReady = FALSE; - PoolFinish(&arena->controlPoolStruct.poolStruct); + PoolFinish(MVPool(&arena->controlPoolStruct)); } /* ArenaDescribe -- describe the arena */ -Res ArenaDescribe(Arena arena, mps_lib_FILE *stream) +Res ArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth) { Res res; - Size reserved; - if (!TESTT(Arena, arena)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Arena, arena)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = WriteF(stream, "Arena $P {\n", (WriteFP)arena, + res = WriteF(stream, depth, "Arena $P {\n", (WriteFP)arena, " class $P (\"$S\")\n", - (WriteFP)arena->class, arena->class->name, + (WriteFP)arena->class, (WriteFS)arena->class->name, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; if (arena->poolReady) { - res = WriteF(stream, - " controlPool $P\n", (WriteFP)&arena->controlPoolStruct, + res = WriteF(stream, depth + 2, + "controlPool $P\n", (WriteFP)&arena->controlPoolStruct, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; } - /* Note: this Describe clause calls a function */ - reserved = ArenaReserved(arena); - res = WriteF(stream, - " reserved $W <-- " - "total size of address-space reserved\n", - (WriteFW)reserved, - NULL); - if (res != ResOK) return res; - - res = WriteF(stream, - " committed $W <-- " - "total bytes currently stored (in RAM or swap)\n", - (WriteFW)arena->committed, - " commitLimit $W\n", (WriteFW)arena->commitLimit, - " spareCommitted $W\n", (WriteFW)arena->spareCommitted, - " spareCommitLimit $W\n", (WriteFW)arena->spareCommitLimit, - " zoneShift $U\n", (WriteFU)arena->zoneShift, - " alignment $W\n", (WriteFW)arena->alignment, + res = WriteF(stream, depth + 2, + "reserved $W\n", (WriteFW)arena->reserved, + "committed $W\n", (WriteFW)arena->committed, + "commitLimit $W\n", (WriteFW)arena->commitLimit, + "spareCommitted $W\n", (WriteFW)arena->spareCommitted, + "spareCommitLimit $W\n", (WriteFW)arena->spareCommitLimit, + "zoneShift $U\n", (WriteFU)arena->zoneShift, + "grainSize $W\n", (WriteFW)arena->grainSize, + "lastTract $P\n", (WriteFP)arena->lastTract, + "lastTractBase $P\n", (WriteFP)arena->lastTractBase, + "primary $P\n", (WriteFP)arena->primary, + "hasFreeLand $S\n", WriteFYesNo(arena->hasFreeLand), + "freeZones $B\n", (WriteFB)arena->freeZones, + "zoned $S\n", WriteFYesNo(arena->zoned), NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; - res = WriteF(stream, - " droppedMessages $U$S\n", (WriteFU)arena->droppedMessages, + res = WriteF(stream, depth + 2, + "droppedMessages $U$S\n", (WriteFU)arena->droppedMessages, (arena->droppedMessages == 0 ? "" : " -- MESSAGES DROPPED!"), NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; - res = (*arena->class->describe)(arena, stream); - if (res != ResOK) return res; + res = (*arena->class->describe)(arena, stream, depth); + if (res != ResOK) + return res; - /* Do not call GlobalsDescribe: it makes too much output, thanks. - * RHSK 2007-04-27 - */ -#if 0 - res = GlobalsDescribe(ArenaGlobals(arena), stream); - if (res != ResOK) return res; -#endif + res = WriteF(stream, depth + 2, "Globals {\n", NULL); + if (res != ResOK) + return res; + res = GlobalsDescribe(ArenaGlobals(arena), stream, depth + 4); + if (res != ResOK) + return res; + res = WriteF(stream, depth + 2, "} Globals\n", NULL); + if (res != ResOK) + return res; - res = WriteF(stream, + res = WriteF(stream, depth, "} Arena $P ($U)\n", (WriteFP)arena, (WriteFU)arena->serial, NULL); @@ -403,47 +568,76 @@ Res ArenaDescribe(Arena arena, mps_lib_FILE *stream) } -/* ArenaDescribeTracts -- describe all the tracts in the arena */ +/* arenaDescribeTractsInChunk -- describe the tracts in a chunk */ -Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream) +static Res arenaDescribeTractsInChunk(Chunk chunk, mps_lib_FILE *stream, Count depth) { Res res; - Tract tract; - Bool b; - Addr oldLimit, base, limit; - Size size; + Index pi; + + if (!TESTT(Chunk, chunk)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, "Chunk [$P, $P) ($U) {\n", + (WriteFP)chunk->base, (WriteFP)chunk->limit, + (WriteFU)chunk->serial, + NULL); + if (res != ResOK) + return res; - if (!TESTT(Arena, arena)) return ResFAIL; - if (stream == NULL) return ResFAIL; - - b = TractFirst(&tract, arena); - oldLimit = TractBase(tract); - while (b) { - base = TractBase(tract); - limit = TractLimit(tract); - size = ArenaAlign(arena); - - if (TractBase(tract) > oldLimit) { - res = WriteF(stream, - "[$P, $P) $W $U ---\n", - (WriteFP)oldLimit, (WriteFP)base, - (WriteFW)AddrOffset(oldLimit, base), - (WriteFU)AddrOffset(oldLimit, base), + for (pi = chunk->allocBase; pi < chunk->pages; ++pi) { + if (BTGet(chunk->allocTable, pi)) { + Tract tract = PageTract(ChunkPage(chunk, pi)); + res = WriteF(stream, depth + 2, "[$P, $P)", + (WriteFP)TractBase(tract), + (WriteFP)TractLimit(tract, ChunkArena(chunk)), NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; + if (TractHasPool(tract)) { + Pool pool = TractPool(tract); + res = WriteF(stream, 0, " $P $U ($S)", + (WriteFP)pool, + (WriteFU)(pool->serial), + (WriteFS)(pool->class->name), + NULL); + if (res != ResOK) + return res; + } + res = WriteF(stream, 0, "\n", NULL); + if (res != ResOK) + return res; } + } - res = WriteF(stream, - "[$P, $P) $W $U $P ($S)\n", - (WriteFP)base, (WriteFP)limit, - (WriteFW)size, (WriteFW)size, - (WriteFP)TractPool(tract), - (WriteFS)(TractPool(tract)->class->name), - NULL); - if (res != ResOK) return res; - b = TractNext(&tract, arena, TractBase(tract)); - oldLimit = limit; + res = WriteF(stream, depth, "} Chunk [$P, $P)\n", + (WriteFP)chunk->base, (WriteFP)chunk->limit, + NULL); + return res; +} + + +/* ArenaDescribeTracts -- describe all the tracts in the arena */ + +Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream, Count depth) +{ + Ring node, next; + Res res; + + if (!TESTT(Arena, arena)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + RING_FOR(node, &arena->chunkRing, next) { + Chunk chunk = RING_ELT(Chunk, arenaRing, node); + res = arenaDescribeTractsInChunk(chunk, stream, depth); + if (res != ResOK) + return res; } + return ResOK; } @@ -466,7 +660,7 @@ Res ControlAlloc(void **baseReturn, Arena arena, size_t size, AVERT(Arena, arena); AVER(baseReturn != NULL); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); AVER(arena->poolReady); res = PoolAlloc(&base, ArenaControlPool(arena), (Size)size, @@ -494,69 +688,467 @@ void ControlFree(Arena arena, void* base, size_t size) /* ControlDescribe -- describe the arena's control pool */ -Res ControlDescribe(Arena arena, mps_lib_FILE *stream) +Res ControlDescribe(Arena arena, mps_lib_FILE *stream, Count depth) +{ + Res res; + + if (!TESTT(Arena, arena)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = PoolDescribe(ArenaControlPool(arena), stream, depth); + + return res; +} + + +/* ArenaChunkInsert -- insert chunk into arena's chunk tree and ring, + * update the total reserved address space, and set the primary chunk + * if not already set. + */ + +void ArenaChunkInsert(Arena arena, Chunk chunk) { + Bool inserted; + Tree tree, updatedTree = NULL; + + AVERT(Arena, arena); + AVERT(Chunk, chunk); + tree = &chunk->chunkTree; + + inserted = TreeInsert(&updatedTree, ArenaChunkTree(arena), + tree, ChunkKey(tree), ChunkCompare); + AVER(inserted); + AVER(updatedTree); + TreeBalance(&updatedTree); + arena->chunkTree = updatedTree; + RingAppend(&arena->chunkRing, &chunk->arenaRing); + + arena->reserved += ChunkReserved(chunk); + + /* As part of the bootstrap, the first created chunk becomes the primary + chunk. This step allows ArenaFreeLandInsert to allocate pages. */ + if (arena->primary == NULL) + arena->primary = chunk; +} + + +/* ArenaChunkRemoved -- chunk was removed from the arena and is being + * finished, so update the total reserved address space, and unset the + * primary chunk if necessary. + */ + +void ArenaChunkRemoved(Arena arena, Chunk chunk) +{ + Size size; + + AVERT(Arena, arena); + AVERT(Chunk, chunk); + + size = ChunkReserved(chunk); + AVER(arena->reserved >= size); + arena->reserved -= size; + + if (chunk == arena->primary) { + /* The primary chunk must be the last chunk to be removed. */ + AVER(RingIsSingle(&arena->chunkRing)); + AVER(arena->reserved == 0); + arena->primary = NULL; + } +} + + +/* arenaAllocPage -- allocate one page from the arena + * + * This is a primitive allocator used to allocate pages for the arena + * Land. It is called rarely and can use a simple search. It may not + * use the Land or any pool, because it is used as part of the + * bootstrap. See design.mps.bootstrap.land.sol.alloc. + */ + +static Res arenaAllocPageInChunk(Addr *baseReturn, Chunk chunk, Pool pool) { Res res; + Index basePageIndex, limitPageIndex; + Arena arena; + + AVER(baseReturn != NULL); + AVERT(Chunk, chunk); + AVERT(Pool, pool); + arena = ChunkArena(chunk); + + if (!BTFindShortResRange(&basePageIndex, &limitPageIndex, + chunk->allocTable, + chunk->allocBase, chunk->pages, 1)) + return ResRESOURCE; + + res = (*arena->class->pagesMarkAllocated)(arena, chunk, + basePageIndex, 1, + pool); + if (res != ResOK) + return res; - if (!TESTT(Arena, arena)) return ResFAIL; - if (stream == NULL) return ResFAIL; + *baseReturn = PageIndexBase(chunk, basePageIndex); + return ResOK; +} - res = PoolDescribe(ArenaControlPool(arena), stream); +static Res arenaAllocPage(Addr *baseReturn, Arena arena, Pool pool) +{ + Res res; + + AVER(baseReturn != NULL); + AVERT(Arena, arena); + AVERT(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. */ + res = arenaAllocPageInChunk(baseReturn, arena->primary, pool); + if (res != ResOK) { + Ring node, next; + RING_FOR(node, &arena->chunkRing, next) { + Chunk chunk = RING_ELT(Chunk, arenaRing, node); + if (chunk != arena->primary) { + res = arenaAllocPageInChunk(baseReturn, chunk, pool); + if (res == ResOK) + break; + } + } + } return res; } +/* arenaFreePage -- free page allocated by arenaAllocPage */ + +static void arenaFreePage(Arena arena, Addr base, Pool pool) +{ + AVERT(Arena, arena); + AVERT(Pool, pool); + (*arena->class->free)(base, ArenaGrainSize(arena), pool); +} + + +/* arenaExtendCBSBlockPool -- add a page of memory to the CBS block pool + * + * IMPORTANT: Must be followed by arenaExcludePage to ensure that the + * page doesn't get allocated by ArenaAlloc. See .insert.exclude. + */ + +static Res arenaExtendCBSBlockPool(Range pageRangeReturn, Arena arena) +{ + Addr pageBase; + Res res; + + res = arenaAllocPage(&pageBase, arena, ArenaCBSBlockPool(arena)); + if (res != ResOK) + return res; + MFSExtend(ArenaCBSBlockPool(arena), pageBase, ArenaGrainSize(arena)); + + RangeInitSize(pageRangeReturn, pageBase, ArenaGrainSize(arena)); + return ResOK; +} + +/* arenaExcludePage -- exclude CBS block pool's page from free land + * + * Exclude the page we specially allocated for the CBS block pool + * so that it doesn't get reallocated. + */ + +static void arenaExcludePage(Arena arena, Range pageRange) +{ + RangeStruct oldRange; + Res res; + + res = LandDelete(&oldRange, ArenaFreeLand(arena), pageRange); + AVER(res == ResOK); /* we just gave memory to the Land */ +} + + +/* arenaFreeLandInsertExtend -- add range to arena's free land, maybe + * extending block pool + * + * 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). See design.mps.bootstrap.land.sol.pool. + * + * Only fails if it can't get a page for the block pool. + */ + +static Res arenaFreeLandInsertExtend(Range rangeReturn, Arena arena, + Range range) +{ + Res res; + + AVER(rangeReturn != NULL); + AVERT(Arena, arena); + AVERT(Range, range); + + res = LandInsert(rangeReturn, ArenaFreeLand(arena), range); + + if (res == ResLIMIT) { /* CBS block pool ran out of blocks */ + RangeStruct pageRange; + res = arenaExtendCBSBlockPool(&pageRange, arena); + if (res != ResOK) + return res; + /* .insert.exclude: Must insert before exclude so that we can + bootstrap when the zoned CBS is empty. */ + res = LandInsert(rangeReturn, ArenaFreeLand(arena), range); + AVER(res == ResOK); /* we just gave memory to the CBS block pool */ + arenaExcludePage(arena, &pageRange); + } + + return ResOK; +} + + +/* arenaFreeLandInsertSteal -- add range to arena's free land, maybe + * stealing memory + * + * 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 arenaFreeLandInsertSteal(Range rangeReturn, Arena arena, + Range rangeIO) +{ + Res res; + + AVER(rangeReturn != NULL); + AVERT(Arena, arena); + AVERT(Range, rangeIO); + + res = arenaFreeLandInsertExtend(rangeReturn, arena, rangeIO); + + if (res != ResOK) { + Addr pageBase; + Tract tract; + AVER(ResIsAllocFailure(res)); + + /* Steal a page from the memory we're about to free. */ + AVER(RangeSize(rangeIO) >= ArenaGrainSize(arena)); + pageBase = RangeBase(rangeIO); + RangeInit(rangeIO, AddrAdd(pageBase, ArenaGrainSize(arena)), + RangeLimit(rangeIO)); + + /* Steal the tract from its owning pool. */ + tract = TractOfBaseAddr(arena, pageBase); + TractFinish(tract); + TractInit(tract, ArenaCBSBlockPool(arena), pageBase); + + MFSExtend(ArenaCBSBlockPool(arena), pageBase, ArenaGrainSize(arena)); + + /* Try again. */ + res = LandInsert(rangeReturn, ArenaFreeLand(arena), rangeIO); + AVER(res == ResOK); /* we just gave memory to the CBS block pool */ + } + + AVER(res == ResOK); /* not expecting other kinds of error from the Land */ +} + + +/* 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 + * object across the boundary, preventing chunk deletion. + */ + +Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit) +{ + RangeStruct range, oldRange; + Res res; + + AVERT(Arena, arena); + + RangeInit(&range, base, limit); + res = arenaFreeLandInsertExtend(&oldRange, arena, &range); + if (res != ResOK) + return res; + + /* .chunk.no-coalesce: Make sure it didn't coalesce. We don't want + chunks to coalesce so that there are no chunk-crossing + allocations that would prevent chunks being destroyed. See + for the mechanism that ensures that + chunks never coalesce. */ + AVER(RangesEqual(&oldRange, &range)); + + return ResOK; +} + + +/* 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. + * + * IMPORTANT: May only be called on whole chunk ranges, because we don't + * deal with the case where the range is coalesced. This restriction would + * be easy to lift by extending the block pool on error, but doesn't happen, + * so we can't test that path. + */ + +void ArenaFreeLandDelete(Arena arena, Addr base, Addr limit) +{ + RangeStruct range, oldRange; + Res res; + + RangeInit(&range, base, limit); + res = LandDelete(&oldRange, ArenaFreeLand(arena), &range); + + /* Shouldn't be any other kind of failure because we were only deleting + a non-coalesced block. See .chunk.no-coalesce and + . */ + AVER(res == ResOK); +} + + +/* ArenaFreeLandAlloc -- allocate a continguous range of tracts of + * size bytes from the arena's free land. + * + * size, zones, and high are as for LandFindInZones. + * + * If successful, mark the allocated tracts as belonging to pool, set + * *tractReturn to point to the first tract in the range, and return + * ResOK. + */ + +Res ArenaFreeLandAlloc(Tract *tractReturn, Arena arena, ZoneSet zones, + Bool high, Size size, Pool pool) +{ + RangeStruct range, oldRange; + Chunk chunk = NULL; /* suppress uninit warning */ + Bool found, b; + Index baseIndex; + Count pages; + Res res; + + AVER(tractReturn != NULL); + AVERT(Arena, arena); + /* ZoneSet is arbitrary */ + AVER(size > (Size)0); + AVERT(Pool, pool); + AVER(arena == PoolArena(pool)); + AVER(SizeIsArenaGrains(size, arena)); + + if (!arena->zoned) + zones = ZoneSetUNIV; + + /* Step 1. Find a range of address space. */ + + res = LandFindInZones(&found, &range, &oldRange, ArenaFreeLand(arena), + size, zones, high); + + if (res == ResLIMIT) { /* found block, but couldn't store info */ + RangeStruct pageRange; + res = arenaExtendCBSBlockPool(&pageRange, arena); + if (res != ResOK) /* disastrously short on memory */ + return res; + arenaExcludePage(arena, &pageRange); + res = LandFindInZones(&found, &range, &oldRange, ArenaFreeLand(arena), + size, zones, high); + AVER(res != ResLIMIT); + } + + AVER(res == ResOK); /* unexpected error from ZoneCBS */ + if (res != ResOK) /* defensive return */ + return res; + + if (!found) /* out of address space */ + return ResRESOURCE; + + /* Step 2. Make memory available in the address space range. */ + + b = ChunkOfAddr(&chunk, arena, RangeBase(&range)); + AVER(b); + AVER(RangeIsAligned(&range, ChunkPageSize(chunk))); + baseIndex = INDEX_OF_ADDR(chunk, RangeBase(&range)); + pages = ChunkSizeToPages(chunk, RangeSize(&range)); + + res = (*arena->class->pagesMarkAllocated)(arena, chunk, baseIndex, pages, pool); + if (res != ResOK) + goto failMark; + + arena->freeZones = ZoneSetDiff(arena->freeZones, + ZoneSetOfRange(arena, + RangeBase(&range), + RangeLimit(&range))); + + *tractReturn = PageTract(ChunkPage(chunk, baseIndex)); + return ResOK; + +failMark: + { + 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. */ + } + return res; +} + + /* ArenaAlloc -- allocate some tracts from the arena */ -Res ArenaAlloc(Addr *baseReturn, SegPref pref, Size size, Pool pool, +Res ArenaAlloc(Addr *baseReturn, LocusPref pref, Size size, Pool pool, Bool withReservoirPermit) { Res res; Arena arena; Addr base; - Tract baseTract; + Tract tract; Reservoir reservoir; AVER(baseReturn != NULL); - AVERT(SegPref, pref); + AVERT(LocusPref, pref); AVER(size > (Size)0); AVERT(Pool, pool); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); arena = PoolArena(pool); AVERT(Arena, arena); - AVER(SizeIsAligned(size, arena->alignment)); + AVER(SizeIsArenaGrains(size, arena)); reservoir = ArenaReservoir(arena); AVERT(Reservoir, reservoir); - res = ReservoirEnsureFull(reservoir); - if (res != ResOK) { - AVER(ResIsAllocFailure(res)); - if (!withReservoirPermit) - return res; + if (pool != ReservoirPool(reservoir)) { + res = ReservoirEnsureFull(reservoir); + if (res != ResOK) { + AVER(ResIsAllocFailure(res)); + if (!withReservoirPermit) + return res; + } } - res = (*arena->class->alloc)(&base, &baseTract, pref, size, pool); - if (res == ResOK) { - goto goodAlloc; - } else if (withReservoirPermit) { - AVER(ResIsAllocFailure(res)); - res = ReservoirWithdraw(&base, &baseTract, reservoir, size, pool); - if (res == ResOK) - goto goodAlloc; + res = PolicyAlloc(&tract, arena, pref, size, pool); + if (res != ResOK) { + if (withReservoirPermit) { + Res resRes = ReservoirWithdraw(&base, &tract, reservoir, size, pool); + if (resRes != ResOK) + goto allocFail; + } else + goto allocFail; } - EVENT3(ArenaAllocFail, arena, size, pool); - return res; + + base = TractBase(tract); -goodAlloc: /* cache the tract - */ - arena->lastTract = baseTract; + arena->lastTract = tract; arena->lastTractBase = base; - EVENT5(ArenaAlloc, arena, baseTract, base, size, pool); + EVENT5(ArenaAlloc, arena, tract, base, size, pool); + *baseReturn = base; return ResOK; + +allocFail: + EVENT3(ArenaAllocFail, arena, size, pool); /* TODO: Should have res? */ + return res; } @@ -568,6 +1160,9 @@ void ArenaFree(Addr base, Size size, Pool pool) Addr limit; Reservoir reservoir; Res res; + Addr wholeBase; + Size wholeSize; + RangeStruct range, oldRange; AVERT(Pool, pool); AVER(base != NULL); @@ -576,8 +1171,8 @@ void ArenaFree(Addr base, Size size, Pool pool) AVERT(Arena, arena); reservoir = ArenaReservoir(arena); AVERT(Reservoir, reservoir); - AVER(AddrIsAligned(base, arena->alignment)); - AVER(SizeIsAligned(size, arena->alignment)); + AVER(AddrIsArenaGrain(base, arena)); + AVER(SizeIsArenaGrains(size, arena)); /* uncache the tract if in range - */ limit = AddrAdd(base, size); @@ -585,19 +1180,33 @@ void ArenaFree(Addr base, Size size, Pool pool) arena->lastTract = NULL; arena->lastTractBase = (Addr)0; } - - res = ReservoirEnsureFull(reservoir); - if (res == ResOK) { - (*arena->class->free)(base, size, pool); - } else { - AVER(ResIsAllocFailure(res)); - ReservoirDeposit(reservoir, base, size); + + wholeBase = base; + wholeSize = size; + + if (pool != ReservoirPool(reservoir)) { + res = ReservoirEnsureFull(reservoir); + if (res != ResOK) { + AVER(ResIsAllocFailure(res)); + if (!ReservoirDeposit(reservoir, &base, &size)) + goto allDeposited; + } } + /* Just in case the shenanigans with the reservoir mucked this up. */ + AVER(limit == AddrAdd(base, size)); + + RangeInit(&range, base, limit); + + arenaFreeLandInsertSteal(&oldRange, arena, &range); /* may update range */ + + (*arena->class->free)(RangeBase(&range), RangeSize(&range), pool); + /* Freeing memory might create spare pages, but not more than this. */ CHECKL(arena->spareCommitted <= arena->spareCommitLimit); - EVENT3(ArenaFree, arena, base, size); +allDeposited: + EVENT3(ArenaFree, arena, wholeBase, wholeSize); return; } @@ -605,7 +1214,7 @@ void ArenaFree(Addr base, Size size, Pool pool) Size ArenaReserved(Arena arena) { AVERT(Arena, arena); - return (*arena->class->reserved)(arena); + return arena->reserved; } Size ArenaCommitted(Arena arena) @@ -638,7 +1247,6 @@ void ArenaSetSpareCommitLimit(Arena arena, Size limit) } EVENT2(SpareCommitLimitSet, arena, limit); - return; } /* Used by arenas which don't use spare committed memory */ @@ -650,6 +1258,15 @@ Size ArenaNoPurgeSpare(Arena arena, Size size) } +Res ArenaNoGrow(Arena arena, LocusPref pref, Size size) +{ + AVERT(Arena, arena); + AVERT(LocusPref, pref); + UNUSED(size); + return ResRESOURCE; +} + + Size ArenaCommitLimit(Arena arena) { AVERT(Arena, arena); @@ -706,6 +1323,15 @@ Size ArenaAvail(Arena arena) } +/* ArenaCollectable -- return estimate of collectable memory in arena */ + +Size ArenaCollectable(Arena arena) +{ + /* Conservative estimate -- see job003929. */ + return ArenaCommitted(arena) - ArenaSpareCommitted(arena); +} + + /* ArenaExtend -- Add a new chunk in the arena */ Res ArenaExtend(Arena arena, Addr base, Size size) @@ -759,10 +1385,10 @@ static void ArenaTrivCompact(Arena arena, Trace trace) Bool ArenaHasAddr(Arena arena, Addr addr) { - Seg seg; + Tract tract; AVERT(Arena, arena); - return SegOfAddr(&seg, arena, addr); + return TractOfAddr(&tract, arena, addr); } @@ -788,7 +1414,7 @@ Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/arenacl.c b/code/arenacl.c index 473ac37a40..cd5c24fe46 100644 --- a/code/arenacl.c +++ b/code/arenacl.c @@ -1,7 +1,7 @@ /* arenacl.c: ARENA CLASS USING CLIENT MEMORY * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .design: See . * @@ -61,13 +61,14 @@ typedef struct ClientChunkStruct { /* ClientChunkCheck -- check the consistency of a client chunk */ +ATTRIBUTE_UNUSED static Bool ClientChunkCheck(ClientChunk clChunk) { Chunk chunk; CHECKS(ClientChunk, clChunk); chunk = ClientChunk2Chunk(clChunk); - CHECKL(ChunkCheck(chunk)); + CHECKD(Chunk, chunk); CHECKL(clChunk->freePages <= chunk->pages); /* check they don't overlap (knowing the order) */ CHECKL((Addr)(chunk + 1) < (Addr)chunk->allocTable); @@ -77,19 +78,28 @@ static Bool ClientChunkCheck(ClientChunk clChunk) /* ClientArenaCheck -- check the consistency of a client arena */ +ATTRIBUTE_UNUSED static Bool ClientArenaCheck(ClientArena clientArena) { + Arena arena; + CHECKS(ClientArena, clientArena); - CHECKD(Arena, ClientArena2Arena(clientArena)); + arena = ClientArena2Arena(clientArena); + CHECKD(Arena, arena); + /* See */ + CHECKL(arena->committed <= arena->reserved); + CHECKL(arena->spareCommitted == 0); + return TRUE; } /* clientChunkCreate -- create a ClientChunk */ -static Res clientChunkCreate(Chunk *chunkReturn, Addr base, Addr limit, - ClientArena clientArena) +static Res clientChunkCreate(Chunk *chunkReturn, ClientArena clientArena, + Addr base, Addr limit) { + Arena arena; ClientChunk clChunk; Chunk chunk; Addr alignedBase; @@ -99,14 +109,15 @@ static Res clientChunkCreate(Chunk *chunkReturn, Addr base, Addr limit, void *p; AVER(chunkReturn != NULL); + AVERT(ClientArena, clientArena); + arena = ClientArena2Arena(clientArena); AVER(base != (Addr)0); - /* TODO: Should refuse on small chunks, instead of AVERring. */ AVER(limit != (Addr)0); AVER(limit > base); /* Initialize boot block. */ /* Chunk has to be page-aligned, and the boot allocs must be within it. */ - alignedBase = AddrAlignUp(base, ARENA_CLIENT_PAGE_SIZE); + alignedBase = AddrAlignUp(base, ArenaGrainSize(arena)); AVER(alignedBase < limit); res = BootBlockInit(boot, (void *)alignedBase, (void *)limit); if (res != ResOK) @@ -117,16 +128,17 @@ static Res clientChunkCreate(Chunk *chunkReturn, Addr base, Addr limit, res = BootAlloc(&p, boot, sizeof(ClientChunkStruct), MPS_PF_ALIGN); if (res != ResOK) goto failChunkAlloc; - clChunk = p; chunk = ClientChunk2Chunk(clChunk); + clChunk = p; + chunk = ClientChunk2Chunk(clChunk); - res = ChunkInit(chunk, ClientArena2Arena(clientArena), - alignedBase, AddrAlignDown(limit, ARENA_CLIENT_PAGE_SIZE), - ARENA_CLIENT_PAGE_SIZE, boot); + res = ChunkInit(chunk, arena, alignedBase, + AddrAlignDown(limit, ArenaGrainSize(arena)), + AddrOffset(base, limit), boot); if (res != ResOK) goto failChunkInit; - ClientArena2Arena(clientArena)->committed += - AddrOffset(base, PageIndexBase(chunk, chunk->allocBase)); + arena->committed += ChunkPagesToSize(chunk, chunk->allocBase); + BootBlockFinish(boot); clChunk->sig = ClientChunkSig; @@ -152,7 +164,6 @@ static Res ClientChunkInit(Chunk chunk, BootBlock boot) /* chunk is supposed to be uninitialized, so don't check it. */ clChunk = Chunk2ClientChunk(chunk); AVERT(BootBlock, boot); - UNUSED(boot); /* TODO: An old comment claimed this is too large. Does it fail to exclude the page table or something? */ @@ -171,15 +182,35 @@ static Res ClientChunkInit(Chunk chunk, BootBlock boot) /* clientChunkDestroy -- destroy a ClientChunk */ -static void clientChunkDestroy(Chunk chunk) +static Bool clientChunkDestroy(Tree tree, void *closureP, Size closureS) { + Arena arena; + Chunk chunk; ClientChunk clChunk; + Size size; + AVERT(Tree, tree); + AVER(closureP == UNUSED_POINTER); + UNUSED(closureP); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); + + chunk = ChunkOfTree(tree); + AVERT(Chunk, chunk); + arena = ChunkArena(chunk); + AVERT(Arena, arena); clChunk = Chunk2ClientChunk(chunk); AVERT(ClientChunk, clChunk); + AVER(chunk->pages == clChunk->freePages); + + size = ChunkPagesToSize(chunk, chunk->allocBase); + AVER(arena->committed >= size); + arena->committed -= size; clChunk->sig = SigInvalid; ChunkFinish(chunk); + + return TRUE; } @@ -188,7 +219,7 @@ static void clientChunkDestroy(Chunk chunk) static void ClientChunkFinish(Chunk chunk) { /* Can't check chunk as it's not valid anymore. */ - UNUSED(chunk); NOOP; + UNUSED(chunk); } @@ -201,7 +232,7 @@ static void ClientArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[1].key = MPS_KEY_ARENA_CL_BASE; args[1].val.addr = va_arg(varargs, Addr); args[2].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } @@ -213,7 +244,7 @@ static void ClientArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) * to do the generic part of init. */ -ARG_DEFINE_KEY(arena_cl_addr, Addr); +ARG_DEFINE_KEY(ARENA_CL_BASE, Addr); static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) { @@ -222,20 +253,30 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) Size size; Size clArenaSize; /* aligned size of ClientArenaStruct */ Addr base, limit, chunkBase; + Align grainSize = 1; Res res; Chunk chunk; mps_arg_s arg; AVER(arenaReturn != NULL); AVER((ArenaClass)mps_arena_class_cl() == class); - AVER(ArgListCheck(args)); + AVERT(ArgList, args); ArgRequire(&arg, args, MPS_KEY_ARENA_SIZE); size = arg.val.size; ArgRequire(&arg, args, MPS_KEY_ARENA_CL_BASE); base = arg.val.addr; + if (ArgPick(&arg, args, MPS_KEY_ARENA_GRAIN_SIZE)) + grainSize = arg.val.size; + grainSize = SizeAlignUp(grainSize, ARENA_CLIENT_GRAIN_SIZE); + grainSize = SizeAlignUp(grainSize, ProtGranularity()); AVER(base != (Addr)0); + AVERT(ArenaGrainSize, grainSize); + + if (size < grainSize * MPS_WORD_WIDTH) + /* Not enough room for a full complement of zones. */ + return ResMEMORY; clArenaSize = SizeAlignUp(sizeof(ClientArenaStruct), MPS_PF_ALIGN); if (size < clArenaSize) @@ -252,14 +293,14 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) arena = ClientArena2Arena(clientArena); /* */ - res = ArenaInit(arena, class); + res = ArenaInit(arena, class, grainSize, args); if (res != ResOK) return res; /* have to have a valid arena before calling ChunkCreate */ clientArena->sig = ClientArenaSig; - res = clientChunkCreate(&chunk, chunkBase, limit, clientArena); + res = clientChunkCreate(&chunk, clientArena, chunkBase, limit); if (res != ResOK) goto failChunkCreate; arena->primary = chunk; @@ -269,7 +310,7 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) /* bits in a word). Note that some zones are discontiguous in the */ /* arena if the size is not a power of 2. */ arena->zoneShift = SizeFloorLog2(size >> MPS_WORD_SHIFT); - arena->alignment = ChunkPageSize(arena->primary); + AVER(ArenaGrainSize(arena) == ChunkPageSize(arena->primary)); EVENT3(ArenaCreateCL, arena, size, base); AVERT(ClientArena, clientArena); @@ -288,19 +329,22 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) static void ClientArenaFinish(Arena arena) { ClientArena clientArena; - Ring node, next; clientArena = Arena2ClientArena(arena); AVERT(ClientArena, clientArena); - /* destroy all chunks */ - RING_FOR(node, &arena->chunkRing, next) { - Chunk chunk = RING_ELT(Chunk, chunkRing, node); - clientChunkDestroy(chunk); - } + /* Destroy all chunks, including the primary. See + * */ + arena->primary = NULL; + TreeTraverseAndDelete(&arena->chunkTree, clientChunkDestroy, + UNUSED_POINTER, UNUSED_SIZE); clientArena->sig = SigInvalid; + /* Destroying the chunks should leave nothing behind. */ + AVER(arena->reserved == 0); + AVER(arena->committed == 0); + ArenaFinish(arena); /* */ } @@ -320,126 +364,43 @@ static Res ClientArenaExtend(Arena arena, Addr base, Size size) limit = AddrAdd(base, size); clientArena = Arena2ClientArena(arena); - res = clientChunkCreate(&chunk, base, limit, clientArena); + res = clientChunkCreate(&chunk, clientArena, base, limit); return res; } -/* ClientArenaReserved -- return the amount of reserved address space */ - -static Size ClientArenaReserved(Arena arena) -{ - Size size; - Ring node, nextNode; - - AVERT(Arena, arena); - - size = 0; - /* .req.extend.slow */ - RING_FOR(node, &arena->chunkRing, nextNode) { - Chunk chunk = RING_ELT(Chunk, chunkRing, node); - AVERT(Chunk, chunk); - size += AddrOffset(chunk->base, chunk->limit); - } - - return size; -} - - -/* chunkAlloc -- allocate some tracts in a chunk */ +/* ClientArenaPagesMarkAllocated -- Mark the pages allocated */ -static Res chunkAlloc(Addr *baseReturn, Tract *baseTractReturn, - SegPref pref, Size pages, Pool pool, Chunk chunk) +static Res ClientArenaPagesMarkAllocated(Arena arena, Chunk chunk, + Index baseIndex, Count pages, + Pool pool) { - Index baseIndex, limitIndex, indx; - Bool b; - Arena arena; + Index i; ClientChunk clChunk; - - AVER(baseReturn != NULL); - AVER(baseTractReturn != NULL); + + AVERT(Arena, arena); + AVERT(Chunk, chunk); clChunk = Chunk2ClientChunk(chunk); + AVERT(ClientChunk, clChunk); + AVER(chunk->allocBase <= baseIndex); + AVER(pages > 0); + AVER(baseIndex + pages <= chunk->pages); + AVERT(Pool, pool); - if (pages > clChunk->freePages) - return ResRESOURCE; - - arena = chunk->arena; - - if (pref->high) - b = BTFindShortResRangeHigh(&baseIndex, &limitIndex, chunk->allocTable, - chunk->allocBase, chunk->pages, pages); - else - b = BTFindShortResRange(&baseIndex, &limitIndex, chunk->allocTable, - chunk->allocBase, chunk->pages, pages); - - if (!b) - return ResRESOURCE; - - /* Check commit limit. Note that if there are multiple reasons */ - /* for failing the allocation we attempt to return other result codes */ - /* in preference to ResCOMMIT_LIMIT. See */ - if (ArenaCommitted(arena) + pages * ChunkPageSize(chunk) - > arena->commitLimit) { - return ResCOMMIT_LIMIT; - } - - /* Initialize the generic tract structures. */ - AVER(limitIndex > baseIndex); - for(indx = baseIndex; indx < limitIndex; ++indx) { - PageAlloc(chunk, indx, pool); - } + for (i = 0; i < pages; ++i) + PageAlloc(chunk, baseIndex + i, pool); + arena->committed += ChunkPagesToSize(chunk, pages); + AVER(clChunk->freePages >= pages); clChunk->freePages -= pages; - *baseReturn = PageIndexBase(chunk, baseIndex); - *baseTractReturn = PageTract(ChunkPage(chunk, baseIndex)); - return ResOK; } -/* ClientAlloc -- allocate a region from the arena */ - -static Res ClientAlloc(Addr *baseReturn, Tract *baseTractReturn, - SegPref pref, Size size, Pool pool) -{ - Arena arena; - Res res; - Ring node, nextNode; - Size pages; - - AVER(baseReturn != NULL); - AVER(baseTractReturn != NULL); - AVERT(SegPref, pref); - AVER(size > 0); - AVERT(Pool, pool); - - arena = PoolArena(pool); - AVERT(Arena, arena); - /* All chunks have same pageSize. */ - AVER(SizeIsAligned(size, ChunkPageSize(arena->primary))); - /* NULL is used as a discriminator (see */ - /* ), therefore the real pool */ - /* must be non-NULL. */ - AVER(pool != NULL); - - pages = ChunkSizeToPages(arena->primary, size); - - /* .req.extend.slow */ - RING_FOR(node, &arena->chunkRing, nextNode) { - Chunk chunk = RING_ELT(Chunk, chunkRing, node); - res = chunkAlloc(baseReturn, baseTractReturn, pref, pages, pool, chunk); - if (res == ResOK || res == ResCOMMIT_LIMIT) { - return res; - } - } - return ResRESOURCE; -} - - -/* ClientFree - free a region in the arena */ +/* ClientArenaFree - free a region in the arena */ -static void ClientFree(Addr base, Size size, Pool pool) +static void ClientArenaFree(Addr base, Size size, Pool pool) { Arena arena; Chunk chunk = NULL; /* suppress "may be used uninitialized" */ @@ -480,6 +441,8 @@ static void ClientFree(Addr base, Size size, Pool pool) AVER(BTIsSetRange(chunk->allocTable, baseIndex, limitIndex)); BTResRange(chunk->allocTable, baseIndex, limitIndex); + AVER(arena->committed >= size); + arena->committed -= size; clChunk->freePages += pages; } @@ -495,12 +458,12 @@ DEFINE_ARENA_CLASS(ClientArenaClass, this) this->varargs = ClientArenaVarargs; this->init = ClientArenaInit; this->finish = ClientArenaFinish; - this->reserved = ClientArenaReserved; this->extend = ClientArenaExtend; - this->alloc = ClientAlloc; - this->free = ClientFree; + this->pagesMarkAllocated = ClientArenaPagesMarkAllocated; + this->free = ClientArenaFree; this->chunkInit = ClientChunkInit; this->chunkFinish = ClientChunkFinish; + AVERT(ArenaClass, this); } @@ -514,7 +477,7 @@ mps_arena_class_t mps_arena_class_cl(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/arenacv.c b/code/arenacv.c index b52345f901..7b02c11bcc 100644 --- a/code/arenacv.c +++ b/code/arenacv.c @@ -1,7 +1,7 @@ /* arenacv.c: ARENA COVERAGE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .coverage: At the moment, we're only trying to cover the new code * (partial mapping of the page table and vm overflow). @@ -14,8 +14,6 @@ * being allocated; this requires using two adjacent zones. */ -#include - #include "mpm.h" #include "poolmv.h" #include "testlib.h" @@ -23,6 +21,9 @@ #include "mpsavm.h" #include "mpsacl.h" +#include /* printf */ +#include /* malloc */ + #define tractsSIZE 500 @@ -54,7 +55,7 @@ typedef struct AllocInfoStruct { } the; } AllocInfoStruct; -typedef Res (*AllocFun)(AllocInfoStruct *aiReturn, SegPref pref, +typedef Res (*AllocFun)(AllocInfoStruct *aiReturn, LocusPref pref, Size size, Pool pool); typedef void (*FreeFun)(AllocInfo ai); @@ -86,9 +87,76 @@ typedef struct AllocatorClassStruct { } AllocatorClassStruct; +/* tractSearchInChunk -- find a tract in a chunk + * + * .tract-search: Searches for a tract in the chunk starting at page + * index i, return FALSE if there is none. + */ + +static Bool tractSearchInChunk(Tract *tractReturn, Chunk chunk, Index i) +{ + AVER_CRITICAL(chunk->allocBase <= i); + AVER_CRITICAL(i <= chunk->pages); + + while (i < chunk->pages + && !(BTGet(chunk->allocTable, i) + && PageIsAllocated(ChunkPage(chunk, i)))) { + ++i; + } + if (i == chunk->pages) + return FALSE; + AVER(i < chunk->pages); + *tractReturn = PageTract(ChunkPage(chunk, i)); + return TRUE; +} + + +/* tractSearch -- find next tract above address + * + * Searches for the next tract in increasing address order. + * The tract returned is the next one along from addr (i.e., + * it has a base address bigger than addr and no other tract + * with a base address bigger than addr has a smaller base address). + * + * Returns FALSE if there is no tract to find (end of the arena). + */ + +static Bool tractSearch(Tract *tractReturn, Arena arena, Addr addr) +{ + Bool b; + Chunk chunk; + Tree tree; + + b = ChunkOfAddr(&chunk, arena, addr); + if (b) { + Index i; + + i = INDEX_OF_ADDR(chunk, addr); + /* There are fewer pages than addresses, therefore the */ + /* page index can never wrap around */ + AVER_CRITICAL(i+1 != 0); + + if (tractSearchInChunk(tractReturn, chunk, i+1)) { + return TRUE; + } + } + while (TreeFindNext(&tree, ArenaChunkTree(arena), TreeKeyOfAddrVar(addr), + ChunkCompare)) + { + chunk = ChunkOfTree(tree); + addr = chunk->base; + /* Start from allocBase to skip the tables. */ + if (tractSearchInChunk(tractReturn, chunk, chunk->allocBase)) { + return TRUE; + } + } + return FALSE; +} + + /* Implementation of the tract-based interchangability interface */ -static Res allocAsTract(AllocInfoStruct *aiReturn, SegPref pref, +static Res allocAsTract(AllocInfoStruct *aiReturn, LocusPref pref, Size size, Pool pool) { Res res; @@ -113,10 +181,10 @@ static Bool firstAsTract(AllocInfoStruct *aiReturn, Arena arena) { Bool res; Tract tract; - res = TractFirst(&tract, arena); + res = tractSearch(&tract, arena, 0); if (res) { aiReturn->the.tractData.base = TractBase(tract); - aiReturn->the.tractData.size = ArenaAlign(arena);; + aiReturn->the.tractData.size = ArenaGrainSize(arena);; aiReturn->the.tractData.pool = TractPool(tract); } return res; @@ -127,10 +195,10 @@ static Bool nextAsTract(AllocInfoStruct *nextReturn, AllocInfo ai, { Bool res; Tract tract; - res = TractNext(&tract, arena, ai->the.tractData.base); + res = tractSearch(&tract, arena, ai->the.tractData.base); if (res) { nextReturn->the.tractData.base = TractBase(tract); - nextReturn->the.tractData.size = ArenaAlign(arena);; + nextReturn->the.tractData.size = ArenaGrainSize(arena);; nextReturn->the.tractData.pool = TractPool(tract); } return res; @@ -176,7 +244,7 @@ static AllocatorClassStruct allocatorTractStruct = { /* Implementation of the segment-based interchangability interface */ -static Res allocAsSeg(AllocInfoStruct *aiReturn, SegPref pref, +static Res allocAsSeg(AllocInfoStruct *aiReturn, LocusPref pref, Size size, Pool pool) { Res res; @@ -261,12 +329,12 @@ static void testAllocAndIterate(Arena arena, Pool pool, AllocatorClass allocator) { AllocInfoStruct offsetRegion, gapRegion, newRegion, topRegion; - SegPrefStruct pref; + LocusPrefStruct pref; Count offset, gap, new; ZoneSet zone = (ZoneSet)2; int i; - SegPrefInit(&pref); + LocusPrefInit(&pref); /* Testing the behaviour with various sizes of gaps in the page table. */ @@ -329,13 +397,12 @@ static void testAllocAndIterate(Arena arena, Pool pool, allocator->free(&offsetRegion); } } - SegPrefExpress(&pref, SegPrefZoneSet, &zone); + LocusPrefExpress(&pref, LocusPrefZONESET, &zone); } - } -static void testPageTable(ArenaClass class, Size size, Addr addr) +static void testPageTable(ArenaClass class, Size size, Addr addr, Bool zoned) { Arena arena; Pool pool; Size pageSize; @@ -344,12 +411,13 @@ static void testPageTable(ArenaClass class, Size size, Addr addr) MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, size); MPS_ARGS_ADD(args, MPS_KEY_ARENA_CL_BASE, addr); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, zoned); die(ArenaCreate(&arena, class, args), "ArenaCreate"); } MPS_ARGS_END(args); die(PoolCreate(&pool, arena, PoolClassMV(), argsNone), "PoolCreate"); - pageSize = ArenaAlign(arena); + pageSize = ArenaGrainSize(arena); tractsPerPage = pageSize / sizeof(TractStruct); printf("%ld tracts per page in the page table.\n", (long)tractsPerPage); @@ -361,6 +429,10 @@ static void testPageTable(ArenaClass class, Size size, Addr addr) testAllocAndIterate(arena, pool, pageSize, tractsPerPage, &allocatorSegStruct); + die(ArenaDescribe(arena, mps_lib_get_stdout(), 0), "ArenaDescribe"); + die(ArenaDescribeTracts(arena, mps_lib_get_stdout(), 0), + "ArenaDescribeTracts"); + PoolDestroy(pool); ArenaDestroy(arena); } @@ -398,14 +470,15 @@ static void testSize(Size size) int main(int argc, char *argv[]) { void *block; - testlib_unused(argc); - testPageTable((ArenaClass)mps_arena_class_vm(), TEST_ARENA_SIZE, 0); - testPageTable((ArenaClass)mps_arena_class_vmnz(), TEST_ARENA_SIZE, 0); + testlib_init(argc, argv); + + testPageTable((ArenaClass)mps_arena_class_vm(), TEST_ARENA_SIZE, 0, TRUE); + testPageTable((ArenaClass)mps_arena_class_vm(), TEST_ARENA_SIZE, 0, FALSE); block = malloc(TEST_ARENA_SIZE); cdie(block != NULL, "malloc"); - testPageTable((ArenaClass)mps_arena_class_cl(), TEST_ARENA_SIZE, block); + testPageTable((ArenaClass)mps_arena_class_cl(), TEST_ARENA_SIZE, block, FALSE); testSize(TEST_ARENA_SIZE); @@ -416,7 +489,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/arenavm.c b/code/arenavm.c index fae4419a3f..f5666a1e7e 100644 --- a/code/arenavm.c +++ b/code/arenavm.c @@ -22,11 +22,14 @@ */ #include "boot.h" -#include "tract.h" #include "bt.h" -#include "sa.h" +#include "cbs.h" #include "mpm.h" #include "mpsavm.h" +#include "poolmfs.h" +#include "sa.h" +#include "tract.h" +#include "vm.h" SRCID(arenavm, "$Id$"); @@ -39,13 +42,14 @@ typedef struct VMChunkStruct *VMChunk; typedef struct VMChunkStruct { ChunkStruct chunkStruct; /* generic chunk */ - VM vm; /* virtual memory handle */ + VMStruct vmStruct; /* virtual memory descriptor */ Addr overheadMappedLimit; /* limit of pages mapped for overhead */ SparseArrayStruct pages; /* to manage backing store of page table */ Sig sig; /* */ } VMChunkStruct; #define VMChunk2Chunk(vmchunk) (&(vmchunk)->chunkStruct) +#define VMChunkVM(vmchunk) (&(vmchunk)->vmStruct) #define Chunk2VMChunk(chunk) PARENT(VMChunkStruct, chunkStruct, chunk) @@ -66,22 +70,20 @@ typedef struct VMArenaStruct *VMArena; typedef struct VMArenaStruct { /* VM arena structure */ ArenaStruct arenaStruct; - VM vm; /* VM where the arena itself is stored */ + VMStruct vmStruct; /* VM descriptor for VM containing arena */ char vmParams[VMParamSize]; /* VM parameter block */ - Size spareSize; /* total size of spare pages */ - ZoneSet blacklist; /* zones to use last */ - ZoneSet freeSet; /* unassigned zones */ + Size spareSize; /* total size of spare pages */ Size extendBy; /* desired arena increment */ Size extendMin; /* minimum arena increment */ ArenaVMExtendedCallback extended; ArenaVMContractedCallback contracted; RingStruct spareRing; /* spare (free but mapped) tracts */ - RingStruct freeRing[MPS_WORD_WIDTH]; /* free page caches, per zone */ Sig sig; /* */ } VMArenaStruct; #define Arena2VMArena(arena) PARENT(VMArenaStruct, arenaStruct, arena) #define VMArena2Arena(vmarena) (&(vmarena)->arenaStruct) +#define VMArenaVM(vmarena) (&(vmarena)->vmStruct) /* Forward declarations */ @@ -89,21 +91,21 @@ typedef struct VMArenaStruct { /* VM arena structure */ static Size VMPurgeSpare(Arena arena, Size size); static void chunkUnmapSpare(Chunk chunk); extern ArenaClass VMArenaClassGet(void); -extern ArenaClass VMNZArenaClassGet(void); static void VMCompact(Arena arena, Trace trace); /* VMChunkCheck -- check the consistency of a VM chunk */ +ATTRIBUTE_UNUSED static Bool VMChunkCheck(VMChunk vmchunk) { Chunk chunk; CHECKS(VMChunk, vmchunk); chunk = VMChunk2Chunk(vmchunk); - CHECKL(ChunkCheck(chunk)); - CHECKL(VMCheck(vmchunk->vm)); - CHECKL(VMAlign(vmchunk->vm) == ChunkPageSize(chunk)); + CHECKD(Chunk, chunk); + CHECKD(VM, VMChunkVM(vmchunk)); + CHECKL(SizeIsAligned(ChunkPageSize(chunk), VMPageSize(VMChunkVM(vmchunk)))); CHECKL(vmchunk->overheadMappedLimit <= (Addr)chunk->pageTable); CHECKD(SparseArray, &vmchunk->pages); /* SparseArrayCheck is agnostic about where the BTs live, so VMChunkCheck @@ -154,9 +156,9 @@ static Bool VMChunkCheck(VMChunk vmchunk) /* VMArenaCheck -- check the consistency of an arena structure */ +ATTRIBUTE_UNUSED static Bool VMArenaCheck(VMArena vmArena) { - Index i; Arena arena; VMChunk primary; @@ -165,7 +167,6 @@ static Bool VMArenaCheck(VMArena vmArena) CHECKD(Arena, arena); /* spare pages are committed, so must be less spare than committed. */ CHECKL(vmArena->spareSize <= arena->committed); - CHECKL(vmArena->blacklist != ZoneSetUNIV); CHECKL(vmArena->extendBy > 0); CHECKL(vmArena->extendMin <= vmArena->extendBy); @@ -175,12 +176,10 @@ static Bool VMArenaCheck(VMArena vmArena) CHECKD(VMChunk, primary); /* We could iterate over all chunks accumulating an accurate */ /* count of committed, but we don't have all day. */ - CHECKL(VMMapped(primary->vm) <= arena->committed); + CHECKL(VMMapped(VMChunkVM(primary)) <= arena->committed); } - CHECKL(RingCheck(&vmArena->spareRing)); - for (i = 0; i < NELEMS(vmArena->freeRing); ++i) - CHECKL(RingCheck(&vmArena->freeRing[i])); + CHECKD_NOSIG(Ring, &vmArena->spareRing); /* FIXME: Can't check VMParams */ @@ -190,15 +189,18 @@ static Bool VMArenaCheck(VMArena vmArena) /* VMArenaDescribe -- describe the VMArena */ -static Res VMArenaDescribe(Arena arena, mps_lib_FILE *stream) +static Res VMArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth) { Res res; VMArena vmArena; - if (!TESTT(Arena, arena)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Arena, arena)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; vmArena = Arena2VMArena(arena); - if (!TESTT(VMArena, vmArena)) return ResFAIL; + if (!TESTT(VMArena, vmArena)) + return ResFAIL; /* Describe the superclass fields first via next-method call */ /* ...but the next method is ArenaTrivDescribe, so don't call it; @@ -206,13 +208,13 @@ static Res VMArenaDescribe(Arena arena, mps_lib_FILE *stream) * super = ARENA_SUPERCLASS(VMArenaClass); res = super->describe(arena, stream); - if (res != ResOK) return res; + if (res != ResOK) + return res; * */ - res = WriteF(stream, - " freeSet: $B\n", (WriteFB)vmArena->freeSet, - " blacklist: $B\n", (WriteFB)vmArena->blacklist, + res = WriteF(stream, depth, + " spareSize: $U\n", (WriteFU)vmArena->spareSize, NULL); if(res != ResOK) return res; @@ -280,10 +282,11 @@ static void vmArenaUnmap(VMArena vmArena, VM vm, Addr base, Addr limit) */ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size) { + Arena arena; Res res; Addr base, limit, chunkStructLimit; - Align pageSize; - VM vm; + VMStruct vmStruct; + VM vm = &vmStruct; BootBlockStruct bootStruct; BootBlock boot = &bootStruct; VMChunk vmChunk; @@ -291,14 +294,13 @@ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size) AVER(chunkReturn != NULL); AVERT(VMArena, vmArena); + arena = VMArena2Arena(vmArena); AVER(size > 0); - res = VMCreate(&vm, size, vmArena->vmParams); + res = VMInit(vm, size, ArenaGrainSize(arena), vmArena->vmParams); if (res != ResOK) - goto failVMCreate; + goto failVMInit; - pageSize = VMAlign(vm); - /* The VM will have aligned the userSize; pick up the actual size. */ base = VMBase(vm); limit = VMLimit(vm); @@ -312,16 +314,17 @@ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size) if (res != ResOK) goto failChunkAlloc; vmChunk = p; - /* Calculate the limit of the page where the chunkStruct resides. */ - chunkStructLimit = AddrAlignUp((Addr)(vmChunk + 1), pageSize); + /* Calculate the limit of the grain where the chunkStruct resides. */ + chunkStructLimit = AddrAlignUp((Addr)(vmChunk + 1), ArenaGrainSize(arena)); res = vmArenaMap(vmArena, vm, base, chunkStructLimit); if (res != ResOK) goto failChunkMap; vmChunk->overheadMappedLimit = chunkStructLimit; - vmChunk->vm = vm; - res = ChunkInit(VMChunk2Chunk(vmChunk), VMArena2Arena(vmArena), - base, limit, pageSize, boot); + /* Copy VM descriptor into its place in the chunk. */ + VMCopy(VMChunkVM(vmChunk), vm); + res = ChunkInit(VMChunk2Chunk(vmChunk), arena, base, limit, + VMReserved(VMChunkVM(vmChunk)), boot); if (res != ResOK) goto failChunkInit; @@ -329,16 +332,17 @@ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size) vmChunk->sig = VMChunkSig; AVERT(VMChunk, vmChunk); + *chunkReturn = VMChunk2Chunk(vmChunk); return ResOK; failChunkInit: - /* No need to unmap, as we're destroying the VM. */ + VMUnmap(vm, VMBase(vm), chunkStructLimit); failChunkMap: failChunkAlloc: failBootInit: - VMDestroy(vm); -failVMCreate: + VMFinish(vm); +failVMInit: return res; } @@ -379,7 +383,7 @@ static Res VMChunkInit(Chunk chunk, BootBlock boot) /* Map memory for the bit tables. */ if (vmChunk->overheadMappedLimit < overheadLimit) { overheadLimit = AddrAlignUp(overheadLimit, ChunkPageSize(chunk)); - res = vmArenaMap(VMChunkVMArena(vmChunk), vmChunk->vm, + res = vmArenaMap(VMChunkVMArena(vmChunk), VMChunkVM(vmChunk), vmChunk->overheadMappedLimit, overheadLimit); if (res != ResOK) goto failTableMap; @@ -390,7 +394,7 @@ static Res VMChunkInit(Chunk chunk, BootBlock boot) chunk->pageTable, sizeof(PageUnion), chunk->pages, - saMapped, saPages, vmChunk->vm); + saMapped, saPages, VMChunkVM(vmChunk)); return ResOK; @@ -405,11 +409,18 @@ static Res VMChunkInit(Chunk chunk, BootBlock boot) /* vmChunkDestroy -- destroy a VMChunk */ -static void vmChunkDestroy(Chunk chunk) +static Bool vmChunkDestroy(Tree tree, void *closureP, Size closureS) { - VM vm; + Chunk chunk; VMChunk vmChunk; + AVERT(Tree, tree); + AVER(closureP == UNUSED_POINTER); + UNUSED(closureP); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); + + chunk = ChunkOfTree(tree); AVERT(Chunk, chunk); vmChunk = Chunk2VMChunk(chunk); AVERT(VMChunk, vmChunk); @@ -419,9 +430,9 @@ static void vmChunkDestroy(Chunk chunk) SparseArrayFinish(&vmChunk->pages); vmChunk->sig = SigInvalid; - vm = vmChunk->vm; ChunkFinish(chunk); - VMDestroy(vm); + + return TRUE; } @@ -429,11 +440,20 @@ static void vmChunkDestroy(Chunk chunk) static void VMChunkFinish(Chunk chunk) { + VMStruct vmStruct; + VM vm = &vmStruct; VMChunk vmChunk = Chunk2VMChunk(chunk); - vmArenaUnmap(VMChunkVMArena(vmChunk), vmChunk->vm, - VMBase(vmChunk->vm), vmChunk->overheadMappedLimit); + /* Copy VM descriptor to stack-local storage so that we can continue + * using the descriptor after the VM has been unmapped. */ + VMCopy(vm, VMChunkVM(vmChunk)); + + vmArenaUnmap(VMChunkVMArena(vmChunk), vm, + VMBase(vm), vmChunk->overheadMappedLimit); + /* No point in finishing the other fields, since they are unmapped. */ + + VMFinish(vm); } @@ -444,7 +464,7 @@ static void VMArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[0].key = MPS_KEY_ARENA_SIZE; args[0].val.size = va_arg(varargs, Size); args[1].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } @@ -486,87 +506,75 @@ ARG_DEFINE_KEY(arena_contracted, Fun); static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) { - Size userSize; /* size requested by user */ - Size chunkSize; /* size actually created */ + Size size = VM_ARENA_SIZE_DEFAULT; /* initial arena size */ + Align grainSize = MPS_PF_ALIGN; /* arena grain size */ + Size pageSize = PageSize(); /* operating system page size */ + Size chunkSize; /* size actually created */ Size vmArenaSize; /* aligned size of VMArenaStruct */ Res res; VMArena vmArena; Arena arena; - VM arenaVM; + VMStruct vmStruct; + VM vm = &vmStruct; Chunk chunk; mps_arg_s arg; char vmParams[VMParamSize]; - Index i; AVER(arenaReturn != NULL); - AVER(class == VMArenaClassGet() || class == VMNZArenaClassGet()); - AVER(ArgListCheck(args)); - - ArgRequire(&arg, args, MPS_KEY_ARENA_SIZE); - userSize = arg.val.size; - - AVER(userSize > 0); + AVER(class == VMArenaClassGet()); + AVERT(ArgList, args); + + if (ArgPick(&arg, args, MPS_KEY_ARENA_GRAIN_SIZE)) + grainSize = arg.val.size; + if (grainSize < pageSize) + /* Make it easier to write portable programs by rounding up. */ + grainSize = pageSize; + AVERT(ArenaGrainSize, grainSize); + + if (ArgPick(&arg, args, MPS_KEY_ARENA_SIZE)) + size = arg.val.size; + if (size < grainSize * MPS_WORD_WIDTH) + /* There has to be enough room in the chunk for a full complement of + zones. Make it easier to write portable programs by rounding up. */ + size = grainSize * MPS_WORD_WIDTH; - /* Parse the arguments into VM parameters, if any. We must do this into - some stack-allocated memory for the moment, since we don't have anywhere - else to put it. It gets copied later. */ + /* Parse remaining arguments, if any, into VM parameters. We must do + this into some stack-allocated memory for the moment, since we + don't have anywhere else to put it. It gets copied later. */ res = VMParamFromArgs(vmParams, sizeof(vmParams), args); if (res != ResOK) - goto failVMCreate; + goto failVMInit; - /* Create a VM to hold the arena and map it. */ + /* Create a VM to hold the arena and map it. Store descriptor on the + stack until we have the arena to put it in. */ vmArenaSize = SizeAlignUp(sizeof(VMArenaStruct), MPS_PF_ALIGN); - res = VMCreate(&arenaVM, vmArenaSize, vmParams); + res = VMInit(vm, vmArenaSize, grainSize, vmParams); if (res != ResOK) - goto failVMCreate; - res = VMMap(arenaVM, VMBase(arenaVM), VMLimit(arenaVM)); + goto failVMInit; + res = VMMap(vm, VMBase(vm), VMLimit(vm)); if (res != ResOK) goto failVMMap; - vmArena = (VMArena)VMBase(arenaVM); + vmArena = (VMArena)VMBase(vm); arena = VMArena2Arena(vmArena); /* */ - res = ArenaInit(arena, class); + res = ArenaInit(arena, class, grainSize, args); if (res != ResOK) goto failArenaInit; - arena->committed = VMMapped(arenaVM); + arena->reserved = VMReserved(vm); + arena->committed = VMMapped(vm); - vmArena->vm = arenaVM; + /* Copy VM descriptor into its place in the arena. */ + VMCopy(VMArenaVM(vmArena), vm); vmArena->spareSize = 0; RingInit(&vmArena->spareRing); - for (i = 0; i < NELEMS(vmArena->freeRing); ++i) - RingInit(&vmArena->freeRing[i]); /* Copy the stack-allocated VM parameters into their home in the VMArena. */ AVER(sizeof(vmArena->vmParams) == sizeof(vmParams)); - mps_lib_memcpy(vmArena->vmParams, vmParams, sizeof(vmArena->vmParams)); + (void)mps_lib_memcpy(vmArena->vmParams, vmParams, sizeof(vmArena->vmParams)); - /* .blacklist: We blacklist the zones that could be referenced by small - integers misinterpreted as references. This isn't a perfect simulation, - but it should catch the common cases. */ - { - union { - mps_word_t word; - mps_addr_t addr; - int i; - long l; - } nono; - vmArena->blacklist = ZoneSetEMPTY; - nono.word = 0; - nono.i = 1; - vmArena->blacklist = ZoneSetAddAddr(arena, vmArena->blacklist, nono.addr); - nono.i = -1; - vmArena->blacklist = ZoneSetAddAddr(arena, vmArena->blacklist, nono.addr); - nono.l = 1; - vmArena->blacklist = ZoneSetAddAddr(arena, vmArena->blacklist, nono.addr); - nono.l = -1; - vmArena->blacklist = ZoneSetAddAddr(arena, vmArena->blacklist, nono.addr); - } - EVENT2(ArenaBlacklistZone, vmArena, vmArena->blacklist); - - vmArena->freeSet = ZoneSetUNIV; /* includes blacklist */ /* */ - vmArena->extendBy = userSize; + vmArena->extendBy = size; vmArena->extendMin = 0; vmArena->extended = vmArenaTrivExtended; @@ -579,25 +587,21 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) /* have to have a valid arena before calling ChunkCreate */ vmArena->sig = VMArenaSig; - res = VMChunkCreate(&chunk, vmArena, userSize); + res = VMChunkCreate(&chunk, vmArena, size); if (res != ResOK) goto failChunkCreate; - arena->primary = chunk; /* .zoneshift: Set the zone shift to divide the chunk into the same */ /* number of stripes as will fit into a reference set (the number of */ /* bits in a word). Fail if the chunk is so small stripes are smaller */ /* than pages. Note that some zones are discontiguous in the chunk if */ /* the size is not a power of 2. See . */ - chunkSize = AddrOffset(chunk->base, chunk->limit); + chunkSize = ChunkSize(chunk); arena->zoneShift = SizeFloorLog2(chunkSize >> MPS_WORD_SHIFT); - arena->alignment = chunk->pageSize; + AVER(ChunkPageSize(chunk) == ArenaGrainSize(arena)); AVERT(VMArena, vmArena); - if ((ArenaClass)mps_arena_class_vm() == class) - EVENT3(ArenaCreateVM, arena, userSize, chunkSize); - else - EVENT3(ArenaCreateVMNZ, arena, userSize, chunkSize); + EVENT3(ArenaCreateVM, arena, size, chunkSize); vmArena->extended(arena, chunk->base, chunkSize); @@ -607,10 +611,10 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) failChunkCreate: ArenaFinish(arena); failArenaInit: - VMUnmap(arenaVM, VMBase(arenaVM), VMLimit(arenaVM)); + VMUnmap(vm, VMBase(vm), VMLimit(vm)); failVMMap: - VMDestroy(arenaVM); -failVMCreate: + VMFinish(vm); +failVMInit: return res; } @@ -619,293 +623,37 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) static void VMArenaFinish(Arena arena) { + VMStruct vmStruct; + VM vm = &vmStruct; VMArena vmArena; - Ring node, next; - VM arenaVM; - Index i; vmArena = Arena2VMArena(arena); AVERT(VMArena, vmArena); - arenaVM = vmArena->vm; - /* destroy all chunks, including the primary */ + EVENT1(ArenaDestroy, vmArena); + + /* Destroy all chunks, including the primary. See + * */ arena->primary = NULL; - RING_FOR(node, &arena->chunkRing, next) { - Chunk chunk = RING_ELT(Chunk, chunkRing, node); - vmChunkDestroy(chunk); - } + TreeTraverseAndDelete(&arena->chunkTree, vmChunkDestroy, + UNUSED_POINTER, UNUSED_SIZE); /* Destroying the chunks should have purged and removed all spare pages. */ RingFinish(&vmArena->spareRing); - for (i = 0; i < NELEMS(vmArena->freeRing); ++i) - RingFinish(&vmArena->freeRing[i]); /* Destroying the chunks should leave only the arena's own VM. */ - AVER(arena->committed == VMMapped(arenaVM)); + AVER(arena->reserved == VMReserved(VMArenaVM(vmArena))); + AVER(arena->committed == VMMapped(VMArenaVM(vmArena))); vmArena->sig = SigInvalid; ArenaFinish(arena); /* */ - VMUnmap(arenaVM, VMBase(arenaVM), VMLimit(arenaVM)); - VMDestroy(arenaVM); - EVENT1(ArenaDestroy, vmArena); -} - - -/* VMArenaReserved -- return the amount of reserved address space - * - * Add up the reserved space from all the chunks. - */ -static Size VMArenaReserved(Arena arena) -{ - Size reserved; - Ring node, next; - - reserved = 0; - RING_FOR(node, &arena->chunkRing, next) { - VMChunk vmChunk = Chunk2VMChunk(RING_ELT(Chunk, chunkRing, node)); - reserved += VMReserved(vmChunk->vm); - } - return reserved; -} - - -/* pagesFindFreeInArea -- find a range of free pages in a given address range - * - * Search for a free run of pages in the free table, between the given - * base and limit. - * - * The downwards arg governs whether we use BTFindShortResRange (if - * downwards is FALSE) or BTFindShortResRangeHigh (if downwards is - * TRUE). This _roughly_ corresponds to allocating pages from top down - * (when downwards is TRUE), at least within an interval. It is used - * for implementing SegPrefHigh. - */ -static Bool pagesFindFreeInArea(Index *baseReturn, Chunk chunk, Size size, - Addr base, Addr limit, Bool downwards) -{ - Word pages; /* number of pages equiv. to size */ - Index basePI, limitPI; /* Index equiv. to base and limit */ - Index start, end; /* base and limit of free run */ - - AVER(AddrIsAligned(base, ChunkPageSize(chunk))); - AVER(AddrIsAligned(limit, ChunkPageSize(chunk))); - AVER(chunk->base <= base); - AVER(base < limit); - AVER(limit <= chunk->limit); - AVER(size <= AddrOffset(base, limit)); - AVER(size > (Size)0); - AVER(SizeIsAligned(size, ChunkPageSize(chunk))); - - basePI = INDEX_OF_ADDR(chunk, base); - limitPI = INDEX_OF_ADDR(chunk, limit); - pages = ChunkSizeToPages(chunk, size); - - if (downwards) { - if (!BTFindShortResRangeHigh(&start, &end, chunk->allocTable, - basePI, limitPI, pages)) - return FALSE; - } else { - if(!BTFindShortResRange(&start, &end, chunk->allocTable, - basePI, limitPI, pages)) - return FALSE; - } - - *baseReturn = start; - return TRUE; -} - - -/* pagesFindFreeInZones -- find a range of free pages with a ZoneSet - * - * This function finds the intersection of ZoneSet and the set of free - * pages and tries to find a free run of pages in the resulting set of - * areas. - * - * In other words, it finds space for a page whose ZoneSet (see - * ZoneSetOfPage) will be a subset of the specified ZoneSet. - * - * For meaning of downwards arg see pagesFindFreeInArea. - * .improve.findfree.downwards: This should be improved so that it - * allocates pages from top down globally, as opposed to (currently) - * just within an interval. - */ -static Bool pagesFindFreeInZones(Index *baseReturn, VMChunk *chunkReturn, - VMArena vmArena, Size size, ZoneSet zones, - Bool downwards) -{ - Arena arena; - Addr chunkBase, base, limit; - Size zoneSize; - Ring node, next; - - arena = VMArena2Arena(vmArena); - zoneSize = (Size)1 << arena->zoneShift; - - /* Try to reuse single pages from the already-mapped spare pages list */ - if (size == ArenaAlign(arena)) { - Index i; - for (i = 0; i < NELEMS(vmArena->freeRing); ++i) { - Ring ring = &vmArena->freeRing[i]; - if (ZoneSetIsMember(zones, i) && !RingIsSingle(ring)) { - Page page = PageOfFreeRing(RingNext(ring)); - Chunk chunk; - Bool b = ChunkOfAddr(&chunk, arena, (Addr)page); - AVER(b); - *baseReturn = (Index)(page - chunk->pageTable); - *chunkReturn = Chunk2VMChunk(chunk); - return TRUE; - } - } - } - - /* Should we check chunk cache first? */ - RING_FOR(node, &arena->chunkRing, next) { - Chunk chunk = RING_ELT(Chunk, chunkRing, node); - AVERT(Chunk, chunk); - - /* .alloc.skip: The first address available for arena allocation, */ - /* is just after the arena tables. */ - chunkBase = PageIndexBase(chunk, chunk->allocBase); - - base = chunkBase; - while(base < chunk->limit) { - if (ZoneSetHasAddr(arena, zones, base)) { - /* Search for a run of zone stripes which are in the ZoneSet */ - /* and the arena. Adding the zoneSize might wrap round (to */ - /* zero, because limit is aligned to zoneSize, which is a */ - /* power of two). */ - limit = base; - do { - /* advance limit to next higher zone stripe boundary */ - limit = AddrAlignUp(AddrAdd(limit, 1), zoneSize); - - AVER(limit > base || limit == (Addr)0); - - if (limit >= chunk->limit || limit < base) { - limit = chunk->limit; - break; - } - - AVER(base < limit); - AVER(limit < chunk->limit); - } while(ZoneSetHasAddr(arena, zones, limit)); - - /* If the ZoneSet was universal, then the area found ought to */ - /* be the whole chunk. */ - AVER(zones != ZoneSetUNIV - || (base == chunkBase && limit == chunk->limit)); - - /* Try to allocate a page in the area. */ - if (AddrOffset(base, limit) >= size - && pagesFindFreeInArea(baseReturn, chunk, size, base, limit, - downwards)) { - *chunkReturn = Chunk2VMChunk(chunk); - return TRUE; - } - - base = limit; - } else { - /* Adding the zoneSize might wrap round (to zero, because */ - /* base is aligned to zoneSize, which is a power of two). */ - base = AddrAlignUp(AddrAdd(base, 1), zoneSize); - AVER(base > chunkBase || base == (Addr)0); - if (base >= chunk->limit || base < chunkBase) { - base = chunk->limit; - break; - } - } - } - - AVER(base == chunk->limit); - } - - return FALSE; -} - - -/* pagesFindFreeWithSegPref -- find a range of free pages with given preferences - * - * Note this does not create or allocate any pages. - * - * basereturn: return parameter for the index in the - * chunk's page table of the base of the free area found. - * chunkreturn: return parameter for the chunk in which - * the free space has been found. - * pref: the SegPref object to be used when considering - * which zones to try. - * size: Size to find space for. - * barge: TRUE iff stealing space in zones used - * by other SegPrefs should be considered (if it's FALSE then only - * zones already used by this segpref or free zones will be used). - */ -static Bool pagesFindFreeWithSegPref(Index *baseReturn, VMChunk *chunkReturn, - VMArena vmArena, SegPref pref, Size size, - Bool barge) -{ - /* Some of these tests might be duplicates. If we're about */ - /* to run out of virtual address space, then slow allocation is */ - /* probably the least of our worries. */ - - /* .alloc.improve.map: Define a function that takes a list */ - /* (say 4 long) of ZoneSets and tries pagesFindFreeInZones on */ - /* each one in turn. Extra ZoneSet args that weren't needed */ - /* could be ZoneSetEMPTY */ - - if (pref->isCollected) { /* GC'd memory */ - /* We look for space in the following places (in order) */ - /* - Zones already allocated to me (preferred) but are not */ - /* blacklisted; */ - /* - Zones that are either allocated to me, or are unallocated */ - /* but not blacklisted; */ - /* - Any non-blacklisted zone; */ - /* - Any zone; */ - /* Note that each is a superset of the previous, unless */ - /* blacklisted zones have been allocated (or the default */ - /* is used). */ - if (pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size, - ZoneSetDiff(pref->zones, vmArena->blacklist), - pref->high) - || pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size, - ZoneSetUnion(pref->zones, - ZoneSetDiff(vmArena->freeSet, - vmArena->blacklist)), - pref->high)) { - return TRUE; /* found */ - } - if (!barge) - /* do not barge into other zones, give up now */ - return FALSE; - if (pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size, - ZoneSetDiff(ZoneSetUNIV, vmArena->blacklist), - pref->high) - || pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size, - ZoneSetUNIV, pref->high)) { - return TRUE; /* found */ - } - } else { /* non-GC'd memory */ - /* We look for space in the following places (in order) */ - /* - Zones preferred (preferred) and blacklisted; */ - /* - Zones preferred; */ - /* - Zones preferred or blacklisted zone; */ - /* - Any zone. */ - /* Note that each is a superset of the previous, unless */ - /* blacklisted zones have been allocated. */ - if (pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size, - ZoneSetInter(pref->zones, vmArena->blacklist), - pref->high) - || pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size, - pref->zones, pref->high) - || pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size, - ZoneSetUnion(pref->zones, vmArena->blacklist), - pref->high) - || pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size, - ZoneSetUNIV, pref->high)) { - return TRUE; - } - } - return FALSE; + /* Copy VM descriptor to stack-local storage so that we can continue + * using the descriptor after the VM has been unmapped. */ + VMCopy(vm, VMArenaVM(vmArena)); + VMUnmap(vm, VMBase(vm), VMLimit(vm)); + VMFinish(vm); } @@ -937,20 +685,29 @@ static Size vmArenaChunkSize(VMArena vmArena, Size size) } -/* vmArenaExtend -- Extend the arena by making a new chunk +/* VMArenaGrow -- Extend the arena by making a new chunk * * The size arg specifies how much we wish to allocate after the extension. */ -static Res vmArenaExtend(VMArena vmArena, Size size) +static Res VMArenaGrow(Arena arena, LocusPref pref, Size size) { Chunk newChunk; Size chunkSize; Res res; + VMArena vmArena; + + AVERT(Arena, arena); + vmArena = Arena2VMArena(arena); + AVERT(VMArena, vmArena); + + /* TODO: Ensure that extended arena will be able to satisfy pref. */ + AVERT(LocusPref, pref); + UNUSED(pref); chunkSize = vmArenaChunkSize(vmArena, size); EVENT3(vmArenaExtendStart, size, chunkSize, - VMArenaReserved(VMArena2Arena(vmArena))); + ArenaReserved(VMArena2Arena(vmArena))); /* .chunk-create.fail: If we fail, try again with a smaller size */ { @@ -964,6 +721,7 @@ static Res vmArenaExtend(VMArena vmArena, Size size) if (chunkSize < chunkMin) chunkSize = chunkMin; + res = ResRESOURCE; for(;; chunkSize = chunkHalf) { chunkHalf = chunkSize / 2; sliceSize = chunkHalf / fidelity; @@ -973,66 +731,26 @@ static Res vmArenaExtend(VMArena vmArena, Size size) for(; chunkSize > chunkHalf; chunkSize -= sliceSize) { if(chunkSize < chunkMin) { EVENT2(vmArenaExtendFail, chunkMin, - VMArenaReserved(VMArena2Arena(vmArena))); - return ResRESOURCE; + ArenaReserved(VMArena2Arena(vmArena))); + return res; } res = VMChunkCreate(&newChunk, vmArena, chunkSize); if(res == ResOK) - goto vmArenaExtend_Done; + goto vmArenaGrow_Done; } } } - -vmArenaExtend_Done: - EVENT2(vmArenaExtendDone, chunkSize, VMArenaReserved(VMArena2Arena(vmArena))); + +vmArenaGrow_Done: + EVENT2(vmArenaExtendDone, chunkSize, ArenaReserved(VMArena2Arena(vmArena))); vmArena->extended(VMArena2Arena(vmArena), newChunk->base, AddrOffset(newChunk->base, newChunk->limit)); - + return res; } -/* VM*AllocPolicy -- allocation policy methods */ - - -/* Used in abstracting allocation policy between VM and VMNZ */ -typedef Res (*VMAllocPolicyMethod)(Index *, VMChunk *, VMArena, SegPref, Size); - -static Res VMAllocPolicy(Index *baseIndexReturn, VMChunk *chunkReturn, - VMArena vmArena, SegPref pref, Size size) -{ - if (!pagesFindFreeWithSegPref(baseIndexReturn, chunkReturn, - vmArena, pref, size, FALSE)) { - /* try and extend, but don't worry if we can't */ - (void)vmArenaExtend(vmArena, size); - - /* We may or may not have a new chunk at this point */ - /* we proceed to try the allocation again anyway. */ - /* We specify barging, but if we have got a new chunk */ - /* then hopefully we won't need to barge. */ - if (!pagesFindFreeWithSegPref(baseIndexReturn, chunkReturn, - vmArena, pref, size, TRUE)) { - /* .improve.alloc-fail: This could be because the request was */ - /* too large, or perhaps the arena is fragmented. We could */ - /* return a more meaningful code. */ - return ResRESOURCE; - } - } - return ResOK; -} - -static Res VMNZAllocPolicy(Index *baseIndexReturn, VMChunk *chunkReturn, - VMArena vmArena, SegPref pref, Size size) -{ - if (pagesFindFreeInZones(baseIndexReturn, chunkReturn, vmArena, size, - ZoneSetUNIV, pref->high)) { - return ResOK; - } - return ResRESOURCE; -} - - /* pageState -- determine page state, even if unmapped * * Parts of the page table may be unmapped if their corresponding pages are @@ -1064,25 +782,31 @@ static void sparePageRelease(VMChunk vmChunk, Index pi) arena->spareCommitted -= ChunkPageSize(chunk); RingRemove(PageSpareRing(page)); - RingRemove(PageFreeRing(page)); } static Res pageDescMap(VMChunk vmChunk, Index basePI, Index limitPI) { - Size before = VMMapped(vmChunk->vm); + Size before = VMMapped(VMChunkVM(vmChunk)); Arena arena = VMArena2Arena(VMChunkVMArena(vmChunk)); Res res = SparseArrayMap(&vmChunk->pages, basePI, limitPI); - arena->committed += VMMapped(vmChunk->vm) - before; + Size after = VMMapped(VMChunkVM(vmChunk)); + AVER(before <= after); + arena->committed += after - before; return res; } static void pageDescUnmap(VMChunk vmChunk, Index basePI, Index limitPI) { - Size before = VMMapped(vmChunk->vm); + Size size, after; + Size before = VMMapped(VMChunkVM(vmChunk)); Arena arena = VMArena2Arena(VMChunkVMArena(vmChunk)); SparseArrayUnmap(&vmChunk->pages, basePI, limitPI); - arena->committed += VMMapped(vmChunk->vm) - before; + after = VMMapped(VMChunkVM(vmChunk)); + AVER(after <= before); + size = before - after; + AVER(arena->committed >= size); + arena->committed -= size; } @@ -1112,7 +836,7 @@ static Res pagesMarkAllocated(VMArena vmArena, VMChunk vmChunk, res = pageDescMap(vmChunk, j, k); if (res != ResOK) goto failSAMap; - res = vmArenaMap(vmArena, vmChunk->vm, + res = vmArenaMap(vmArena, VMChunkVM(vmChunk), PageIndexBase(chunk, j), PageIndexBase(chunk, k)); if (res != ResOK) goto failVMMap; @@ -1136,7 +860,7 @@ static Res pagesMarkAllocated(VMArena vmArena, VMChunk vmChunk, /* region from basePI to j needs deallocating */ /* TODO: Consider making pages spare instead, then purging. */ if (basePI < j) { - vmArenaUnmap(vmArena, vmChunk->vm, + vmArenaUnmap(vmArena, VMChunkVM(vmChunk), PageIndexBase(chunk, basePI), PageIndexBase(chunk, j)); for (i = basePI; i < j; ++i) @@ -1146,113 +870,42 @@ static Res pagesMarkAllocated(VMArena vmArena, VMChunk vmChunk, return res; } - -/* vmAllocComm -- allocate a region from the arena - * - * Common code used by mps_arena_class_vm and - * mps_arena_class_vmnz. - */ -static Res vmAllocComm(Addr *baseReturn, Tract *baseTractReturn, - VMAllocPolicyMethod policy, - SegPref pref, Size size, Pool pool) +static Res VMPagesMarkAllocated(Arena arena, Chunk chunk, + Index baseIndex, Count pages, Pool pool) { - Addr base, limit; - Tract baseTract; - Arena arena; - Count pages; - Index baseIndex; - ZoneSet zones; Res res; - VMArena vmArena; - VMChunk vmChunk; - Chunk chunk; - AVER(baseReturn != NULL); - AVER(baseTractReturn != NULL); - AVER(FunCheck((Fun)policy)); - AVERT(SegPref, pref); - AVER(size > (Size)0); + AVERT(Arena, arena); + AVERT(Chunk, chunk); + AVER(chunk->allocBase <= baseIndex); + AVER(pages > 0); + AVER(baseIndex + pages <= chunk->pages); AVERT(Pool, pool); - arena = PoolArena(pool); - vmArena = Arena2VMArena(arena); - AVERT(VMArena, vmArena); - /* All chunks have same pageSize. */ - AVER(SizeIsAligned(size, ChunkPageSize(arena->primary))); - - /* NULL is used as a discriminator */ - /* (see ) therefore the real pool */ - /* must be non-NULL. */ - AVER(pool != NULL); - - /* Early check on commit limit. */ - if (arena->spareCommitted < size) { - Size necessaryCommitIncrease = size - arena->spareCommitted; - if (arena->committed + necessaryCommitIncrease > arena->commitLimit - || arena->committed + necessaryCommitIncrease < arena->committed) { - return ResCOMMIT_LIMIT; - } - } - - res = (*policy)(&baseIndex, &vmChunk, vmArena, pref, size); - if (res != ResOK) - return res; - - /* chunk (and baseIndex) should be initialised by policy */ - AVERT(VMChunk, vmChunk); - chunk = VMChunk2Chunk(vmChunk); - - /* Compute number of pages to be allocated. */ - pages = ChunkSizeToPages(chunk, size); - - res = pagesMarkAllocated(vmArena, vmChunk, baseIndex, pages, pool); + res = pagesMarkAllocated(Arena2VMArena(arena), + Chunk2VMChunk(chunk), + baseIndex, + pages, + pool); + /* TODO: Could this loop be pushed down into vmArenaMap? */ while (res != ResOK) { /* Try purging spare pages in the hope that the OS will give them back at the new address. Will eventually run out of spare pages, so this loop will terminate. */ /* TODO: Investigate implementing VMRemap so that we can guarantee success if we have enough spare pages. */ - if (VMPurgeSpare(arena, size) == 0) - goto failPagesMap; - res = pagesMarkAllocated(vmArena, vmChunk, baseIndex, pages, pool); + if (VMPurgeSpare(arena, pages * ChunkPageSize(chunk)) == 0) + break; + res = pagesMarkAllocated(Arena2VMArena(arena), + Chunk2VMChunk(chunk), + baseIndex, + pages, + pool); } - - base = PageIndexBase(chunk, baseIndex); - baseTract = PageTract(ChunkPage(chunk, baseIndex)); - limit = AddrAdd(base, size); - zones = ZoneSetOfRange(arena, base, limit); - - if (ZoneSetInter(vmArena->freeSet, zones) != ZoneSetEMPTY) { - EVENT2(ArenaUseFreeZone, arena, ZoneSetInter(vmArena->freeSet, zones)); - } - vmArena->freeSet = ZoneSetDiff(vmArena->freeSet, zones); - - *baseReturn = base; - *baseTractReturn = baseTract; - return ResOK; - -failPagesMap: return res; } -static Res VMAlloc(Addr *baseReturn, Tract *baseTractReturn, - SegPref pref, Size size, Pool pool) -{ - /* All checks performed in common vmAllocComm */ - return vmAllocComm(baseReturn, baseTractReturn, - VMAllocPolicy, pref, size, pool); -} - -static Res VMNZAlloc(Addr *baseReturn, Tract *baseTractReturn, - SegPref pref, Size size, Pool pool) -{ - /* All checks performed in common vmAllocComm */ - return vmAllocComm(baseReturn, baseTractReturn, - VMNZAllocPolicy, pref, size, pool); -} - - /* chunkUnmapAroundPage -- unmap spare pages in a chunk including this one * * Unmap the spare page passed, and possibly other pages in the chunk, @@ -1300,7 +953,7 @@ static Size chunkUnmapAroundPage(Chunk chunk, Size size, Page page) } vmArenaUnmap(VMChunkVMArena(vmChunk), - vmChunk->vm, + VMChunkVM(vmChunk), PageIndexBase(chunk, basePI), PageIndexBase(chunk, limitPI)); @@ -1317,8 +970,6 @@ static Size chunkUnmapAroundPage(Chunk chunk, Size size, Page page) * unmapped. */ -#define ArenaChunkRing(arena) (&(arena)->chunkRing) - static Size arenaUnmapSpare(Arena arena, Size size, Chunk filter) { Ring node; @@ -1341,7 +992,7 @@ static Size arenaUnmapSpare(Arena arena, Size size, Chunk filter) while (RingNext(node) != &vmArena->spareRing && purged < size) { Ring next = RingNext(node); Page page = PageOfSpareRing(next); - Chunk chunk; + Chunk chunk = NULL; /* suppress uninit warning */ Bool b; /* Use the fact that the page table resides in the chunk to find the chunk that owns the page. */ @@ -1370,9 +1021,7 @@ static Size VMPurgeSpare(Arena arena, Size size) static void chunkUnmapSpare(Chunk chunk) { AVERT(Chunk, chunk); - (void)arenaUnmapSpare(ChunkArena(chunk), - AddrOffset(chunk->base, chunk->limit), - chunk); + (void)arenaUnmapSpare(ChunkArena(chunk), ChunkSize(chunk), chunk); } @@ -1423,16 +1072,14 @@ static void VMFree(Addr base, Size size, Pool pool) tract and will contain junk. */ RingInit(PageSpareRing(page)); RingAppend(&vmArena->spareRing, PageSpareRing(page)); - RingInit(PageFreeRing(page)); - RingInsert(&vmArena->freeRing[AddrZone(arena, PageIndexBase(chunk, pi))], - PageFreeRing(page)); } arena->spareCommitted += ChunkPagesToSize(chunk, piLimit - piBase); BTResRange(chunk->allocTable, piBase, piLimit); /* Consider returning memory to the OS. */ - /* TODO: Chunks are only destroyed when ArenaCompact is called, and that is - only called from TraceReclaim. Should consider destroying chunks here. */ + /* TODO: Chunks are only destroyed when ArenaCompact is called, and + that is only called from traceReclaim. Should consider destroying + chunks here. See job003815. */ if (arena->spareCommitted > arena->spareCommitLimit) { /* Purge half of the spare memory, not just the extra sliver, so that we return a reasonable amount of memory in one go, and avoid @@ -1445,43 +1092,60 @@ static void VMFree(Addr base, Size size, Pool pool) } -static void VMCompact(Arena arena, Trace trace) +/* vmChunkCompact -- delete chunk if empty and not primary */ + +static Bool vmChunkCompact(Tree tree, void *closureP, Size closureS) { + Chunk chunk; + Arena arena = closureP; VMArena vmArena; - Ring node, next; - Size vmem1; + + AVERT(Tree, tree); + AVERT(Arena, arena); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); vmArena = Arena2VMArena(arena); AVERT(VMArena, vmArena); - AVERT(Trace, trace); + chunk = ChunkOfTree(tree); + AVERT(Chunk, chunk); + 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, UNUSED_SIZE); + return TRUE; + } else { + /* Keep this chunk. */ + return FALSE; + } +} - vmem1 = VMArenaReserved(arena); - /* Destroy any empty chunks (except the primary). */ - /* TODO: Avoid a scan of the allocTable by keeping a count of allocated - pages in a chunk. */ - /* TODO: Avoid oscillations in chunk creation by adding some hysteresis. */ - RING_FOR(node, &arena->chunkRing, next) { - Chunk chunk = RING_ELT(Chunk, chunkRing, node); - if(chunk != arena->primary - && BTIsResRange(chunk->allocTable, 0, chunk->pages)) { - Addr base = chunk->base; - Size size = AddrOffset(chunk->base, chunk->limit); +static void VMCompact(Arena arena, Trace trace) +{ + VMArena vmArena; + Size vmem1; - /* Ensure there are no spare (mapped) pages left in the chunk. - This could be short-cut if we're about to destroy the chunk, - provided we can do the correct accounting in the arena. */ - chunkUnmapSpare(chunk); + vmArena = Arena2VMArena(arena); + AVERT(VMArena, vmArena); + AVERT(Trace, trace); - vmChunkDestroy(chunk); + vmem1 = ArenaReserved(arena); - vmArena->contracted(arena, base, size); - } - } + /* Destroy chunks that are completely free, but not the primary + * chunk. See + * TODO: add hysteresis here. See job003815. */ + TreeTraverseAndDelete(&arena->chunkTree, vmChunkCompact, arena, + UNUSED_SIZE); { Size vmem0 = trace->preTraceArenaReserved; - Size vmem2 = VMArenaReserved(arena); + Size vmem2 = ArenaReserved(arena); /* VMCompact event: emit for all client-requested collections, */ /* plus any others where chunks were gained or lost during the */ @@ -1508,11 +1172,8 @@ mps_res_t mps_arena_vm_growth(mps_arena_t mps_arena, vmArena = Arena2VMArena(arena); AVERT(VMArena, vmArena); - if(desired < minimum) { - /* May not desire an increment smaller than the minimum! */ - ArenaLeave(arena); - return MPS_RES_PARAM; - } + /* Must desire at least the minimum increment! */ + AVER(desired >= minimum); vmArena->extendBy = desired; vmArena->extendMin = minimum; @@ -1534,26 +1195,15 @@ DEFINE_ARENA_CLASS(VMArenaClass, this) this->varargs = VMArenaVarargs; this->init = VMArenaInit; this->finish = VMArenaFinish; - this->reserved = VMArenaReserved; this->purgeSpare = VMPurgeSpare; - this->alloc = VMAlloc; + this->grow = VMArenaGrow; this->free = VMFree; this->chunkInit = VMChunkInit; this->chunkFinish = VMChunkFinish; this->compact = VMCompact; this->describe = VMArenaDescribe; -} - - -/* VMNZArenaClass -- The VMNZ arena class definition - * - * VMNZ is just VMArena with a different allocation policy. - */ -DEFINE_ARENA_CLASS(VMNZArenaClass, this) -{ - INHERIT_CLASS(this, VMArenaClass); - this->name = "VMNZ"; - this->alloc = VMNZAlloc; + this->pagesMarkAllocated = VMPagesMarkAllocated; + AVERT(ArenaClass, this); } @@ -1565,14 +1215,6 @@ mps_arena_class_t mps_arena_class_vm(void) } -/* mps_arena_class_vmnz -- return the arena class VMNZ */ - -mps_arena_class_t mps_arena_class_vmnz(void) -{ - return (mps_arena_class_t)VMNZArenaClassGet(); -} - - /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2001-2014 Ravenbrook Limited . diff --git a/code/arg.c b/code/arg.c index caced7e6f1..4721c415cc 100644 --- a/code/arg.c +++ b/code/arg.c @@ -1,7 +1,7 @@ /* arg.c: ARGUMENT LISTS * * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. * * .source: See . */ @@ -52,7 +52,7 @@ Bool ArgCheckAddr(Arg arg) { } Bool ArgCheckPoolDebugOptions(Arg arg) { - CHECKL(PoolDebugOptionsCheck((PoolDebugOptions)arg->val.pool_debug_options)); + CHECKD_NOSIG(PoolDebugOptions, (PoolDebugOptions)arg->val.pool_debug_options); return TRUE; } @@ -99,8 +99,13 @@ Bool ArgCheckdouble(Arg arg) { return TRUE; } +Bool ArgCheckPool(Arg arg) { + CHECKD(Pool, arg->val.pool); + return TRUE; +} -ARG_DEFINE_KEY(args_end, Shouldnt); + +ARG_DEFINE_KEY(ARGS_END, Shouldnt); ArgStruct mps_args_none[] = {{MPS_KEY_ARGS_END, {0}}}; @@ -133,7 +138,7 @@ Bool ArgListCheck(ArgList args) CHECKL(args != NULL); for (i = 0; args[i].key != MPS_KEY_ARGS_END; ++i) { CHECKL(i < MPS_ARGS_MAX); - CHECKL(ArgCheck(&args[i])); + CHECKD_NOSIG(Arg, &args[i]); } return TRUE; } @@ -145,7 +150,7 @@ Bool ArgPick(ArgStruct *argOut, ArgList args, Key key) { Index i; AVER(argOut != NULL); - AVER(ArgListCheck(args)); + AVERT(ArgList, args); AVERT(Key, key); for (i = 0; args[i].key != MPS_KEY_ARGS_END; ++i) @@ -154,6 +159,7 @@ Bool ArgPick(ArgStruct *argOut, ArgList args, Key key) { return FALSE; found: + AVERT(Arg, &args[i]); *argOut = args[i]; for(;;) { args[i] = args[i + 1]; @@ -168,9 +174,8 @@ Bool ArgPick(ArgStruct *argOut, ArgList args, Key key) { /* ArgRequire -- take a required argument out of the argument list by keyword */ void ArgRequire(ArgStruct *argOut, ArgList args, Key key) { - if (ArgPick(argOut, args, key)) - return; - NOTREACHED; + Bool b = ArgPick(argOut, args, key); + ASSERT(b, key->name); } @@ -180,14 +185,14 @@ void ArgTrivVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) { UNUSED(varargs); args[0].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/arg.h b/code/arg.h index 1598707355..2b5b3fbb31 100644 --- a/code/arg.h +++ b/code/arg.h @@ -1,7 +1,7 @@ /* arg.h: Keyword argument lists * * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. * * .source: See . */ @@ -28,7 +28,8 @@ typedef struct mps_key_s { } KeyStruct; #define ARG_DEFINE_KEY(id, type) \ - const KeyStruct _mps_key_##id = {KeySig, #id, ArgCheck##type} + extern const KeyStruct _mps_key_##id; \ + const KeyStruct _mps_key_##id = {KeySig, "MPS_KEY_" #id, ArgCheck##type} #define argsNone mps_args_none @@ -54,6 +55,7 @@ extern Bool ArgCheckPointer(Arg arg); extern Bool ArgCheckRankSet(Arg arg); extern Bool ArgCheckRank(Arg arg); extern Bool ArgCheckdouble(Arg arg); +extern Bool ArgCheckPool(Arg arg); #endif /* arg_h */ @@ -61,7 +63,7 @@ extern Bool ArgCheckdouble(Arg arg); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/awlut.c b/code/awlut.c index 25ceb34b29..464a2dba91 100644 --- a/code/awlut.c +++ b/code/awlut.c @@ -1,7 +1,7 @@ /* awlut.c: POOL CLASS AWL UNIT TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -16,10 +16,9 @@ #include "mpslib.h" #include "mps.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif -#include + +#include /* printf */ +#include /* strlen */ #define testArenaSIZE ((size_t)64<<20) @@ -118,7 +117,7 @@ static mps_word_t *alloc_string(const char *s, mps_ap_t ap) * .assume.dylan-obj */ -static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap) +static mps_word_t *alloc_table(size_t n, mps_ap_t ap) { size_t objsize; void *p; @@ -127,7 +126,7 @@ static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap) objsize = (3 + n) * sizeof(mps_word_t); objsize = size_tAlignUp(objsize, MPS_PF_ALIGN); do { - unsigned long i; + size_t i; die(mps_reserve(&p, ap, objsize), "Reserve Table\n"); object = p; @@ -145,7 +144,7 @@ static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap) /* gets the nth slot from a table * .assume.dylan-obj */ -static mps_word_t *table_slot(mps_word_t *table, unsigned long n) +static mps_word_t *table_slot(mps_word_t *table, size_t n) { return (mps_word_t *)table[3+n]; } @@ -154,8 +153,7 @@ static mps_word_t *table_slot(mps_word_t *table, unsigned long n) /* sets the nth slot in a table * .assume.dylan-obj */ -static void set_table_slot(mps_word_t *table, - unsigned long n, mps_word_t *p) +static void set_table_slot(mps_word_t *table, size_t n, mps_word_t *p) { cdie(table[0] == (mps_word_t)table_wrapper, "set_table_slot"); table[3+n] = (mps_word_t)p; @@ -182,7 +180,7 @@ static void test(mps_arena_t arena, mps_word_t *exacttable; mps_word_t *preserve[TABLE_SLOTS]; /* preserves objects in the weak */ /* table by referring to them */ - unsigned long i, j; + size_t i, j; void *p; exacttable = alloc_table(TABLE_SLOTS, exactap); @@ -194,11 +192,12 @@ static void test(mps_arena_t arena, for(i = 0; i < TABLE_SLOTS; ++i) { mps_word_t *string; - /* Ensure that the last entry in the table is preserved, so that - * we don't get a false positive due to the local variable - * 'string' keeping this entry alive (see job003436). + /* Ensure that the first and last entries in the table are + * preserved, so that we don't get false positives due to the + * local variables 'weak_table' and 'string' keeping these entries + * alive (see job003436). */ - if (rnd() % 2 == 0 || i + 1 == TABLE_SLOTS) { + if (rnd() % 2 == 0 || i == 0 || i + 1 == TABLE_SLOTS) { string = alloc_string("iamalive", leafap); preserve[i] = string; } else { @@ -216,17 +215,19 @@ static void test(mps_arena_t arena, } } - mps_arena_collect(arena); + die(mps_arena_collect(arena), "mps_arena_collect"); mps_arena_release(arena); for(i = 0; i < TABLE_SLOTS; ++i) { if (preserve[i] == 0) { if (table_slot(weaktable, i)) { - error("Strongly unreachable weak table entry found, slot %lu.\n", i); + error("Strongly unreachable weak table entry found, " + "slot %"PRIuLONGEST".\n", (ulongest_t)i); } else { if (table_slot(exacttable, i) != 0) { error("Weak table entry deleted, but corresponding " - "exact table entry not deleted, slot %lu.\n", i); + "exact table entry not deleted, slot %"PRIuLONGEST".\n", + (ulongest_t)i); } } } @@ -274,9 +275,6 @@ static void *setup(void *v, size_t s) die(mps_fmt_create_A(&dylanweakfmt, arena, dylan_fmt_A_weak()), "Format Create (weak)\n"); MPS_ARGS_BEGIN(args) { - /* Ask the leafpool to allocate in the nursery, as we're using it to test - weaknesss and want things to die in it promptly. */ - MPS_ARGS_ADD(args, MPS_KEY_GEN, 0); MPS_ARGS_ADD(args, MPS_KEY_FORMAT, dylanfmt); die(mps_pool_create_k(&leafpool, arena, mps_class_lo(), args), "Leaf Pool Create\n"); @@ -316,8 +314,7 @@ int main(int argc, char *argv[]) mps_thr_t thread; void *r; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); initialise_wrapper(wrapper_wrapper); initialise_wrapper(string_wrapper); @@ -339,7 +336,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/awluthe.c b/code/awluthe.c index 6b56d05f78..6ea468977f 100644 --- a/code/awluthe.c +++ b/code/awluthe.c @@ -1,7 +1,7 @@ /* awluthe.c: POOL CLASS AWL UNIT TEST WITH OBJECT HEADERS * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -17,10 +17,9 @@ #include "mpslib.h" #include "mps.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif -#include + +#include /* strlen */ +#include /* printf */ #define testArenaSIZE ((size_t)64<<20) @@ -121,7 +120,7 @@ static mps_word_t *alloc_string(const char *s, mps_ap_t ap) * .assume.dylan-obj */ -static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap) +static mps_word_t *alloc_table(size_t n, mps_ap_t ap) { size_t objsize; void *p; @@ -130,7 +129,7 @@ static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap) objsize = (3 + n) * sizeof(mps_word_t); objsize = size_tAlignUp(objsize, MPS_PF_ALIGN); do { - unsigned long i; + size_t i; die(mps_reserve(&p, ap, objsize + headerSIZE), "Reserve Table\n"); object = (mps_word_t *)((char *)p + headerSIZE); @@ -150,7 +149,7 @@ static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap) /* gets the nth slot from a table * .assume.dylan-obj */ -static mps_word_t *table_slot(mps_word_t *table, unsigned long n) +static mps_word_t *table_slot(mps_word_t *table, size_t n) { return (mps_word_t *)table[3+n]; } @@ -159,8 +158,7 @@ static mps_word_t *table_slot(mps_word_t *table, unsigned long n) /* sets the nth slot in a table * .assume.dylan-obj */ -static void set_table_slot(mps_word_t *table, - unsigned long n, mps_word_t *p) +static void set_table_slot(mps_word_t *table, size_t n, mps_word_t *p) { cdie(table[0] == (mps_word_t)table_wrapper, "set_table_slot"); table[3+n] = (mps_word_t)p; @@ -187,7 +185,7 @@ static void test(mps_arena_t arena, mps_word_t *exacttable; mps_word_t *preserve[TABLE_SLOTS]; /* preserves objects in the weak */ /* table by referring to them */ - unsigned long i, j; + size_t i, j; void *p; exacttable = alloc_table(TABLE_SLOTS, exactap); @@ -221,17 +219,19 @@ static void test(mps_arena_t arena, } } - mps_arena_collect(arena); + die(mps_arena_collect(arena), "mps_arena_collect"); mps_arena_release(arena); for(i = 0; i < TABLE_SLOTS; ++i) { if (preserve[i] == 0) { if (table_slot(weaktable, i)) { - error("Strongly unreachable weak table entry found, slot %lu.\n", i); + error("Strongly unreachable weak table entry found, " + "slot %"PRIuLONGEST".\n", (ulongest_t)i); } else { if (table_slot(exacttable, i) != 0) { error("Weak table entry deleted, but corresponding " - "exact table entry not deleted, slot %lu.\n", i); + "exact table entry not deleted, slot %"PRIuLONGEST".\n", + (ulongest_t)i); } } } @@ -274,12 +274,9 @@ static void *setup(void *v, size_t s) die(mps_root_create_reg(&stack, arena, mps_rank_ambig(), 0, thr, mps_stack_scan_ambig, v, 0), "Root Create\n"); - EnsureHeaderFormat(&dylanfmt, arena); - EnsureHeaderWeakFormat(&dylanweakfmt, arena); + die(EnsureHeaderFormat(&dylanfmt, arena), "EnsureHeaderFormat"); + die(EnsureHeaderWeakFormat(&dylanweakfmt, arena), "EnsureHeaderWeakFormat"); MPS_ARGS_BEGIN(args) { - /* Ask the leafpool to allocate in the nursery, as we're using it to test - weaknesss and want things to die in it promptly. */ - MPS_ARGS_ADD(args, MPS_KEY_GEN, 0); MPS_ARGS_ADD(args, MPS_KEY_FORMAT, dylanfmt); die(mps_pool_create_k(&leafpool, arena, mps_class_lo(), args), "Leaf Pool Create\n"); @@ -319,8 +316,7 @@ int main(int argc, char *argv[]) mps_thr_t thread; void *r; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); initialise_wrapper(wrapper_wrapper); initialise_wrapper(string_wrapper); @@ -342,7 +338,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/awlutth.c b/code/awlutth.c index c937535a0c..2bfaddc381 100644 --- a/code/awlutth.c +++ b/code/awlutth.c @@ -1,7 +1,7 @@ /* awlutth.c: THREADING UNIT TEST USING POOL CLASS AWL * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -13,16 +13,13 @@ #include "mpsavm.h" #include "fmtdy.h" #include "testlib.h" +#include "testthr.h" #include "mpslib.h" #include "mps.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif -#include -#if defined(MPS_OS_LI) || defined(MPS_OS_FR) || defined(MPS_OS_XC) -#include -#endif + +#include /* printf, puts */ +#include /* strlen */ #define testArenaSIZE ((size_t)64<<20) @@ -314,10 +311,9 @@ static void *setup_thr(void *v) int main(int argc, char *argv[]) { mps_arena_t arena; - pthread_t pthread1; + testthr_t thread1; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); initialise_wrapper(wrapper_wrapper); initialise_wrapper(string_wrapper); @@ -325,9 +321,9 @@ int main(int argc, char *argv[]) die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "arena_create\n"); - pthread_create(&pthread1, NULL, setup_thr, (void *)arena); + testthr_create(&thread1, setup_thr, arena); setup_thr(arena); - pthread_join(pthread1, NULL); + testthr_join(&thread1, NULL); mps_arena_destroy(arena); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); @@ -337,7 +333,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/boot.c b/code/boot.c index f42d5ded7c..af365d3b4f 100644 --- a/code/boot.c +++ b/code/boot.c @@ -1,7 +1,7 @@ /* boot.c: BOOTSTRAP ALLOCATOR * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .overview: A structure and protocols for allocating memory from a * given block. Very simple, it basically just increments a pointer. @@ -30,7 +30,7 @@ Bool BootBlockCheck(BootBlock boot) CHECKL(boot->limit != NULL); CHECKL(boot->base <= boot->alloc); CHECKL(boot->alloc <= boot->limit); - CHECKL(boot->alloc < boot->limit); + CHECKL(boot->base < boot->limit); return TRUE; } @@ -101,7 +101,7 @@ Res BootAlloc(void **pReturn, BootBlock boot, size_t size, size_t align) AVER(pReturn != NULL); AVERT(BootBlock, boot); AVER(size > 0); - AVER(AlignCheck((Align)align)); + AVERT(Align, (Align)align); /* Align alloc pointer up and bounds check. */ blockBase = PointerAlignUp(boot->alloc, align); @@ -127,7 +127,7 @@ Res BootAlloc(void **pReturn, BootBlock boot, size_t size, size_t align) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/bt.c b/code/bt.c index a58e501ee5..1a79b82f5f 100644 --- a/code/bt.c +++ b/code/bt.c @@ -1,7 +1,7 @@ /* bt.c: BIT TABLES * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * READERSHIP * @@ -10,6 +10,12 @@ * DESIGN * * .design: see + * + * .aver.critical: The function BTIsResRange (and anything it calls) + * is on the critical path because it is + * called by NailboardIsResRange, which is called for every object in + * a nailboarded segment when the segment is scanned or reclaimed; see + * . */ #include "bt.h" @@ -90,7 +96,9 @@ SRCID(bt, "$Id$"); } else { \ Index actInnerBase = BTIndexAlignUp((base)); \ if (actInnerBase > (limit)) { /* no inner range */ \ - AVER((base) < (limit)); /* caught by small range case */ \ + /* Must have base < limit otherwise caught by small range case */ \ + /* And see .aver.critical. */ \ + AVER_CRITICAL((base) < (limit)); \ bits_action(BTWordIndex((base)), \ BTBitIndex((base)), \ BTBitIndex((limit))); \ @@ -215,7 +223,7 @@ void BTDestroy(BT bt, Arena arena, Count length) * discussed in review.impl.c.bt.4. */ -static Bool BTCheck(BT bt) +Bool BTCheck(BT bt) { AVER(bt != NULL); AVER(AddrIsAligned((Addr)bt, sizeof(Word))); @@ -244,7 +252,7 @@ Size (BTSize)(Count n) Bool (BTGet)(BT t, Index i) { - AVER(BTCheck(t)); + AVERT(BT, t); /* Can't check i */ /* see macro in */ @@ -259,7 +267,7 @@ Bool (BTGet)(BT t, Index i) void (BTSet)(BT t, Index i) { - AVER(BTCheck(t)); + AVERT(BT, t); /* Can't check i */ /* see macro in */ @@ -274,7 +282,7 @@ void (BTSet)(BT t, Index i) void (BTRes)(BT t, Index i) { - AVER(BTCheck(t)); + AVERT(BT, t); /* Can't check i */ /* see macro in */ @@ -289,7 +297,7 @@ void (BTRes)(BT t, Index i) void BTSetRange(BT t, Index base, Index limit) { - AVER(BTCheck(t)); + AVERT(BT, t); AVER(base < limit); #define SINGLE_SET_RANGE(i) \ @@ -311,8 +319,8 @@ void BTSetRange(BT t, Index base, Index limit) Bool BTIsResRange(BT bt, Index base, Index limit) { - AVER(BTCheck(bt)); - AVER(base < limit); + AVERT_CRITICAL(BT, bt); /* See .aver.critical */ + AVER_CRITICAL(base < limit); /* Can't check range of base or limit */ #define SINGLE_IS_RES_RANGE(i) \ @@ -335,7 +343,7 @@ Bool BTIsResRange(BT bt, Index base, Index limit) Bool BTIsSetRange(BT bt, Index base, Index limit) { - AVER(BTCheck(bt)); + AVERT(BT, bt); AVER(base < limit); /* Can't check range of base or limit */ @@ -363,7 +371,7 @@ Bool BTIsSetRange(BT bt, Index base, Index limit) void BTResRange(BT t, Index base, Index limit) { - AVER(BTCheck(t)); + AVERT(BT, t); AVER(base < limit); #define SINGLE_RES_RANGE(i) \ @@ -876,8 +884,8 @@ Bool BTFindShortResRangeHigh(Index *baseReturn, Index *limitReturn, Bool BTRangesSame(BT comparand, BT comparator, Index base, Index limit) { - AVER(BTCheck(comparand)); - AVER(BTCheck(comparator)); + AVERT(BT, comparand); + AVERT(BT, comparator); AVER(base < limit); #define SINGLE_RANGES_SAME(i) \ @@ -912,8 +920,8 @@ Bool BTRangesSame(BT comparand, BT comparator, Index base, Index limit) void BTCopyInvertRange(BT fromBT, BT toBT, Index base, Index limit) { - AVER(BTCheck(fromBT)); - AVER(BTCheck(toBT)); + AVERT(BT, fromBT); + AVERT(BT, toBT); AVER(fromBT != toBT); AVER(base < limit); @@ -947,8 +955,8 @@ void BTCopyInvertRange(BT fromBT, BT toBT, Index base, Index limit) void BTCopyRange(BT fromBT, BT toBT, Index base, Index limit) { - AVER(BTCheck(fromBT)); - AVER(BTCheck(toBT)); + AVERT(BT, fromBT); + AVERT(BT, toBT); AVER(fromBT != toBT); AVER(base < limit); @@ -991,8 +999,8 @@ void BTCopyOffsetRange(BT fromBT, BT toBT, { Index fromBit, toBit; - AVER(BTCheck(fromBT)); - AVER(BTCheck(toBT)); + AVERT(BT, fromBT); + AVERT(BT, toBT); AVER(fromBT != toBT); AVER(fromBase < fromLimit); AVER(toBase < toLimit); @@ -1016,18 +1024,19 @@ Count BTCountResRange(BT bt, Index base, Index limit) Count c = 0; Index bit; - AVER(BTCheck(bt)); + AVERT(BT, bt); AVER(base < limit); for (bit = base; bit < limit; ++bit) - if (!BTGet(bt, bit)) ++c; + if (!BTGet(bt, bit)) + ++c; return c; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/bt.h b/code/bt.h index 29000b957b..4876f66715 100644 --- a/code/bt.h +++ b/code/bt.h @@ -1,7 +1,7 @@ /* bt.h: Bit Table Interface * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .source: */ @@ -39,6 +39,7 @@ extern void (BTRes)(BT bt, Index index); END +extern Bool BTCheck(BT bt); extern Res BTCreate(BT *btReturn, Arena arena, Count length); extern void BTDestroy(BT bt, Arena arena, Count length); @@ -76,7 +77,7 @@ extern Count BTCountResRange(BT bt, Index base, Index limit); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/btcv.c b/code/btcv.c index 72256a5f69..33e3df8be2 100644 --- a/code/btcv.c +++ b/code/btcv.c @@ -1,7 +1,7 @@ /* btss.c: BIT TABLE COVERAGE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .readership: MPS developers * @@ -18,7 +18,7 @@ #include "testlib.h" #include "mpslib.h" -#include +#include /* printf */ SRCID(btcv, "$Id$"); @@ -550,8 +550,7 @@ int main(int argc, char *argv[]) /* tests need 4 whole words plus a few extra bits */ btSize = MPS_WORD_WIDTH * 4 + 10; - testlib_unused(argc); - testlib_unused(argv); + testlib_init(argc, argv); die(mps_arena_create(&mpsArena, mps_arena_class_vm(), testArenaSIZE), "mps_arena_create"); @@ -572,7 +571,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/bttest.c b/code/bttest.c index ea2b00849b..20bb4cab4e 100644 --- a/code/bttest.c +++ b/code/bttest.c @@ -1,7 +1,7 @@ /* bttest.c: BIT TABLE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ @@ -10,11 +10,10 @@ #include "mpsavm.h" #include "testlib.h" #include "mpslib.h" - -#include -#include #include "mpstd.h" -#include + +#include /* fflush, fgets, printf, putchar, puts */ +#include /* exit, strtol */ SRCID(bttest, "$Id$"); @@ -125,7 +124,7 @@ static void get(void) { if (argInRange(0)) { Bool b = (BTGet)(bt, args[0]); - printf(b ? "TRUE\n" : "FALSE\n"); + puts(b ? "TRUE" : "FALSE"); } } @@ -148,7 +147,7 @@ static void isSetRange(void) { if (checkDefaultRange(0)) { Bool b = BTIsSetRange(bt, args[0], args[1]); - printf(b ? "TRUE\n" : "FALSE\n"); + puts(b ? "TRUE" : "FALSE"); } } @@ -157,7 +156,7 @@ static void isResRange(void) { if (checkDefaultRange(0)) { Bool b = BTIsResRange(bt, args[0], args[1]); - printf(b ? "TRUE\n" : "FALSE\n"); + puts(b ? "TRUE" : "FALSE"); } } @@ -312,11 +311,6 @@ static void obeyCommand(const char *command) } -#ifdef MPS_BUILD_MV -/* disable "conversion from int to char" */ -#pragma warning(disable: 4244) -#endif - static void showBT(void) { Index i; char c; @@ -325,7 +319,7 @@ static void showBT(void) { i = 0; while((i < btSize) && (i < 50)) { if (i % 10 == 0) - c = (char)((i / 10) % 10) + '0'; + c = (char)(((i / 10) % 10) + '0'); else c = ' '; putchar(c); @@ -334,7 +328,7 @@ static void showBT(void) { putchar('\n'); i = 0; while((i < btSize) && (i < 50)) { - c = (char)(i % 10) +'0'; + c = (char)((i % 10) +'0'); putchar(c); ++ i; } @@ -353,11 +347,6 @@ static void showBT(void) { putchar('\n'); } -#ifdef MPS_BUILD_MV -/* disable "conversion from int to char" */ -#pragma warning(default: 4244) -#endif - #define testArenaSIZE (((size_t)64)<<20) @@ -366,7 +355,7 @@ extern int main(int argc, char *argv[]) bt = NULL; btSize = 0; - testlib_unused(argc); testlib_unused(argv); + testlib_init(argc, argv); die(mps_arena_create((mps_arena_t*)&arena, mps_arena_class_vm(), testArenaSIZE), @@ -374,7 +363,7 @@ extern int main(int argc, char *argv[]) while(1) { char input[100]; printf("bt test> "); - fflush(stdout); + (void)fflush(stdout); if (fgets(input, 100, stdin)) { obeyCommand(input); showBT(); @@ -387,7 +376,7 @@ extern int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/buffer.c b/code/buffer.c index c63310980e..edef611c21 100644 --- a/code/buffer.c +++ b/code/buffer.c @@ -1,7 +1,7 @@ /* buffer.c: ALLOCATION BUFFER IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This is (part of) the implementation of allocation buffers. * Several macros which also form part of the implementation are in @@ -19,10 +19,8 @@ * * TRANSGRESSIONS * - * .trans.mod: There are several instances where pool structures are - * directly accessed by this module because does not provide - * an adequate (or adequately documented) interface. They bear this - * tag. + * .trans.mod: pool->bufferSerial is directly accessed by this module + * because does not provide an interface. */ #include "mpm.h" @@ -45,7 +43,7 @@ Bool BufferCheck(Buffer buffer) CHECKU(Arena, buffer->arena); CHECKU(Pool, buffer->pool); CHECKL(buffer->arena == buffer->pool->arena); - CHECKL(RingCheck(&buffer->poolRing)); /* */ + CHECKD_NOSIG(Ring, &buffer->poolRing); CHECKL(BoolCheck(buffer->isMutator)); CHECKL(buffer->fillSize >= 0.0); CHECKL(buffer->emptySize >= 0.0); @@ -146,47 +144,48 @@ Bool BufferCheck(Buffer buffer) * * See for structure definitions. */ -Res BufferDescribe(Buffer buffer, mps_lib_FILE *stream) +Res BufferDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth) { Res res; - char abzMode[5]; - if (!TESTT(Buffer, buffer)) return ResFAIL; - if (stream == NULL) return ResFAIL; - - abzMode[0] = (char)( (buffer->mode & BufferModeTRANSITION) ? 't' : '_' ); - abzMode[1] = (char)( (buffer->mode & BufferModeLOGGED) ? 'l' : '_' ); - abzMode[2] = (char)( (buffer->mode & BufferModeFLIPPED) ? 'f' : '_' ); - abzMode[3] = (char)( (buffer->mode & BufferModeATTACHED) ? 'a' : '_' ); - abzMode[4] = '\0'; + if (!TESTT(Buffer, buffer)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = WriteF(stream, + res = WriteF(stream, depth, "Buffer $P ($U) {\n", (WriteFP)buffer, (WriteFU)buffer->serial, " class $P (\"$S\")\n", - (WriteFP)buffer->class, buffer->class->name, + (WriteFP)buffer->class, (WriteFS)buffer->class->name, " Arena $P\n", (WriteFP)buffer->arena, " Pool $P\n", (WriteFP)buffer->pool, - buffer->isMutator ? - " Mutator Buffer\n" : " Internal Buffer\n", - " mode $S (TRANSITION, LOGGED, FLIPPED, ATTACHED)\n", - (WriteFS)abzMode, + " ", buffer->isMutator ? "Mutator" : "Internal", " Buffer\n", + " mode $C$C$C$C (TRANSITION, LOGGED, FLIPPED, ATTACHED)\n", + (WriteFC)((buffer->mode & BufferModeTRANSITION) ? 't' : '_'), + (WriteFC)((buffer->mode & BufferModeLOGGED) ? 'l' : '_'), + (WriteFC)((buffer->mode & BufferModeFLIPPED) ? 'f' : '_'), + (WriteFC)((buffer->mode & BufferModeATTACHED) ? 'a' : '_'), " fillSize $UKb\n", (WriteFU)(buffer->fillSize / 1024), " emptySize $UKb\n", (WriteFU)(buffer->emptySize / 1024), " alignment $W\n", (WriteFW)buffer->alignment, - " base $A\n", buffer->base, - " initAtFlip $A\n", buffer->initAtFlip, - " init $A\n", buffer->ap_s.init, - " alloc $A\n", buffer->ap_s.alloc, - " limit $A\n", buffer->ap_s.limit, - " poolLimit $A\n", buffer->poolLimit, + " base $A\n", (WriteFA)buffer->base, + " initAtFlip $A\n", (WriteFA)buffer->initAtFlip, + " init $A\n", (WriteFA)buffer->ap_s.init, + " alloc $A\n", (WriteFA)buffer->ap_s.alloc, + " limit $A\n", (WriteFA)buffer->ap_s.limit, + " poolLimit $A\n", (WriteFA)buffer->poolLimit, + " alignment $W\n", (WriteFW)buffer->alignment, + " rampCount $U\n", (WriteFU)buffer->rampCount, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; - res = buffer->class->describe(buffer, stream); - if (res != ResOK) return res; + res = buffer->class->describe(buffer, stream, depth + 2); + if (res != ResOK) + return res; - res = WriteF(stream, "} Buffer $P ($U)\n", + res = WriteF(stream, depth, "} Buffer $P ($U)\n", (WriteFP)buffer, (WriteFU)buffer->serial, NULL); return res; @@ -204,8 +203,6 @@ static Res BufferInit(Buffer buffer, BufferClass class, AVER(buffer != NULL); AVERT(BufferClass, class); AVERT(Pool, pool); - /* The PoolClass should support buffer protocols */ - AVER((pool->class->attr & AttrBUF)); /* .trans.mod */ arena = PoolArena(pool); /* Initialize the buffer. See for a definition of */ @@ -222,7 +219,7 @@ static Res BufferInit(Buffer buffer, BufferClass class, } buffer->fillSize = 0.0; buffer->emptySize = 0.0; - buffer->alignment = pool->alignment; /* .trans.mod */ + buffer->alignment = PoolAlignment(pool); buffer->base = (Addr)0; buffer->initAtFlip = (Addr)0; /* In the next three assignments we really mean zero, not NULL, because @@ -329,12 +326,10 @@ void BufferDetach(Buffer buffer, Pool pool) spare = AddrOffset(init, limit); buffer->emptySize += spare; if (buffer->isMutator) { - buffer->pool->emptyMutatorSize += spare; ArenaGlobals(buffer->arena)->emptyMutatorSize += spare; ArenaGlobals(buffer->arena)->allocMutatorSize += AddrOffset(buffer->base, init); } else { - buffer->pool->emptyInternalSize += spare; ArenaGlobals(buffer->arena)->emptyInternalSize += spare; } @@ -382,8 +377,6 @@ void BufferFinish(Buffer buffer) pool = BufferPool(buffer); - /* The PoolClass should support buffer protocols */ - AVER((pool->class->attr & AttrBUF)); /* .trans.mod */ AVER(BufferIsReady(buffer)); /* */ @@ -605,7 +598,7 @@ Res BufferReserve(Addr *pReturn, Buffer buffer, Size size, AVER(size > 0); AVER(SizeIsAligned(size, BufferPool(buffer)->alignment)); AVER(BufferIsReady(buffer)); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); /* Is there enough room in the unallocated portion of the buffer to */ /* satisfy the request? If so, just increase the alloc marker and */ @@ -660,10 +653,8 @@ void BufferAttach(Buffer buffer, Addr base, Addr limit, Size prealloc = AddrOffset(base, init); ArenaGlobals(buffer->arena)->allocMutatorSize -= prealloc; } - buffer->pool->fillMutatorSize += filled; ArenaGlobals(buffer->arena)->fillMutatorSize += filled; } else { - buffer->pool->fillInternalSize += filled; ArenaGlobals(buffer->arena)->fillInternalSize += filled; } @@ -980,20 +971,21 @@ Bool BufferIsTrappedByMutator(Buffer buffer) * * Just represent the two patterns by two different pointers to dummies. */ -AllocPatternStruct AllocPatternRampStruct = {'\0'}; +static AllocPatternStruct AllocPatternRampStruct = {'\0'}; AllocPattern AllocPatternRamp(void) { return &AllocPatternRampStruct; } -AllocPatternStruct AllocPatternRampCollectAllStruct = {'\0'}; +static AllocPatternStruct AllocPatternRampCollectAllStruct = {'\0'}; AllocPattern AllocPatternRampCollectAll(void) { return &AllocPatternRampCollectAllStruct; } +ATTRIBUTE_UNUSED static Bool AllocPatternCheck(AllocPattern pattern) { CHECKL(pattern == &AllocPatternRampCollectAllStruct @@ -1075,7 +1067,7 @@ static Res bufferTrivInit(Buffer buffer, Pool pool, ArgList args) AVERT(Buffer, buffer); AVERT(Pool, pool); UNUSED(args); - EVENT3(BufferInit, buffer, pool, buffer->isMutator); + EVENT3(BufferInit, buffer, pool, BOOLOF(buffer->isMutator)); return ResOK; } @@ -1169,10 +1161,13 @@ static void bufferNoReassignSeg(Buffer buffer, Seg seg) /* bufferTrivDescribe -- basic Buffer describe method */ -static Res bufferTrivDescribe(Buffer buffer, mps_lib_FILE *stream) +static Res bufferTrivDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth) { - if (!TESTT(Buffer, buffer)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Buffer, buffer)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + UNUSED(depth); /* dispatching function does it all */ return ResOK; } @@ -1182,7 +1177,7 @@ static Res bufferTrivDescribe(Buffer buffer, mps_lib_FILE *stream) Bool BufferClassCheck(BufferClass class) { - CHECKL(ProtocolClassCheck(&class->protocol)); + CHECKD(ProtocolClass, &class->protocol); CHECKL(class->name != NULL); /* Should be <=6 char C identifier */ CHECKL(class->size >= sizeof(BufferStruct)); CHECKL(FUNCHECK(class->varargs)); @@ -1220,6 +1215,7 @@ DEFINE_CLASS(BufferClass, class) class->setRankSet = bufferNoSetRankSet; class->reassignSeg = bufferNoReassignSeg; class->sig = BufferClassSig; + AVERT(BufferClass, class); } @@ -1240,7 +1236,7 @@ Bool SegBufCheck(SegBuf segbuf) CHECKS(SegBuf, segbuf); buffer = &segbuf->bufferStruct; - CHECKL(BufferCheck(buffer)); + CHECKD(Buffer, buffer); CHECKL(RankSetCheck(segbuf->rankSet)); if (buffer->mode & BufferModeTRANSITION) { @@ -1250,7 +1246,7 @@ Bool SegBufCheck(SegBuf segbuf) } else { /* The buffer is attached to a segment. */ CHECKL(segbuf->seg != NULL); - CHECKL(SegCheck(segbuf->seg)); + CHECKD(Seg, segbuf->seg); /* To avoid recursive checking, leave it to SegCheck to make */ /* sure the buffer and segment fields tally. */ @@ -1287,7 +1283,7 @@ static Res segBufInit(Buffer buffer, Pool pool, ArgList args) segbuf->rankSet = RankSetEMPTY; AVERT(SegBuf, segbuf); - EVENT3(BufferInitSeg, buffer, pool, buffer->isMutator); + EVENT3(BufferInitSeg, buffer, pool, BOOLOF(buffer->isMutator)); return ResOK; } @@ -1426,25 +1422,29 @@ static void segBufReassignSeg (Buffer buffer, Seg seg) /* segBufDescribe -- describe method for SegBuf */ -static Res segBufDescribe(Buffer buffer, mps_lib_FILE *stream) +static Res segBufDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth) { SegBuf segbuf; BufferClass super; Res res; - if (!TESTT(Buffer, buffer)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Buffer, buffer)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; segbuf = BufferSegBuf(buffer); - if (!TESTT(SegBuf, segbuf)) return ResFAIL; + if (!TESTT(SegBuf, segbuf)) + return ResFAIL; /* Describe the superclass fields first via next-method call */ super = BUFFER_SUPERCLASS(SegBufClass); - res = super->describe(buffer, stream); - if (res != ResOK) return res; + res = super->describe(buffer, stream, depth); + if (res != ResOK) + return res; - res = WriteF(stream, - " Seg $P\n", (WriteFP)segbuf->seg, - " rankSet $U\n", (WriteFU)segbuf->rankSet, + res = WriteF(stream, depth, + "Seg $P\n", (WriteFP)segbuf->seg, + "rankSet $U\n", (WriteFU)segbuf->rankSet, NULL); return res; @@ -1472,6 +1472,7 @@ DEFINE_CLASS(SegBufClass, class) class->rankSet = segBufRankSet; class->setRankSet = segBufSetRankSet; class->reassignSeg = segBufReassignSeg; + AVERT(BufferClass, class); } @@ -1485,7 +1486,7 @@ static void rankBufVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[0].key = MPS_KEY_RANK; args[0].val.rank = va_arg(varargs, Rank); args[1].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } /* rankBufInit -- RankBufClass init method */ @@ -1499,10 +1500,10 @@ static Res rankBufInit(Buffer buffer, Pool pool, ArgList args) AVERT(Buffer, buffer); AVERT(Pool, pool); - AVER(ArgListCheck(args)); + AVERT(ArgList, args); if (ArgPick(&arg, args, MPS_KEY_RANK)) rank = arg.val.rank; - AVER(RankCheck(rank)); + AVERT(Rank, rank); /* Initialize the superclass fields first via next-method call */ super = BUFFER_SUPERCLASS(RankBufClass); @@ -1513,7 +1514,7 @@ static Res rankBufInit(Buffer buffer, Pool pool, ArgList args) BufferSetRankSet(buffer, RankSetSingle(rank)); /* There's nothing to check that the superclass doesn't, so no AVERT. */ - EVENT4(BufferInitRank, buffer, pool, buffer->isMutator, rank); + EVENT4(BufferInitRank, buffer, pool, BOOLOF(buffer->isMutator), rank); return ResOK; } @@ -1532,12 +1533,13 @@ DEFINE_CLASS(RankBufClass, class) class->name = "RANKBUF"; class->varargs = rankBufVarargs; class->init = rankBufInit; + AVERT(BufferClass, class); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/cbs.c b/code/cbs.c index 84913dd9ea..0b30b68acb 100644 --- a/code/cbs.c +++ b/code/cbs.c @@ -1,7 +1,7 @@ /* cbs.c: COALESCING BLOCK STRUCTURE IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * * .intro: This is a portable implementation of coalescing block * structures. @@ -21,49 +21,29 @@ SRCID(cbs, "$Id$"); -typedef struct CBSBlockStruct *CBSBlock; -typedef struct CBSBlockStruct { - SplayNodeStruct splayNode; - Addr base; - Addr limit; - Size maxSize; /* accurate maximum block size of sub-tree */ -} CBSBlockStruct; - #define CBSBlockBase(block) ((block)->base) #define CBSBlockLimit(block) ((block)->limit) #define CBSBlockSize(block) AddrOffset((block)->base, (block)->limit) -#define cbsOfSplayTree(tree) PARENT(CBSStruct, splayTree, (tree)) -#define cbsBlockOfSplayNode(node) PARENT(CBSBlockStruct, splayNode, (node)) -#define splayTreeOfCBS(tree) (&((cbs)->splayTree)) -#define splayNodeOfCBSBlock(block) (&((block)->splayNode)) -#define keyOfCBSBlock(block) ((void *)&((block)->base)) - - -/* cbsEnter, cbsLeave -- Avoid re-entrance - * - * .enter-leave: The callbacks are restricted in what they may call. - * These functions enforce this. - * - * .enter-leave.simple: Simple queries may be called from callbacks. - */ - -static void cbsEnter(CBS cbs) -{ - /* Don't need to check as always called from interface function. */ - AVER(!cbs->inCBS); - cbs->inCBS = TRUE; - return; -} +#define cbsOfLand(land) PARENT(CBSStruct, landStruct, land) +#define cbsSplay(cbs) (&((cbs)->splayTreeStruct)) +#define cbsOfSplay(_splay) PARENT(CBSStruct, splayTreeStruct, _splay) +#define cbsBlockTree(block) (&((block)->treeStruct)) +#define cbsBlockOfTree(_tree) TREE_ELT(CBSBlock, treeStruct, _tree) +#define cbsFastBlockOfTree(_tree) \ + PARENT(CBSFastBlockStruct, cbsBlockStruct, cbsBlockOfTree(_tree)) +#define cbsZonedBlockOfTree(_tree) \ + PARENT(CBSZonedBlockStruct, cbsFastBlockStruct, cbsFastBlockOfTree(_tree)) +#define cbsBlockPool(cbs) RVALUE((cbs)->blockPool) -static void cbsLeave(CBS cbs) -{ - /* Don't need to check as always called from interface function. */ - AVER(cbs->inCBS); - cbs->inCBS = FALSE; - return; -} +/* We pass the block base directly as a TreeKey (void *) assuming that + Addr can be encoded, and possibly breaking . + On an exotic platform where this isn't true, pass the address of base. + i.e. add an & */ +#define cbsBlockKey(block) ((TreeKey)(block)->base) +#define keyOfBaseVar(baseVar) ((TreeKey)(baseVar)) +#define baseOfKey(key) ((Addr)(key)) /* CBSCheck -- Check CBS */ @@ -71,25 +51,28 @@ static void cbsLeave(CBS cbs) Bool CBSCheck(CBS cbs) { /* See .enter-leave.simple. */ + Land land; CHECKS(CBS, cbs); - CHECKL(cbs != NULL); - CHECKL(SplayTreeCheck(splayTreeOfCBS(cbs))); - /* nothing to check about splayTreeSize */ + land = CBSLand(cbs); + CHECKD(Land, land); + CHECKD(SplayTree, cbsSplay(cbs)); CHECKD(Pool, cbs->blockPool); - CHECKL(BoolCheck(cbs->fastFind)); - CHECKL(BoolCheck(cbs->inCBS)); - /* No MeterCheck */ + CHECKL(cbs->blockStructSize > 0); + CHECKL(BoolCheck(cbs->ownPool)); + CHECKL(SizeIsAligned(cbs->size, LandAlignment(land))); + STATISTIC_STAT({CHECKL((cbs->size == 0) == (cbs->treeSize == 0));}); return TRUE; } +ATTRIBUTE_UNUSED static Bool CBSBlockCheck(CBSBlock block) { - /* See .enter-leave.simple. */ UNUSED(block); /* Required because there is no signature */ CHECKL(block != NULL); - CHECKL(SplayNodeCheck(splayNodeOfCBSBlock(block))); + /* Can't use CHECKD_NOSIG because TreeEMPTY is NULL. */ + CHECKL(TreeCheck(cbsBlockTree(block))); /* If the block is in the middle of being deleted, */ /* the pointers will be equal. */ @@ -99,24 +82,22 @@ static Bool CBSBlockCheck(CBSBlock block) } -/* cbsSplayCompare -- Compare key to [base,limit) +/* cbsCompare -- Compare key to [base,limit) * * See */ -static Compare cbsSplayCompare(void *key, SplayNode node) +static Compare cbsCompare(Tree tree, TreeKey key) { Addr base1, base2, limit2; CBSBlock cbsBlock; - /* NULL key compares less than everything. */ - if (key == NULL) - return CompareLESS; - - AVER(node != NULL); + AVERT_CRITICAL(Tree, tree); + AVER_CRITICAL(tree != TreeEMPTY); + AVER_CRITICAL(key != NULL); - base1 = *(Addr *)key; - cbsBlock = cbsBlockOfSplayNode(node); + base1 = baseOfKey(key); + cbsBlock = cbsBlockOfTree(tree); base2 = cbsBlock->base; limit2 = cbsBlock->limit; @@ -128,167 +109,254 @@ static Compare cbsSplayCompare(void *key, SplayNode node) return CompareEQUAL; } +static TreeKey cbsKey(Tree tree) +{ + return cbsBlockKey(cbsBlockOfTree(tree)); +} + /* cbsTestNode, cbsTestTree -- test for nodes larger than the S parameter */ -static Bool cbsTestNode(SplayTree tree, SplayNode node, +static Bool cbsTestNode(SplayTree splay, Tree tree, void *closureP, Size size) { CBSBlock block; - AVERT(SplayTree, tree); - AVERT(SplayNode, node); + AVERT(SplayTree, splay); + AVERT(Tree, tree); AVER(closureP == NULL); AVER(size > 0); - AVER(cbsOfSplayTree(tree)->fastFind); + AVER(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSFastLandClass)); - block = cbsBlockOfSplayNode(node); + block = cbsBlockOfTree(tree); return CBSBlockSize(block) >= size; } -static Bool cbsTestTree(SplayTree tree, SplayNode node, +static Bool cbsTestTree(SplayTree splay, Tree tree, void *closureP, Size size) { - CBSBlock block; + CBSFastBlock block; - AVERT(SplayTree, tree); - AVERT(SplayNode, node); + AVERT(SplayTree, splay); + AVERT(Tree, tree); AVER(closureP == NULL); AVER(size > 0); - AVER(cbsOfSplayTree(tree)->fastFind); + AVER(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSFastLandClass)); - block = cbsBlockOfSplayNode(node); + block = cbsFastBlockOfTree(tree); return block->maxSize >= size; } -/* cbsUpdateNode -- update size info after restructuring */ +/* cbsUpdateFastNode -- update size info after restructuring */ -static void cbsUpdateNode(SplayTree tree, SplayNode node, - SplayNode leftChild, SplayNode rightChild) +static void cbsUpdateFastNode(SplayTree splay, Tree tree) { Size maxSize; - CBSBlock block; - AVERT(SplayTree, tree); - AVERT(SplayNode, node); - if (leftChild != NULL) - AVERT(SplayNode, leftChild); - if (rightChild != NULL) - AVERT(SplayNode, rightChild); - AVER(cbsOfSplayTree(tree)->fastFind); + AVERT_CRITICAL(SplayTree, splay); + AVERT_CRITICAL(Tree, tree); + AVER_CRITICAL(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSFastLandClass)); - block = cbsBlockOfSplayNode(node); - maxSize = CBSBlockSize(block); + maxSize = CBSBlockSize(cbsBlockOfTree(tree)); - if (leftChild != NULL) { - Size size = cbsBlockOfSplayNode(leftChild)->maxSize; + if (TreeHasLeft(tree)) { + Size size = cbsFastBlockOfTree(TreeLeft(tree))->maxSize; if (size > maxSize) maxSize = size; } - if (rightChild != NULL) { - Size size = cbsBlockOfSplayNode(rightChild)->maxSize; + if (TreeHasRight(tree)) { + Size size = cbsFastBlockOfTree(TreeRight(tree))->maxSize; if (size > maxSize) maxSize = size; } - block->maxSize = maxSize; + cbsFastBlockOfTree(tree)->maxSize = maxSize; } -/* CBSInit -- Initialise a CBS structure +/* cbsUpdateZonedNode -- update size and zone info after restructuring */ + +static void cbsUpdateZonedNode(SplayTree splay, Tree tree) +{ + ZoneSet zones; + CBSZonedBlock zonedBlock; + CBSBlock block; + Arena arena; + + AVERT_CRITICAL(SplayTree, splay); + AVERT_CRITICAL(Tree, tree); + AVER_CRITICAL(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSZonedLandClass)); + + cbsUpdateFastNode(splay, tree); + + zonedBlock = cbsZonedBlockOfTree(tree); + block = &zonedBlock->cbsFastBlockStruct.cbsBlockStruct; + arena = LandArena(CBSLand(cbsOfSplay(splay))); + zones = ZoneSetOfRange(arena, CBSBlockBase(block), CBSBlockLimit(block)); + + if (TreeHasLeft(tree)) + zones = ZoneSetUnion(zones, cbsZonedBlockOfTree(TreeLeft(tree))->zones); + + if (TreeHasRight(tree)) + zones = ZoneSetUnion(zones, cbsZonedBlockOfTree(TreeRight(tree))->zones); + + zonedBlock->zones = zones; +} + + +/* cbsInit -- Initialise a CBS structure * - * See . + * See . */ -ARG_DEFINE_KEY(cbs_extend_by, Size); +ARG_DEFINE_KEY(cbs_block_pool, Pool); -Res CBSInit(Arena arena, CBS cbs, void *owner, Align alignment, - Bool fastFind, ArgList args) +static Res cbsInitComm(Land land, ArgList args, SplayUpdateNodeFunction update, + Size blockStructSize) { - Size extendBy = CBS_EXTEND_BY_DEFAULT; + CBS cbs; + LandClass super; ArgStruct arg; Res res; + Pool blockPool = NULL; - AVERT(Arena, arena); - - if (ArgPick(&arg, args, MPS_KEY_CBS_EXTEND_BY)) - extendBy = arg.val.size; - - SplayTreeInit(splayTreeOfCBS(cbs), &cbsSplayCompare, - fastFind ? &cbsUpdateNode : NULL); - MPS_ARGS_BEGIN(pcArgs) { - MPS_ARGS_ADD(pcArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSBlockStruct)); - MPS_ARGS_ADD(pcArgs, MPS_KEY_EXTEND_BY, extendBy); - res = PoolCreate(&(cbs->blockPool), arena, PoolClassMFS(), pcArgs); - } MPS_ARGS_END(pcArgs); + AVERT(Land, land); + super = LAND_SUPERCLASS(CBSLandClass); + res = (*super->init)(land, args); if (res != ResOK) return res; - cbs->splayTreeSize = 0; - cbs->fastFind = fastFind; - cbs->alignment = alignment; - cbs->inCBS = TRUE; + if (ArgPick(&arg, args, CBSBlockPool)) + blockPool = arg.val.pool; - METER_INIT(cbs->splaySearch, "size of splay tree", (void *)cbs); + cbs = cbsOfLand(land); + SplayTreeInit(cbsSplay(cbs), cbsCompare, cbsKey, update); + + if (blockPool != NULL) { + cbs->blockPool = blockPool; + cbs->ownPool = FALSE; + } else { + MPS_ARGS_BEGIN(pcArgs) { + MPS_ARGS_ADD(pcArgs, MPS_KEY_MFS_UNIT_SIZE, blockStructSize); + res = PoolCreate(&cbs->blockPool, LandArena(land), PoolClassMFS(), pcArgs); + } MPS_ARGS_END(pcArgs); + if (res != ResOK) + return res; + cbs->ownPool = TRUE; + } + cbs->treeSize = 0; + cbs->size = 0; + + cbs->blockStructSize = blockStructSize; + + METER_INIT(cbs->treeSearch, "size of tree", (void *)cbs); cbs->sig = CBSSig; AVERT(CBS, cbs); - EVENT2(CBSInit, cbs, owner); - cbsLeave(cbs); return ResOK; } +static Res cbsInit(Land land, ArgList args) +{ + return cbsInitComm(land, args, SplayTrivUpdate, + sizeof(CBSBlockStruct)); +} + +static Res cbsInitFast(Land land, ArgList args) +{ + return cbsInitComm(land, args, cbsUpdateFastNode, + sizeof(CBSFastBlockStruct)); +} + +static Res cbsInitZoned(Land land, ArgList args) +{ + return cbsInitComm(land, args, cbsUpdateZonedNode, + sizeof(CBSZonedBlockStruct)); +} -/* CBSFinish -- Finish a CBS structure + +/* cbsFinish -- Finish a CBS structure * - * See . + * See . */ -void CBSFinish(CBS cbs) +static void cbsFinish(Land land) { + CBS cbs; + + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); - cbsEnter(cbs); - METER_EMIT(&cbs->splaySearch); + METER_EMIT(&cbs->treeSearch); cbs->sig = SigInvalid; - SplayTreeFinish(splayTreeOfCBS(cbs)); - PoolDestroy(cbs->blockPool); + SplayTreeFinish(cbsSplay(cbs)); + if (cbs->ownPool) + PoolDestroy(cbsBlockPool(cbs)); } -/* Node change operators +/* cbsSize -- total size of ranges in CBS * - * These four functions are called whenever blocks are created, - * destroyed, grow, or shrink. They maintain the maxSize if fastFind is - * enabled. + * See . */ -static void cbsBlockDelete(CBS cbs, CBSBlock block) +static Size cbsSize(Land land) { - Res res; + CBS cbs; + + AVERT(Land, land); + cbs = cbsOfLand(land); + AVERT(CBS, cbs); + + return cbs->size; +} + +/* cbsBlockDestroy -- destroy a block */ + +static void cbsBlockDestroy(CBS cbs, CBSBlock block) +{ + Size size; AVERT(CBS, cbs); AVERT(CBSBlock, block); + size = CBSBlockSize(block); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - res = SplayTreeDelete(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block), - keyOfCBSBlock(block)); - AVER(res == ResOK); /* Must be possible to delete node */ - STATISTIC(--cbs->splayTreeSize); + STATISTIC(--cbs->treeSize); + AVER(cbs->size >= size); + cbs->size -= size; /* make invalid */ block->limit = block->base; + PoolFree(cbsBlockPool(cbs), (Addr)block, cbs->blockStructSize); +} - PoolFree(cbs->blockPool, (Addr)block, sizeof(CBSBlockStruct)); - return; +/* Node change operators + * + * These four functions are called whenever blocks are created, + * destroyed, grow, or shrink. They maintain the maxSize if fastFind is + * enabled. + */ + +static void cbsBlockDelete(CBS cbs, CBSBlock block) +{ + Bool b; + + AVERT(CBS, cbs); + AVERT(CBSBlock, block); + + METER_ACC(cbs->treeSearch, cbs->treeSize); + b = SplayTreeDelete(cbsSplay(cbs), cbsBlockTree(block)); + AVER(b); /* expect block to be in the tree */ + cbsBlockDestroy(cbs, block); } static void cbsBlockShrunk(CBS cbs, CBSBlock block, Size oldSize) @@ -300,12 +368,10 @@ static void cbsBlockShrunk(CBS cbs, CBSBlock block, Size oldSize) newSize = CBSBlockSize(block); AVER(oldSize > newSize); + AVER(cbs->size >= oldSize - newSize); - if (cbs->fastFind) { - SplayNodeRefresh(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block), - keyOfCBSBlock(block)); - AVER(CBSBlockSize(block) <= block->maxSize); - } + SplayNodeRefresh(cbsSplay(cbs), cbsBlockTree(block)); + cbs->size -= oldSize - newSize; } static void cbsBlockGrew(CBS cbs, CBSBlock block, Size oldSize) @@ -318,15 +384,12 @@ static void cbsBlockGrew(CBS cbs, CBSBlock block, Size oldSize) newSize = CBSBlockSize(block); AVER(oldSize < newSize); - if (cbs->fastFind) { - SplayNodeRefresh(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block), - keyOfCBSBlock(block)); - AVER(CBSBlockSize(block) <= block->maxSize); - } + SplayNodeRefresh(cbsSplay(cbs), cbsBlockTree(block)); + cbs->size += newSize - oldSize; } /* cbsBlockAlloc -- allocate a new block and set its base and limit, - but do not insert it into the splay tree yet */ + but do not insert it into the tree yet */ static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range) { @@ -338,16 +401,17 @@ static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range) AVERT(CBS, cbs); AVERT(Range, range); - res = PoolAlloc(&p, cbs->blockPool, sizeof(CBSBlockStruct), + res = PoolAlloc(&p, cbsBlockPool(cbs), cbs->blockStructSize, /* withReservoirPermit */ FALSE); if (res != ResOK) goto failPoolAlloc; block = (CBSBlock)p; - SplayNodeInit(splayNodeOfCBSBlock(block)); + TreeInit(cbsBlockTree(block)); block->base = RangeBase(range); block->limit = RangeLimit(range); - block->maxSize = CBSBlockSize(block); + + SplayNodeInit(cbsSplay(cbs), cbsBlockTree(block)); AVERT(CBSBlock, block); *blockReturn = block; @@ -358,71 +422,81 @@ static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range) return res; } -/* cbsBlockInsert -- insert a block into the splay tree */ +/* cbsBlockInsert -- insert a block into the tree */ static void cbsBlockInsert(CBS cbs, CBSBlock block) { - Res res; + Bool b; AVERT(CBS, cbs); AVERT(CBSBlock, block); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - res = SplayTreeInsert(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block), - keyOfCBSBlock(block)); - AVER(res == ResOK); - STATISTIC(++cbs->splayTreeSize); + METER_ACC(cbs->treeSearch, cbs->treeSize); + b = SplayTreeInsert(cbsSplay(cbs), cbsBlockTree(block)); + AVER(b); + STATISTIC(++cbs->treeSize); + cbs->size += CBSBlockSize(block); } -/* cbsInsertIntoTree -- Insert a range into the splay tree */ +/* cbsInsert -- Insert a range into the CBS + * + * See . + * + * .insert.alloc: Will only allocate a block if the range does not + * abut an existing range. + */ -static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range) +static Res cbsInsert(Range rangeReturn, Land land, Range range) { + CBS cbs; + Bool b; Res res; Addr base, limit, newBase, newLimit; - SplayNode leftSplay, rightSplay; + Tree leftSplay, rightSplay; CBSBlock leftCBS, rightCBS; Bool leftMerge, rightMerge; Size oldSize; AVER(rangeReturn != NULL); - AVERT(CBS, cbs); + AVERT(Land, land); AVERT(Range, range); - AVER(RangeIsAligned(range, cbs->alignment)); + AVER(RangeIsAligned(range, LandAlignment(land))); + cbs = cbsOfLand(land); base = RangeBase(range); limit = RangeLimit(range); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - res = SplayTreeNeighbours(&leftSplay, &rightSplay, - splayTreeOfCBS(cbs), (void *)&base); - if (res != ResOK) + METER_ACC(cbs->treeSearch, cbs->treeSize); + b = SplayTreeNeighbours(&leftSplay, &rightSplay, cbsSplay(cbs), keyOfBaseVar(base)); + if (!b) { + res = ResFAIL; goto fail; + } /* The two cases below are not quite symmetrical, because base was * passed into the call to SplayTreeNeighbours(), but limit was not. * So we know that if there is a left neighbour, then leftCBS->limit - * <= base (this is ensured by cbsSplayCompare, which is the - * comparison method on the splay tree). But if there is a right + * <= base (this is ensured by cbsCompare, which is the + * comparison method on the tree). But if there is a right * neighbour, all we know is that base < rightCBS->base. But for the * range to fit, we need limit <= rightCBS->base too. Hence the extra * check and the possibility of failure in the second case. */ - if (leftSplay == NULL) { + if (leftSplay == TreeEMPTY) { leftCBS = NULL; leftMerge = FALSE; } else { - leftCBS = cbsBlockOfSplayNode(leftSplay); + leftCBS = cbsBlockOfTree(leftSplay); AVER(leftCBS->limit <= base); leftMerge = leftCBS->limit == base; } - if (rightSplay == NULL) { + if (rightSplay == TreeEMPTY) { rightCBS = NULL; rightMerge = FALSE; } else { - rightCBS = cbsBlockOfSplayNode(rightSplay); + rightCBS = cbsBlockOfTree(rightSplay); if (rightCBS != NULL && limit > CBSBlockLimit(rightCBS)) { res = ResFAIL; goto fail; @@ -470,52 +544,38 @@ static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range) } -/* CBSInsert -- Insert a range into the CBS +/* cbsDelete -- Remove a range from a CBS * - * See . + * See . + * + * .delete.alloc: Will only allocate a block if the range splits + * an existing range. */ -Res CBSInsert(Range rangeReturn, CBS cbs, Range range) -{ - Res res; - - AVERT(CBS, cbs); - cbsEnter(cbs); - - AVER(rangeReturn != NULL); - AVERT(Range, range); - AVER(RangeIsAligned(range, cbs->alignment)); - - res = cbsInsertIntoTree(rangeReturn, cbs, range); - - cbsLeave(cbs); - return res; -} - - -/* cbsDeleteFromTree -- delete blocks from the splay tree */ - -static Res cbsDeleteFromTree(Range rangeReturn, CBS cbs, Range range) +static Res cbsDelete(Range rangeReturn, Land land, Range range) { + CBS cbs; Res res; CBSBlock cbsBlock; - SplayNode splayNode; + Tree tree; Addr base, limit, oldBase, oldLimit; Size oldSize; + AVERT(Land, land); + cbs = cbsOfLand(land); AVER(rangeReturn != NULL); - AVERT(CBS, cbs); AVERT(Range, range); - AVER(RangeIsAligned(range, cbs->alignment)); + AVER(RangeIsAligned(range, LandAlignment(land))); base = RangeBase(range); limit = RangeLimit(range); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - res = SplayTreeSearch(&splayNode, splayTreeOfCBS(cbs), (void *)&base); - if (res != ResOK) + METER_ACC(cbs->treeSearch, cbs->treeSize); + if (!SplayTreeFind(&tree, cbsSplay(cbs), keyOfBaseVar(base))) { + res = ResFAIL; goto failSplayTreeSearch; - cbsBlock = cbsBlockOfSplayNode(splayNode); + } + cbsBlock = cbsBlockOfTree(tree); if (limit > cbsBlock->limit) { res = ResFAIL; @@ -570,111 +630,201 @@ static Res cbsDeleteFromTree(Range rangeReturn, CBS cbs, Range range) } -/* CBSDelete -- Remove a range from a CBS - * - * See . - */ - -Res CBSDelete(Range rangeReturn, CBS cbs, Range range) +static Res cbsBlockDescribe(CBSBlock block, mps_lib_FILE *stream) { Res res; - AVERT(CBS, cbs); - cbsEnter(cbs); + if (stream == NULL) + return ResFAIL; - AVER(rangeReturn != NULL); - AVERT(Range, range); - AVER(RangeIsAligned(range, cbs->alignment)); + res = WriteF(stream, 0, + "[$P,$P)", + (WriteFP)block->base, + (WriteFP)block->limit, + NULL); + return res; +} + +static Res cbsSplayNodeDescribe(Tree tree, mps_lib_FILE *stream) +{ + Res res; - res = cbsDeleteFromTree(rangeReturn, cbs, range); + if (tree == TreeEMPTY) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - cbsLeave(cbs); + res = cbsBlockDescribe(cbsBlockOfTree(tree), stream); return res; } - -static Res cbsBlockDescribe(CBSBlock block, mps_lib_FILE *stream) +static Res cbsFastBlockDescribe(CBSFastBlock block, mps_lib_FILE *stream) { Res res; - if (stream == NULL) return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = WriteF(stream, + res = WriteF(stream, 0, "[$P,$P) {$U}", - (WriteFP)block->base, - (WriteFP)block->limit, + (WriteFP)block->cbsBlockStruct.base, + (WriteFP)block->cbsBlockStruct.limit, (WriteFU)block->maxSize, NULL); return res; } -static Res cbsSplayNodeDescribe(SplayNode splayNode, mps_lib_FILE *stream) +static Res cbsFastSplayNodeDescribe(Tree tree, mps_lib_FILE *stream) { Res res; - if (splayNode == NULL) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (tree == TreeEMPTY) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = cbsBlockDescribe(cbsBlockOfSplayNode(splayNode), stream); + res = cbsFastBlockDescribe(cbsFastBlockOfTree(tree), stream); return res; } +static Res cbsZonedBlockDescribe(CBSZonedBlock block, mps_lib_FILE *stream) +{ + Res res; + + if (stream == NULL) + return ResFAIL; -/* CBSIterate -- Iterate all blocks in CBS + res = WriteF(stream, 0, + "[$P,$P) {$U, $B}", + (WriteFP)block->cbsFastBlockStruct.cbsBlockStruct.base, + (WriteFP)block->cbsFastBlockStruct.cbsBlockStruct.limit, + (WriteFU)block->cbsFastBlockStruct.maxSize, + (WriteFB)block->zones, + NULL); + return res; +} + +static Res cbsZonedSplayNodeDescribe(Tree tree, mps_lib_FILE *stream) +{ + Res res; + + if (tree == TreeEMPTY) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = cbsZonedBlockDescribe(cbsZonedBlockOfTree(tree), stream); + return res; +} + + +/* cbsIterate -- iterate over all blocks in CBS * - * This is not necessarily efficient. - * See . + * See . */ -void CBSIterate(CBS cbs, CBSIterateMethod iterate, - void *closureP, Size closureS) +typedef struct CBSIterateClosure { + Land land; + LandVisitor visitor; + void *closureP; +} CBSIterateClosure; + +static Bool cbsIterateVisit(Tree tree, void *closureP, Size closureS) { - SplayNode splayNode; - SplayTree splayTree; - CBSBlock cbsBlock; + CBSIterateClosure *closure = closureP; + Land land = closure->land; + CBSBlock cbsBlock = cbsBlockOfTree(tree); + RangeStruct range; + RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock)); + return (*closure->visitor)(land, &range, closure->closureP, closureS); +} +static Bool cbsIterate(Land land, LandVisitor visitor, + void *closureP, Size closureS) +{ + CBS cbs; + SplayTree splay; + CBSIterateClosure closure; + + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); - cbsEnter(cbs); - AVER(FUNCHECK(iterate)); + AVER(FUNCHECK(visitor)); - splayTree = splayTreeOfCBS(cbs); + splay = cbsSplay(cbs); /* .splay-iterate.slow: We assume that splay tree iteration does */ /* searches and meter it. */ - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - splayNode = SplayTreeFirst(splayTree, NULL); - while(splayNode != NULL) { - RangeStruct range; - cbsBlock = cbsBlockOfSplayNode(splayNode); - RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock)); - if (!(*iterate)(cbs, &range, closureP, closureS)) - break; - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - splayNode = SplayTreeNext(splayTree, splayNode, keyOfCBSBlock(cbsBlock)); - } + METER_ACC(cbs->treeSearch, cbs->treeSize); - cbsLeave(cbs); - return; + closure.land = land; + closure.visitor = visitor; + closure.closureP = closureP; + return TreeTraverse(SplayTreeRoot(splay), splay->compare, splay->nodeKey, + cbsIterateVisit, &closure, closureS); } -/* FindDeleteCheck -- check method for a FindDelete value */ +/* cbsIterateAndDelete -- iterate over all blocks in CBS + * + * See . + */ -Bool FindDeleteCheck(FindDelete findDelete) +typedef struct CBSIterateAndDeleteClosure { + Land land; + LandDeleteVisitor visitor; + Bool cont; + void *closureP; +} CBSIterateAndDeleteClosure; + +static Bool cbsIterateAndDeleteVisit(Tree tree, void *closureP, Size closureS) { - CHECKL(findDelete == FindDeleteNONE - || findDelete == FindDeleteLOW - || findDelete == FindDeleteHIGH - || findDelete == FindDeleteENTIRE); - UNUSED(findDelete); /* */ + CBSIterateAndDeleteClosure *closure = closureP; + Land land = closure->land; + CBS cbs = cbsOfLand(land); + CBSBlock cbsBlock = cbsBlockOfTree(tree); + Bool deleteNode = FALSE; + RangeStruct range; + + RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock)); + if (closure->cont) + closure->cont = (*closure->visitor)(&deleteNode, land, &range, + closure->closureP, closureS); + if (deleteNode) + cbsBlockDestroy(cbs, cbsBlock); + return deleteNode; +} - return TRUE; +static Bool cbsIterateAndDelete(Land land, LandDeleteVisitor visitor, + void *closureP, Size closureS) +{ + CBS cbs; + SplayTree splay; + CBSIterateAndDeleteClosure closure; + + AVERT(Land, land); + cbs = cbsOfLand(land); + AVERT(CBS, cbs); + AVER(FUNCHECK(visitor)); + + splay = cbsSplay(cbs); + /* .splay-iterate.slow: We assume that splay tree iteration does */ + /* searches and meter it. */ + METER_ACC(cbs->treeSearch, cbs->treeSize); + + closure.land = land; + closure.visitor = visitor; + closure.closureP = closureP; + closure.cont = TRUE; + TreeTraverseAndDelete(&splay->root, cbsIterateAndDeleteVisit, + &closure, closureS); + return closure.cont; } /* cbsFindDeleteRange -- delete appropriate range of block found */ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Range range, Size size, + Land land, Range range, Size size, FindDelete findDelete) { Bool callDelete = TRUE; @@ -682,11 +832,11 @@ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVERT(CBS, cbs); + AVERT(Land, land); AVERT(Range, range); - AVER(RangeIsAligned(range, cbs->alignment)); + AVER(RangeIsAligned(range, LandAlignment(land))); AVER(size > 0); - AVER(SizeIsAligned(size, cbs->alignment)); + AVER(SizeIsAligned(size, LandAlignment(land))); AVER(RangeSize(range) >= size); AVERT(FindDelete, findDelete); @@ -720,170 +870,345 @@ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, if (callDelete) { Res res; - res = cbsDeleteFromTree(oldRangeReturn, cbs, rangeReturn); + res = cbsDelete(oldRangeReturn, land, rangeReturn); /* Can't have run out of memory, because all our callers pass in - blocks that were just found in the splay tree, and we only - deleted from one end of the block, so cbsDeleteFromTree did not + blocks that were just found in the tree, and we only + deleted from one end of the block, so cbsDelete did not need to allocate a new block. */ AVER(res == ResOK); + } else { + RangeCopy(oldRangeReturn, rangeReturn); } } /* CBSFindFirst -- find the first block of at least the given size */ -Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete) +static Bool cbsFindFirst(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + CBS cbs; Bool found; - SplayNode node; + Tree tree; + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); - cbsEnter(cbs); + AVER(IsLandSubclass(CBSLand(cbs), CBSFastLandClass)); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVER(size > 0); - AVER(SizeIsAligned(size, cbs->alignment)); - AVER(cbs->fastFind); + AVER(SizeIsAligned(size, LandAlignment(land))); AVERT(FindDelete, findDelete); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - found = SplayFindFirst(&node, splayTreeOfCBS(cbs), &cbsTestNode, + METER_ACC(cbs->treeSearch, cbs->treeSize); + found = SplayFindFirst(&tree, cbsSplay(cbs), &cbsTestNode, &cbsTestTree, NULL, size); if (found) { CBSBlock block; RangeStruct range; - block = cbsBlockOfSplayNode(node); + block = cbsBlockOfTree(tree); AVER(CBSBlockSize(block) >= size); RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); AVER(RangeSize(&range) >= size); - cbsFindDeleteRange(rangeReturn, oldRangeReturn, cbs, &range, + cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range, size, findDelete); } - cbsLeave(cbs); return found; } +/* cbsFindInZones -- find a block of at least the given size that lies + * entirely within a zone set. (The first such block, if high is + * FALSE, or the last, if high is TRUE.) + */ + +typedef struct cbsTestNodeInZonesClosureStruct { + Size size; + Arena arena; + ZoneSet zoneSet; + Addr base; + Addr limit; + Bool high; +} cbsTestNodeInZonesClosureStruct, *cbsTestNodeInZonesClosure; + +static Bool cbsTestNodeInZones(SplayTree splay, Tree tree, + void *closureP, Size closureS) +{ + CBSBlock block = cbsBlockOfTree(tree); + cbsTestNodeInZonesClosure closure = closureP; + RangeInZoneSet search; + + UNUSED(splay); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); + + search = closure->high ? RangeInZoneSetLast : RangeInZoneSetFirst; + + return search(&closure->base, &closure->limit, + CBSBlockBase(block), CBSBlockLimit(block), + closure->arena, closure->zoneSet, closure->size); +} -/* CBSFindLast -- find the last block of at least the given size */ +static Bool cbsTestTreeInZones(SplayTree splay, Tree tree, + void *closureP, Size closureS) +{ + CBSFastBlock fastBlock = cbsFastBlockOfTree(tree); + CBSZonedBlock zonedBlock = cbsZonedBlockOfTree(tree); + cbsTestNodeInZonesClosure closure = closureP; + + UNUSED(splay); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); + + return fastBlock->maxSize >= closure->size + && ZoneSetInter(zonedBlock->zones, closure->zoneSet) != ZoneSetEMPTY; +} + + +/* cbsFindLast -- find the last block of at least the given size */ -Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete) +static Bool cbsFindLast(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + CBS cbs; Bool found; - SplayNode node; + Tree tree; + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); - cbsEnter(cbs); + AVER(IsLandSubclass(CBSLand(cbs), CBSFastLandClass)); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVER(size > 0); - AVER(SizeIsAligned(size, cbs->alignment)); - AVER(cbs->fastFind); + AVER(SizeIsAligned(size, LandAlignment(land))); AVERT(FindDelete, findDelete); - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - found = SplayFindLast(&node, splayTreeOfCBS(cbs), &cbsTestNode, + METER_ACC(cbs->treeSearch, cbs->treeSize); + found = SplayFindLast(&tree, cbsSplay(cbs), &cbsTestNode, &cbsTestTree, NULL, size); if (found) { CBSBlock block; RangeStruct range; - block = cbsBlockOfSplayNode(node); + block = cbsBlockOfTree(tree); AVER(CBSBlockSize(block) >= size); RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); AVER(RangeSize(&range) >= size); - cbsFindDeleteRange(rangeReturn, oldRangeReturn, cbs, &range, + cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range, size, findDelete); } - cbsLeave(cbs); return found; } -/* CBSFindLargest -- find the largest block in the CBS */ +/* cbsFindLargest -- find the largest block in the CBS */ -Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete) +static Bool cbsFindLargest(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + CBS cbs; Bool found = FALSE; - SplayNode root; - Bool notEmpty; + AVERT(Land, land); + cbs = cbsOfLand(land); AVERT(CBS, cbs); - cbsEnter(cbs); + AVER(IsLandSubclass(CBSLand(cbs), CBSFastLandClass)); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVER(cbs->fastFind); + AVER(size > 0); AVERT(FindDelete, findDelete); - notEmpty = SplayRoot(&root, splayTreeOfCBS(cbs)); - if (notEmpty) { + if (!SplayTreeIsEmpty(cbsSplay(cbs))) { RangeStruct range; - CBSBlock block; - SplayNode node = NULL; /* suppress "may be used uninitialized" */ + Tree tree = TreeEMPTY; /* suppress "may be used uninitialized" */ Size maxSize; - maxSize = cbsBlockOfSplayNode(root)->maxSize; + maxSize = cbsFastBlockOfTree(SplayTreeRoot(cbsSplay(cbs)))->maxSize; if (maxSize >= size) { - METER_ACC(cbs->splaySearch, cbs->splayTreeSize); - found = SplayFindFirst(&node, splayTreeOfCBS(cbs), &cbsTestNode, + CBSBlock block; + METER_ACC(cbs->treeSearch, cbs->treeSize); + found = SplayFindFirst(&tree, cbsSplay(cbs), &cbsTestNode, &cbsTestTree, NULL, maxSize); AVER(found); /* maxSize is exact, so we will find it. */ - block = cbsBlockOfSplayNode(node); + block = cbsBlockOfTree(tree); AVER(CBSBlockSize(block) >= maxSize); RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); AVER(RangeSize(&range) >= maxSize); - cbsFindDeleteRange(rangeReturn, oldRangeReturn, cbs, &range, - maxSize, findDelete); + cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range, + size, findDelete); } } - cbsLeave(cbs); return found; } -/* CBSDescribe -- describe a CBS +static Res cbsFindInZones(Bool *foundReturn, Range rangeReturn, + Range oldRangeReturn, Land land, Size size, + ZoneSet zoneSet, Bool high) +{ + CBS cbs; + CBSBlock block; + Tree tree; + cbsTestNodeInZonesClosureStruct closure; + Res res; + LandFindMethod landFind; + SplayFindFunction splayFind; + RangeStruct rangeStruct, oldRangeStruct; + + AVER(foundReturn != NULL); + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + cbs = cbsOfLand(land); + AVERT(CBS, cbs); + AVER(IsLandSubclass(CBSLand(cbs), CBSZonedLandClass)); + /* AVERT(ZoneSet, zoneSet); */ + AVERT(Bool, high); + + landFind = high ? cbsFindLast : cbsFindFirst; + splayFind = high ? SplayFindLast : SplayFindFirst; + + if (zoneSet == ZoneSetEMPTY) + goto fail; + if (zoneSet == ZoneSetUNIV) { + FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; + *foundReturn = (*landFind)(rangeReturn, oldRangeReturn, land, size, fd); + return ResOK; + } + if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) + goto fail; + + /* It would be nice if there were a neat way to eliminate all runs of + zones in zoneSet too small for size.*/ + + closure.arena = LandArena(land); + closure.zoneSet = zoneSet; + closure.size = size; + closure.high = high; + if (!(*splayFind)(&tree, cbsSplay(cbs), + cbsTestNodeInZones, cbsTestTreeInZones, + &closure, UNUSED_SIZE)) + goto fail; + + block = cbsBlockOfTree(tree); + + AVER(CBSBlockBase(block) <= closure.base); + AVER(AddrOffset(closure.base, closure.limit) >= size); + AVER(ZoneSetSub(ZoneSetOfRange(LandArena(land), closure.base, closure.limit), zoneSet)); + AVER(closure.limit <= CBSBlockLimit(block)); + + if (!high) + RangeInit(&rangeStruct, closure.base, AddrAdd(closure.base, size)); + else + RangeInit(&rangeStruct, AddrSub(closure.limit, size), closure.limit); + res = cbsDelete(&oldRangeStruct, land, &rangeStruct); + if (res != ResOK) + /* not enough memory to split block */ + return res; + RangeCopy(rangeReturn, &rangeStruct); + RangeCopy(oldRangeReturn, &oldRangeStruct); + *foundReturn = TRUE; + return ResOK; + +fail: + *foundReturn = FALSE; + return ResOK; +} + + +/* cbsDescribe -- describe a CBS * - * See . + * See . */ -Res CBSDescribe(CBS cbs, mps_lib_FILE *stream) +static Res cbsDescribe(Land land, mps_lib_FILE *stream, Count depth) { + CBS cbs; Res res; + Res (*describe)(Tree, mps_lib_FILE *); - if (!TESTT(CBS, cbs)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Land, land)) + return ResFAIL; + cbs = cbsOfLand(land); + if (!TESTT(CBS, cbs)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = WriteF(stream, + res = WriteF(stream, depth, "CBS $P {\n", (WriteFP)cbs, - " alignment: $U\n", (WriteFU)cbs->alignment, - " blockPool: $P\n", (WriteFP)cbs->blockPool, - " fastFind: $U\n", (WriteFU)cbs->fastFind, - " inCBS: $U\n", (WriteFU)cbs->inCBS, - " splayTreeSize: $U\n", (WriteFU)cbs->splayTreeSize, + " blockPool: $P\n", (WriteFP)cbsBlockPool(cbs), + " ownPool: $U\n", (WriteFU)cbs->ownPool, + " treeSize: $U\n", (WriteFU)cbs->treeSize, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; - res = SplayTreeDescribe(splayTreeOfCBS(cbs), stream, &cbsSplayNodeDescribe); - if (res != ResOK) return res; + METER_WRITE(cbs->treeSearch, stream, depth + 2); - res = METER_WRITE(cbs->splaySearch, stream); - if (res != ResOK) return res; + if (IsLandSubclass(land, CBSZonedLandClass)) + describe = cbsZonedSplayNodeDescribe; + else if (IsLandSubclass(land, CBSFastLandClass)) + describe = cbsFastSplayNodeDescribe; + else + describe = cbsSplayNodeDescribe; - res = WriteF(stream, "}\n", NULL); + res = SplayTreeDescribe(cbsSplay(cbs), stream, depth + 2, describe); + if (res != ResOK) + return res; + + res = WriteF(stream, depth, "} CBS $P\n", (WriteFP)cbs, NULL); + + res = WriteF(stream, 0, "}\n", NULL); return res; } +DEFINE_LAND_CLASS(CBSLandClass, class) +{ + INHERIT_CLASS(class, LandClass); + class->name = "CBS"; + class->size = sizeof(CBSStruct); + class->init = cbsInit; + class->finish = cbsFinish; + class->sizeMethod = cbsSize; + class->insert = cbsInsert; + class->delete = cbsDelete; + class->iterate = cbsIterate; + class->iterateAndDelete = cbsIterateAndDelete; + class->findFirst = cbsFindFirst; + class->findLast = cbsFindLast; + class->findLargest = cbsFindLargest; + class->findInZones = cbsFindInZones; + class->describe = cbsDescribe; + AVERT(LandClass, class); +} + +DEFINE_LAND_CLASS(CBSFastLandClass, class) +{ + INHERIT_CLASS(class, CBSLandClass); + class->name = "FASTCBS"; + class->init = cbsInitFast; + AVERT(LandClass, class); +} + +DEFINE_LAND_CLASS(CBSZonedLandClass, class) +{ + INHERIT_CLASS(class, CBSFastLandClass); + class->name = "ZONEDCBS"; + class->init = cbsInitZoned; + AVERT(LandClass, class); +} + /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/cbs.h b/code/cbs.h index aba66cf5c1..a1496b3f77 100644 --- a/code/cbs.h +++ b/code/cbs.h @@ -10,51 +10,42 @@ #define cbs_h #include "arg.h" -#include "meter.h" #include "mpmtypes.h" +#include "mpmst.h" #include "range.h" #include "splay.h" +typedef struct CBSBlockStruct *CBSBlock; +typedef struct CBSBlockStruct { + TreeStruct treeStruct; + Addr base; + Addr limit; +} CBSBlockStruct; + +typedef struct CBSFastBlockStruct *CBSFastBlock; +typedef struct CBSFastBlockStruct { + struct CBSBlockStruct cbsBlockStruct; + Size maxSize; /* accurate maximum block size of sub-tree */ +} CBSFastBlockStruct; + +typedef struct CBSZonedBlockStruct *CBSZonedBlock; +typedef struct CBSZonedBlockStruct { + struct CBSFastBlockStruct cbsFastBlockStruct; + ZoneSet zones; /* union zone set of all ranges in sub-tree */ +} CBSZonedBlockStruct; typedef struct CBSStruct *CBS; -typedef Bool (*CBSIterateMethod)(CBS cbs, Range range, - void *closureP, Size closureS); - - -#define CBSSig ((Sig)0x519CB599) /* SIGnature CBS */ - -typedef struct CBSStruct { - SplayTreeStruct splayTree; - Count splayTreeSize; - Pool blockPool; - Align alignment; - Bool fastFind; - Bool inCBS; /* prevent reentrance */ - /* meters for sizes of search structures at each op */ - METER_DECL(splaySearch); - Sig sig; /* sig at end because embeded */ -} CBSStruct; extern Bool CBSCheck(CBS cbs); +#define CBSLand(cbs) (&(cbs)->landStruct) -extern Res CBSInit(Arena arena, CBS cbs, void *owner, - Align alignment, Bool fastFind, ArgList args); -extern void CBSFinish(CBS cbs); - -extern Res CBSInsert(Range rangeReturn, CBS cbs, Range range); -extern Res CBSDelete(Range rangeReturn, CBS cbs, Range range); -extern void CBSIterate(CBS cbs, CBSIterateMethod iterate, - void *closureP, Size closureS); - -extern Res CBSDescribe(CBS cbs, mps_lib_FILE *stream); - -extern Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete); -extern Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete); -extern Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn, - CBS cbs, Size size, FindDelete findDelete); +extern LandClass CBSLandClassGet(void); +extern LandClass CBSFastLandClassGet(void); +extern LandClass CBSZonedLandClassGet(void); +extern const struct mps_key_s _mps_key_cbs_block_pool; +#define CBSBlockPool (&_mps_key_cbs_block_pool) +#define CBSBlockPool_FIELD pool #endif /* cbs_h */ diff --git a/code/chain.h b/code/chain.h index e47f8000c0..6dddf0f5e2 100644 --- a/code/chain.h +++ b/code/chain.h @@ -1,7 +1,7 @@ /* chain.h: GENERATION CHAINS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef chain_h @@ -14,6 +14,8 @@ /* GenParamStruct -- structure for specifying generation parameters */ /* .gen-param: This structure must match . */ +typedef struct GenParamStruct *GenParam; + typedef struct GenParamStruct { Size capacity; /* capacity in kB */ double mortality; @@ -31,7 +33,6 @@ typedef struct GenDescStruct { ZoneSet zones; /* zoneset for this generation */ Size capacity; /* capacity in kB */ double mortality; - double proflow; /* predicted proportion of survivors promoted */ RingStruct locusRing; /* Ring of all PoolGen's in this GenDesc (locus) */ } GenDescStruct; @@ -44,19 +45,19 @@ typedef struct PoolGenStruct *PoolGen; typedef struct PoolGenStruct { Sig sig; - Serial nr; /* generation number */ Pool pool; /* pool this belongs to */ - Chain chain; /* chain this belongs to */ + GenDesc gen; /* generation this belongs to */ /* link in ring of all PoolGen's in this GenDesc (locus) */ RingStruct genRing; - Size totalSize; /* total size of segs in gen in this pool */ - Size newSize; /* size allocated since last GC */ - /* newSize when TraceCreate was called. This is used in the - * TraceStartPoolGen event emitted at the start of a trace; at that - * time, newSize has already been diminished by Whiten so we can't - * use that value. TODO: This will not work well with multiple - * traces. */ - Size newSizeAtCreate; + + /* Accounting of memory in this generation for this pool */ + STATISTIC_DECL(Size segs); /* number of segments */ + Size totalSize; /* total (sum of segment sizes) */ + STATISTIC_DECL(Size freeSize); /* unused (free or lost to fragmentation) */ + Size newSize; /* allocated since last collection */ + STATISTIC_DECL(Size oldSize); /* allocated prior to last collection */ + Size newDeferredSize; /* new (but deferred) */ + STATISTIC_DECL(Size oldDeferredSize); /* old (but deferred) */ } PoolGenStruct; @@ -70,38 +71,49 @@ typedef struct mps_chain_s { RingStruct chainRing; /* list of chains in the arena */ TraceSet activeTraces; /* set of traces collecting this chain */ size_t genCount; /* number of generations */ - GenDescStruct *gens; /* the array of generations */ + GenDesc gens; /* the array of generations */ } ChainStruct; +extern Bool GenDescCheck(GenDesc gen); +extern Size GenDescNewSize(GenDesc gen); +extern Size GenDescTotalSize(GenDesc gen); +extern Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth); + extern Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount, - GenParamStruct *params); + GenParam params); extern void ChainDestroy(Chain chain); extern Bool ChainCheck(Chain chain); extern double ChainDeferral(Chain chain); -extern Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace); -extern Res ChainCondemnAll(Chain chain, Trace trace); extern void ChainStartGC(Chain chain, Trace trace); extern void ChainEndGC(Chain chain, Trace trace); extern size_t ChainGens(Chain chain); -extern Res ChainAlloc(Seg *segReturn, Chain chain, Serial genNr, - SegClass class, Size size, Pool pool, - Bool withReservoirPermit, ArgList args); - -extern Bool PoolGenCheck(PoolGen gen); -extern Res PoolGenInit(PoolGen gen, Chain chain, Serial nr, Pool pool); -extern void PoolGenFinish(PoolGen gen); -extern void PoolGenFlip(PoolGen gen); -#define PoolGenNr(gen) ((gen)->nr) - +extern GenDesc ChainGen(Chain chain, Index gen); +extern Res ChainDescribe(Chain chain, mps_lib_FILE *stream, Count depth); + +extern Bool PoolGenCheck(PoolGen pgen); +extern Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool); +extern void PoolGenFinish(PoolGen pgen); +extern Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class, + Size size, Bool withReservoirPermit, ArgList args); +extern void PoolGenFree(PoolGen pgen, Seg seg, Size freeSize, Size oldSize, + Size newSize, Bool deferred); +extern void PoolGenAccountForFill(PoolGen pgen, Size size, Bool deferred); +extern void PoolGenAccountForEmpty(PoolGen pgen, Size unused, Bool deferred); +extern void PoolGenAccountForAge(PoolGen pgen, Size aged, Bool deferred); +extern void PoolGenAccountForReclaim(PoolGen pgen, Size reclaimed, Bool deferred); +extern void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize); +extern void PoolGenAccountForSegSplit(PoolGen pgen); +extern void PoolGenAccountForSegMerge(PoolGen pgen); +extern Res PoolGenDescribe(PoolGen gen, mps_lib_FILE *stream, Count depth); #endif /* chain_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/check.h b/code/check.h index d4abc0fd80..a2450bebd9 100644 --- a/code/check.h +++ b/code/check.h @@ -1,7 +1,7 @@ /* check.h: ASSERTION INTERFACE * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .aver: This header defines a family of AVER and NOTREACHED macros. @@ -52,7 +52,7 @@ #define ASSERT(cond, condstring) \ BEGIN \ if (cond) NOOP; else \ - mps_lib_assert_fail(__FILE__ , __LINE__, (condstring)); \ + mps_lib_assert_fail(MPS_FILE, __LINE__, (condstring)); \ END #define ASSERT_TYPECHECK(type, val) \ @@ -85,9 +85,6 @@ * * TODO: Should also allow the check level variable to come from an * environment variable. - * - * TODO: CheckLevelDEEP asserts on arena creation with bootstrapping - * problems. It clearly hasn't been tried for a while. RB 2012-09-01 */ enum { @@ -284,10 +281,10 @@ extern unsigned CheckLevel; /* COMPAT* -- type compatibility checking * * .check.macros: The COMPAT* macros use some C trickery to attempt to - * verify that certain types and fields are equivalent. They do not - * do a complete job. This trickery is justified by the security gained - * in knowing that matches the MPM. See also - * mail.richard.1996-08-07.09-49. [This paragraph is intended to + * verify that certain types and fields are equivalent. They do not do + * a complete job. This trickery is justified by the security gained + * in knowing that matches the MPM. See + * . [This paragraph is intended to * satisfy rule.impl.trick.] */ @@ -327,7 +324,7 @@ extern unsigned CheckLevel; /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/clock.h b/code/clock.h index 5e103ff439..3bf2a6deed 100644 --- a/code/clock.h +++ b/code/clock.h @@ -1,13 +1,12 @@ /* clock.h -- Fast clocks and timers * - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * $Id$ */ #ifndef clock_h #define clock_h -#include #include "mpmtypes.h" /* for Word */ @@ -15,10 +14,6 @@ * * On platforms that support it, we want to stamp events with a very cheap * and fast high-resolution timer. - * - * TODO: This is a sufficiently complicated nest of ifdefs that it should - * be quarantined in its own header with KEEP OUT signs attached. - * RB 2012-09-11 */ /* Microsoft C provides an intrinsic for the Intel rdtsc instruction. @@ -34,6 +29,12 @@ typedef union EventClockUnion { unsigned __int64 whole; } EventClockUnion; +#define EVENT_CLOCK_MAKE(lvalue, low, high) \ + BEGIN \ + ((EventClockUnion*)&(lvalue))->half.low = (low); \ + ((EventClockUnion*)&(lvalue))->half.high = (high); \ + END + #if _MSC_VER >= 1400 #pragma intrinsic(__rdtsc) @@ -49,7 +50,7 @@ typedef union EventClockUnion { using Microsoft Visual Studio 6 because of support for CodeView debugging information. */ -#include /* KILL IT WITH FIRE! */ +#include "mpswin.h" /* KILL IT WITH FIRE! */ #define EVENT_CLOCK(lvalue) \ BEGIN \ @@ -70,8 +71,8 @@ typedef union EventClockUnion { (*(EventClockUnion *)&(clock)).half.high, \ (*(EventClockUnion *)&(clock)).half.low) -#define EVENT_CLOCK_WRITE(stream, clock) \ - WriteF(stream, "$W$W", \ +#define EVENT_CLOCK_WRITE(stream, depth, clock) \ + WriteF(stream, depth, "$W$W", \ (*(EventClockUnion *)&(clock)).half.high, \ (*(EventClockUnion *)&(clock)).half.low, \ NULL) @@ -90,8 +91,8 @@ typedef union EventClockUnion { #endif -#define EVENT_CLOCK_WRITE(stream, clock) \ - WriteF(stream, "$W", (WriteFW)(clock), NULL) +#define EVENT_CLOCK_WRITE(stream, depth, clock) \ + WriteF(stream, depth, "$W", (WriteFW)(clock), NULL) #endif @@ -106,6 +107,9 @@ typedef union EventClockUnion { GCC or Clang. */ __extension__ typedef unsigned long long EventClock; +#define EVENT_CLOCK_MAKE(lvalue, low, high) \ + ((lvalue) = ((EventClock)(high) << 32) + ((EventClock)(low) & (0xfffffffful))) + /* Clang provides a cross-platform builtin for a fast timer, but it was not available on Mac OS X 10.8 until the release of XCode 4.6. */ @@ -140,8 +144,8 @@ __extension__ typedef unsigned long long EventClock; (unsigned long)((clock) >> 32), \ (unsigned long)((clock) & 0xffffffff)) -#define EVENT_CLOCK_WRITE(stream, clock) \ - WriteF(stream, "$W$W", (WriteFW)((clock) >> 32), (WriteFW)clock, NULL) +#define EVENT_CLOCK_WRITE(stream, depth, clock) \ + WriteF(stream, depth, "$W$W", (WriteFW)((clock) >> 32), (WriteFW)clock, NULL) #endif /* Intel, GCC or Clang */ @@ -150,6 +154,9 @@ __extension__ typedef unsigned long long EventClock; typedef mps_clock_t EventClock; +#define EVENT_CLOCK_MAKE(lvalue, low, high) \ + ((lvalue) = ((EventClock)(high) << 32) + ((EventClock)(low) & (0xfffffffful))) + #define EVENT_CLOCK(lvalue) \ BEGIN \ (lvalue) = mps_clock(); \ @@ -158,8 +165,8 @@ typedef mps_clock_t EventClock; #define EVENT_CLOCK_PRINT(stream, clock) \ fprintf(stream, "%lu", (unsigned long)clock) -#define EVENT_CLOCK_WRITE(stream, clock) \ - WriteF(stream, "$W", (WriteFW)clock, NULL) +#define EVENT_CLOCK_WRITE(stream, depth, clock) \ + WriteF(stream, depth, "$W", (WriteFW)clock, NULL) #endif @@ -169,7 +176,7 @@ typedef mps_clock_t EventClock; /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/comm.gmk b/code/comm.gmk index 1fca1af7b0..dbd11f70d3 100644 --- a/code/comm.gmk +++ b/code/comm.gmk @@ -3,7 +3,7 @@ # comm.gmk: COMMON GNUMAKEFILE FRAGMENT # # $Id$ -# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. # # DESCRIPTION # @@ -15,8 +15,8 @@ # Assumes the following variables and definitions: # EXTRA_TARGETS a list of extra targets to build # CFLAGSCOMPILER a list of flags for all compilations -# CFLAGSSTRICT a list of flags for almost all compilations -# CFLAGSLAX a list of flags for compilations which can't be as +# CFLAGSCOMPILERSTRICT a list of flags for almost all compilations +# CFLAGSCOMPILERLAX a list of flags for compilations which can't be as # strict (e.g. because they have to include a third- # party header file that isn't -ansi -pedantic). # CFLAGSDEBUG a list of flags for compilations with maximum debug @@ -36,7 +36,6 @@ # NOISY if defined and non-empty, causes commands to be emitted # MPMPF platform-dependent C sources for the "mpm" part # MPMS assembler sources for the "mpm" part (.s files) -# MPMPS pre-processor assembler sources for the "mpm" part (.S files) # # %%PART: When adding a new part, add a new parameter above for the # files included in the part. @@ -108,31 +107,28 @@ endif # These flags are included in all compilations. # Avoid using PFMDEFS in platform makefiles, as they prevent the MPS being # built with a simple command like "cc -c mps.c". -CFLAGSCOMMON = $(PFMDEFS) $(CFLAGSCOMPILER) $(CFLAGSCOMPILERSTRICT) +CFLAGSCOMMONSTRICT = $(PFMDEFS) $(CFLAGSCOMPILER) $(CFLAGSCOMPILERSTRICT) CFLAGSCOMMONLAX = $(PFMDEFS) $(CFLAGSCOMPILER) $(CFLAGSCOMPILERLAX) # %%VARIETY: When adding a new variety, define a macro containing the set # of flags for the new variety. # These flags are added to compilations for the indicated variety. -CFRASH = -DCONFIG_VAR_RASH -DNDEBUG $(CFLAGSOPT) -CFHOT = -DCONFIG_VAR_HOT -DNDEBUG $(CFLAGSOPT) -CFCOOL = -DCONFIG_VAR_COOL $(CFLAGSDEBUG) +CFRASH = -DCONFIG_VAR_RASH $(CFLAGSOPT) +CFHOT = -DCONFIG_VAR_HOT $(CFLAGSOPT) +CFCOOL = -DCONFIG_VAR_COOL $(CFLAGSDEBUG) -# Bind CFLAGS to the appropriate set of flags for the variety. -# %%VARIETY: When adding a new variety, add a test for the variety and set -# CFLAGS here. +# Bind CFLAGSVARIETY to the appropriate set of flags for the variety. +# %%VARIETY: When adding a new variety, add a test for the variety and +# set CFLAGSVARIETY here. ifeq ($(VARIETY),rash) -CFLAGS=$(CFLAGSCOMMON) $(CFRASH) -CFLAGSLAX=$(CFLAGSCOMMONLAX) $(CFRASH) +CFLAGSVARIETY=$(CFRASH) else ifeq ($(VARIETY),hot) -CFLAGS=$(CFLAGSCOMMON) $(CFHOT) -CFLAGSLAX=$(CFLAGSCOMMONLAX) $(CFHOT) +CFLAGSVARIETY=$(CFHOT) else ifeq ($(VARIETY),cool) -CFLAGS=$(CFLAGSCOMMON) $(CFCOOL) -CFLAGSLAX=$(CFLAGSCOMMONLAX) $(CFCOOL) +CFLAGSVARIETY=$(CFCOOL) else ifneq ($(VARIETY),) $(error Variety "$(VARIETY)" not recognized: must be rash/hot/cool) @@ -141,7 +137,8 @@ endif endif endif - +CFLAGSSTRICT=$(CFLAGSCOMMONSTRICT) $(CFLAGSVARIETY) $(CFLAGS) +CFLAGSLAX=$(CFLAGSCOMMONLAX) $(CFLAGSVARIETY) $(CFLAGS) ARFLAGS=rc$(ARFLAGSPFM) @@ -159,18 +156,65 @@ POOLN = pooln.c MV2 = poolmv2.c MVFF = poolmvff.c TESTLIB = testlib.c +TESTTHR = testthrix.c FMTDY = fmtdy.c fmtno.c FMTDYTST = fmtdy.c fmtno.c fmtdytst.c FMTHETST = fmthe.c fmtdy.c fmtno.c fmtdytst.c +FMTSCM = fmtscheme.c PLINTH = mpsliban.c mpsioan.c -EVENTPROC = eventcnv.c table.c -MPMCOMMON = abq.c arena.c arenacl.c arenavm.c arg.c boot.c bt.c \ - buffer.c cbs.c dbgpool.c dbgpooli.c event.c format.c \ - freelist.c global.c ld.c locus.c message.c meter.c mpm.c mpsi.c \ - pool.c poolabs.c poolmfs.c poolmrg.c poolmv.c protocol.c range.c \ - ref.c reserv.c ring.c root.c sa.c sac.c seg.c shield.c splay.c ss.c \ - table.c trace.c traceanc.c tract.c walk.c -MPM = $(MPMCOMMON) $(MPMPF) +MPMCOMMON = \ + abq.c \ + arena.c \ + arenacl.c \ + arenavm.c \ + arg.c \ + boot.c \ + bt.c \ + buffer.c \ + cbs.c \ + dbgpool.c \ + dbgpooli.c \ + event.c \ + failover.c \ + format.c \ + freelist.c \ + global.c \ + land.c \ + ld.c \ + locus.c \ + message.c \ + meter.c \ + mpm.c \ + mpsi.c \ + nailboard.c \ + policy.c \ + pool.c \ + poolabs.c \ + poolmfs.c \ + poolmrg.c \ + poolmv.c \ + protocol.c \ + range.c \ + ref.c \ + reserv.c \ + ring.c \ + root.c \ + sa.c \ + sac.c \ + seg.c \ + shield.c \ + splay.c \ + ss.c \ + table.c \ + trace.c \ + traceanc.c \ + tract.c \ + tree.c \ + version.c \ + vm.c \ + walk.c +POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC) +MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH) # These map the source file lists onto object files and dependency files @@ -182,35 +226,14 @@ MPM = $(MPMCOMMON) $(MPMPF) ifdef VARIETY MPMOBJ = $(MPM:%.c=$(PFM)/$(VARIETY)/%.o) \ $(MPMS:%.s=$(PFM)/$(VARIETY)/%.o) -MPMDEP = $(MPM:%.c=$(PFM)/$(VARIETY)/%.d) -AMCOBJ = $(AMC:%.c=$(PFM)/$(VARIETY)/%.o) -AMCDEP = $(AMC:%.c=$(PFM)/$(VARIETY)/%.d) -AMSOBJ = $(AMS:%.c=$(PFM)/$(VARIETY)/%.o) -AMSDEP = $(AMS:%.c=$(PFM)/$(VARIETY)/%.d) -AWLOBJ = $(AWL:%.c=$(PFM)/$(VARIETY)/%.o) -AWLDEP = $(AWL:%.c=$(PFM)/$(VARIETY)/%.d) -LOOBJ = $(LO:%.c=$(PFM)/$(VARIETY)/%.o) -LODEP = $(LO:%.c=$(PFM)/$(VARIETY)/%.d) -SNCOBJ = $(SNC:%.c=$(PFM)/$(VARIETY)/%.o) -SNCDEP = $(SNC:%.c=$(PFM)/$(VARIETY)/%.d) -POOLNOBJ = $(POOLN:%.c=$(PFM)/$(VARIETY)/%.o) -POOLNDEP = $(POOLN:%.c=$(PFM)/$(VARIETY)/%.d) -MV2OBJ = $(MV2:%.c=$(PFM)/$(VARIETY)/%.o) -MV2DEP = $(MV2:%.c=$(PFM)/$(VARIETY)/%.d) -MVFFOBJ = $(MVFF:%.c=$(PFM)/$(VARIETY)/%.o) -MVFFDEP = $(MVFF:%.c=$(PFM)/$(VARIETY)/%.d) - -TESTLIBOBJ = $(TESTLIB:%.c=$(PFM)/$(VARIETY)/%.o) -TESTLIBDEP = $(TESTLIB:%.c=$(PFM)/$(VARIETY)/%.d) FMTDYOBJ = $(FMTDY:%.c=$(PFM)/$(VARIETY)/%.o) -FMTDYDEP = $(FMTDY:%.c=$(PFM)/$(VARIETY)/%.d) FMTDYTSTOBJ = $(FMTDYTST:%.c=$(PFM)/$(VARIETY)/%.o) FMTHETSTOBJ = $(FMTHETST:%.c=$(PFM)/$(VARIETY)/%.o) -FMTHETSTDEP = $(FMTHETST:%.c=$(PFM)/$(VARIETY)/%.d) +FMTSCMOBJ = $(FMTSCM:%.c=$(PFM)/$(VARIETY)/%.o) PLINTHOBJ = $(PLINTH:%.c=$(PFM)/$(VARIETY)/%.o) -PLINTHDEP = $(PLINTH:%.c=$(PFM)/$(VARIETY)/%.d) -EVENTPROCOBJ = $(EVENTPROC:%.c=$(PFM)/$(VARIETY)/%.o) -EVENTPROCDEP = $(EVENTPROC:%.c=$(PFM)/$(VARIETY)/%.d) +POOLNOBJ = $(POOLN:%.c=$(PFM)/$(VARIETY)/%.o) +TESTLIBOBJ = $(TESTLIB:%.c=$(PFM)/$(VARIETY)/%.o) +TESTTHROBJ = $(TESTTHR:%.c=$(PFM)/$(VARIETY)/%.o) endif @@ -221,30 +244,58 @@ endif LIB_TARGETS=mps.a mpsplan.a -# If it is suitable for running regularly (for example, after every -# build) as an automated test case, add it to AUTO_TEST_TARGETS. - -AUTO_TEST_TARGETS=abqtest amcss amcsshe amcssth amsss amssshe apss \ - arenacv awlut awluthe awlutth btcv exposet0 expt825 fbmtest finalcv \ - finaltest fotest lockcov locv messtest mpmss mpsicv mv2test \ - poolncv qs sacss segsmss steptest walkt0 zmess - -# If it is not runnable as an automated test case, but is buildable, -# add it to OTHER_TEST_TARGETS with a note. -# -# bttest and teletest -- interactive and so cannot be run unattended. -# zcoll -- takes too long to be useful as a regularly run smoke test. - -OTHER_TEST_TARGETS=bttest teletest zcoll +# Test executables go in TEST_TARGETS. + +TEST_TARGETS=\ + abqtest \ + airtest \ + amcss \ + amcsshe \ + amcssth \ + amsss \ + amssshe \ + apss \ + arenacv \ + awlut \ + awluthe \ + awlutth \ + btcv \ + bttest \ + djbench \ + exposet0 \ + expt825 \ + finalcv \ + finaltest \ + fotest \ + gcbench \ + landtest \ + locbwcss \ + lockcov \ + lockut \ + locusss \ + locv \ + messtest \ + mpmss \ + mpsicv \ + mv2test \ + nailboardtest \ + poolncv \ + qs \ + sacss \ + segsmss \ + steptest \ + teletest \ + walkt0 \ + zcoll \ + zmess # This target records programs that we were once able to build but # can't at the moment: -# -# replay -- depends on the EPVM pool. -UNBUILDABLE_TARGETS=replay +UNBUILDABLE_TARGETS=\ + replay # depends on the EPVM pool -ALL_TARGETS=$(LIB_TARGETS) $(AUTO_TEST_TARGETS) $(OTHER_TEST_TARGETS) $(EXTRA_TARGETS) +ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS) # == Pseudo-targets == @@ -252,15 +303,24 @@ ALL_TARGETS=$(LIB_TARGETS) $(AUTO_TEST_TARGETS) $(OTHER_TEST_TARGETS) $(EXTRA_TA all: $(ALL_TARGETS) -# Run the automated tests. +# == Automated test suites == +# +# testrun = "smoke test", fast enough to run before every commit +# testci = continuous integration tests, must be known good +# testall = all test cases, for ensuring quality of a release +# testansi = tests that run on the generic ("ANSI") platform +# testpollnone = tests that run on the generic platform with CONFIG_POLL_NONE + +TEST_SUITES=testrun testci testall testansi testpollnone + +$(addprefix $(PFM)/$(VARIETY)/,$(TEST_SUITES)): $(TEST_TARGETS) + ../tool/testrun.sh -s "$(notdir $@)" "$(PFM)/$(VARIETY)" -testrun: $(AUTO_TEST_TARGETS) - ../tool/testrun.sh $(addprefix $(PFM)/$(VARIETY)/,$(AUTO_TEST_TARGETS)) # These convenience targets allow one to type "make foo" to build target # foo in selected varieties (or none, for the latter rule). -$(ALL_TARGETS): phony +$(ALL_TARGETS) $(TEST_SUITES): phony ifdef VARIETY $(MAKE) -f $(PFM).gmk TARGET=$@ variety else @@ -275,17 +335,25 @@ clean: phony $(ECHO) "$(PFM): $@" rm -rf "$(PFM)" -# "target" builds some varieties of the target named in the TARGET macro. +# "target" builds some varieties of the target named in the TARGET +# macro. +# # %%VARIETY: When adding a new target, optionally add a recursive make call # for the new variety, if it should be built by default. It probably # shouldn't without a product design decision and an update of the readme # and build manual! +# +# Note that we build VARIETY=cool before VARIETY=hot because +# the former doesn't need to optimize and so detects errors more +# quickly; and because the former uses file-at-a-time compilation and +# so can pick up where it left off instead of having to start from the +# beginning of mps.c ifdef TARGET ifndef VARIETY target: phony - $(MAKE) -f $(PFM).gmk VARIETY=hot variety $(MAKE) -f $(PFM).gmk VARIETY=cool variety + $(MAKE) -f $(PFM).gmk VARIETY=hot variety endif endif @@ -321,10 +389,7 @@ endif $(PFM)/rash/mps.a: $(PFM)/rash/mps.o $(PFM)/hot/mps.a: $(PFM)/hot/mps.o - -$(PFM)/cool/mps.a: \ - $(MPMOBJ) $(AMCOBJ) $(AMSOBJ) $(AWLOBJ) $(LOOBJ) $(SNCOBJ) \ - $(MV2OBJ) $(MVFFOBJ) $(PLINTHOBJ) $(POOLNOBJ) +$(PFM)/cool/mps.a: $(MPMOBJ) # OTHER GENUINE TARGETS @@ -341,6 +406,9 @@ ifdef VARIETY $(PFM)/$(VARIETY)/abqtest: $(PFM)/$(VARIETY)/abqtest.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/airtest: $(PFM)/$(VARIETY)/airtest.o \ + $(FMTSCMOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/amcss: $(PFM)/$(VARIETY)/amcss.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a @@ -348,7 +416,7 @@ $(PFM)/$(VARIETY)/amcsshe: $(PFM)/$(VARIETY)/amcsshe.o \ $(FMTHETSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a $(PFM)/$(VARIETY)/amcssth: $(PFM)/$(VARIETY)/amcssth.o \ - $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a $(PFM)/$(VARIETY)/amsss: $(PFM)/$(VARIETY)/amsss.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a @@ -369,7 +437,7 @@ $(PFM)/$(VARIETY)/awluthe: $(PFM)/$(VARIETY)/awluthe.o \ $(FMTHETSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a $(PFM)/$(VARIETY)/awlutth: $(PFM)/$(VARIETY)/awlutth.o \ - $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a $(PFM)/$(VARIETY)/btcv: $(PFM)/$(VARIETY)/btcv.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a @@ -377,15 +445,15 @@ $(PFM)/$(VARIETY)/btcv: $(PFM)/$(VARIETY)/btcv.o \ $(PFM)/$(VARIETY)/bttest: $(PFM)/$(VARIETY)/bttest.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/djbench: $(PFM)/$(VARIETY)/djbench.o \ + $(TESTLIBOBJ) $(TESTTHROBJ) + $(PFM)/$(VARIETY)/exposet0: $(PFM)/$(VARIETY)/exposet0.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a $(PFM)/$(VARIETY)/expt825: $(PFM)/$(VARIETY)/expt825.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a -$(PFM)/$(VARIETY)/fbmtest: $(PFM)/$(VARIETY)/fbmtest.o \ - $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a - $(PFM)/$(VARIETY)/finalcv: $(PFM)/$(VARIETY)/finalcv.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a @@ -395,9 +463,24 @@ $(PFM)/$(VARIETY)/finaltest: $(PFM)/$(VARIETY)/finaltest.o \ $(PFM)/$(VARIETY)/fotest: $(PFM)/$(VARIETY)/fotest.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/gcbench: $(PFM)/$(VARIETY)/gcbench.o \ + $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) + +$(PFM)/$(VARIETY)/landtest: $(PFM)/$(VARIETY)/landtest.o \ + $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + +$(PFM)/$(VARIETY)/locbwcss: $(PFM)/$(VARIETY)/locbwcss.o \ + $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/lockcov: $(PFM)/$(VARIETY)/lockcov.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/lockut: $(PFM)/$(VARIETY)/lockut.o \ + $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a + +$(PFM)/$(VARIETY)/locusss: $(PFM)/$(VARIETY)/locusss.o \ + $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/locv: $(PFM)/$(VARIETY)/locv.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a @@ -413,9 +496,12 @@ $(PFM)/$(VARIETY)/mpsicv: $(PFM)/$(VARIETY)/mpsicv.o \ $(PFM)/$(VARIETY)/mv2test: $(PFM)/$(VARIETY)/mv2test.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a -$(PFM)/$(VARIETY)/poolncv: $(PFM)/$(VARIETY)/poolncv.o \ +$(PFM)/$(VARIETY)/nailboardtest: $(PFM)/$(VARIETY)/nailboardtest.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/poolncv: $(PFM)/$(VARIETY)/poolncv.o \ + $(POOLNOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/qs: $(PFM)/$(VARIETY)/qs.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a @@ -465,11 +551,11 @@ endif # Object files -define run-cc +define run-cc-strict $(ECHO) "$(PFM): $@" mkdir -p $(PFM) mkdir -p $(PFM)/$(VARIETY) -$(CC) $(CFLAGS) -c -o $@ $< +$(CC) $(CFLAGSSTRICT) -c -o $@ $< endef define run-cc-lax @@ -481,16 +567,16 @@ endef # .rule.c-to-o: $(PFM)/$(VARIETY)/%.o: %.c - $(run-cc) + $(run-cc-strict) $(PFM)/$(VARIETY)/eventsql.o: eventsql.c $(run-cc-lax) $(PFM)/$(VARIETY)/%.o: %.s - $(run-cc) + $(run-cc-strict) $(PFM)/$(VARIETY)/%.o: %.S - $(run-cc) + $(run-cc-strict) # Dependencies # @@ -517,18 +603,28 @@ else ifeq ($(VARIETY),hot) include $(PFM)/$(VARIETY)/mps.d else -# %%PART: When adding a new part, add the dependency file macro for the new -# part here. -include $(MPMDEP) $(AMSDEP) $(AMCDEP) $(LODEP) \ - $(AWLDEP) $(POOLNDEP) $(TESTLIBDEP) $(FMTDYDEP) $(FMTHETSTDEP) \ - $(PLINTHDEP) $(EVENTPROCDEP) -endif -endif - -endif -endif - -endif +include $(MPM:%.c=$(PFM)/$(VARIETY)/%.d) +endif # VARIETY != hot +endif # VARIETY != rash + +# %%PART: When adding a new part, add the dependencies file for the +# new part here. +include \ + $(FMTDY:%.c=$(PFM)/$(VARIETY)/%.d) \ + $(FMTDYTST:%.c=$(PFM)/$(VARIETY)/%.d) \ + $(FMTHETST:%.c=$(PFM)/$(VARIETY)/%.d) \ + $(FMTSCM:%.c=$(PFM)/$(VARIETY)/%.d) \ + $(PLINTH:%.c=$(PFM)/$(VARIETY)/%.d) \ + $(POOLN:%.c=$(PFM)/$(VARIETY)/%.d) \ + $(TESTLIB:%.c=$(PFM)/$(VARIETY)/%.d) \ + $(TESTTHR:%.c=$(PFM)/$(VARIETY)/%.d) \ + $(EXTRA_TARGETS:mps%=$(PFM)/$(VARIETY)/%.d) \ + $(TEST_TARGETS:%=$(PFM)/$(VARIETY)/%.d) + +endif # !defined TARGET +endif # !defined VARIETY + +endif # !defined gendep # Library @@ -539,19 +635,18 @@ endif $(PFM)/$(VARIETY)/%.a: $(ECHO) "$(PFM): $@" rm -f $@ - $(CC) $(CFLAGS) -c -o $(PFM)/$(VARIETY)/version.o version.c - $(AR) $(ARFLAGS) $@ $^ $(PFM)/$(VARIETY)/version.o + $(AR) $(ARFLAGS) $@ $^ $(RANLIB) $@ # Executable $(PFM)/$(VARIETY)/%: $(ECHO) "$(PFM): $@" - $(CC) $(CFLAGS) $(LINKFLAGS) -o $@ $^ $(LIBS) + $(CC) $(CFLAGSSTRICT) $(LINKFLAGS) -o $@ $^ $(LIBS) $(PFM)/$(VARIETY)/mpseventsql: $(ECHO) "$(PFM): $@" - $(CC) $(CFLAGS) $(LINKFLAGS) -o $@ $^ $(LIBS) -lsqlite3 + $(CC) $(CFLAGSLAX) $(LINKFLAGS) -o $@ $^ $(LIBS) -lsqlite3 # Special targets for development @@ -567,7 +662,7 @@ find-puns: phony # C. COPYRIGHT AND LICENSE # -# Copyright (c) 2001-2013 Ravenbrook Limited . +# Copyright (c) 2001-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/code/commpost.nmk b/code/commpost.nmk index 808307b3b5..201094905b 100644 --- a/code/commpost.nmk +++ b/code/commpost.nmk @@ -1,7 +1,7 @@ # commpost.nmk: SECOND COMMON FRAGMENT FOR PLATFORMS USING NMAKE -*- makefile -*- # # $Id$ -# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. # # DESCRIPTION # @@ -24,10 +24,13 @@ $(ALL_TARGETS) $(OPTIONAL_TARGETS): # "clean" removes the directory containing the build results. # Depends on there being no file called "clean". +# Note that we suspend error processing on the if line because rmdir +# sometimes exits with an error and the message "The directory is not +# empty" even if the /s option is given. See job003854. clean: $(ECHO) $(PFM): $@ - -echo y | rmdir/s $(PFM) + -if exist $(PFM) rmdir /q /s $(PFM) # target target # %%VARIETY: When adding a new variety, optionally, add a recursive make @@ -39,8 +42,8 @@ clean: !IFDEF TARGET !IFNDEF VARIETY target: - $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot variety $(MAKE) /nologo /f $(PFM).nmk VARIETY=cool variety + $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot variety !ENDIF !ENDIF @@ -53,21 +56,80 @@ variety: $(PFM)\$(VARIETY)\$(TARGET) !ENDIF !ENDIF -mpsicv.cov: - $(MAKE) /nologo /f $(PFM).nmk TARGET=$@ VARIETY=cv variety - -# testrun +# testrun testci testall testansi testpollnone # Runs automated test cases. -testrun: $(AUTO_TEST_TARGETS) +testrun testci testall testansi testpollnone: $(TEST_TARGETS) !IFDEF VARIETY - set MPS_TESTLIB_NOABORT=true - ..\tool\testrun.bat $(PFM) $(VARIETY) $(AUTO_TEST_TARGETS) + ..\tool\testrun.bat $(PFM) $(VARIETY) $@ !ELSE - $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot testrun - $(MAKE) /nologo /f $(PFM).nmk VARIETY=cool testrun + $(MAKE) /nologo /f $(PFM).nmk VARIETY=cool $@ + $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot $@ +!ENDIF + + +# FLAGS AMALGAMATION +# +# %%VARIETY: When adding a new variety, add the following macros that +# expand to sets of flags that the variety should use: +# +# CFLAGS -- when compiling C; +# CFLAGSSQL -- when compiling mpseventsql; +# LINKFLAGS -- when building executables; +# LIBFLAGS -- when building libraries. + +!IF "$(VARIETY)" == "hot" +CFLAGS=$(CFLAGSCOMMONPRE) $(CFHOT) $(CFLAGSCOMMONPOST) +CFLAGSSQL=$(CFLAGSSQLPRE) $(CFHOT) $(CFLAGSSQLPOST) +LINKFLAGS=$(LINKFLAGSCOMMON) $(LFHOT) +LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSHOT) + +!ELSEIF "$(VARIETY)" == "cool" +CFLAGS=$(CFLAGSCOMMONPRE) $(CFCOOL) $(CFLAGSCOMMONPOST) +CFLAGSSQL=$(CFLAGSSQLPRE) $(CFCOOL) $(CFLAGSSQLPOST) +LINKFLAGS=$(LINKFLAGSCOMMON) $(LFCOOL) +LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSCOOL) + +!ELSEIF "$(VARIETY)" == "rash" +CFLAGS=$(CFLAGSCOMMONPRE) $(CFRASH) $(CFLAGSCOMMONPOST) +CFLAGSSQL=$(CFLAGSSQLPRE) $(CFRASH) $(CFLAGSSQLPOST) +LINKFLAGS=$(LINKFLAGSCOMMON) $(LFRASH) +LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSRASH) + +!ENDIF + + +# SOURCE TO OBJECT FILE MAPPINGS +# +# %%PART: When adding a new part, add new macros which expand to the object +# files included in the part +# +# Note: nmake doesn't expand variables within a string replacement +# operation. We work around this by writing out a temporary makefile +# and including it. + +TEMPMAKE=$(TEMP)\mps.nmk +!IF [echo MPMOBJ0 = $$(MPM:[=$(PFM)\$(VARIETY)\) > $(TEMPMAKE)] == 0 \ + && [echo FMTDYOBJ0 = $$(FMTDY:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \ + && [echo FMTTESTOBJ0 = $$(FMTTEST:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \ + && [echo FMTSCHEMEOBJ0 = $$(FMTSCHEME:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \ + && [echo POOLNOBJ0 = $$(POOLN:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \ + && [echo TESTLIBOBJ0 = $$(TESTLIB:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \ + && [echo TESTTHROBJ0 = $$(TESTTHR:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 +!INCLUDE $(TEMPMAKE) +!IF [del $(TEMPMAKE)] != 0 +!ERROR Failed to delete $(TEMPMAKE) +!ENDIF !ENDIF +MPMOBJ = $(MPMOBJ0:]=.obj) +FMTDYOBJ = $(FMTDYOBJ0:]=.obj) +FMTTESTOBJ = $(FMTTESTOBJ0:]=.obj) +FMTSCHEMEOBJ = $(FMTSCHEMEOBJ0:]=.obj) +POOLNOBJ = $(POOLNOBJ0:]=.obj) +TESTLIBOBJ = $(TESTLIBOBJ0:]=.obj) +TESTTHROBJ = $(TESTTHROBJ0:]=.obj) + # THE MPS LIBRARY # @@ -96,12 +158,9 @@ $(PFM)\hot\mps.lib: $(PFM)\hot\mps.obj $(ECHO) $@ $(LIBMAN) $(LIBFLAGS) /OUT:$@ $** -$(PFM)\cool\mps.lib: \ - $(MPMOBJ) $(AMCOBJ) $(AMSOBJ) $(AWLOBJ) $(LOOBJ) $(SNCOBJ) \ - $(MVFFOBJ) $(PLINTHOBJ) $(POOLNOBJ) +$(PFM)\cool\mps.lib: $(MPMOBJ) $(ECHO) $@ - cl /c $(CFLAGS) /Fd$(PFM)\$(VARIETY)\ /Fo$(PFM)\$(VARIETY)\version.o version.c - $(LIBMAN) $(LIBFLAGS) /OUT:$@ $** $(PFM)\$(VARIETY)\version.o + $(LIBMAN) $(LIBFLAGS) /OUT:$@ $** # OTHER GENUINE TARGETS @@ -118,6 +177,9 @@ $(PFM)\cool\mps.lib: \ $(PFM)\$(VARIETY)\abqtest.exe: $(PFM)\$(VARIETY)\abqtest.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\airtest.exe: $(PFM)\$(VARIETY)\airtest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTSCHEMEOBJ) $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\amcss.exe: $(PFM)\$(VARIETY)\amcss.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) @@ -125,7 +187,7 @@ $(PFM)\$(VARIETY)\amcsshe.exe: $(PFM)\$(VARIETY)\amcsshe.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) $(PFM)\$(VARIETY)\amcssth.exe: $(PFM)\$(VARIETY)\amcssth.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)\$(VARIETY)\amsss.exe: $(PFM)\$(VARIETY)\amsss.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) @@ -147,6 +209,10 @@ $(PFM)\$(VARIETY)\awluthe.exe: $(PFM)\$(VARIETY)\awluthe.obj \ $(FMTTESTOBJ) \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\awlutth.exe: $(PFM)\$(VARIETY)\awlutth.obj \ + $(FMTTESTOBJ) \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ) + $(PFM)\$(VARIETY)\btcv.exe: $(PFM)\$(VARIETY)\btcv.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) @@ -156,15 +222,15 @@ $(PFM)\$(VARIETY)\bttest.exe: $(PFM)\$(VARIETY)\bttest.obj \ $(PFM)\$(VARIETY)\cvmicv.exe: $(PFM)\$(VARIETY)\cvmicv.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\djbench.exe: $(PFM)\$(VARIETY)\djbench.obj \ + $(TESTLIBOBJ) $(TESTTHROBJ) + $(PFM)\$(VARIETY)\exposet0.exe: $(PFM)\$(VARIETY)\exposet0.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) $(PFM)\$(VARIETY)\expt825.exe: $(PFM)\$(VARIETY)\expt825.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\fbmtest.exe: $(PFM)\$(VARIETY)\fbmtest.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - $(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) @@ -174,14 +240,20 @@ $(PFM)\$(VARIETY)\finaltest.exe: $(PFM)\$(VARIETY)\finaltest.obj \ $(PFM)\$(VARIETY)\fotest.exe: $(PFM)\$(VARIETY)\fotest.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\gcbench.exe: $(PFM)\$(VARIETY)\gcbench.obj \ + $(FMTTESTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) + +$(PFM)\$(VARIETY)\landtest.exe: $(PFM)\$(VARIETY)\landtest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\locbwcss.exe: $(PFM)\$(VARIETY)\locbwcss.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(PFM)\$(VARIETY)\lockcov.exe: $(PFM)\$(VARIETY)\lockcov.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\lockutw3.exe: $(PFM)\$(VARIETY)\lockutw3.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\lockut.exe: $(PFM)\$(VARIETY)\lockut.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)\$(VARIETY)\locusss.exe: $(PFM)\$(VARIETY)\locusss.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) @@ -201,9 +273,12 @@ $(PFM)\$(VARIETY)\mpsicv.exe: $(PFM)\$(VARIETY)\mpsicv.obj \ $(PFM)\$(VARIETY)\mv2test.exe: $(PFM)\$(VARIETY)\mv2test.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\poolncv.exe: $(PFM)\$(VARIETY)\poolncv.obj \ +$(PFM)\$(VARIETY)\nailboardtest.exe: $(PFM)\$(VARIETY)\nailboardtest.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\poolncv.exe: $(PFM)\$(VARIETY)\poolncv.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(POOLNOBJ) + $(PFM)\$(VARIETY)\qs.exe: $(PFM)\$(VARIETY)\qs.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) @@ -271,13 +346,13 @@ $(PFM)\$(VARIETY)\mpseventsql.obj: $(PFM)\$(VARIETY)\eventsql.obj $(ECHO) $@ @if not exist $(PFM) mkdir $(PFM) @if not exist $(PFM)\$(VARIETY) mkdir $(PFM)\$(VARIETY) - cl /c $(CFLAGS) /Fd$(PFM)\$(VARIETY)\ /Fo$@ $< + $(CC) /c $(CFLAGS) /Fo$@ $< $(PFM)\$(VARIETY)\sqlite3.obj: $(ECHO) $@ @if not exist $(PFM) mkdir $(PFM) @if not exist $(PFM)\$(VARIETY) mkdir $(PFM)\$(VARIETY) - cl /c $(CFLAGSSQL) /Fd$(PFM)\$(VARIETY)\ /Fo$@ sqlite3.c + $(CC) /c $(CFLAGSSQL) /Fo$@ sqlite3.c {}.asm{$(PFM)\$(VARIETY)}.obj: $(ECHO) $@ @@ -299,12 +374,12 @@ $(PFM)\$(VARIETY)\sqlite3.obj: {$(PFM)\$(VARIETY)}.obj{$(PFM)\$(VARIETY)}.exe: $(ECHO) $@ - $(LINKER) $(LINKFLAGS) /PDB:$*.pdb /OUT:$@ $(**) + $(LINKER) $(LINKFLAGS) /OUT:$@ $(**) # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2013 Ravenbrook Limited . +# Copyright (C) 2001-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/code/commpre.nmk b/code/commpre.nmk index a78eee3ef2..eaaa46c84a 100644 --- a/code/commpre.nmk +++ b/code/commpre.nmk @@ -1,7 +1,7 @@ # commpre.nmk: FIRST COMMON FRAGMENT FOR PLATFORMS USING NMAKE -*- makefile -*-1 # # $Id$ -# Copyright (c) 2001 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. # # DESCRIPTION # @@ -14,24 +14,29 @@ # %%PART: When adding a new part, add a new parameter for the files included # in the part # Parameters: -# PFM platform code, e.g. "w3i3mv" -# PFMDEFS /D options to define platforms preprocessor symbols -# to the compiler. Eg "/DOS_NT /DARCH_386 /DBUILD_MVC" +# PFM platform code, e.g. "w3i3mv" +# PFMDEFS /D options to define platforms preprocessor symbols +# to the compiler. Avoid using this if possible, as it +# prevents the MPS being built with a simple command like +# "cl mps.c". # MPMCOMMON list of sources which make up the "mpm" part for all -# platforms. Each source is stripped of its .c extension -# and surrounded in angle brackets (<>) -# MPM as above, plus sources for the "mpm" part for the current -# platform. -# PLINTH as above for the "plinth" part -# AMC as above for the "amc" part -# AMS as above for the "ams" part -# LO as above for the "lo" part -# POOLN as above for the "pooln" part -# SNC as above for the "snc" part -# DW as above for the "dw" part -# FMTTEST as above for the "fmttest" part -# TESTLIB as above for the "testlib" part -# NOISY if defined, causes command to be emitted +# platforms. Each source is stripped of its .c extension +# and surrounded with [brackets]. +# MPMPF as above for the current platform. +# PLINTH as above for the "plinth" part +# AMC as above for the "amc" part +# AMS as above for the "ams" part +# LO as above for the "lo" part +# POOLN as above for the "pooln" part +# SNC as above for the "snc" part +# POOLS as above for all pools included in the target +# MPM as above for the MPMCOMMON + MPMPF + PLINTH + POOLS +# DW as above for the "dw" part +# FMTTEST as above for the "fmttest" part +# FMTSCHEME as above for the "fmtscheme" part +# TESTLIB as above for the "testlib" part +# TESTTHR as above for the "testthr" part +# NOISY if defined, causes command to be emitted # # # EDITING @@ -50,25 +55,51 @@ LIB_TARGETS=mps.lib -# If it is suitable for running regularly (for example, after every -# build) as an automated test case, add it to AUTO_TEST_TARGETS. - -AUTO_TEST_TARGETS=abqtest.exe amcss.exe amcsshe.exe amsss.exe \ - amssshe.exe apss.exe arenacv.exe awlut.exe awluthe.exe btcv.exe \ - exposet0.exe expt825.exe fbmtest.exe finalcv.exe finaltest.exe \ - fotest.exe locbwcss.exe lockcov.exe lockutw3.exe locusss.exe \ - locv.exe messtest.exe mpmss.exe mpsicv.exe mv2test.exe \ - poolncv.exe qs.exe sacss.exe segsmss.exe steptest.exe walkt0.exe \ +# Test cases go in TEST_TARGETS. + +TEST_TARGETS=\ + abqtest.exe \ + airtest.exe \ + amcss.exe \ + amcsshe.exe \ + amcssth.exe \ + amsss.exe \ + amssshe.exe \ + apss.exe \ + arenacv.exe \ + awlut.exe \ + awluthe.exe \ + awlutth.exe \ + btcv.exe \ + bttest.exe \ + djbench.exe \ + exposet0.exe \ + expt825.exe \ + finalcv.exe \ + finaltest.exe \ + fotest.exe \ + gcbench.exe \ + landtest.exe \ + locbwcss.exe \ + lockcov.exe \ + lockut.exe \ + locusss.exe \ + locv.exe \ + messtest.exe \ + mpmss.exe \ + mpsicv.exe \ + mv2test.exe \ + nailboardtest.exe \ + poolncv.exe \ + qs.exe \ + sacss.exe \ + segsmss.exe \ + steptest.exe \ + teletest.exe \ + walkt0.exe \ + zcoll.exe \ zmess.exe -# If it is not runnable as an automated test case, but is buildable, -# add it to OTHER_TEST_TARGETS with a note. -# -# bttest and teletest -- interactive and so cannot be run unattended. -# zcoll -- takes too long to be useful as a regularly run smoke test. - -OTHER_TEST_TARGETS=bttest.exe teletest.exe zcoll.exe - # Stand-alone programs go in EXTRA_TARGETS if they should always be # built, or in OPTIONAL_TARGETS if they should only be built if @@ -82,7 +113,7 @@ OPTIONAL_TARGETS=mpseventsql.exe UNBUILDABLE_TARGETS=replay.exe -ALL_TARGETS=$(LIB_TARGETS) $(AUTO_TEST_TARGETS) $(OTHER_TEST_TARGETS) $(EXTRA_TARGETS) +ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS) # PARAMETERS @@ -90,24 +121,72 @@ ALL_TARGETS=$(LIB_TARGETS) $(AUTO_TEST_TARGETS) $(OTHER_TEST_TARGETS) $(EXTRA_TA # # %%PART: When adding a new part, add the sources for the new part here. -MPMCOMMON = \ - \ - \ - \ - \ - \ - -PLINTH = -AMC = -AMS = -AWL = -LO = -MVFF = -POOLN = -SNC = -DW = -FMTTEST = -TESTLIB = +MPMCOMMON=\ + [abq] \ + [arena] \ + [arenacl] \ + [arenavm] \ + [arg] \ + [boot] \ + [bt] \ + [buffer] \ + [cbs] \ + [dbgpool] \ + [dbgpooli] \ + [event] \ + [failover] \ + [format] \ + [freelist] \ + [global] \ + [land] \ + [ld] \ + [locus] \ + [message] \ + [meter] \ + [mpm] \ + [mpsi] \ + [nailboard] \ + [pool] \ + [poolabs] \ + [poolmfs] \ + [poolmrg] \ + [poolmv2] \ + [poolmv] \ + [protocol] \ + [range] \ + [ref] \ + [reserv] \ + [ring] \ + [root] \ + [sa] \ + [sac] \ + [seg] \ + [shield] \ + [splay] \ + [ss] \ + [table] \ + [trace] \ + [traceanc] \ + [tract] \ + [tree] \ + [version] \ + [vm] \ + [walk] +PLINTH = [mpsliban] [mpsioan] +AMC = [poolamc] +AMS = [poolams] [poolamsi] +AWL = [poolawl] +LO = [poollo] +MVFF = [poolmvff] +POOLN = [pooln] +SNC = [poolsnc] +FMTDY = [fmtdy] [fmtno] +FMTTEST = [fmthe] [fmtdy] [fmtno] [fmtdytst] +FMTSCHEME = [fmtscheme] +TESTLIB = [testlib] [getoptl] +TESTTHR = [testthrw3] +POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC) +MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH) # CHECK PARAMETERS @@ -119,12 +198,15 @@ TESTLIB = !IFNDEF PFM !ERROR commpre.nmk: PFM not defined !ENDIF -!IFNDEF PFMDEFS -!ERROR commpre.nmk: PFMDEFS not defined +!IFNDEF MPM +!ERROR commpre.nmk: MPM not defined !ENDIF !IFNDEF MPMCOMMON !ERROR commpre.nmk: MPMCOMMON not defined !ENDIF +!IFNDEF MPMPF +!ERROR commpre.nmk: MPMPF not defined +!ENDIF !IFNDEF PLINTH !ERROR commpre.nmk: PLINTH not defined !ENDIF @@ -137,9 +219,27 @@ TESTLIB = !IFNDEF AMS !ERROR commpre.nmk: AMS not defined !ENDIF +!IFNDEF POOLN +!ERROR commpre.nmk: POOLN not defined +!ENDIF +!IFNDEF SNC +!ERROR commpre.nmk: SNC not defined +!ENDIF +!IFNDEF FMTDY +!ERROR commpre.nmk: FMTDY not defined +!ENDIF +!IFNDEF FMTTEST +!ERROR commpre.nmk: FMTTEST not defined +!ENDIF +!IFNDEF FMTSCHEME +!ERROR commpre.nmk: FMTSCHEME not defined +!ENDIF !IFNDEF TESTLIB !ERROR commpre.nmk: TESTLIB not defined !ENDIF +!IFNDEF TESTTHR +!ERROR commpre.nmk: TESTTHR not defined +!ENDIF # DECLARATIONS @@ -155,25 +255,20 @@ ECHO = echo # C FLAGS -# /MD means compile for multi-threaded environment with separate C library DLL. -# /MT means compile for multi-threaded environment. -# /ML means compile for single-threaded environment. -# A 'd' at the end means compile for debugging. - CFLAGSTARGETPRE = CFLAGSTARGETPOST = -CRTFLAGSHOT = /MT -CRTFLAGSCOOL = /MTd -LINKFLAGSHOT = libcmt.lib -LINKFLAGSCOOL = libcmtd.lib +CRTFLAGSHOT = +CRTFLAGSCOOL = +LINKFLAGSHOT = +LINKFLAGSCOOL = CFLAGSSQLPRE = /nologo $(PFMDEFS) -CFLAGSCOMMONPRE = /nologo /W4 /WX $(PFMDEFS) $(CFLAGSTARGETPRE) +CFLAGSCOMMONPRE = /nologo $(PFMDEFS) $(CFLAGSTARGETPRE) CFLAGSSQLPOST = CFLAGSCOMMONPOST = $(CFLAGSTARGETPOST) # Flags for use in the variety combinations -CFLAGSHOT = /O2 /DNDEBUG +CFLAGSHOT = /O2 # (above /O2 (maximise speed) used to be set to /Ox # (maximise optimisations) in for tool versions before VS 9) # We used to have /GZ here (stack probe). @@ -183,7 +278,7 @@ CFLAGSHOT = /O2 /DNDEBUG # building a DLL, mpsdy.dll, the linker step will fail (error LNK2001: # unresolved external symbol __chkesp). See # http://support.microsoft.com/kb/q191669/ -CFLAGSCOOL = /Od +CFLAGSCOOL = CFLAGSINTERNAL = /Zi CFLAGSEXTERNAL = @@ -216,7 +311,7 @@ LFCOOL = $(LINKFLAGSCOOL) $(LINKFLAGSINTERNAL) # %%VARIETY: When adding a new variety, define a macro containing the flags # for the new variety LIBMAN = lib # can't call this LIB - it screws the environment -LIBFLAGSCOMMON = /nologo +LIBFLAGSCOMMON = LIBFLAGSRASH = LIBFLAGSHOT = @@ -235,7 +330,7 @@ LIBFLAGSCOOL = # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2013 Ravenbrook Limited . +# Copyright (C) 2001-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/code/config.h b/code/config.h index 4ad6ea51f1..11d5a943a0 100644 --- a/code/config.h +++ b/code/config.h @@ -1,7 +1,7 @@ /* config.h: MPS CONFIGURATION * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * PURPOSE @@ -147,11 +147,60 @@ * cc -O2 -c -DCONFIG_PLINTH_NONE mps.c */ -#if defined(CONFIG_PLINTH_NONE) +#if !defined(CONFIG_PLINTH_NONE) +#define PLINTH +#else #define PLINTH_NONE #endif +/* CONFIG_PF_ANSI -- use the ANSI platform + * + * This symbol tells mps.c to exclude the sources for the + * auto-detected platform, and use the generic ("ANSI") platform + * instead. + */ + +#if defined(CONFIG_PF_ANSI) +#define PLATFORM_ANSI +#endif + + +/* CONFIG_THREAD_SINGLE -- support single-threaded execution only + * + * This symbol causes the MPS to be built for single-threaded + * execution only, where locks are not needed and so lock operations + * can be defined as no-ops by lock.h. + */ + +#if !defined(CONFIG_THREAD_SINGLE) +#define LOCK +#else +#define LOCK_NONE +#endif + + +/* CONFIG_POLL_NONE -- no support for polling + * + * This symbol causes the MPS to built without support for polling. + * This means that garbage collections will only happen if requested + * explicitly via mps_arena_collect() or mps_arena_step(), but it also + * means that protection is not needed, and so shield operations can + * be replaced with no-ops in mpm.h. + */ + +#if !defined(CONFIG_POLL_NONE) +#define REMEMBERED_SET +#define SHIELD +#else +#if !defined(CONFIG_THREAD_SINGLE) +#error "CONFIG_POLL_NONE without CONFIG_THREAD_SINGLE" +#endif +#define REMEMBERED_SET_NONE +#define SHIELD_NONE +#endif + + #define MPS_VARIETY_STRING \ MPS_ASSERT_STRING "." MPS_LOG_STRING "." MPS_STATS_STRING @@ -160,55 +209,99 @@ #include "mpstd.h" -/* Suppress Visual C warnings at warning level 4, */ -/* see mail.richard.1997-09-25.13-26. */ -/* Essentially the same settings are done in testlib.h. */ +/* Suppress Visual C warnings at /W4 (warning level 4) */ +/* This is also done in testlib.h. */ #ifdef MPS_BUILD_MV -/* "unreferenced inline function has been removed" (windows.h) */ -#pragma warning(disable: 4514) - -/* "constant conditional" (MPS_END) */ +/* "constant conditional" (provoked by MPS_END) */ #pragma warning(disable: 4127) -/* "unreachable code" (ASSERT, if cond is constantly true). */ -#pragma warning(disable: 4702) +#endif /* MPS_BUILD_MV */ -/* "expression evaluates to a function which is missing an argument list" */ -#pragma warning(disable: 4550) -/* "local variable is initialized but not referenced" */ -#pragma warning(disable: 4189) +/* Suppress Pelles C warnings at /W2 (warning level 2) */ +/* Some of the same settings are done in testlib.h. */ -/* "not all control paths return a value" */ -#pragma warning(disable: 4715) +#ifdef MPS_BUILD_PC -/* MSVC 2.0 generates a warning when using NOCHECK or UNUSED */ -#ifdef _MSC_VER -#if _MSC_VER < 1000 -#pragma warning(disable: 4705) -#endif -#else /* _MSC_VER */ -#error "Expected _MSC_VER to be defined for builder.mv" -#endif /* _MSC_VER */ +/* "Unreachable code" (provoked by AVER, if condition is constantly true). */ +#pragma warn(disable: 2154) +/* "Consider changing type to 'size_t' for loop variable" */ +#pragma warn(disable: 2804) -/* Non-checking varieties give many spurious warnings because parameters - * are suddenly unused, etc. We aren't interested in these +#endif /* MPS_BUILD_PC */ + + +/* MPS_FILE -- expands to __FILE__ in nested macros */ + +#ifdef MPS_BUILD_PC + +/* Pelles C loses definition of __FILE__ in deeply nested macro + * expansions. See */ +#define MPS_FILE "<__FILE__ unavailable in " MPS_PF_STRING ">" + +#else -#if defined(AVER_AND_CHECK_NONE) +#define MPS_FILE __FILE__ -/* "unreferenced formal parameter" */ -#pragma warning(disable: 4100) +#endif -/* "unreferenced local function has been removed" */ -#pragma warning(disable: 4505) -#endif /* AVER_AND_CHECK_NONE */ +/* Function attributes */ +/* Some of these are also defined in testlib.h */ -#endif /* MPS_BUILD_MV */ +/* Attribute for functions that take a printf-like format argument, so + * that the compiler can check the format specifiers against the types + * of the arguments. + * GCC: + * Clang: + */ +#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL) +#define ATTRIBUTE_FORMAT(ARGLIST) __attribute__((__format__ ARGLIST)) +#else +#define ATTRIBUTE_FORMAT(ARGLIST) +#endif + +/* Attribute for functions that should not be instrumented by Clang's + * address sanitizer. + * + */ +#if defined(MPS_BUILD_LL) +#if __has_feature(address_sanitizer) +#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((__no_sanitize_address__)) +#else +#define ATTRIBUTE_NO_SANITIZE_ADDRESS +#endif +#else +#define ATTRIBUTE_NO_SANITIZE_ADDRESS +#endif + +/* Attribute for functions that do not return. + * GCC: + * Clang: + */ +#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL) +#define ATTRIBUTE_NORETURN __attribute__((__noreturn__)) +#else +#define ATTRIBUTE_NORETURN +#endif + +/* Attribute for functions that may be unused in some build configurations. + * GCC: + * + * This attribute must be applied to all Check functions, otherwise + * the RASH variety fails to compile with -Wunused-function. (It + * should not be applied to functions that are unused in all build + * configurations: these functions should not be compiled.) + */ +#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL) +#define ATTRIBUTE_UNUSED __attribute__((__unused__)) +#else +#define ATTRIBUTE_UNUSED +#endif /* EPVMDefaultSubsequentSegSIZE is a default for the alignment of @@ -224,11 +317,6 @@ #define BUFFER_RANK_DEFAULT (mps_rank_exact()) -/* CBS Configuration -- see */ - -#define CBS_EXTEND_BY_DEFAULT ((Size)4096) - - /* Format defaults: see */ #define FMT_ALIGN_DEFAULT ((Align)MPS_PF_ALIGN) @@ -241,15 +329,27 @@ #define FMT_CLASS_DEFAULT (&FormatDefaultClass) +/* Pool AMC Configuration -- see */ + +#define AMC_INTERIOR_DEFAULT TRUE +/* AMC treats objects larger than or equal to this as "Large" */ +#define AMC_LARGE_SIZE_DEFAULT ((Size)32768) +#define AMC_EXTEND_BY_DEFAULT ((Size)8192) + + /* Pool AMS Configuration -- see */ -#define AMS_SUPPORT_AMBIGUOUS_DEFAULT FALSE +#define AMS_SUPPORT_AMBIGUOUS_DEFAULT TRUE #define AMS_GEN_DEFAULT 0 /* Pool AWL Configuration -- see */ #define AWL_GEN_DEFAULT 0 +#define AWL_HAVE_SEG_SA_LIMIT TRUE +#define AWL_SEG_SA_LIMIT 200 /* TODO: Improve guesswork with measurements */ +#define AWL_HAVE_TOTAL_SA_LIMIT FALSE +#define AWL_TOTAL_SA_LIMIT 0 /* Pool LO Configuration -- see */ @@ -259,6 +359,7 @@ /* Pool MV Configuration -- see */ +#define MV_ALIGN_DEFAULT MPS_PF_ALIGN #define MV_EXTEND_BY_DEFAULT ((Size)65536) #define MV_AVG_SIZE_DEFAULT ((Size)32) #define MV_MAX_SIZE_DEFAULT ((Size)65536) @@ -277,6 +378,7 @@ #define MVFF_SLOT_HIGH_DEFAULT FALSE #define MVFF_ARENA_HIGH_DEFAULT FALSE #define MVFF_FIRST_FIT_DEFAULT TRUE +#define MVFF_SPARE_DEFAULT 0.75 /* Pool MVT Configuration -- see */ @@ -290,48 +392,81 @@ #define MVT_FRAG_LIMIT_DEFAULT 30 -/* Arena Configuration -- see - * - * .client.seg-size: ARENA_CLIENT_PAGE_SIZE is the size in bytes of a - * "page" (i.e., segment granule) in the client arena. It's set at 8192 - * with no particular justification. - */ +/* Arena Configuration -- see */ #define ArenaPollALLOCTIME (65536.0) #define ARENA_ZONESHIFT ((Shift)20) -#define ARENA_CLIENT_PAGE_SIZE ((Size)8192) +/* .client.seg-size: ARENA_CLIENT_GRAIN_SIZE is the minimum size, in + * bytes, of a grain in the client arena. It's set at 8192 with no + * particular justification. */ + +#define ARENA_CLIENT_GRAIN_SIZE ((Size)8192) + +#define ARENA_DEFAULT_COMMIT_LIMIT ((Size)-1) + +/* TODO: This should be proportional to the memory usage of the MPS, not + * a constant. That will require design, and then some interface and + * documenation changes. */ +#define ARENA_DEFAULT_SPARE_COMMIT_LIMIT ((Size)10uL*1024uL*1024uL) + +#define ARENA_DEFAULT_ZONED TRUE + +/* ARENA_MINIMUM_COLLECTABLE_SIZE is the minimum size (in bytes) of + * collectable memory that might be considered worthwhile to run a + * full garbage collection. */ + +#define ARENA_MINIMUM_COLLECTABLE_SIZE ((Size)1000000) + +/* ARENA_DEFAULT_COLLECTION_RATE is an estimate of the MPS's + * collection rate (in bytes per second), for use in the case where + * there isn't enough data to use a measured value. */ + +#define ARENA_DEFAULT_COLLECTION_RATE (25000000.0) + +/* ARENA_DEFAULT_COLLECTION_OVERHEAD is an estimate of the MPS's + * collection overhead (in seconds), for use in the case where there + * isn't enough data to use a measured value. */ + +#define ARENA_DEFAULT_COLLECTION_OVERHEAD (0.1) + +/* ARENA_MAX_COLLECT_FRACTION is the maximum fraction of runtime that + * ArenaStep is prepared to spend in collections. */ + +#define ARENA_MAX_COLLECT_FRACTION (0.1) + +/* ArenaDefaultZONESET is the zone set used by LocusPrefDEFAULT. + * + * TODO: This is left over from before branches 2014-01-29/mps-chain-zones + * and 2014-01-17/cbs-tract-alloc reformed allocation, and may now be + * doing more harm than good. Experiment with setting to ZoneSetUNIV. */ #define ArenaDefaultZONESET (ZoneSetUNIV << (MPS_WORD_WIDTH / 2)) -/* @@@@ knows the implementation of ZoneSets */ - -/* .segpref.default: For EPcore, non-DL segments should be placed high */ -/* to reduce fragmentation of DL pools (see request.epcore.170193_). */ -/* .. _request.epcore.170193: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/epcore/170193 */ -#define SegPrefDEFAULT { \ - SegPrefSig, /* sig */ \ - TRUE, /* high */ \ + +/* LocusPrefDEFAULT is the allocation preference used by manual pool + * classes (these don't care where they allocate). */ + +#define LocusPrefDEFAULT { \ + LocusPrefSig, /* sig */ \ + FALSE, /* high */ \ ArenaDefaultZONESET, /* zoneSet */ \ - FALSE, /* isCollected */ \ + ZoneSetEMPTY, /* avoid */ \ } #define LDHistoryLENGTH ((Size)4) -/* Value of MPS_KEY_EXTEND_BY for the arena control pool. - Deliberately smaller than the default, because we don't expect the control - pool to be very heavily used. */ -#define CONTROL_EXTEND_BY 4096 +/* Value of MPS_KEY_EXTEND_BY for the arena control pool. */ +#define CONTROL_EXTEND_BY ((Size)32768) +#define VM_ARENA_SIZE_DEFAULT ((Size)1 << 28) -/* Stack configuration */ -/* Currently StackProbe has a useful implementation only on - * Intel platforms and only when using Microsoft build tools (builder.mv) - */ -#if defined(MPS_ARCH_I3) && defined(MPS_BUILD_MV) -#define StackProbeDEPTH ((Size)500) -#elif defined(MPS_PF_W3I6MV) +/* Stack configuration -- see */ + +/* Currently StackProbe has a useful implementation only on Windows. */ +#if defined(MPS_OS_W3) +/* See for a justification of this value. */ #define StackProbeDEPTH ((Size)500) #else #define StackProbeDEPTH ((Size)0) @@ -346,7 +481,7 @@ /* VM Configuration -- see */ -#define VMANPageALIGNMENT ((Align)4096) +#define VMAN_PAGE_SIZE ((Align)4096) #define VMJunkBYTE ((unsigned char)0xA9) #define VMParamSize (sizeof(Word)) @@ -358,6 +493,7 @@ * * Source Symbols Header Feature * =========== ========================= ============= ==================== + * eventtxt.c setenv _GNU_SOURCE * lockli.c pthread_mutexattr_settype _XOPEN_SOURCE >= 500 * prmci3li.c REG_EAX etc. _GNU_SOURCE * prmci6li.c REG_RAX etc. _GNU_SOURCE @@ -376,9 +512,14 @@ #if defined(MPS_OS_LI) +#if defined(_XOPEN_SOURCE) && _XOPEN_SOURCE < 500 +#undef _XOPEN_SOURCE +#endif +#if !defined(_XOPEN_SOURCE) #define _XOPEN_SOURCE 500 +#endif -#ifndef _GNU_SOURCE +#if !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif @@ -504,40 +645,18 @@ #define MPS_PROD_STRING "mps" #define MPS_PROD_MPS -#define THREAD_MULTI -#define PROTECTION -#define PROD_CHECKLEVEL_INITIAL CheckLevelSHALLOW - -/* TODO: This should be proportional to the memory usage of the MPS, not - a constant. That will require design, and then some interface and - documenation changes. */ -#define ARENA_INIT_SPARE_COMMIT_LIMIT ((Size)10uL*1024uL*1024uL) - - -/* Pool Class AMC configuration */ - -/* AMC treats segments of this many pages (or more) as "Large" */ -#define AMCLargeSegPAGES ((Count)8) - - -/* Pool Class AWL configuration -- see poolawl.c for usage */ - -#define AWL_HAVE_SEG_SA_LIMIT TRUE -#define AWL_SEG_SA_LIMIT 200 /* TODO: Improve guesswork with measurements */ -#define AWL_HAVE_TOTAL_SA_LIMIT FALSE -#define AWL_TOTAL_SA_LIMIT 0 /* Default chain for GC pools * * TODO: The default should be to measure liveness and make sensible - * decisions. + * decisions. See job003794. */ #define ChainDEFAULT \ { \ - { 8 * 1024, 0.85 }, /* 8MiB nursery */ \ - { 32 * 1024, 0.45 } /* 32MiB second gen, after which dynamic */ \ + { 8 * 1024, 0.85 }, /* nursery */ \ + { 36 * 1024, 0.45 } /* second gen, after which dynamic */ \ } @@ -546,7 +665,7 @@ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/dbgpool.c b/code/dbgpool.c index db4fab371c..6a8417595f 100644 --- a/code/dbgpool.c +++ b/code/dbgpool.c @@ -1,7 +1,7 @@ /* dbgpool.c: POOL DEBUG MIXIN * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .source: design.mps.object-debug @@ -22,19 +22,18 @@ typedef struct tagStruct { /* We don't want to pay the expense of a sig in every tag */ Addr addr; Size size; - SplayNodeStruct splayNode; + TreeStruct treeStruct; char userdata[1 /* actually variable length */]; } tagStruct; -#define SplayNode2Tag(node) PARENT(tagStruct, splayNode, (node)) +#define TagTree(tag) (&(tag)->treeStruct) +#define TagOfTree(tree) TREE_ELT(tag, treeStruct, tree) typedef tagStruct *Tag; /* tag init methods: copying the user-supplied data into the tag */ -#define TagInitMethodCheck(f) FUNCHECK(f) - static void TagTrivInit(void* tag, va_list args) { UNUSED(tag); UNUSED(args); @@ -43,22 +42,27 @@ static void TagTrivInit(void* tag, va_list args) /* TagComp -- splay comparison function for address ordering of tags */ -static Compare TagComp(void *key, SplayNode node) +static Compare TagCompare(Tree node, TreeKey key) { Addr addr1, addr2; addr1 = *(Addr *)key; - addr2 = SplayNode2Tag(node)->addr; + addr2 = TagOfTree(node)->addr; if (addr1 < addr2) return CompareLESS; else if (addr1 > addr2) { /* Check key is not inside the object of this tag */ - AVER_CRITICAL(AddrAdd(addr2, SplayNode2Tag(node)->size) <= addr1); + AVER_CRITICAL(AddrAdd(addr2, TagOfTree(node)->size) <= addr1); return CompareGREATER; } else return CompareEQUAL; } +static TreeKey TagKey(Tree node) +{ + return &TagOfTree(node)->addr; +} + /* PoolDebugMixinCheck -- check a PoolDebugMixin */ @@ -69,12 +73,12 @@ Bool PoolDebugMixinCheck(PoolDebugMixin debug) /* Nothing to check about freeTemplate */ /* Nothing to check about freeSize */ if (debug->tagInit != NULL) { - CHECKL(TagInitMethodCheck(debug->tagInit)); + CHECKL(FUNCHECK(debug->tagInit)); /* Nothing to check about tagSize */ CHECKD(Pool, debug->tagPool); CHECKL(COMPATTYPE(Addr, void*)); /* tagPool relies on this */ /* Nothing to check about missingTags */ - CHECKL(SplayTreeCheck(&debug->index)); + CHECKD(SplayTree, &debug->index); } UNUSED(debug); /* see */ return TRUE; @@ -117,23 +121,25 @@ Bool PoolDebugOptionsCheck(PoolDebugOptions opt) * Someday, this could be split into fence and tag init methods. */ -ARG_DEFINE_KEY(pool_debug_options, PoolDebugOptions); +ARG_DEFINE_KEY(POOL_DEBUG_OPTIONS, PoolDebugOptions); + +static PoolDebugOptionsStruct debugPoolOptionsDefault = { + "POST", 4, "DEAD", 4, +}; static Res DebugPoolInit(Pool pool, ArgList args) { Res res; - PoolDebugOptions options; + PoolDebugOptions options = &debugPoolOptionsDefault; PoolDebugMixin debug; - TagInitMethod tagInit; + TagInitFunction tagInit; Size tagSize; ArgStruct arg; AVERT(Pool, pool); - /* TODO: Split this structure into separate keyword arguments, - now that we can support them. */ - ArgRequire(&arg, args, MPS_KEY_POOL_DEBUG_OPTIONS); - options = (PoolDebugOptions)arg.val.pool_debug_options; + if (ArgPick(&arg, args, MPS_KEY_POOL_DEBUG_OPTIONS)) + options = (PoolDebugOptions)arg.val.pool_debug_options; AVERT(PoolDebugOptions, options); @@ -154,10 +160,6 @@ static Res DebugPoolInit(Pool pool, ArgList args) /* into Addr memory, to avoid breaking . */ debug->fenceSize = options->fenceSize; if (debug->fenceSize != 0) { - if (debug->fenceSize % PoolAlignment(pool) != 0) { - res = ResPARAM; - goto alignFail; - } /* Fenceposting turns on tagging */ if (tagInit == NULL) { tagSize = 0; @@ -172,10 +174,6 @@ static Res DebugPoolInit(Pool pool, ArgList args) /* into Addr memory, to avoid breaking . */ debug->freeSize = options->freeSize; if (debug->freeSize != 0) { - if (PoolAlignment(pool) % debug->freeSize != 0) { - res = ResPARAM; - goto alignFail; - } debug->freeTemplate = options->freeTemplate; } @@ -186,14 +184,17 @@ static Res DebugPoolInit(Pool pool, ArgList args) /* This pool has to be like the arena control pool: the blocks */ /* allocated must be accessible using void*. */ MPS_ARGS_BEGIN(pcArgs) { - MPS_ARGS_ADD(pcArgs, MPS_KEY_EXTEND_BY, debug->tagSize); /* FIXME: Check this */ + /* By setting EXTEND_BY to debug->tagSize we get the smallest + possible extensions compatible with the tags, and so the + least amount of wasted space. */ + MPS_ARGS_ADD(pcArgs, MPS_KEY_EXTEND_BY, debug->tagSize); MPS_ARGS_ADD(pcArgs, MPS_KEY_MFS_UNIT_SIZE, debug->tagSize); res = PoolCreate(&debug->tagPool, PoolArena(pool), PoolClassMFS(), pcArgs); } MPS_ARGS_END(pcArgs); if (res != ResOK) goto tagFail; debug->missingTags = 0; - SplayTreeInit(&debug->index, TagComp, NULL); + SplayTreeInit(&debug->index, TagCompare, TagKey, SplayTrivUpdate); } debug->sig = PoolDebugMixinSig; @@ -201,7 +202,6 @@ static Res DebugPoolInit(Pool pool, ArgList args) return ResOK; tagFail: -alignFail: SuperclassOfPool(pool)->finish(pool); AVER(res != ResOK); return res; @@ -227,39 +227,151 @@ static void DebugPoolFinish(Pool pool) } -/* freeSplat -- splat free block with splat pattern +/* patternIterate -- call visitor for occurrences of pattern between + * base and limit + * + * pattern is an arbitrary pattern that's size bytes long. * - * If base is in a segment, the whole block has to be in it. + * Imagine that the entirety of memory were covered by contiguous + * copies of pattern starting at address 0. Then call visitor for each + * copy (or part) of pattern that lies between base and limit. In each + * call, target is the address of the copy or part (where base <= + * target < limit); source is the corresponding byte of the pattern + * (where pattern <= source < pattern + size); and size is the length + * of the copy or part. */ +typedef Bool (*patternVisitor)(Addr target, ReadonlyAddr source, Size size); + +static Bool patternIterate(ReadonlyAddr pattern, Size size, + Addr base, Addr limit, patternVisitor visitor) +{ + Addr p; + + AVER(pattern != NULL); + AVER(0 < size); + AVER(base != NULL); + AVER(base <= limit); + + p = base; + while (p < limit) { + Addr end = AddrAdd(p, size); + Addr rounded = AddrRoundUp(p, size); + Size offset = (Word)p % size; + if (end < p || rounded < p) { + /* Address range overflow */ + break; + } else if (p == rounded && end <= limit) { + /* Room for a whole copy */ + if (!(*visitor)(p, pattern, size)) + return FALSE; + p = end; + } else if (p < rounded && rounded <= end && rounded <= limit) { + /* Copy up to rounded */ + if (!(*visitor)(p, ReadonlyAddrAdd(pattern, offset), + AddrOffset(p, rounded))) + return FALSE; + p = rounded; + } else { + /* Copy up to limit */ + AVER(limit <= end); + AVER(p == rounded || limit <= rounded); + if (!(*visitor)(p, ReadonlyAddrAdd(pattern, offset), + AddrOffset(p, limit))) + return FALSE; + p = limit; + } + } + + return TRUE; +} + + +/* patternCopy -- copy pattern to fill a range + * + * Fill the range of addresses from base (inclusive) to limit + * (exclusive) with copies of pattern (which is size bytes long). + */ + +static Bool patternCopyVisitor(Addr target, ReadonlyAddr source, Size size) +{ + (void)AddrCopy(target, source, size); + return TRUE; +} + +static void patternCopy(ReadonlyAddr pattern, Size size, Addr base, Addr limit) +{ + (void)patternIterate(pattern, size, base, limit, patternCopyVisitor); +} + + +/* patternCheck -- check pattern against a range + * + * Compare the range of addresses from base (inclusive) to limit + * (exclusive) with copies of pattern (which is size bytes long). The + * copies of pattern must be arranged so that fresh copies start at + * aligned addresses wherever possible. + */ + +static Bool patternCheckVisitor(Addr target, ReadonlyAddr source, Size size) +{ + return AddrComp(target, source, size) == 0; +} + +static Bool patternCheck(ReadonlyAddr pattern, Size size, Addr base, Addr limit) +{ + return patternIterate(pattern, size, base, limit, patternCheckVisitor); +} + + +/* debugPoolSegIterate -- iterate over a range of segments in an arena + * + * Expects to be called on a range corresponding to objects withing a + * single pool. + * + * NOTE: This relies on pools consistently using segments + * contiguously. + */ + +static void debugPoolSegIterate(Arena arena, Addr base, Addr limit, + void (*visitor)(Arena, Seg)) +{ + Seg seg; + + if (SegOfAddr(&seg, arena, base)) { + do { + base = SegLimit(seg); + (*visitor)(arena, seg); + } while (base < limit && SegOfAddr(&seg, arena, base)); + AVER(base >= limit); /* shouldn't run out of segments */ + } +} + +static void debugPoolShieldExpose(Arena arena, Seg seg) +{ + ShieldExpose(arena, seg); +} + +static void debugPoolShieldCover(Arena arena, Seg seg) +{ + ShieldCover(arena, seg); +} + + +/* freeSplat -- splat free block with splat pattern */ + static void freeSplat(PoolDebugMixin debug, Pool pool, Addr base, Addr limit) { - Addr p, next; - Size freeSize = debug->freeSize; Arena arena; - Seg seg = NULL; /* suppress "may be used uninitialized" */ - Bool inSeg; AVER(base < limit); - /* If the block is in a segment, make sure any shield is up. */ + /* If the block is in one or more segments, make sure the segments + are exposed so that we can overwrite the block with the pattern. */ arena = PoolArena(pool); - inSeg = SegOfAddr(&seg, arena, base); - if (inSeg) { - AVER(limit <= SegLimit(seg)); - ShieldExpose(arena, seg); - } - /* Write as many copies of the template as fit in the block. */ - for (p = base, next = AddrAdd(p, freeSize); - next <= limit && p < next /* watch out for overflow in next */; - p = next, next = AddrAdd(next, freeSize)) - (void)AddrCopy(p, debug->freeTemplate, freeSize); - /* Fill the tail of the block with a partial copy of the template. */ - if (next > limit || next < p) - (void)AddrCopy(p, debug->freeTemplate, AddrOffset(p, limit)); - if (inSeg) { - ShieldCover(arena, seg); - } + debugPoolSegIterate(arena, base, limit, debugPoolShieldExpose); + patternCopy(debug->freeTemplate, debug->freeSize, base, limit); + debugPoolSegIterate(arena, base, limit, debugPoolShieldCover); } @@ -267,41 +379,17 @@ static void freeSplat(PoolDebugMixin debug, Pool pool, Addr base, Addr limit) static Bool freeCheck(PoolDebugMixin debug, Pool pool, Addr base, Addr limit) { - Addr p, next; - Size freeSize = debug->freeSize; - Res res; + Bool res; Arena arena; - Seg seg = NULL; /* suppress "may be used uninitialized" */ - Bool inSeg; AVER(base < limit); - /* If the block is in a segment, make sure any shield is up. */ + /* If the block is in one or more segments, make sure the segments + are exposed so we can read the pattern. */ arena = PoolArena(pool); - inSeg = SegOfAddr(&seg, arena, base); - if (inSeg) { - AVER(limit <= SegLimit(seg)); - ShieldExpose(arena, seg); - } - /* Compare this to the AddrCopys in freeSplat. */ - /* Check the complete copies of the template in the block. */ - for (p = base, next = AddrAdd(p, freeSize); - next <= limit && p < next /* watch out for overflow in next */; - p = next, next = AddrAdd(next, freeSize)) - if (AddrComp(p, debug->freeTemplate, freeSize) != 0) { - res = FALSE; goto done; - } - /* Check the partial copy of the template at the tail of the block. */ - if (next > limit || next < p) - if (AddrComp(p, debug->freeTemplate, AddrOffset(p, limit)) != 0) { - res = FALSE; goto done; - } - res = TRUE; - -done: - if (inSeg) { - ShieldCover(arena, seg); - } + debugPoolSegIterate(arena, base, limit, debugPoolShieldExpose); + res = patternCheck(debug->freeTemplate, debug->freeSize, base, limit); + debugPoolSegIterate(arena, base, limit, debugPoolShieldCover); return res; } @@ -347,63 +435,75 @@ static void freeCheckFree(PoolDebugMixin debug, * start fp client object slop end fp * * slop is the extra allocation from rounding up the client request to - * the pool's alignment. The fenceposting code does this, so there's a - * better chance of the end fencepost being flush with the next object - * (can't be guaranteed, since the underlying pool could have allocated - * an even larger block). The alignment slop is filled from the - * fencepost template as well (as much as fits, .fence.size guarantees - * the template is larger). + * the pool's alignment. The fenceposting code adds this slop so that + * there's a better chance of the end fencepost being flush with the + * next object (though it can't be guaranteed, since the underlying + * pool could have allocated an even larger block). The alignment slop + * is filled from the fencepost template as well. + * + * Keep in sync with fenceCheck. */ static Res fenceAlloc(Addr *aReturn, PoolDebugMixin debug, Pool pool, Size size, Bool withReservoir) { Res res; - Addr new, clientNew; - Size alignedSize; + Addr obj, startFence, clientNew, clientLimit, limit; + Size alignedFenceSize, alignedSize; AVER(aReturn != NULL); + AVERT(PoolDebugMixin, debug); + AVERT(Pool, pool); + alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool)); alignedSize = SizeAlignUp(size, PoolAlignment(pool)); - res = freeCheckAlloc(&new, debug, pool, alignedSize + 2*debug->fenceSize, + res = freeCheckAlloc(&obj, debug, pool, + alignedSize + 2 * alignedFenceSize, withReservoir); if (res != ResOK) return res; - clientNew = AddrAdd(new, debug->fenceSize); + + startFence = obj; + clientNew = AddrAdd(startFence, alignedFenceSize); + clientLimit = AddrAdd(clientNew, size); + limit = AddrAdd(clientNew, alignedSize + alignedFenceSize); + /* @@@@ shields? */ - /* start fencepost */ - (void)AddrCopy(new, debug->fenceTemplate, debug->fenceSize); - /* alignment slop */ - (void)AddrCopy(AddrAdd(clientNew, size), - debug->fenceTemplate, alignedSize - size); - /* end fencepost */ - (void)AddrCopy(AddrAdd(clientNew, alignedSize), - debug->fenceTemplate, debug->fenceSize); + patternCopy(debug->fenceTemplate, debug->fenceSize, startFence, clientNew); + patternCopy(debug->fenceTemplate, debug->fenceSize, clientLimit, limit); *aReturn = clientNew; - return res; + return ResOK; } -/* fenceCheck -- check fences of an object */ +/* fenceCheck -- check fences of an object + * + * Keep in sync with fenceAlloc. + */ static Bool fenceCheck(PoolDebugMixin debug, Pool pool, Addr obj, Size size) { - Size alignedSize; + Addr startFence, clientNew, clientLimit, limit; + Size alignedFenceSize, alignedSize; AVERT_CRITICAL(PoolDebugMixin, debug); AVERT_CRITICAL(Pool, pool); /* Can't check obj */ + alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool)); alignedSize = SizeAlignUp(size, PoolAlignment(pool)); + + startFence = AddrSub(obj, alignedFenceSize); + clientNew = obj; + clientLimit = AddrAdd(clientNew, size); + limit = AddrAdd(clientNew, alignedSize + alignedFenceSize); + /* @@@@ shields? */ - /* Compare this to the AddrCopys in fenceAlloc */ - return (AddrComp(AddrSub(obj, debug->fenceSize), debug->fenceTemplate, - debug->fenceSize) == 0 - && AddrComp(AddrAdd(obj, size), debug->fenceTemplate, - alignedSize - size) == 0 - && AddrComp(AddrAdd(obj, alignedSize), debug->fenceTemplate, - debug->fenceSize) == 0); + return patternCheck(debug->fenceTemplate, debug->fenceSize, + startFence, clientNew) + && patternCheck(debug->fenceTemplate, debug->fenceSize, + clientLimit, limit); } @@ -412,13 +512,14 @@ static Bool fenceCheck(PoolDebugMixin debug, Pool pool, Addr obj, Size size) static void fenceFree(PoolDebugMixin debug, Pool pool, Addr old, Size size) { - Size alignedSize; + Size alignedFenceSize, alignedSize; ASSERT(fenceCheck(debug, pool, old, size), "fencepost check on free"); + alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool)); alignedSize = SizeAlignUp(size, PoolAlignment(pool)); - freeCheckFree(debug, pool, AddrSub(old, debug->fenceSize), - alignedSize + 2*debug->fenceSize); + freeCheckFree(debug, pool, AddrSub(old, alignedFenceSize), + alignedSize + 2 * alignedFenceSize); } @@ -429,6 +530,7 @@ static Res tagAlloc(PoolDebugMixin debug, { Tag tag; Res res; + Bool b; Addr addr; UNUSED(pool); @@ -443,10 +545,10 @@ static Res tagAlloc(PoolDebugMixin debug, } tag = (Tag)addr; tag->addr = new; tag->size = size; - SplayNodeInit(&tag->splayNode); + TreeInit(TagTree(tag)); /* In the future, we might call debug->tagInit here. */ - res = SplayTreeInsert(&debug->index, &tag->splayNode, (void *)&new); - AVER(res == ResOK); + b = SplayTreeInsert(&debug->index, TagTree(tag)); + AVER(b); return ResOK; } @@ -455,25 +557,25 @@ static Res tagAlloc(PoolDebugMixin debug, static void tagFree(PoolDebugMixin debug, Pool pool, Addr old, Size size) { - SplayNode node; + Tree node; Tag tag; - Res res; + Bool b; AVERT(PoolDebugMixin, debug); AVERT(Pool, pool); AVER(size > 0); - res = SplayTreeSearch(&node, &debug->index, (void *)&old); - if (res != ResOK) { + if (!SplayTreeFind(&node, &debug->index, &old)) { AVER(debug->missingTags > 0); debug->missingTags--; return; } - tag = SplayNode2Tag(node); + tag = TagOfTree(node); AVER(tag->size == size); - res = SplayTreeDelete(&debug->index, node, (void *)&old); - AVER(res == ResOK); - SplayNodeFinish(node); + AVER(tag->addr == old); + b = SplayTreeDelete(&debug->index, node); + AVER(b); /* expect tag to be in the tree */ + TreeFinish(node); PoolFree(debug->tagPool, (Addr)tag, debug->tagSize); } @@ -546,33 +648,28 @@ static void DebugPoolFree(Pool pool, Addr old, Size size) /* TagWalk -- walk all objects in the pool using tags */ -typedef void (*ObjectsStepMethod)(Addr addr, Size size, Format fmt, - Pool pool, void *tagData, void *p); - -#define ObjectsStepMethodCheck(f) \ - ((f) != NULL) /* that's the best we can do */ +typedef void (*ObjectsVisitor)(Addr addr, Size size, Format fmt, + Pool pool, void *tagData, void *p); -static void TagWalk(Pool pool, ObjectsStepMethod step, void *p) +static void TagWalk(Pool pool, ObjectsVisitor visitor, void *p) { - SplayNode node; + Tree node; PoolDebugMixin debug; - Addr dummy = NULL; /* Breaks , but it's */ - /* only temporary until SplayTreeFirst is fixed. */ AVERT(Pool, pool); - AVERT(ObjectsStepMethod, step); + AVER(FUNCHECK(visitor)); /* Can't check p */ debug = DebugPoolDebugMixin(pool); AVER(debug != NULL); AVERT(PoolDebugMixin, debug); - node = SplayTreeFirst(&debug->index, (void *)&dummy); - while (node != NULL) { - Tag tag = SplayNode2Tag(node); + node = SplayTreeFirst(&debug->index); + while (node != TreeEMPTY) { + Tag tag = TagOfTree(node); - step(tag->addr, tag->size, NULL, pool, &tag->userdata, p); - node = SplayTreeNext(&debug->index, node, (void *)&tag->addr); + (*visitor)(tag->addr, tag->size, NULL, pool, &tag->userdata, p); + node = SplayTreeNext(&debug->index, &tag->addr); } } @@ -686,7 +783,7 @@ void PoolClassMixInDebug(PoolClass class) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/dbgpool.h b/code/dbgpool.h index bacecb1125..e01d8c3b65 100644 --- a/code/dbgpool.h +++ b/code/dbgpool.h @@ -3,7 +3,7 @@ * See . * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. */ @@ -15,9 +15,9 @@ #include -/* tag init methods: copying the user-supplied data into the tag */ +/* tag init function: copies the user-supplied data into the tag */ -typedef void (*TagInitMethod)(void* tag, va_list args); +typedef void (*TagInitFunction)(void *tag, va_list args); /* PoolDebugOptions -- option structure for debug pool init @@ -26,11 +26,11 @@ typedef void (*TagInitMethod)(void* tag, va_list args); */ typedef struct PoolDebugOptionsStruct { - void* fenceTemplate; + const void *fenceTemplate; Size fenceSize; - void* freeTemplate; + const void *freeTemplate; Size freeSize; - /* TagInitMethod tagInit; */ + /* TagInitFunction tagInit; */ /* Size tagSize; */ } PoolDebugOptionsStruct; @@ -43,11 +43,11 @@ typedef PoolDebugOptionsStruct *PoolDebugOptions; typedef struct PoolDebugMixinStruct { Sig sig; - Addr fenceTemplate; + const struct AddrStruct *fenceTemplate; Size fenceSize; - Addr freeTemplate; + const struct AddrStruct *freeTemplate; Size freeSize; - TagInitMethod tagInit; + TagInitFunction tagInit; Size tagSize; Pool tagPool; Count missingTags; @@ -73,7 +73,7 @@ extern void DebugPoolFreeCheck(Pool pool, Addr base, Addr limit); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/djbench.c b/code/djbench.c index 09eaa6d513..fbc6dcabc1 100644 --- a/code/djbench.c +++ b/code/djbench.c @@ -13,14 +13,18 @@ #include "mps.c" -#include -#include -#include -#include -#include -#include "getopt.h" #include "testlib.h" +#include "testthr.h" + +#ifdef MPS_OS_W3 +#include "getopt.h" +#else +#include +#endif +#include /* fprintf, stderr */ +#include /* alloca, exit, EXIT_SUCCESS, EXIT_FAILURE */ +#include /* CLOCKS_PER_SEC, clock */ #define DJMUST(expr) \ do { \ @@ -48,6 +52,9 @@ static unsigned sshift = 18; /* log2 max block size in words */ static double pact = 0.2; /* probability per pass of acting */ static unsigned rinter = 75; /* pass interval for recursion */ static unsigned rmax = 10; /* maximum recursion depth */ +static mps_bool_t zoned = TRUE; /* arena allocates using zones */ +static size_t arena_size = 256ul * 1024 * 1024; /* arena size */ +static size_t arena_grain_size = 1; /* arena grain size */ #define DJRUN(fname, alloc, free) \ static unsigned fname##_inner(mps_ap_t ap, unsigned depth, unsigned r) { \ @@ -56,6 +63,7 @@ static unsigned rmax = 10; /* maximum recursion depth */ \ for (k = 0; k < nblocks; ++k) { \ blocks[k].p = NULL; \ + blocks[k].s = 0; \ } \ \ for (j = 0; j < npass; ++j) { \ @@ -64,7 +72,8 @@ static unsigned rmax = 10; /* maximum recursion depth */ if (blocks[k].p == NULL) { \ size_t s = rnd() % ((sizeof(void *) << (rnd() % sshift)) - 1); \ void *p = NULL; \ - if (s > 0) alloc(p, s); \ + if (s > 0) \ + alloc(p, s); \ blocks[k].p = p; \ blocks[k].s = s; \ } else { \ @@ -124,8 +133,8 @@ DJRUN(dj_alloc, MPS_ALLOC, MPS_FREE) #define RESERVE_ALLOC(p, s) \ do { \ size_t _s = ALIGN_UP(s, (size_t)MPS_PF_ALIGN); \ - mps_reserve(&p, ap, _s); \ - mps_commit(ap, p, _s); \ + (void)mps_reserve(&p, ap, _s); \ + (void)mps_commit(ap, p, _s); \ } while(0) #define RESERVE_FREE(p, s) do { mps_free(pool, p, s); } while(0) @@ -135,24 +144,14 @@ typedef void *(*dj_t)(void *); static void weave(dj_t dj) { - pthread_t *threads = alloca(sizeof(threads[0]) * nthreads); + testthr_t *threads = alloca(sizeof(threads[0]) * nthreads); unsigned t; - for (t = 0; t < nthreads; ++t) { - int err = pthread_create(&threads[t], NULL, dj, NULL); - if (err != 0) { - fprintf(stderr, "Unable to create thread: %d\n", err); - exit(EXIT_FAILURE); - } - } + for (t = 0; t < nthreads; ++t) + testthr_create(&threads[t], dj, NULL); - for (t = 0; t < nthreads; ++t) { - int err = pthread_join(threads[t], NULL); - if (err != 0) { - fprintf(stderr, "Unable to join thread: %d\n", err); - exit(EXIT_FAILURE); - } - } + for (t = 0; t < nthreads; ++t) + testthr_join(&threads[t], NULL); } @@ -173,7 +172,7 @@ static void watch(dj_t dj, const char *name) /* Wrap a call to dj benchmark that doesn't require MPS setup */ -static void wrap(dj_t dj, mps_class_t dummy, const char *name) +static void wrap(dj_t dj, mps_pool_class_t dummy, const char *name) { (void)dummy; pool = NULL; @@ -183,10 +182,12 @@ static void wrap(dj_t dj, mps_class_t dummy, const char *name) /* Wrap a call to a dj benchmark that requires MPS setup */ -static void arena_wrap(dj_t dj, mps_class_t pool_class, const char *name) +static void arena_wrap(dj_t dj, mps_pool_class_t pool_class, const char *name) { MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 256ul * 1024 * 1024); /* FIXME: Why is there no default? */ + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arena_size); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, zoned); DJMUST(mps_arena_create_k(&arena, mps_arena_class_vm(), args)); } MPS_ARGS_END(args); DJMUST(mps_pool_create_k(&pool, arena, pool_class, mps_args_none)); @@ -199,32 +200,35 @@ static void arena_wrap(dj_t dj, mps_class_t pool_class, const char *name) /* Command-line options definitions. See getopt_long(3). */ static struct option longopts[] = { - {"help", no_argument, NULL, 'h'}, - {"nthreads",required_argument, NULL, 't'}, - {"niter", required_argument, NULL, 'i'}, - {"npass", required_argument, NULL, 'p'}, - {"nblocks", required_argument, NULL, 'b'}, - {"sshift", required_argument, NULL, 's'}, - {"pact", required_argument, NULL, 'a'}, - {"rinter", required_argument, NULL, 'r'}, - {"rmax", required_argument, NULL, 'd'}, - {"seed", required_argument, NULL, 'x'}, - {NULL, 0, NULL, 0} + {"help", no_argument, NULL, 'h'}, + {"nthreads", required_argument, NULL, 't'}, + {"niter", required_argument, NULL, 'i'}, + {"npass", required_argument, NULL, 'p'}, + {"nblocks", required_argument, NULL, 'b'}, + {"sshift", required_argument, NULL, 's'}, + {"pact", required_argument, NULL, 'c'}, + {"rinter", required_argument, NULL, 'r'}, + {"rmax", required_argument, NULL, 'd'}, + {"seed", required_argument, NULL, 'x'}, + {"arena-size", required_argument, NULL, 'm'}, + {"arena-grain-size", required_argument, NULL, 'a'}, + {"arena-unzoned", no_argument, NULL, 'z'}, + {NULL, 0, NULL, 0 } }; /* Test definitions. */ -static mps_class_t dummy_class(void) +static mps_pool_class_t dummy_class(void) { return NULL; } static struct { const char *name; - void (*wrap)(dj_t, mps_class_t, const char *name); + void (*wrap)(dj_t, mps_pool_class_t, const char *name); dj_t dj; - mps_class_t (*pool_class)(void); + mps_pool_class_t (*pool_class)(void); } pools[] = { {"mvt", arena_wrap, dj_reserve, mps_class_mvt}, {"mvff", arena_wrap, dj_reserve, mps_class_mvff}, @@ -242,7 +246,7 @@ int main(int argc, char *argv[]) { seed = rnd_seed(); - while ((ch = getopt_long(argc, argv, "ht:i:p:b:s:a:r:d:x:", longopts, NULL)) != -1) + while ((ch = getopt_long(argc, argv, "ht:i:p:b:s:c:r:d:m:a:x:z", longopts, NULL)) != -1) switch (ch) { case 't': nthreads = (unsigned)strtoul(optarg, NULL, 10); @@ -259,7 +263,7 @@ int main(int argc, char *argv[]) { case 's': sshift = (unsigned)strtoul(optarg, NULL, 10); break; - case 'a': + case 'c': pact = strtod(optarg, NULL); break; case 'r': @@ -271,10 +275,47 @@ int main(int argc, char *argv[]) { case 'x': seed = strtoul(optarg, NULL, 10); break; + case 'z': + zoned = FALSE; + break; + case 'm': { + char *p; + arena_size = (unsigned)strtoul(optarg, &p, 10); + switch(toupper(*p)) { + case 'G': arena_size <<= 30; break; + case 'M': arena_size <<= 20; break; + case 'K': arena_size <<= 10; break; + case '\0': break; + default: + fprintf(stderr, "Bad arena size %s\n", optarg); + return EXIT_FAILURE; + } + } + break; + case 'a': { + char *p; + arena_grain_size = (unsigned)strtoul(optarg, &p, 10); + switch(toupper(*p)) { + case 'G': arena_grain_size <<= 30; break; + case 'M': arena_grain_size <<= 20; break; + case 'K': arena_grain_size <<= 10; break; + case '\0': break; + default: + fprintf(stderr, "Bad arena grain size %s\n", optarg); + return EXIT_FAILURE; + } + } + break; default: + /* This is printed in parts to keep within the 509 character + limit for string literals in portable standard C. */ fprintf(stderr, "Usage: %s [option...] [test...]\n" "Options:\n" + " -m n, --arena-size=n[KMG]?\n" + " Initial size of arena (default %lu).\n" + " -g n, --arena-grain-size=n[KMG]?\n" + " Arena grain size (default %lu).\n" " -t n, --nthreads=n\n" " Launch n threads each running the test\n" " -i n, --niter=n\n" @@ -284,28 +325,32 @@ int main(int argc, char *argv[]) { " -b n, --nblocks=n\n" " Length of the block array (default %u).\n" " -s n, --sshift=n\n" - " Log2 max block size in words (default %u).\n" - " -a p, --pact=p\n" - " Probability of acting on a block (default %g).\n", + " Log2 max block size in words (default %u).\n", argv[0], + (unsigned long)arena_size, + (unsigned long)arena_grain_size, niter, npass, nblocks, - sshift, - pact); + sshift); fprintf(stderr, + " -c p, --pact=p\n" + " Probability of acting on a block (default %g).\n" " -r n, --rinter=n\n" " Recurse every n passes if n > 0 (default %u).\n" " -d n, --rmax=n\n" " Maximum recursion depth (default %u).\n" " -x n, --seed=n\n" " Random number seed (default from entropy).\n" + " -z, --arena-unzoned\n" + " Disabled zoned allocation in the arena\n" "Tests:\n" " mvt pool class MVT\n" " mvff pool class MVFF\n" " mv pool class MV\n" " mvb pool class MV with buffers\n" " an malloc\n", + pact, rinter, rmax); return EXIT_FAILURE; @@ -314,14 +359,16 @@ int main(int argc, char *argv[]) { argv += optind; printf("seed: %lu\n", seed); - + (void)fflush(stdout); + while (argc > 0) { - for (i = 0; i < sizeof(pools) / sizeof(pools[0]); ++i) + for (i = 0; i < NELEMS(pools); ++i) if (strcmp(argv[0], pools[i].name) == 0) goto found; fprintf(stderr, "unknown pool test \"%s\"\n", argv[0]); return EXIT_FAILURE; found: + (void)mps_lib_assert_fail_install(assert_die); rnd_state_set(seed); pools[i].wrap(pools[i].dj, pools[i].pool_class(), pools[i].name); --argc; @@ -334,7 +381,7 @@ int main(int argc, char *argv[]) { /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/event.c b/code/event.c index 8198319f26..cf9d4e0a8b 100644 --- a/code/event.c +++ b/code/event.c @@ -1,7 +1,7 @@ /* event.c: EVENT LOGGING * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .sources: mps.design.event * @@ -44,13 +44,13 @@ char EventBuffer[EventKindLIMIT][EventBufferSIZE]; char *EventLast[EventKindLIMIT]; /* Pointers to the last even written out of each buffer. */ -char *EventWritten[EventKindLIMIT]; +static char *EventWritten[EventKindLIMIT]; EventControlSet EventKindControl; /* Bit set used to control output. */ /* A single event structure output once per buffer flush. */ -EventEventClockSyncStruct eventClockSyncStruct; +static EventEventClockSyncStruct eventClockSyncStruct; /* eventClockSync -- Populate and write the clock sync event. */ @@ -192,7 +192,8 @@ void EventInit(void) AVER(size_tAlignUp(sizeof(Event##name##Struct), MPS_PF_ALIGN) \ <= EventSizeMAX); \ AVER(Event##name##Code == code); \ - AVER(0 <= code && code <= EventCodeMAX); \ + AVER(0 <= code); \ + AVER(code <= EventCodeMAX); \ AVER(sizeof(#name) - 1 <= EventNameMAX); \ AVER((Bool)Event##name##Always == always); \ AVERT(Bool, always); \ @@ -200,7 +201,7 @@ void EventInit(void) AVER((EventKind)Event##name##Kind < EventKindLIMIT); \ EVENT_##name##_PARAMS(EVENT_PARAM_CHECK, name) - EVENT_LIST(EVENT_CHECK, X) + EVENT_LIST(EVENT_CHECK, X); /* Ensure that no event can be larger than the maximum event size. */ AVER(EventBufferSIZE <= EventSizeMAX); @@ -319,7 +320,7 @@ void EventLabelAddr(Addr addr, EventStringId id) " $U", (WriteFU)event->name.f##index, -Res EventDescribe(Event event, mps_lib_FILE *stream) +Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth) { Res res; @@ -329,15 +330,18 @@ Res EventDescribe(Event event, mps_lib_FILE *stream) if (stream == NULL) return ResFAIL; - res = WriteF(stream, + res = WriteF(stream, depth, "Event $P {\n", (WriteFP)event, " code $U\n", (WriteFU)event->any.code, " clock ", NULL); - if (res != ResOK) return res; - res = EVENT_CLOCK_WRITE(stream, event->any.clock); - if (res != ResOK) return res; - res = WriteF(stream, "\n size $U\n", (WriteFU)event->any.size, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; + res = EVENT_CLOCK_WRITE(stream, depth, event->any.clock); + if (res != ResOK) + return res; + res = WriteF(stream, depth, "\n size $U\n", (WriteFU)event->any.size, NULL); + if (res != ResOK) + return res; switch (event->any.code) { @@ -347,23 +351,25 @@ Res EventDescribe(Event event, mps_lib_FILE *stream) #define EVENT_DESC(X, name, _code, always, kind) \ case _code: \ - res = WriteF(stream, \ + res = WriteF(stream, depth, \ " event \"$S\"", (WriteFS)#name, \ EVENT_##name##_PARAMS(EVENT_DESC_PARAM, name) \ NULL); \ - if (res != ResOK) return res; \ + if (res != ResOK) \ + return res; \ break; EVENT_LIST(EVENT_DESC, X) default: - res = WriteF(stream, " event type unknown", NULL); - if (res != ResOK) return res; + res = WriteF(stream, depth, " event type unknown", NULL); + if (res != ResOK) + return res; /* TODO: Hexdump unknown event contents. */ break; } - res = WriteF(stream, + res = WriteF(stream, depth, "\n} Event $P\n", (WriteFP)event, NULL); return res; @@ -374,10 +380,12 @@ Res EventWrite(Event event, mps_lib_FILE *stream) { Res res; - if (event == NULL) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (event == NULL) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = EVENT_CLOCK_WRITE(stream, event->any.clock); + res = EVENT_CLOCK_WRITE(stream, 0, event->any.clock); if (res != ResOK) return res; @@ -388,16 +396,19 @@ Res EventWrite(Event event, mps_lib_FILE *stream) #define EVENT_WRITE(X, name, code, always, kind) \ case code: \ - res = WriteF(stream, " $S", #name, \ + res = WriteF(stream, 0, " $S", (WriteFS)#name, \ EVENT_##name##_PARAMS(EVENT_WRITE_PARAM, name) \ NULL); \ - if (res != ResOK) return res; \ + if (res != ResOK) \ + return res; \ break; EVENT_LIST(EVENT_WRITE, X) default: - res = WriteF(stream, " ", event->any.code, NULL); - if (res != ResOK) return res; + res = WriteF(stream, 0, " ", + (WriteFU)event->any.code, NULL); + if (res != ResOK) + return res; /* TODO: Hexdump unknown event contents. */ break; } @@ -416,18 +427,18 @@ void EventDump(mps_lib_FILE *stream) /* This can happen if there's a backtrace very early in the life of the MPS, and will cause an access violation if we continue. */ if (!eventInited) { - WriteF(stream, "No events\n", NULL); + (void)WriteF(stream, 0, "No events\n", NULL); return; } for (kind = 0; kind < EventKindLIMIT; ++kind) { for (event = (Event)EventLast[kind]; - event < (Event)(EventBuffer[kind] + EventBufferSIZE); + (char *)event < EventBuffer[kind] + EventBufferSIZE; event = (Event)((char *)event + event->any.size)) { /* Try to keep going even if there's an error, because this is used as a backtrace and we'll take what we can get. */ (void)EventWrite(event, stream); - (void)WriteF(stream, "\n", NULL); + (void)WriteF(stream, 0, "\n", NULL); } } } @@ -468,7 +479,7 @@ EventStringId EventInternString(const char *label) UNUSED(label); /* EventInternString is reached in varieties without events, but the result is not used for anything. */ - return (EventStringId)0x9024EAC8; + return (EventStringId)0x4026EAC8; } @@ -477,7 +488,7 @@ Word EventInternGenString(size_t len, const char *label) UNUSED(len); UNUSED(label); /* EventInternGenString is reached in varieties without events, but the result is not used for anything. */ - return (EventStringId)0x9024EAC8; + return (EventStringId)0x4026EAC8; } @@ -490,10 +501,11 @@ void EventLabelAddr(Addr addr, Word id) } -Res EventDescribe(Event event, mps_lib_FILE *stream) +Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth) { UNUSED(event); UNUSED(stream); + UNUSED(depth); return ResUNIMPL; } @@ -517,7 +529,7 @@ extern void EventDump(mps_lib_FILE *stream) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/event.h b/code/event.h index 3e1463527b..a586509f23 100644 --- a/code/event.h +++ b/code/event.h @@ -33,7 +33,7 @@ extern EventStringId EventInternString(const char *label); extern EventStringId EventInternGenString(size_t, const char *label); extern void EventLabelAddr(Addr addr, Word id); extern void EventFlush(EventKind kind); -extern Res EventDescribe(Event event, mps_lib_FILE *stream); +extern Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth); extern Res EventWrite(Event event, mps_lib_FILE *stream); extern void EventDump(mps_lib_FILE *stream); @@ -87,7 +87,7 @@ extern Word EventKindControl; size = offsetof(Event##name##Struct, f1) + _string_len + sizeof('\0'); \ EVENT_BEGIN(name, size) \ _event->f0 = (p0); \ - mps_lib_memcpy(_event->f1, (string), _string_len); \ + (void)mps_lib_memcpy(_event->f1, (string), _string_len); \ _event->f1[_string_len] = '\0'; \ EVENT_END(name, size); \ END diff --git a/code/eventcnv.c b/code/eventcnv.c index 7cca6846a4..baf8b34323 100644 --- a/code/eventcnv.c +++ b/code/eventcnv.c @@ -1,5 +1,5 @@ /* eventcnv.c: Simple event log converter - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * This is a command-line tool that converts a binary format telemetry output * stream from the MPS into a more-portable textual format. @@ -46,12 +46,6 @@ #include /* for strcmp */ #include "mpstd.h" -#ifdef MPS_BUILD_MV -/* MSVC warning 4996 = stdio / C runtime 'unsafe' */ -/* Objects to: strncpy, sscanf, fopen. See job001934. */ -#pragma warning( disable : 4996 ) -#endif - #define DEFAULT_TELEMETRY_FILENAME "mpsio.log" #define TELEMETRY_FILENAME_ENVAR "MPS_TELEMETRY_FILENAME" @@ -62,18 +56,20 @@ static const char *prog; /* program name */ /* fevwarn -- flush stdout, write message to stderr */ +ATTRIBUTE_FORMAT((printf, 2, 0)) static void fevwarn(const char *prefix, const char *format, va_list args) { - fflush(stdout); /* sync */ - fprintf(stderr, "%s: %s @", prog, prefix); - EVENT_CLOCK_PRINT(stderr, eventTime); - fprintf(stderr, " "); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); + (void)fflush(stdout); /* sync */ + (void)fprintf(stderr, "%s: %s @", prog, prefix); + (void)EVENT_CLOCK_PRINT(stderr, eventTime); + (void)fprintf(stderr, " "); + (void)vfprintf(stderr, format, args); + (void)fprintf(stderr, "\n"); } /* evwarn -- flush stdout, warn to stderr */ +ATTRIBUTE_FORMAT((printf, 1, 2)) static void evwarn(const char *format, ...) { va_list args; @@ -85,6 +81,7 @@ static void evwarn(const char *format, ...) /* everror -- flush stdout, message to stderr, exit */ +ATTRIBUTE_FORMAT((printf, 1, 2)) static void everror(const char *format, ...) { va_list args; @@ -100,10 +97,9 @@ static void everror(const char *format, ...) static void usage(void) { - fprintf(stderr, - "Usage: %s [-f logfile] [-h]\n" - "See \"Telemetry\" in the reference manual for instructions.\n", - prog); + (void)fprintf(stderr, "Usage: %s [-f logfile] [-h]\n" + "See \"Telemetry\" in the reference manual for instructions.\n", + prog); } @@ -176,7 +172,8 @@ static void printParamS(const char *str) putchar('"'); for (i = 0; str[i] != '\0'; ++i) { char c = str[i]; - if (c == '"' || c == '\\') putchar('\\'); + if (c == '"' || c == '\\') + putchar('\\'); putchar(c); } putchar('"'); @@ -201,6 +198,12 @@ static Res eventRead(Bool *eofOut, EventUnion *event, FILE *stream) return ResIO; } + if (event->any.size < sizeof(event->any)) + return ResFAIL; /* invalid size: too small */ + + if (event->any.size > sizeof(*event)) + return ResFAIL; /* invalid size: too large */ + /* Read the rest of the event. */ rest = event->any.size - sizeof(event->any); if (rest > 0) { @@ -268,9 +271,12 @@ static void readLog(FILE *stream) event->EventInit.f5, MPS_WORD_WIDTH); break; + default: + /* No special treatment needed. */ + break; } - EVENT_CLOCK_PRINT(stdout, eventTime); + (void)EVENT_CLOCK_PRINT(stdout, eventTime); printf(" %4X", (unsigned)code); switch (code) { @@ -286,7 +292,7 @@ static void readLog(FILE *stream) } putchar('\n'); - fflush(stdout); + (void)fflush(stdout); } /* while(!feof(input)) */ } @@ -332,7 +338,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/eventcom.h b/code/eventcom.h index 6114b045d4..931c975596 100644 --- a/code/eventcom.h +++ b/code/eventcom.h @@ -1,6 +1,6 @@ /* -- Event Logging Common Definitions * - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * $Id$ * * .sources: mps.design.telemetry @@ -56,7 +56,8 @@ ENUM_DECLARE(EventKind) enum EventDefinitionsEnum { EVENT_LIST(EVENT_ENUM, X) - EventEnumWarningSuppressor /* suppress comma-at-end-of-enum warning */ + /* suppress comma-at-end-of-enum warning */ + EventEnumWarningSuppressor = USHRT_MAX }; @@ -89,7 +90,11 @@ typedef Word EventFW; /* word */ typedef unsigned EventFU; /* unsigned integer */ typedef char EventFS[EventStringLengthMAX + sizeof('\0')]; /* string */ typedef double EventFD; /* double */ -typedef int EventFB; /* boolean */ +/* EventFB must be unsigned (even though Bool is a typedef for int) + * because it used as the type of a bitfield with width 1, and we need + * the legals values of the field to be 0 and 1 (not 0 and -1 which + * would be the case for int : 1). */ +typedef unsigned EventFB; /* Boolean */ /* Event packing bitfield specifiers */ #define EventFP_BITFIELD @@ -133,7 +138,7 @@ typedef union EventUnion { /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/eventdef.h b/code/eventdef.h index 0d9e2ea910..081b45de7c 100644 --- a/code/eventdef.h +++ b/code/eventdef.h @@ -1,7 +1,7 @@ /* -- Event Logging Definitions * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .source: * @@ -31,14 +31,13 @@ * the median version when changing an existing event, * and the major version when changing the format of the event file. * - * TODO: These should go into a header that appears at the start of a - * telemetry stream, but they aren't currently used. Keep updating them - * anyway. RB 2012-09-07 + * These are passed as parameters to the EventInit event at the start + * of a telemetry stream, allowing that stream to be identified. */ #define EVENT_VERSION_MAJOR ((unsigned)1) -#define EVENT_VERSION_MEDIAN ((unsigned)1) -#define EVENT_VERSION_MINOR ((unsigned)6) +#define EVENT_VERSION_MEDIAN ((unsigned)4) +#define EVENT_VERSION_MINOR ((unsigned)0) /* EVENT_LIST -- list of event types and general properties @@ -96,7 +95,7 @@ EVENT(X, PoolFinish , 0x0016, TRUE, Pool) \ EVENT(X, PoolAlloc , 0x0017, TRUE, Object) \ EVENT(X, PoolFree , 0x0018, TRUE, Object) \ - EVENT(X, CBSInit , 0x0019, TRUE, Pool) \ + EVENT(X, LandInit , 0x0019, TRUE, Pool) \ EVENT(X, Intern , 0x001a, TRUE, User) \ EVENT(X, Label , 0x001b, TRUE, User) \ EVENT(X, TraceStart , 0x001c, TRUE, Trace) \ @@ -116,8 +115,8 @@ /* TraceScanArea{Tagged} abuses kind, see .kind.abuse */ \ EVENT(X, TraceScanArea , 0x0029, TRUE, Seg) \ EVENT(X, TraceScanAreaTagged, 0x002a, TRUE, Seg) \ - EVENT(X, VMCreate , 0x002b, TRUE, Arena) \ - EVENT(X, VMDestroy , 0x002c, TRUE, Arena) \ + EVENT(X, VMInit , 0x002b, TRUE, Arena) \ + EVENT(X, VMFinish , 0x002c, TRUE, Arena) \ EVENT(X, VMMap , 0x002d, TRUE, Seg) \ EVENT(X, VMUnmap , 0x002e, TRUE, Seg) \ EVENT(X, ArenaExtend , 0x002f, TRUE, Arena) \ @@ -188,13 +187,13 @@ EVENT(X, VMCompact , 0x0079, TRUE, Arena) \ EVENT(X, amcScanNailed , 0x0080, TRUE, Seg) \ EVENT(X, AMCTraceEnd , 0x0081, TRUE, Trace) \ - EVENT(X, TraceStartPoolGen , 0x0082, TRUE, Trace) \ + EVENT(X, TraceCreatePoolGen , 0x0082, TRUE, Trace) \ /* new events for performance analysis of large heaps. */ \ EVENT(X, TraceCondemnZones , 0x0083, TRUE, Trace) \ EVENT(X, ArenaGenZoneAdd , 0x0084, TRUE, Arena) \ EVENT(X, ArenaUseFreeZone , 0x0085, TRUE, Arena) \ - EVENT(X, ArenaBlacklistZone , 0x0086, TRUE, Arena) - + /* EVENT(X, ArenaBlacklistZone , 0x0086, TRUE, Arena) */ + /* Remember to update EventNameMAX and EventCodeMAX above! (These are checked in EventInit.) */ @@ -312,8 +311,8 @@ PARAM(X, 1, A, old) \ PARAM(X, 2, W, size) -#define EVENT_CBSInit_PARAMS(PARAM, X) \ - PARAM(X, 0, P, cbs) \ +#define EVENT_LandInit_PARAMS(PARAM, X) \ + PARAM(X, 0, P, land) \ PARAM(X, 1, P, owner) #define EVENT_Intern_PARAMS(PARAM, X) \ @@ -369,12 +368,12 @@ PARAM(X, 1, P, base) \ PARAM(X, 2, P, limit) -#define EVENT_VMCreate_PARAMS(PARAM, X) \ +#define EVENT_VMInit_PARAMS(PARAM, X) \ PARAM(X, 0, P, vm) \ PARAM(X, 1, A, base) \ PARAM(X, 2, A, limit) -#define EVENT_VMDestroy_PARAMS(PARAM, X) \ +#define EVENT_VMFinish_PARAMS(PARAM, X) \ PARAM(X, 0, P, vm) #define EVENT_VMMap_PARAMS(PARAM, X) \ @@ -505,7 +504,8 @@ PARAM(X, 0, P, pool) \ PARAM(X, 1, P, arena) \ PARAM(X, 2, W, extendBy) \ - PARAM(X, 3, W, unitSize) + PARAM(X, 3, B, extendSelf) \ + PARAM(X, 4, W, unitSize) #define EVENT_PoolInitAMS_PARAMS(PARAM, X) \ PARAM(X, 0, P, pool) \ @@ -692,8 +692,8 @@ #define EVENT_AMCTraceEnd_PARAMS(PARAM, X) \ PARAM(X, 0, W, epoch) /* current arena epoch */ \ PARAM(X, 1, U, why) /* reason trace started */ \ - PARAM(X, 2, W, align) /* arena alignment */ \ - PARAM(X, 3, W, large) /* AMCLargeSegPAGES */ \ + PARAM(X, 2, W, grainSize) /* arena grain size */ \ + PARAM(X, 3, W, large) /* AMC large size */ \ PARAM(X, 4, W, pRetMin) /* threshold for event */ \ /* remaining parameters are copy of PageRetStruct, which see */ \ PARAM(X, 5, W, pCond) \ @@ -713,18 +713,18 @@ PARAM(X, 19, W, pRL) \ PARAM(X, 20, W, pRLr) -#define EVENT_TraceStartPoolGen_PARAMS(PARAM, X) \ - PARAM(X, 0, P, chain) /* chain (or NULL for topGen) */ \ - PARAM(X, 1, B, top) /* 1 for topGen, 0 otherwise */ \ - PARAM(X, 2, W, index) /* index of generation in the chain */ \ - PARAM(X, 3, P, gendesc) /* generation description */ \ - PARAM(X, 4, W, capacity) /* capacity of generation */ \ - PARAM(X, 5, D, mortality) /* mortality of generation */ \ - PARAM(X, 6, W, zone) /* zone set of generation */ \ - PARAM(X, 7, P, pool) /* pool */ \ - PARAM(X, 8, W, serial) /* pool gen serial number */ \ - PARAM(X, 9, W, totalSize) /* total size of pool gen */ \ - PARAM(X, 10, W, newSizeAtCreate) /* new size of pool gen at trace create */ +#define EVENT_TraceCreatePoolGen_PARAMS(PARAM, X) \ + PARAM(X, 0, P, gendesc) /* generation description */ \ + PARAM(X, 1, W, capacity) /* capacity of generation */ \ + PARAM(X, 2, D, mortality) /* mortality of generation */ \ + PARAM(X, 3, W, zone) /* zone set of generation */ \ + PARAM(X, 4, P, pool) /* pool */ \ + PARAM(X, 5, W, totalSize) /* total size of pool gen */ \ + PARAM(X, 6, W, freeSize) /* free size of pool gen */ \ + PARAM(X, 7, W, newSize) /* new size of pool gen */ \ + PARAM(X, 8, W, oldSize) /* old size of pool gen */ \ + PARAM(X, 9, W, newDeferredSize) /* new size (deferred) of pool gen */ \ + PARAM(X, 10, W, oldDeferredSize) /* old size (deferred) of pool gen */ #define EVENT_TraceCondemnZones_PARAMS(PARAM, X) \ PARAM(X, 0, P, trace) /* the trace */ \ @@ -733,23 +733,19 @@ #define EVENT_ArenaGenZoneAdd_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) /* the arena */ \ - PARAM(X, 1, W, gen) /* the generation number */ \ + PARAM(X, 1, P, gendesc) /* the generation description */ \ PARAM(X, 2, W, zoneSet) /* the new zoneSet */ #define EVENT_ArenaUseFreeZone_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) /* the arena */ \ PARAM(X, 1, W, zoneSet) /* zones that aren't free any longer */ -#define EVENT_ArenaBlacklistZone_PARAMS(PARAM, X) \ - PARAM(X, 0, P, arena) /* the arena */ \ - PARAM(X, 1, W, zoneSet) /* the blacklist zoneset */ - #endif /* eventdef_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/eventrep.c b/code/eventrep.c index c2f611f707..9dae5056fc 100644 --- a/code/eventrep.c +++ b/code/eventrep.c @@ -1,5 +1,5 @@ /* eventrep.c: Allocation replayer routines - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * $Id$ */ @@ -31,7 +31,7 @@ #include "mpstd.h" -#ifdef MPS_PF_W3I6MV +#if defined(MPS_OS_W3) && defined(MPS_ARCH_I6) #define PRIuLONGEST "llu" #define PRIXPTR "016llX" typedef unsigned long long ulongest_t; @@ -116,6 +116,7 @@ typedef struct apRepStruct *apRep; /* error -- error signalling */ +ATTRIBUTE_FORMAT((printf, 1, 2)) static void error(const char *format, ...) { va_list args; @@ -142,37 +143,6 @@ static void error(const char *format, ...) MPS_BEGIN if (!(cond)) error("line %d " #cond, __LINE__); MPS_END -#ifdef MPS_PROD_EPCORE - - -/* ensurePSFormat -- return the PS format, creating it, if necessary */ - -static mps_fmt_t psFormat = NULL; - -static void ensurePSFormat(mps_fmt_t *fmtOut, mps_arena_t arena) -{ - mps_res_t eres; - - if (psFormat == NULL) { - eres = mps_fmt_create_A(&psFormat, arena, ps_fmt_A()); - verifyMPS(eres); - } - *fmtOut = psFormat; -} - - -/* finishPSFormat -- finish the PS format, if necessary */ - -static void finishPSFormat(void) -{ - if (psFormat != NULL) - mps_fmt_destroy(psFormat); -} - - -#endif - - /* objectTableCreate -- create an objectTable */ static objectTable objectTableCreate(poolSupport support) @@ -272,7 +242,8 @@ static void objRemove(void **objReturn, objectTable table, /* poolRecreate -- create and record a pool */ -static void poolRecreate(void *logPool, void *logArena, mps_class_t class, +static void poolRecreate(void *logPool, void *logArena, + mps_pool_class_t pool_class, poolSupport support, int bufferClassLevel, ...) { va_list args; @@ -417,10 +388,6 @@ void EventReplay(Event event, Word etime) case EventArenaDestroy: { /* arena */ found = TableLookup(&entry, arenaTable, (Word)event->p.p0); verify(found); -#ifdef MPS_PROD_EPCORE - /* @@@@ assuming there's only one arena at a time */ - finishPSFormat(); -#endif mps_arena_destroy((mps_arena_t)entry); ires = TableRemove(arenaTable, (Word)event->pw.p0); verify(ires == ResOK); @@ -455,30 +422,6 @@ void EventReplay(Event event, Word etime) /* all internal only */ ++discardedEvents; } break; -#ifdef MPS_PROD_EPCORE - case EventPoolInitEPVM: { - /* pool, arena, format, maxSaveLevel, saveLevel */ - mps_arena_t arena; - mps_fmt_t format; - - found = TableLookup(&entry, arenaTable, (Word)event->pppuu.p1); - verify(found); - arena = (mps_arena_t)entry; - ensurePSFormat(&format, arena); /* We know what the format is. */ - poolRecreate(event->pppuu.p0, event->pppuu.p1, - mps_class_epvm(), supportNothing, 2, format, - (mps_epvm_save_level_t)event->pppuu.u3, - (mps_epvm_save_level_t)event->pppuu.u4); - } break; - case EventPoolInitEPDL: { - /* pool, arena, isEPDL, extendBy, avgSize, align */ - poolRecreate(event->ppuwww.p0, event->ppuwww.p1, - event->ppuwww.u2 ? mps_class_epdl() : mps_class_epdr(), - event->ppuwww.u2 ? supportTruncate : supportFree, 0, - (size_t)event->ppuwww.w3, (size_t)event->ppuwww.w4, - (size_t)event->ppuwww.w5); - } break; -#endif case EventPoolFinish: { /* pool */ found = TableLookup(&entry, poolTable, (Word)event->p.p0); if (found) { @@ -541,22 +484,6 @@ void EventReplay(Event event, Word etime) ++discardedEvents; } } break; -#ifdef MPS_PROD_EPCORE - case EventBufferInitEPVM: { /* buffer, pool, isObj */ - found = TableLookup(&entry, poolTable, (Word)event->ppu.p1); - if (found) { - poolRep rep = (poolRep)entry; - - if(rep->bufferClassLevel == 2) { /* see .bufclass */ - apRecreate(event->ppu.p0, event->ppu.p1, (mps_bool_t)event->ppu.u2); - } else { - ++discardedEvents; - } - } else { - ++discardedEvents; - } - } break; -#endif case EventBufferFinish: { /* buffer */ found = TableLookup(&entry, apTable, (Word)event->p.p0); if (found) { @@ -619,26 +546,6 @@ void EventReplay(Event event, Word etime) ++discardedEvents; } } break; -#ifdef MPS_PROD_EPCORE - case EventPoolPush: { /* pool */ - found = TableLookup(&entry, poolTable, (Word)event->p.p0); - if (found) { - poolRep rep = (poolRep)entry; - - /* It must be EPVM. */ - mps_epvm_save(rep->pool); - } - } break; - case EventPoolPop: { /* pool, level */ - found = TableLookup(&entry, poolTable, (Word)event->pu.p0); - if (found) { - poolRep rep = (poolRep)entry; - - /* It must be EPVM. */ - mps_epvm_restore(rep->pool, (mps_epvm_save_level_t)event->pu.u1); - } - } break; -#endif case EventCommitLimitSet: { /* arena, limit, succeeded */ found = TableLookup(&entry, arenaTable, (Word)event->pwu.p0); verify(found); @@ -659,7 +566,7 @@ void EventReplay(Event event, Word etime) mps_reservoir_limit_set((mps_arena_t)entry, (size_t)event->pw.w1); } break; case EventVMMap: case EventVMUnmap: - case EventVMCreate: case EventVMDestroy: + case EventVMInit: case EventVMFinish: case EventArenaWriteFaults: case EventArenaAlloc: case EventArenaAllocFail: case EventArenaFree: case EventSegAlloc: case EventSegAllocFail: case EventSegFree: @@ -713,11 +620,14 @@ Res EventRepInit(void) totalEvents = 0; discardedEvents = 0; unknownEvents = 0; res = TableCreate(&arenaTable, (size_t)1); - if (res != ResOK) goto failArena; + if (res != ResOK) + goto failArena; res = TableCreate(&poolTable, (size_t)1<<4); - if (res != ResOK) goto failPool; + if (res != ResOK) + goto failPool; res = TableCreate(&apTable, (size_t)1<<6); - if (res != ResOK) goto failAp; + if (res != ResOK) + goto failAp; return ResOK; @@ -744,7 +654,7 @@ void EventRepFinish(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/eventsql.c b/code/eventsql.c index 0fa36dea73..0883862077 100644 --- a/code/eventsql.c +++ b/code/eventsql.c @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (c) 2012-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2012-2014 Ravenbrook Limited. See end of file for license. * * This is a command-line tool that imports events from a text-format * MPS telemetry file into a SQLite database file. @@ -86,9 +86,6 @@ #define DEFAULT_DATABASE_NAME "mpsevent.db" #ifdef MPS_BUILD_MV -/* MSVC warning 4996 = stdio / C runtime 'unsafe' */ -/* Objects to: getenv, sprintf. See job001934. */ -#pragma warning( disable : 4996 ) #define strtoll _strtoi64 #endif @@ -105,7 +102,7 @@ typedef sqlite3_int64 int64; * and for reporting errors. */ -unsigned int verbosity = 0; +static unsigned int verbosity = 0; #define LOG_ALWAYS 0 #define LOG_OFTEN 1 @@ -113,6 +110,7 @@ unsigned int verbosity = 0; #define LOG_SELDOM 3 #define LOG_RARELY 4 +ATTRIBUTE_FORMAT((printf, 2, 0)) static void vlog(unsigned int level, const char *format, va_list args) { if (level <= verbosity) { @@ -123,6 +121,7 @@ static void vlog(unsigned int level, const char *format, va_list args) } } +ATTRIBUTE_FORMAT((printf, 2, 3)) static void evlog(unsigned int level, const char *format, ...) { va_list args; @@ -131,6 +130,7 @@ static void evlog(unsigned int level, const char *format, ...) va_end(args); } +ATTRIBUTE_FORMAT((printf, 1, 2)) static void error(const char *format, ...) { va_list args; @@ -386,7 +386,7 @@ static void testTableExists(sqlite3 *db) size_t i; int defects = 0; int tests = 0; - for (i=0; i < (sizeof(tableTests)/sizeof(tableTests[0])); ++i) { + for (i=0; i < NELEMS(tableTests); ++i) { const char *name = tableTests[i].name; int exists = tableExists(db, name); if (exists) @@ -452,7 +452,7 @@ static void registerLogFile(sqlite3 *db, name = sqlite3_column_text(statement, 0); logSerial = sqlite3_column_int64(statement, 1); completed = sqlite3_column_int64(statement, 2); - evlog(force ? LOG_OFTEN : LOG_ALWAYS, "Log file matching '%s' already in event_log, named \"%s\" (serial %lu, completed %lu).", + evlog(force ? LOG_OFTEN : LOG_ALWAYS, "Log file matching '%s' already in event_log, named \"%s\" (serial %llu, completed %llu).", filename, name, logSerial, completed); if (force) { evlog(LOG_OFTEN, "Continuing anyway because -f specified."); @@ -486,7 +486,7 @@ static void registerLogFile(sqlite3 *db, if (res != SQLITE_DONE) sqlite_error(res, db, "insert into event_log failed."); logSerial = sqlite3_last_insert_rowid(db); - evlog(LOG_SOMETIMES, "Log file %s added to event_log with serial %lu", + evlog(LOG_SOMETIMES, "Log file %s added to event_log with serial %llu", filename, logSerial); finalizeStatement(db, statement); } @@ -508,7 +508,7 @@ static void logFileCompleted(sqlite3 *db, res = sqlite3_step(statement); if (res != SQLITE_DONE) sqlite_error(res, db, "insert into event_log failed."); - evlog(LOG_SOMETIMES, "Marked in event_log: %lu events", completed); + evlog(LOG_SOMETIMES, "Marked in event_log: %llu events", completed); finalizeStatement(db, statement); } @@ -533,7 +533,7 @@ static void logFileCompleted(sqlite3 *db, /* An array of table-creation statement strings. */ -const char *createStatements[] = { +static const char *createStatements[] = { "CREATE TABLE IF NOT EXISTS event_kind (name TEXT," " description TEXT," " enum INTEGER PRIMARY KEY)", @@ -566,12 +566,12 @@ static void makeTables(sqlite3 *db) size_t i; evlog(LOG_SOMETIMES, "Creating tables."); - for (i=0; i < (sizeof(createStatements)/sizeof(createStatements[0])); ++i) { + for (i=0; i < NELEMS(createStatements); ++i) { runStatement(db, createStatements[i], "Table creation"); } } -const char *glueTables[] = { +static const char *glueTables[] = { "event_kind", "event_type", "event_param", @@ -585,7 +585,7 @@ static void dropGlueTables(sqlite3 *db) evlog(LOG_ALWAYS, "Dropping glue tables so they are rebuilt."); - for (i=0; i < (sizeof(glueTables)/sizeof(glueTables[0])); ++i) { + for (i=0; i < NELEMS(glueTables); ++i) { evlog(LOG_SOMETIMES, "Dropping table %s", glueTables[i]); sprintf(sql, "DROP TABLE %s", glueTables[i]); res = sqlite3_exec(db, @@ -864,7 +864,7 @@ static int64 readLog(FILE *input, /* this macro sets statement and last_index */ EVENT_LIST(EVENT_TYPE_WRITE_SQL, X); default: - error("Event %llu has Unknown event code %d", eventCount, code); + error("Event %llu has Unknown event code %ld", eventCount, code); } /* bind the fields we store for every event */ \ res = sqlite3_bind_int64(statement, last_index+1, logSerial); @@ -951,7 +951,7 @@ int main(int argc, char *argv[]) makeTables(db); fillGlueTables(db); count = writeEventsToSQL(db); - evlog(LOG_ALWAYS, "Imported %llu events from %s to %s, serial %lu.", + evlog(LOG_ALWAYS, "Imported %llu events from %s to %s, serial %llu.", count, logFileName, databaseName, logSerial); if (runTests) { @@ -965,7 +965,7 @@ int main(int argc, char *argv[]) /* COPYRIGHT AND LICENSE * - * Copyright (c) 2012-2013 Ravenbrook Limited . + * Copyright (c) 2012-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/eventtxt.c b/code/eventtxt.c index 3971d0e84d..7091474076 100644 --- a/code/eventtxt.c +++ b/code/eventtxt.c @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (c) 2012-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2012-2014 Ravenbrook Limited. See end of file for license. * * This is a command-line tool that converts events from a text-format * MPS telemetry file into a more human-readable format. @@ -29,51 +29,50 @@ * $Id$ */ +#include "check.h" #include "config.h" -#include "eventdef.h" #include "eventcom.h" +#include "eventdef.h" +#include "mps.h" +#include "mpsavm.h" +#include "mpscmvff.h" #include "table.h" #include "testlib.h" /* for ulongest_t and associated print formats */ +#include +#include #include -#include -#include - -#ifdef MPS_BUILD_MV -/* MSVC warning 4996 = stdio / C runtime 'unsafe' */ -/* Objects to: strncpy, sscanf, fopen. See job001934. */ -#pragma warning( disable : 4996 ) -#endif +#include /* exit, EXIT_FAILURE, EXIT_SUCCESS */ +#include /* strcpy, strerror, strlen */ static const char *prog; /* program name */ static const char *logFileName = NULL; /* everror -- error signalling */ +ATTRIBUTE_FORMAT((printf, 1, 2)) static void everror(const char *format, ...) { va_list args; - fflush(stdout); /* sync */ - fprintf(stderr, "%s: ", prog); + (void)fflush(stdout); /* sync */ + (void)fprintf(stderr, "%s: ", prog); va_start(args, format); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); + (void)vfprintf(stderr, format, args); + (void)fprintf(stderr, "\n"); va_end(args); exit(EXIT_FAILURE); } static void usage(void) { - fprintf(stderr, - "Usage: %s [-l ]\n", - prog); + (void)fprintf(stderr, "Usage: %s [-l ]\n", prog); } static void usageError(void) { - usage(); - everror("Bad usage"); + usage(); + everror("Bad usage"); } /* parseArgs -- parse command line arguments */ @@ -113,15 +112,19 @@ static void parseArgs(int argc, char *argv[]) static void *tableAlloc(void *closure, size_t size) { - UNUSED(closure); - return malloc(size); + mps_pool_t pool = closure; + mps_addr_t p; + mps_res_t res; + res = mps_alloc(&p, pool, size); + if (res != MPS_RES_OK) + everror("allocation failed: %d", res); + return p; } static void tableFree(void *closure, void *p, size_t size) { - UNUSED(closure); - UNUSED(size); - free(p); + mps_pool_t pool = closure; + mps_free(pool, p, size); } /* Printing routines */ @@ -135,16 +138,33 @@ static void printStr(const char *str) putchar('"'); for (i = 0; str[i] != '\0'; ++i) { char c = str[i]; - if (c == '"' || c == '\\') putchar('\\'); + if (c == '"' || c == '\\') + putchar('\\'); putchar(c); } putchar('"'); } -/* Reading hex numbers, and doubles, and quoted-and-escaped +/* Reading clocks, hex numbers, and doubles, and quoted-and-escaped * strings. */ +static EventClock parseClock(char **pInOut) +{ + EventClock val; + int i, l; + unsigned long low, high; + char *p = *pInOut; + + i = sscanf(p, "%08lX%08lX%n", &high, &low, &l); + if (i != 2) + everror("Couldn't read a clock from '%s'", p); + EVENT_CLOCK_MAKE(val, low, high); + + *pInOut = p + l; + return val; +} + static ulongest_t parseHex(char **pInOut) { ulongest_t val; @@ -178,7 +198,7 @@ static double parseDouble(char **pInOut) #define MAX_STRING_LENGTH 1024 -char strBuf[MAX_STRING_LENGTH]; +static char strBuf[MAX_STRING_LENGTH]; static char *parseString(char **pInOut) { @@ -222,21 +242,21 @@ static Table internTable; /* dictionary of intern ids to strings */ static Table labelTable; /* dictionary of addrs to intern ids */ -static void createTables(void) +static void createTables(mps_pool_t pool) { Res res; /* MPS intern IDs are serials from zero up, so we can use -1 * and -2 as specials. */ res = TableCreate(&internTable, (size_t)1<<4, - tableAlloc, tableFree, NULL, + tableAlloc, tableFree, pool, (Word)-1, (Word)-2); if (res != ResOK) everror("Couldn't make intern table."); /* We assume that 0 and 1 are invalid as Addrs. */ res = TableCreate(&labelTable, (size_t)1<<7, - tableAlloc, tableFree, NULL, + tableAlloc, tableFree, pool, 0, 1); if (res != ResOK) everror("Couldn't make label table."); @@ -245,19 +265,19 @@ static void createTables(void) /* recordIntern -- record an interned string in the table. a copy of * the string from the parsed buffer into a newly-allocated block. */ -static void recordIntern(char *p) +static void recordIntern(mps_pool_t pool, char *p) { ulongest_t stringId; char *string; - char *copy; + mps_addr_t copy; size_t len; Res res; stringId = parseHex(&p); string = parseString(&p); len = strlen(string); - copy = malloc(len+1); - if (copy == NULL) + res = mps_alloc(©, pool, len + 1); + if (res != MPS_RES_OK) everror("Couldn't allocate space for a string."); (void)strcpy(copy, string); res = TableDefine(internTable, (Word)stringId, (void *)copy); @@ -265,12 +285,55 @@ static void recordIntern(char *p) everror("Couldn't create an intern mapping."); } -/* recordLabel records a label (an association between an address and - * a string ID). Note that the event log may have been generated on a - * platform with addresses larger than Word on the current platform. - * If that happens then we are scuppered because our Table code uses - * Word as the key type: there's nothing we can do except detect this - * bad case (see also the EventInit handling and warning code). +/* Over time there may be multiple labels associated with an address, + * so we keep a list, recording for each label the clock when the + * association was made. This means that printAddr can select the + * label that was in force at the time of the event. + */ + +typedef struct LabelStruct *Label; +typedef struct LabelStruct { + EventClock clock; /* clock of this label */ + ulongest_t id; /* string id of this label */ +} LabelStruct; + +typedef struct LabelListStruct *LabelList; +typedef struct LabelListStruct { + size_t n; /* number of labels in array */ + Label labels; /* labels, sorted in order by clock */ +} LabelListStruct; + +/* labelFind returns the index of the first entry in list with a clock + * value that's greater than 'clock', or list->n if there is no such + * label. The list is assumed to be sorted. + */ + +static size_t labelFind(LabelList list, EventClock clock) +{ + size_t low = 0, high = list->n; + while (low < high) { + size_t mid = (low + high) / 2; + assert(NONNEGATIVE(mid) && mid < list->n); + if (list->labels[mid].clock > clock) { + high = mid; + } else { + low = mid + 1; + } + } + assert(NONNEGATIVE(low) && low <= list->n); + assert(low == list->n || list->labels[low].clock > clock); + return low; +} + +/* recordLabel records a label: an association (made at the time given + * by 'clock') between an address and a string ID. These are encoded + * as two hexadecimal numbers in the string pointed to by 'p'. + * + * Note that the event log may have been generated on a platform with + * addresses larger than Word on the current platform. If that happens + * then we are scuppered because our Table code uses Word as the key + * type: there's nothing we can do except detect this bad case (see + * also the EventInit handling and warning code). * * We can and do handle the case where string IDs (which are Words on * the MPS platform) are larger than void* on the current platform. @@ -281,25 +344,50 @@ static void recordIntern(char *p) * probably a bad idea and maybe doomed to failure. */ -static void recordLabel(char *p) +static void recordLabel(mps_pool_t pool, EventClock clock, char *p) { ulongest_t address; - ulongest_t *stringIdP; + LabelList list; + Label newlabels; + mps_addr_t tmp; + size_t pos; Res res; - + address = parseHex(&p); if (address > (Word)-1) { - printf("label address too large!"); + (void)printf("label address too large!"); return; } - - stringIdP = malloc(sizeof(ulongest_t)); - if (stringIdP == NULL) - everror("Can't allocate space for a string's ID"); - *stringIdP = parseHex(&p); - res = TableDefine(labelTable, (Word)address, (void *)stringIdP); + + if (TableLookup(&tmp, labelTable, address)) { + list = tmp; + } else { + /* First label for this address */ + res = mps_alloc(&tmp, pool, sizeof(LabelListStruct)); + if (res != MPS_RES_OK) + everror("Can't allocate space for a label list"); + list = tmp; + list->n = 0; + res = TableDefine(labelTable, (Word)address, list); + if (res != ResOK) + everror("Couldn't create a label mapping."); + } + + res = mps_alloc(&tmp, pool, sizeof(LabelStruct) * (list->n + 1)); if (res != ResOK) - everror("Couldn't create an intern mapping."); + everror("Couldn't allocate space for list of labels."); + newlabels = tmp; + + pos = labelFind(list, clock); + memcpy(newlabels, list->labels, sizeof(LabelStruct) * pos); + newlabels[pos].clock = clock; + newlabels[pos].id = parseHex(&p); + memcpy(newlabels + pos + 1, list->labels + pos, + sizeof(LabelStruct) * (list->n - pos)); + if (list->n > 0) + mps_free(pool, list->labels, sizeof(LabelStruct) * list->n); + list->labels = newlabels; + ++ list->n; } /* output code */ @@ -315,20 +403,23 @@ static int hexWordWidth = (MPS_WORD_WIDTH+3)/4; /* printAddr -- output a ulongest_t in hex, with the interned string * if the value is in the label table */ -static void printAddr(ulongest_t addr, const char *ident) +static void printAddr(EventClock clock, ulongest_t addr, const char *ident) { - ulongest_t label; - void *alias; + void *tmp; printf("%s:%0*" PRIXLONGEST, ident, hexWordWidth, addr); - if (TableLookup(&alias, labelTable, addr)) { - label = *(ulongest_t*)alias; - putchar('['); - if (TableLookup(&alias, internTable, label)) - printStr((char *)alias); - else - printf("unknown label %" PRIuLONGEST, label); - putchar(']'); + if (TableLookup(&tmp, labelTable, addr)) { + LabelList list = tmp; + size_t pos = labelFind(list, clock); + if (pos > 0) { + ulongest_t id = list->labels[pos - 1].id; + putchar('['); + if (TableLookup(&tmp, internTable, id)) + printStr((char *)tmp); + else + printf("unknown label %" PRIXLONGEST, id); + putchar(']'); + } } putchar(' '); } @@ -339,7 +430,7 @@ static void printAddr(ulongest_t addr, const char *ident) #define processParamA(ident) \ val_hex = parseHex(&p); \ - printAddr(val_hex, #ident); + printAddr(clock, val_hex, #ident); #define processParamP processParamA #define processParamW processParamA @@ -382,7 +473,7 @@ static const char *eventName[EventCodeMAX+EventCodeMAX]; /* readLog -- read and parse log. Returns the number of events written. */ -static void readLog(FILE *input) +static void readLog(mps_pool_t pool, FILE *input) { int i; @@ -394,7 +485,7 @@ static void readLog(FILE *input) while (TRUE) { /* loop for each event */ char line[MAX_LOG_LINE_LENGTH]; char *p, *q; - ulongest_t clock; + EventClock clock; int code; ulongest_t val_hex; double val_float; @@ -408,23 +499,23 @@ static void readLog(FILE *input) everror("Couldn't read line from input."); } - clock = parseHex(&p); - code = (int)parseHex(&p); + clock = parseClock(&p); + EVENT_CLOCK_PRINT(stdout, clock); + code = (int)parseHex(&p); + printf(" %04X ", code); if (eventName[code]) - printf("%0*" PRIXLONGEST " %04X %-19s ", hexWordWidth, clock, code, - eventName[code]); + printf("%-19s ", eventName[code]); else - printf("%0*" PRIXLONGEST " %04X %-19s ", hexWordWidth, clock, code, - "[Unknown]"); + printf("%-19s ", "[Unknown]"); q = p; /* for a few particular codes, we do local processing. */ if (code == EventInternCode) { - recordIntern(q); + recordIntern(pool, q); } else if (code == EventLabelCode) { - recordLabel(q); + recordLabel(pool, clock, q); } else if (code == EventEventInitCode) { ulongest_t major, median, minor, maxCode, maxNameLen, wordWidth, clocksPerSec; major = parseHex(&q); /* EVENT_VERSION_MAJOR */ @@ -440,24 +531,27 @@ static void readLog(FILE *input) if ((major != EVENT_VERSION_MAJOR) || (median != EVENT_VERSION_MEDIAN) || (minor != EVENT_VERSION_MINOR)) { - fprintf(stderr, "Event log version does not match: %d.%d.%d vs %d.%d.%d\n", - (int)major, (int)median, (int)minor, - EVENT_VERSION_MAJOR, - EVENT_VERSION_MEDIAN, - EVENT_VERSION_MINOR); + (void)fprintf(stderr, "Event log version does not match: " + "%d.%d.%d vs %d.%d.%d\n", + (int)major, (int)median, (int)minor, + EVENT_VERSION_MAJOR, + EVENT_VERSION_MEDIAN, + EVENT_VERSION_MINOR); } if (maxCode > EventCodeMAX) { - fprintf(stderr, "Event log may contain unknown events with codes from %d to %d\n", - EventCodeMAX+1, (int)maxCode); + (void)fprintf(stderr, "Event log may contain unknown events " + "with codes from %d to %d\n", + EventCodeMAX+1, (int)maxCode); } if (wordWidth > MPS_WORD_WIDTH) { int newHexWordWidth = (int)((wordWidth + 3) / 4); if (newHexWordWidth > hexWordWidth) { - fprintf(stderr, - "Event log word width is greater than on current platform;" - "previous values may be printed too narrowly.\n"); + (void)fprintf(stderr, + "Event log word width is greater than on current " + "platform; previous values may be printed too " + "narrowly.\n"); } hexWordWidth = newHexWordWidth; } @@ -480,6 +574,9 @@ static void readLog(FILE *input) int main(int argc, char *argv[]) { + mps_arena_t arena; + mps_pool_t pool; + mps_res_t res; FILE *input; parseArgs(argc, argv); @@ -492,15 +589,32 @@ int main(int argc, char *argv[]) everror("unable to open %s", logFileName); } - createTables(); - readLog(input); + /* Ensure no telemetry output. */ + res = setenv("MPS_TELEMETRY_CONTROL", "0", 1); + if (res != 0) + everror("failed to set MPS_TELEMETRY_CONTROL: %s", strerror(errno)); + + res = mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none); + if (res != MPS_RES_OK) + everror("failed to create arena: %d", res); + + res = mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none); + if (res != MPS_RES_OK) + everror("failed to create pool: %d", res); + + createTables(pool); + readLog(pool, input); + + mps_pool_destroy(pool); + mps_arena_destroy(arena); + (void)fclose(input); return 0; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2012-2013 Ravenbrook Limited . + * Copyright (C) 2012-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/exposet0.c b/code/exposet0.c index b204b59d26..7e097d6b03 100644 --- a/code/exposet0.c +++ b/code/exposet0.c @@ -1,7 +1,7 @@ /* exposet0.c: ARENA EXPOSE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * The primary purpose of this test is to test that mps_arena_expose does @@ -19,12 +19,9 @@ #include "mpscamc.h" #include "mpsavm.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif #include "mps.h" -#include -#include + +#include /* fflush, printf, puts, stdout */ /* These values have been tuned in the hope of getting one dynamic collection. */ @@ -75,12 +72,6 @@ static void report(mps_arena_t arena) printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); mps_message_discard(arena, message); - - if (condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024) - /* When condemned size is larger than could happen in a gen 2 - * collection (discounting ramps, natch), guess that was a dynamic - * collection, and reset the commit limit, so it doesn't run out. */ - die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit"); } } @@ -115,15 +106,7 @@ static void test_stepper(mps_addr_t object, mps_fmt_t fmt, mps_pool_t pool, testlib_unused(fmt); testlib_unused(pool); testlib_unused(s); -#ifdef MPS_OS_W3 - __try { - dylan_mutate(object); - } __except(EXCEPTION_EXECUTE_HANDLER) { - error("Unexpected exception.\n"); - } -#else dylan_mutate(object); -#endif (*(unsigned long *)p)++; } @@ -185,7 +168,8 @@ static void *test(void *arg, size_t s) if (collections != c) { collections = c; - printf("\nCollection %lu started, %lu objects.\n", c, objs); + printf("\nCollection %"PRIuLONGEST" started, %lu objects.\n", + (ulongest_t)c, objs); report(arena); for (i = 0; i < exactRootsCOUNT; ++i) { @@ -231,13 +215,14 @@ static void *test(void *arg, size_t s) if (objs % 1024 == 0) { report(arena); putchar('.'); - fflush(stdout); + (void)fflush(stdout); } ++objs; } (void)mps_commit(busy_ap, busy_init, 64); + mps_arena_park(arena); mps_ap_destroy(busy_ap); mps_ap_destroy(ap); mps_root_destroy(exactRoot); @@ -255,13 +240,12 @@ int main(int argc, char *argv[]) mps_thr_t thread; void *r; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), 2*testArenaSIZE), "arena_create"); mps_message_type_enable(arena, mps_message_type_gc()); - die(mps_arena_commit_limit_set(arena, testArenaSIZE), "set limit"); + die(mps_arena_commit_limit_set(arena, 2*testArenaSIZE), "set limit"); die(mps_thread_reg(&thread, arena), "thread_reg"); mps_tramp(&r, test, arena, 0); mps_thread_dereg(thread); @@ -275,7 +259,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/expt825.c b/code/expt825.c index 9c7c4556d7..bd6c5f2b1e 100644 --- a/code/expt825.c +++ b/code/expt825.c @@ -1,7 +1,7 @@ /* expt825.c: Test for bug described in job000825 * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * DESIGN @@ -34,10 +34,8 @@ #include "fmtdy.h" #include "fmtdytst.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif -#include + +#include /* printf, fflush, stdout */ #define testArenaSIZE ((size_t)16<<20) @@ -86,7 +84,8 @@ static void register_numbered_tree(mps_word_t tree, mps_arena_t arena) { /* don't finalize ints */ if ((tree & 1) == 0) { - mps_finalize(arena, (mps_addr_t *)&tree); + mps_addr_t addr = (void *)tree; + die(mps_finalize(arena, &addr), "mps_finalize"); register_numbered_tree(DYLAN_VECTOR_SLOT(tree, 0), arena); register_numbered_tree(DYLAN_VECTOR_SLOT(tree, 1), arena); } @@ -124,8 +123,8 @@ static void register_indirect_tree(mps_word_t tree, mps_arena_t arena) { /* don't finalize ints */ if ((tree & 1) == 0) { - mps_word_t indirect = DYLAN_VECTOR_SLOT(tree,2); - mps_finalize(arena, (mps_addr_t *)&indirect); + mps_addr_t indirect = (void *)DYLAN_VECTOR_SLOT(tree,2); + die(mps_finalize(arena, &indirect), "mps_finalize"); register_indirect_tree(DYLAN_VECTOR_SLOT(tree, 0), arena); register_indirect_tree(DYLAN_VECTOR_SLOT(tree, 1), arena); } @@ -186,7 +185,7 @@ static void *test(void *arg, size_t s) (mps_collections(arena) < collectionCOUNT)) { mps_word_t final_this_time = 0; printf("Collecting..."); - fflush(stdout); + (void)fflush(stdout); die(mps_arena_collect(arena), "collect"); printf(" Done.\n"); while (mps_message_poll(arena)) { @@ -202,8 +201,10 @@ static void *test(void *arg, size_t s) testlib_unused(obj); } finals += final_this_time; - printf("%lu objects finalized: total %lu of %lu\n", - final_this_time, finals, object_count); + printf("%"PRIuLONGEST" objects finalized: total %"PRIuLONGEST + " of %"PRIuLONGEST"\n", + (ulongest_t)final_this_time, (ulongest_t)finals, + (ulongest_t)object_count); } object_count = 0; @@ -227,7 +228,7 @@ static void *test(void *arg, size_t s) (mps_collections(arena) < collectionCOUNT)) { mps_word_t final_this_time = 0; printf("Collecting..."); - fflush(stdout); + (void)fflush(stdout); die(mps_arena_collect(arena), "collect"); printf(" Done.\n"); while (mps_message_poll(arena)) { @@ -243,10 +244,13 @@ static void *test(void *arg, size_t s) testlib_unused(obj); } finals += final_this_time; - printf("%lu objects finalized: total %lu of %lu\n", - final_this_time, finals, object_count); + printf("%"PRIuLONGEST" objects finalized: total %"PRIuLONGEST + " of %"PRIuLONGEST"\n", + (ulongest_t)final_this_time, (ulongest_t)finals, + (ulongest_t)object_count); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_root_destroy(mps_root); mps_pool_destroy(amc); @@ -262,7 +266,8 @@ int main(int argc, char *argv[]) mps_arena_t arena; mps_thr_t thread; void *r; - testlib_unused(argc); + + testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "arena_create\n"); @@ -278,7 +283,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/failover.c b/code/failover.c new file mode 100644 index 0000000000..750c6b1b17 --- /dev/null +++ b/code/failover.c @@ -0,0 +1,363 @@ +/* failover.c: FAILOVER IMPLEMENTATION + * + * $Id$ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * .design: + */ + +#include "failover.h" +#include "mpm.h" +#include "range.h" + +SRCID(failover, "$Id$"); + + +#define failoverOfLand(land) PARENT(FailoverStruct, landStruct, land) + + +ARG_DEFINE_KEY(failover_primary, Pointer); +ARG_DEFINE_KEY(failover_secondary, Pointer); + + +Bool FailoverCheck(Failover fo) +{ + CHECKS(Failover, fo); + CHECKD(Land, &fo->landStruct); + CHECKD(Land, fo->primary); + CHECKD(Land, fo->secondary); + return TRUE; +} + + +static Res failoverInit(Land land, ArgList args) +{ + Failover fo; + LandClass super; + Land primary, secondary; + ArgStruct arg; + Res res; + + AVERT(Land, land); + super = LAND_SUPERCLASS(FailoverLandClass); + res = (*super->init)(land, args); + if (res != ResOK) + return res; + + ArgRequire(&arg, args, FailoverPrimary); + primary = arg.val.p; + ArgRequire(&arg, args, FailoverSecondary); + secondary = arg.val.p; + + fo = failoverOfLand(land); + fo->primary = primary; + fo->secondary = secondary; + fo->sig = FailoverSig; + AVERT(Failover, fo); + return ResOK; +} + + +static void failoverFinish(Land land) +{ + Failover fo; + + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + + fo->sig = SigInvalid; +} + + +static Size failoverSize(Land land) +{ + Failover fo; + + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + + return LandSize(fo->primary) + LandSize(fo->secondary); +} + + +static Res failoverInsert(Range rangeReturn, Land land, Range range) +{ + Failover fo; + Res res; + + AVER(rangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVERT(Range, range); + + /* Provide more opportunities for coalescence. See + * . + */ + (void)LandFlush(fo->primary, fo->secondary); + + res = LandInsert(rangeReturn, fo->primary, range); + if (res != ResOK && res != ResFAIL) + res = LandInsert(rangeReturn, fo->secondary, range); + + return res; +} + + +static Res failoverDelete(Range rangeReturn, Land land, Range range) +{ + Failover fo; + Res res; + RangeStruct oldRange, dummyRange, left, right; + + AVER(rangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVERT(Range, range); + + /* Prefer efficient search in the primary. See + * . + */ + (void)LandFlush(fo->primary, fo->secondary); + + res = LandDelete(&oldRange, fo->primary, range); + + if (res == ResFAIL) { + /* Range not found in primary: try secondary. */ + return LandDelete(rangeReturn, fo->secondary, range); + } else if (res != ResOK) { + /* Range was found in primary, but couldn't be deleted. The only + * case we expect to encounter here is the case where the primary + * is out of memory. (In particular, we don't handle the case of a + * CBS returning ResLIMIT because its block pool has been + * configured not to automatically extend itself.) + */ + AVER(ResIsAllocFailure(res)); + + /* Delete the whole of oldRange, and re-insert the fragments + * (which might end up in the secondary). See + * . + */ + res = LandDelete(&dummyRange, fo->primary, &oldRange); + if (res != ResOK) + return res; + + AVER(RangesEqual(&oldRange, &dummyRange)); + RangeInit(&left, RangeBase(&oldRange), RangeBase(range)); + if (!RangeIsEmpty(&left)) { + /* Don't call LandInsert(..., land, ...) here: that would be + * re-entrant and fail the landEnter check. */ + res = LandInsert(&dummyRange, fo->primary, &left); + if (res != ResOK) { + /* The range was successful deleted from the primary above. */ + AVER(res != ResFAIL); + res = LandInsert(&dummyRange, fo->secondary, &left); + AVER(res == ResOK); + } + } + RangeInit(&right, RangeLimit(range), RangeLimit(&oldRange)); + if (!RangeIsEmpty(&right)) { + res = LandInsert(&dummyRange, fo->primary, &right); + if (res != ResOK) { + /* The range was successful deleted from the primary above. */ + AVER(res != ResFAIL); + res = LandInsert(&dummyRange, fo->secondary, &right); + AVER(res == ResOK); + } + } + } + if (res == ResOK) { + AVER(RangesNest(&oldRange, range)); + RangeCopy(rangeReturn, &oldRange); + } + return res; +} + + +static Bool failoverIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +{ + Failover fo; + + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVER(visitor != NULL); + + return LandIterate(fo->primary, visitor, closureP, closureS) + && LandIterate(fo->secondary, visitor, closureP, closureS); +} + + +static Bool failoverFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + Failover fo; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVERT(FindDelete, findDelete); + + /* See . */ + (void)LandFlush(fo->primary, fo->secondary); + + return LandFindFirst(rangeReturn, oldRangeReturn, fo->primary, size, findDelete) + || LandFindFirst(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete); +} + + +static Bool failoverFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + Failover fo; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVERT(FindDelete, findDelete); + + /* See . */ + (void)LandFlush(fo->primary, fo->secondary); + + return LandFindLast(rangeReturn, oldRangeReturn, fo->primary, size, findDelete) + || LandFindLast(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete); +} + + +static Bool failoverFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + Failover fo; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + AVERT(FindDelete, findDelete); + + /* See . */ + (void)LandFlush(fo->primary, fo->secondary); + + return LandFindLargest(rangeReturn, oldRangeReturn, fo->primary, size, findDelete) + || LandFindLargest(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete); +} + + +static Bool failoverFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) +{ + Failover fo; + Bool found = FALSE; + Res res; + + AVER(FALSE); /* TODO: this code is completely untested! */ + AVER(foundReturn != NULL); + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fo = failoverOfLand(land); + AVERT(Failover, fo); + /* AVERT(ZoneSet, zoneSet); */ + AVERT(Bool, high); + + /* See . */ + (void)LandFlush(fo->primary, fo->secondary); + + res = LandFindInZones(&found, rangeReturn, oldRangeReturn, fo->primary, size, zoneSet, high); + if (res != ResOK || !found) + res = LandFindInZones(&found, rangeReturn, oldRangeReturn, fo->secondary, size, zoneSet, high); + + *foundReturn = found; + return res; +} + + +static Res failoverDescribe(Land land, mps_lib_FILE *stream, Count depth) +{ + Failover fo; + Res res; + + if (!TESTT(Land, land)) + return ResFAIL; + fo = failoverOfLand(land); + if (!TESTT(Failover, fo)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "Failover $P {\n", (WriteFP)fo, + " primary = $P ($S)\n", (WriteFP)fo->primary, + (WriteFS)fo->primary->class->name, + " secondary = $P ($S)\n", (WriteFP)fo->secondary, + (WriteFS)fo->secondary->class->name, + "}\n", NULL); + + return res; +} + + +DEFINE_LAND_CLASS(FailoverLandClass, class) +{ + INHERIT_CLASS(class, LandClass); + class->name = "FAILOVER"; + class->size = sizeof(FailoverStruct); + class->init = failoverInit; + class->finish = failoverFinish; + class->sizeMethod = failoverSize; + class->insert = failoverInsert; + class->delete = failoverDelete; + class->iterate = failoverIterate; + class->findFirst = failoverFindFirst; + class->findLast = failoverFindLast; + class->findLargest = failoverFindLargest; + class->findInZones = failoverFindInZones; + class->describe = failoverDescribe; + AVERT(LandClass, class); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/mpsw3.h b/code/failover.h similarity index 69% rename from code/mpsw3.h rename to code/failover.h index 2f543ddc0f..3676bade10 100644 --- a/code/mpsw3.h +++ b/code/failover.h @@ -1,45 +1,37 @@ -/* mpsw3.h: RAVENBROOK MEMORY POOL SYSTEM C INTERFACE, WINDOWS PART +/* failover.h: FAILOVER ALLOCATOR INTERFACE * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. * - * .readership: customers, MPS developers. - * .sources: . + * .source: . */ -#ifndef mpsw3_h -#define mpsw3_h +#ifndef failover_h +#define failover_h -#include "mps.h" /* needed for mps_tramp_t */ -#include /* needed for SEH filter */ +#include "mpmtypes.h" +typedef struct FailoverStruct *Failover; -extern LONG mps_SEH_filter(LPEXCEPTION_POINTERS, void **, size_t *); -extern void mps_SEH_handler(void *, size_t); +#define FailoverLand(fo) (&(fo)->landStruct) +extern Bool FailoverCheck(Failover failover); -#define mps_tramp(r_o, f, p, s) \ - MPS_BEGIN \ - void **_r_o = (r_o); \ - mps_tramp_t _f = (f); \ - void *_p = (p); \ - size_t _s = (s); \ - void *_hp = NULL; size_t _hs = 0; \ - __try { \ - *_r_o = (*_f)(_p, _s); \ - } __except(mps_SEH_filter(GetExceptionInformation(), \ - &_hp, &_hs)) { \ - mps_SEH_handler(_hp, _hs); \ - } \ - MPS_END +extern LandClass FailoverLandClassGet(void); +extern const struct mps_key_s _mps_key_failover_primary; +#define FailoverPrimary (&_mps_key_failover_primary) +#define FailoverPrimary_FIELD p +extern const struct mps_key_s _mps_key_failover_secondary; +#define FailoverSecondary (&_mps_key_failover_secondary) +#define FailoverSecondary_FIELD p -#endif /* mpsw3_h */ +#endif /* failover.h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/fbmtest.c b/code/fbmtest.c index 954a647dfd..f5aa6831db 100644 --- a/code/fbmtest.c +++ b/code/fbmtest.c @@ -1,7 +1,7 @@ /* fbmtest.c: FREE BLOCK MANAGEMENT TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * The MPS contains two free block management modules: * @@ -21,12 +21,9 @@ #include "mpm.h" #include "mps.h" #include "mpsavm.h" -#include "mpstd.h" #include "testlib.h" -#include -#include -#include +#include /* printf */ SRCID(fbmtest, "$Id$"); @@ -41,7 +38,7 @@ SRCID(fbmtest, "$Id$"); static Count NAllocateTried, NAllocateSucceeded, NDeallocateTried, NDeallocateSucceeded; -static int verbose = 0; +static Bool verbose = FALSE; typedef unsigned FBMType; enum { @@ -83,13 +80,15 @@ static Index (indexOfAddr)(FBMState state, Addr a) static void describe(FBMState state) { switch (state->type) { case FBMTypeCBS: - CBSDescribe(state->the.cbs, mps_lib_get_stdout()); + die(CBSDescribe(state->the.cbs, mps_lib_get_stdout(), 0), + "CBSDescribe"); break; case FBMTypeFreelist: - FreelistDescribe(state->the.fl, mps_lib_get_stdout()); + die(FreelistDescribe(state->the.fl, mps_lib_get_stdout(), 0), + "FreelistDescribe"); break; default: - fail(); + cdie(0, "invalid state->type"); break; } } @@ -100,6 +99,7 @@ static Bool checkCallback(Range range, void *closureP, Size closureS) Addr base, limit; CheckFBMClosure cl = (CheckFBMClosure)closureP; + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); Insist(cl != NULL); @@ -151,13 +151,13 @@ static void check(FBMState state) switch (state->type) { case FBMTypeCBS: - CBSIterate(state->the.cbs, checkCBSCallback, (void *)&closure, 0); + CBSIterate(state->the.cbs, checkCBSCallback, &closure, UNUSED_SIZE); break; case FBMTypeFreelist: - FreelistIterate(state->the.fl, checkFLCallback, (void *)&closure, 0); + FreelistIterate(state->the.fl, checkFLCallback, &closure, UNUSED_SIZE); break; default: - fail(); + cdie(0, "invalid state->type"); return; } @@ -311,7 +311,7 @@ static void allocate(FBMState state, Addr base, Addr limit) res = FreelistDelete(&oldRange, state->the.fl, &range); break; default: - fail(); + cdie(0, "invalid state->type"); return; } @@ -387,7 +387,7 @@ static void deallocate(FBMState state, Addr base, Addr limit) res = FreelistInsert(&freeRange, state->the.fl, &range); break; default: - fail(); + cdie(0, "invalid state->type"); return; } @@ -432,20 +432,23 @@ static void find(FBMState state, Size size, Bool high, FindDelete findDelete) remainderLimit = origLimit = addrOfIndex(state, expectedLimit); switch(findDelete) { - case FindDeleteNONE: { + case FindDeleteNONE: /* do nothing */ - } break; - case FindDeleteENTIRE: { + break; + case FindDeleteENTIRE: remainderBase = remainderLimit; - } break; - case FindDeleteLOW: { + break; + case FindDeleteLOW: expectedLimit = expectedBase + size; remainderBase = addrOfIndex(state, expectedLimit); - } break; - case FindDeleteHIGH: { + break; + case FindDeleteHIGH: expectedBase = expectedLimit - size; remainderLimit = addrOfIndex(state, expectedBase); - } break; + break; + default: + cdie(0, "invalid findDelete"); + break; } if (findDelete != FindDeleteNONE) { @@ -467,7 +470,7 @@ static void find(FBMState state, Size size, Bool high, FindDelete findDelete) (&foundRange, &oldRange, state->the.fl, size * state->align, findDelete); break; default: - fail(); + cdie(0, "invalid state->type"); return; } @@ -528,9 +531,7 @@ static void test(FBMState state, unsigned n) { size = fbmRnd(ArraySize / 10) + 1; high = fbmRnd(2) ? TRUE : FALSE; switch(fbmRnd(6)) { - case 0: - case 1: - case 2: findDelete = FindDeleteNONE; break; + default: findDelete = FindDeleteNONE; break; case 3: findDelete = FindDeleteLOW; break; case 4: findDelete = FindDeleteHIGH; break; case 5: findDelete = FindDeleteENTIRE; break; @@ -538,11 +539,13 @@ static void test(FBMState state, unsigned n) { find(state, size, high, findDelete); break; default: - fail(); + cdie(0, "invalid state->type"); return; } if ((i + 1) % 1000 == 0) check(state); + if (i == 100) + describe(state); } } @@ -560,8 +563,8 @@ extern int main(int argc, char *argv[]) CBSStruct cbsStruct; Align align; - randomize(argc, argv); - align = (1 << rnd() % 4) * MPS_PF_ALIGN; + testlib_init(argc, argv); + align = sizeof(void *) << (rnd() % 4); NAllocateTried = NAllocateSucceeded = NDeallocateTried = NDeallocateSucceeded = 0; @@ -585,7 +588,8 @@ extern int main(int argc, char *argv[]) (char *)dummyBlock + ArraySize); } - die((mps_res_t)CBSInit(arena, &cbsStruct, arena, align, TRUE, mps_args_none), + die((mps_res_t)CBSInit(&cbsStruct, arena, arena, align, + /* fastFind */ TRUE, /* zoned */ FALSE, mps_args_none), "failed to initialise CBS"); state.type = FBMTypeCBS; state.align = align; @@ -604,10 +608,14 @@ extern int main(int argc, char *argv[]) mps_arena_destroy(arena); - printf("\nNumber of allocations attempted: %ld\n", NAllocateTried); - printf("Number of allocations succeeded: %ld\n", NAllocateSucceeded); - printf("Number of deallocations attempted: %ld\n", NDeallocateTried); - printf("Number of deallocations succeeded: %ld\n", NDeallocateSucceeded); + printf("\nNumber of allocations attempted: %"PRIuLONGEST"\n", + (ulongest_t)NAllocateTried); + printf("Number of allocations succeeded: %"PRIuLONGEST"\n", + (ulongest_t)NAllocateSucceeded); + printf("Number of deallocations attempted: %"PRIuLONGEST"\n", + (ulongest_t)NDeallocateTried); + printf("Number of deallocations succeeded: %"PRIuLONGEST"\n", + (ulongest_t)NDeallocateSucceeded); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; } @@ -615,7 +623,7 @@ extern int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/finalcv.c b/code/finalcv.c index 8236df4279..2be7f08373 100644 --- a/code/finalcv.c +++ b/code/finalcv.c @@ -1,7 +1,7 @@ /* finalcv.c: FINALIZATION COVERAGE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * DESIGN @@ -18,18 +18,20 @@ * This code was created by first copying */ -#include "testlib.h" -#include "mpslib.h" -#include "mps.h" -#include "mpscamc.h" -#include "mpsavm.h" #include "fmtdy.h" #include "fmtdytst.h" +#include "mpm.h" +#include "mps.h" +#include "mpsavm.h" +#include "mpscamc.h" +#include "mpscams.h" +#include "mpscawl.h" +#include "mpsclo.h" +#include "mpslib.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif -#include +#include "testlib.h" + +#include /* printf */ #define testArenaSIZE ((size_t)16<<20) @@ -38,6 +40,7 @@ #define finalizationRATE 6 #define gcINTERVAL ((size_t)150 * 1024) #define collectionCOUNT 3 +#define messageCOUNT 3 /* 3 words: wrapper | vector-len | first-slot */ #define vectorSIZE (3*sizeof(mps_word_t)) @@ -96,35 +99,37 @@ enum { }; -static void *test(void *arg, size_t s) +static void test(mps_arena_t arena, mps_pool_class_t pool_class) { - unsigned i; /* index */ + size_t i; /* index */ mps_ap_t ap; mps_fmt_t fmt; mps_chain_t chain; - mps_pool_t amc; + mps_pool_t pool; mps_res_t e; mps_root_t mps_root[2]; mps_addr_t nullref = NULL; int state[rootCOUNT]; - mps_arena_t arena; - void *p = NULL; mps_message_t message; + size_t messages = 0; + void *p; - arena = (mps_arena_t)arg; - (void)s; + printf("---- finalcv: pool class %s ----\n", pool_class->name); die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n"); die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - die(mps_pool_create(&amc, arena, mps_class_amc(), fmt, chain), - "pool_create amc\n"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); + die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create\n"); + } MPS_ARGS_END(args); die(mps_root_create_table(&mps_root[0], arena, mps_rank_exact(), (mps_rm_t)0, root, (size_t)rootCOUNT), "root_create\n"); die(mps_root_create_table(&mps_root[1], arena, mps_rank_exact(), (mps_rm_t)0, &p, (size_t)1), "root_create\n"); - die(mps_ap_create(&ap, amc, mps_rank_exact()), "ap_create\n"); + die(mps_ap_create(&ap, pool, mps_rank_exact()), "ap_create\n"); /* Make registered-for-finalization objects. */ /* */ @@ -146,7 +151,7 @@ static void *test(void *arg, size_t s) mps_message_type_enable(arena, mps_message_type_finalization()); /* */ - while (mps_collections(arena) < collectionCOUNT) { + while (messages < messageCOUNT && mps_collections(arena) < collectionCOUNT) { /* Perhaps cause (minor) collection */ churn(ap); @@ -186,7 +191,8 @@ static void *test(void *arg, size_t s) mps_message_finalization_ref(&objaddr, arena, message); obj = objaddr; objind = dylan_int_int(obj[vectorSLOT]); - printf("Finalizing: object %lu at %p\n", objind, objaddr); + printf("Finalizing: object %"PRIuLONGEST" at %p\n", + (ulongest_t)objind, objaddr); /* */ cdie(root[objind] == NULL, "finalized live"); cdie(state[objind] == finalizableSTATE, "finalized dead"); @@ -195,36 +201,34 @@ static void *test(void *arg, size_t s) if (rnd() % 2 == 0) root[objind] = objaddr; mps_message_discard(arena, message); + ++ messages; } } - /* @@@@ missing */ - mps_ap_destroy(ap); mps_root_destroy(mps_root[1]); mps_root_destroy(mps_root[0]); - mps_pool_destroy(amc); + mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(fmt); - - return NULL; } int main(int argc, char *argv[]) { mps_arena_t arena; - mps_thr_t thread; - void *r; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "arena_create\n"); - die(mps_thread_reg(&thread, arena), "thread_reg\n"); - mps_tramp(&r, test, arena, 0); - mps_thread_dereg(thread); + + test(arena, mps_class_amc()); + test(arena, mps_class_amcz()); + test(arena, mps_class_awl()); + test(arena, mps_class_ams()); + test(arena, mps_class_lo()); + mps_arena_destroy(arena); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); @@ -234,7 +238,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/finaltest.c b/code/finaltest.c index f5ef8a9a99..9f3ef13b96 100644 --- a/code/finaltest.c +++ b/code/finaltest.c @@ -1,11 +1,25 @@ /* finaltest.c: LARGE-SCALE FINALIZATION TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * DESIGN * + * .mode: This test has two modes. + * + * .mode.park: In this mode, we use the arena's default generation + * chain, leave the arena parked and call mps_arena_collect. This + * tests that the default generation chain works and that all segments + * get condemned via TraceStartCollectAll. (See job003771 item 4.) + * + * .mode.poll: In this mode, we use our own generation chain (with + * small generations), allocate into generation 1, unclamp the arena, + * and provoke collection by allocating. This tests that custom + * generation chains work, and that segments get condemned via + * TracePoll even if there is no allocation into generation 0 of the + * chain. (See job003771 item 5.) + * * DEPENDENCIES * * This test uses the dylan object format, but the reliance on this @@ -16,30 +30,31 @@ * This code was created by first copying */ +#include "mpm.h" #include "testlib.h" #include "mpslib.h" #include "mps.h" #include "mpscamc.h" +#include "mpscams.h" +#include "mpscawl.h" +#include "mpsclo.h" #include "mpsavm.h" #include "fmtdy.h" #include "fmtdytst.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif -#include + +#include /* fflush, printf, stdout */ + +enum { + ModePARK, /* .mode.park */ + ModePOLL /* .mode.poll */ +}; #define testArenaSIZE ((size_t)16<<20) #define rootCOUNT 20 -#define maxtreeDEPTH 12 +#define maxtreeDEPTH 9 #define collectionCOUNT 10 -#define genCOUNT 2 - -/* testChain -- generation parameters for the test */ - -static mps_gen_param_s testChain[genCOUNT] = { - { 150, 0.85 }, { 170, 0.45 } }; /* global object counter */ @@ -77,7 +92,7 @@ static void register_numbered_tree(mps_word_t tree, mps_arena_t arena) /* don't finalize ints */ if ((tree & 1) == 0) { mps_addr_t tree_ref = (mps_addr_t)tree; - mps_finalize(arena, &tree_ref); + die(mps_finalize(arena, &tree_ref), "mps_finalize"); register_numbered_tree(DYLAN_VECTOR_SLOT(tree, 0), arena); register_numbered_tree(DYLAN_VECTOR_SLOT(tree, 1), arena); } @@ -117,126 +132,130 @@ static void register_indirect_tree(mps_word_t tree, mps_arena_t arena) if ((tree & 1) == 0) { mps_word_t indirect = DYLAN_VECTOR_SLOT(tree,2); mps_addr_t indirect_ref = (mps_addr_t)indirect; - mps_finalize(arena, &indirect_ref); + die(mps_finalize(arena, &indirect_ref), "mps_finalize"); register_indirect_tree(DYLAN_VECTOR_SLOT(tree, 0), arena); register_indirect_tree(DYLAN_VECTOR_SLOT(tree, 1), arena); } } - static void *root[rootCOUNT]; -static void *test(void *arg, size_t s) +static void test_trees(int mode, const char *name, mps_arena_t arena, + mps_pool_t pool, mps_ap_t ap, + mps_word_t (*make)(mps_word_t, mps_ap_t), + void (*reg)(mps_word_t, mps_arena_t)) { - mps_ap_t ap; - mps_fmt_t fmt; - mps_chain_t chain; - mps_word_t finals; - mps_pool_t amc; - mps_root_t mps_root; - mps_arena_t arena; - mps_message_t message; + size_t collections = 0; + size_t finals = 0; size_t i; + int object_alloc; - arena = (mps_arena_t)arg; - (void)s; - - die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n"); - die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - die(mps_pool_create(&amc, arena, mps_class_amc(), fmt, chain), - "pool_create amc\n"); - die(mps_root_create_table(&mps_root, arena, mps_rank_exact(), (mps_rm_t)0, - root, (size_t)rootCOUNT), - "root_create\n"); - die(mps_ap_create(&ap, amc, mps_rank_exact()), "ap_create\n"); - - mps_message_type_enable(arena, mps_message_type_finalization()); + object_count = 0; + printf("---- Mode %s, pool class %s, %s trees ----\n", + mode == ModePARK ? "PARK" : "POLL", + pool->class->name, name); mps_arena_park(arena); - object_count = 0; - - printf("Making some finalized trees of objects.\n"); /* make some trees */ for(i = 0; i < rootCOUNT; ++i) { - root[i] = (void *)make_numbered_tree(maxtreeDEPTH, ap); - register_numbered_tree((mps_word_t)root[i], arena); + root[i] = (void *)(*make)(maxtreeDEPTH, ap); + (*reg)((mps_word_t)root[i], arena); } - printf("Losing all pointers to the trees.\n"); /* clean out the roots */ for(i = 0; i < rootCOUNT; ++i) { - root[i] = 0; + root[i] = 0; } - finals = 0; - - while ((finals < object_count) && - (mps_collections(arena) < collectionCOUNT)) { - mps_word_t final_this_time = 0; - printf("Collecting..."); - fflush(stdout); - die(mps_arena_collect(arena), "collect"); - printf(" Done.\n"); - while (mps_message_poll(arena)) { - mps_addr_t objaddr; - cdie(mps_message_get(&message, arena, - mps_message_type_finalization()), - "get"); - mps_message_finalization_ref(&objaddr, arena, message); - mps_message_discard(arena, message); - ++ final_this_time; - } - finals += final_this_time; - printf("%lu objects finalized: total %lu of %lu\n", - final_this_time, finals, object_count); + while (finals < object_count && collections < collectionCOUNT) { + mps_word_t final_this_time = 0; + switch (mode) { + default: + case ModePARK: + printf("Collecting..."); + (void)fflush(stdout); + die(mps_arena_collect(arena), "collect"); + printf(" Done.\n"); + break; + case ModePOLL: + mps_arena_release(arena); + printf("Allocating..."); + (void)fflush(stdout); + object_alloc = 0; + while (object_alloc < 1000 && !mps_message_poll(arena)) + (void)DYLAN_INT(object_alloc++); + printf(" Done.\n"); + break; + } + ++ collections; + { + size_t live_size = (object_count - finals) * sizeof(void *) * 3; + size_t alloc_size = mps_pool_total_size(pool) - mps_pool_free_size(pool); + Insist(live_size <= alloc_size); + } + while (mps_message_poll(arena)) { + mps_message_t message; + mps_addr_t objaddr; + cdie(mps_message_get(&message, arena, mps_message_type_finalization()), + "message_get"); + mps_message_finalization_ref(&objaddr, arena, message); + mps_message_discard(arena, message); + ++ final_this_time; + } + finals += final_this_time; + printf("%"PRIuLONGEST" objects finalized: total %"PRIuLONGEST + " of %"PRIuLONGEST"\n", (ulongest_t)final_this_time, + (ulongest_t)finals, (ulongest_t)object_count); } + if (finals != object_count) + error("Not all objects were finalized for %s in mode %s.", + BufferOfAP(ap)->pool->class->name, + mode == ModePOLL ? "POLL" : "PARK"); +} - object_count = 0; - - printf("Making some indirectly finalized trees of objects.\n"); - /* make some trees */ - for(i = 0; i < rootCOUNT; ++i) { - root[i] = (void *)make_indirect_tree(maxtreeDEPTH, ap); - register_indirect_tree((mps_word_t)root[i], arena); - } +static void test_pool(int mode, mps_arena_t arena, mps_chain_t chain, + mps_pool_class_t pool_class) +{ + mps_ap_t ap; + mps_fmt_t fmt; + mps_pool_t pool; + mps_root_t mps_root; - printf("Losing all pointers to the trees.\n"); - /* clean out the roots */ - for(i = 0; i < rootCOUNT; ++i) { - root[i] = 0; - } + die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); + if (mode == ModePOLL) { + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + MPS_ARGS_ADD(args, MPS_KEY_GEN, 1); + } + die(mps_pool_create_k(&pool, arena, pool_class, args), + "pool_create\n"); + } MPS_ARGS_END(args); + die(mps_root_create_table(&mps_root, arena, mps_rank_exact(), (mps_rm_t)0, + root, (size_t)rootCOUNT), + "root_create\n"); + die(mps_ap_create(&ap, pool, mps_rank_exact()), "ap_create\n"); - finals = 0; - - while ((finals < object_count) && - (mps_collections(arena) < collectionCOUNT)) { - mps_word_t final_this_time = 0; - printf("Collecting..."); - fflush(stdout); - die(mps_arena_collect(arena), "collect"); - printf(" Done.\n"); - while (mps_message_poll(arena)) { - mps_addr_t objaddr; - cdie(mps_message_get(&message, arena, - mps_message_type_finalization()), - "get"); - mps_message_finalization_ref(&objaddr, arena, message); - mps_message_discard(arena, message); - ++ final_this_time; - } - finals += final_this_time; - printf("%lu objects finalized: total %lu of %lu\n", - final_this_time, finals, object_count); - } + test_trees(mode, "numbered", arena, pool, ap, make_numbered_tree, + register_numbered_tree); + test_trees(mode, "indirect", arena, pool, ap, make_indirect_tree, + register_indirect_tree); mps_ap_destroy(ap); mps_root_destroy(mps_root); - mps_pool_destroy(amc); - mps_chain_destroy(chain); + mps_pool_destroy(pool); mps_fmt_destroy(fmt); +} + - return NULL; +static void test_mode(int mode, mps_arena_t arena, mps_chain_t chain) +{ + test_pool(mode, arena, chain, mps_class_amc()); + test_pool(mode, arena, chain, mps_class_amcz()); + test_pool(mode, arena, chain, mps_class_ams()); + test_pool(mode, arena, chain, mps_class_awl()); + test_pool(mode, arena, chain, mps_class_lo()); } @@ -244,13 +263,28 @@ int main(int argc, char *argv[]) { mps_arena_t arena; mps_thr_t thread; - void *r; - testlib_unused(argc); + mps_chain_t chain; + mps_gen_param_s params[2]; + size_t gens = 2; + size_t i; + + testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "arena_create\n"); + mps_message_type_enable(arena, mps_message_type_finalization()); die(mps_thread_reg(&thread, arena), "thread_reg\n"); - mps_tramp(&r, test, arena, 0); + for (i = 0; i < gens; ++i) { + params[i].mps_capacity = 1; + params[i].mps_mortality = 0.5; + } + die(mps_chain_create(&chain, arena, gens, params), "chain_create\n"); + + test_mode(ModePOLL, arena, chain); + test_mode(ModePARK, arena, NULL); + + mps_arena_park(arena); + mps_chain_destroy(chain); mps_thread_dereg(thread); mps_arena_destroy(arena); @@ -261,7 +295,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/fmtdy.c b/code/fmtdy.c index f622bfe128..0cb4adea11 100644 --- a/code/fmtdy.c +++ b/code/fmtdy.c @@ -1,7 +1,7 @@ /* fmtdy.c: DYLAN OBJECT FORMAT IMPLEMENTATION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * .readership: MPS developers, Dylan developers @@ -69,10 +69,6 @@ /* MPS_END causes "constant conditional" warnings. */ #pragma warning(disable: 4127) -/* windows.h causes warnings about "unreferenced inline function */ -/* has been removed". */ -#pragma warning(disable: 4514) - #endif /* _MSC_VER */ @@ -470,8 +466,8 @@ extern mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io) break; case 1: /* stretchy non-traceable */ - notreached(); /* Not used by DylanWorks yet */ p += vt + 1; + notreached(); /* Not used by DylanWorks yet */ break; case 2: /* non-stretchy traceable */ @@ -482,7 +478,6 @@ extern mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io) break; case 3: /* stretchy traceable */ - notreached(); /* DW doesn't create them yet */ vl = *(mps_word_t *)p; /* vector length */ assert((vl & 3) == 1); /* check Dylan integer tag */ vl >>= 2; /* untag it */ @@ -490,21 +485,22 @@ extern mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io) res = dylan_scan_contig(mps_ss, p, p + vl); if(res) return res; p += vt; /* skip to end of whole vector */ + notreached(); /* DW doesn't create them yet */ break; case 4: /* non-word */ - es = (vh & 0xff) >> 3; - vb = (vh >> 16) & 0xff; + es = (unsigned)(vh & 0xff) >> 3; + vb = (unsigned)((vh >> 16) & 0xff); vt += vb; p += NONWORD_LENGTH(vt, es); break; case 5: /* stretchy non-word */ - notreached(); /* DW doesn't create them yet */ - es = (vh & 0xff) >> 3; - vb = (vh >> 16) & 0xff; + es = (unsigned)(vh & 0xff) >> 3; + vb = (unsigned)((vh >> 16) & 0xff); vt += vb; p += NONWORD_LENGTH(vt, es) + 1; + notreached(); /* DW doesn't create them yet */ break; default: @@ -521,10 +517,13 @@ static mps_res_t dylan_scan(mps_ss_t mps_ss, mps_addr_t base, mps_addr_t limit) { mps_res_t res; + mps_addr_t prev = base; while(base < limit) { + prev = base; res = dylan_scan1(mps_ss, &base); if(res) return res; + assert(prev < base); } assert(base == limit); @@ -678,8 +677,8 @@ static mps_addr_t dylan_skip(mps_addr_t object) if((vf & 6) == 4) /* non-word */ { - es = (vh & 0xff) >> 3; - vb = (vh >> 16) & 0xff; + es = (unsigned)(vh & 0xff) >> 3; + vb = (unsigned)((vh >> 16) & 0xff); vt += vb; p += NONWORD_LENGTH(vt, es); } @@ -721,6 +720,7 @@ static void dylan_fwd(mps_addr_t old, mps_addr_t new) mps_addr_t limit; assert(dylan_isfwd(old) == NULL); + assert((*(mps_word_t *)old & 3) == 0); /* mustn't forward padding objects */ assert(((mps_word_t)new & 3) == 0); p = (mps_word_t *)old; @@ -844,7 +844,7 @@ mps_res_t dylan_fmt_weak(mps_fmt_t *mps_fmt_o, mps_arena_t arena) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/fmtdytst.c b/code/fmtdytst.c index 790840f549..c797109870 100644 --- a/code/fmtdytst.c +++ b/code/fmtdytst.c @@ -1,7 +1,7 @@ /* fmtdytst.c: DYLAN FORMAT TEST CODE * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .readership: MPS developers, Dylan developers. */ @@ -17,12 +17,6 @@ #define unused(param) ((void)param) -#ifdef MPS_BUILD_MV -/* windows.h causes warnings about "unreferenced inline function */ -/* has been removed". */ -#pragma warning(disable: 4514) -#endif /* MPS_BUILD_MV */ - static mps_word_t *ww = NULL; static mps_word_t *tvw; @@ -79,8 +73,12 @@ mps_res_t dylan_make_wrappers(void) * If the raw memory is large enough, initialises it to a dylan-vector, * whose slots are initialised to either dylan-ints, or valid refs, at * random. - * Caller must supply an array of (at least 1) valid refs to copy, via - * the "refs" and "nr_refs" arguments. + * + * Caller must supply an array of valid refs to copy, via the "refs" + * and "nr_refs" arguments. If "nr_refs" is 0, all slots are + * initialized to dylan-ints: this may be useful for making leaf + * objects. + * * (Makes a pad if the raw memory is too small to hold a dylan-vector) */ @@ -106,7 +104,7 @@ mps_res_t dylan_init(mps_addr_t addr, size_t size, for(i = 0; i < t; ++i) { mps_word_t r = rnd(); - if(r & 1) + if(nr_refs == 0 || (r & 1)) p[2+i] = ((r & ~(mps_word_t)3) | 1); /* random int */ else p[2+i] = (mps_word_t)refs[(r >> 1) % nr_refs]; /* random ptr */ @@ -222,7 +220,7 @@ mps_bool_t dylan_check(mps_addr_t addr) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/fmtno.c b/code/fmtno.c index 6c632fef06..05f84651bd 100644 --- a/code/fmtno.c +++ b/code/fmtno.c @@ -1,7 +1,7 @@ /* fmtno.c: NULL OBJECT FORMAT IMPLEMENTATION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .readership: MPS developers */ @@ -17,22 +17,6 @@ #define notreached() assert(0) #define unused(param) ((void)param) -#ifdef MPS_BUILD_MV - -/* MSVC 2.0 generates a warning for unused(). */ -#ifdef _MSC_VER -#if _MSC_VER < 1000 -#pragma warning(disable: 4705) -#endif -#else /* _MSC_VER */ -#error "Expected _MSC_VER to be defined for builder.mv" -#endif /* _MSC_VER */ - -/* windows.h causes warnings about "unreferenced inline function */ -/* has been removed". */ -#pragma warning(disable: 4514) - -#endif /* MPS_BUILD_MV */ #define ALIGN sizeof(mps_word_t) @@ -137,7 +121,7 @@ mps_res_t no_fmt(mps_fmt_t *mps_fmt_o, mps_arena_t arena) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/fmtscheme.c b/code/fmtscheme.c new file mode 100644 index 0000000000..24fa06db87 --- /dev/null +++ b/code/fmtscheme.c @@ -0,0 +1,500 @@ +/* fmtscheme.c: SCHEME OBJECT FORMAT IMPLEMENTATION + * + * $Id: //info.ravenbrook.com/project/mps/branch/2014-01-15/nailboard/code/fmtdy.c#1 $ + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + */ + +#include + +#include "fmtscheme.h" +#include "testlib.h" + + +/* special objects */ + +static obj_t obj_true; /* #t, boolean true */ +static obj_t obj_false; /* #f, boolean false */ + + +/* MPS globals */ + +mps_arena_t scheme_arena; /* the arena */ +mps_pool_t obj_pool; /* pool for ordinary Scheme objects */ +mps_ap_t obj_ap; /* allocation point used to allocate objects */ + + +/* make_* -- object constructors */ + +#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)) + +/* Align size upwards to the next multiple of the word size, and + * additionally ensure that it's big enough to store a forwarding + * pointer. Evaluates its argument twice. */ +#define ALIGN_OBJ(size) \ + (ALIGN_WORD(size) >= ALIGN_WORD(sizeof(fwd_s)) \ + ? ALIGN_WORD(size) \ + : ALIGN_WORD(sizeof(fwd_s))) + +obj_t scheme_make_bool(int condition) +{ + return condition ? obj_true : obj_false; +} + +obj_t scheme_make_pair(mps_ap_t ap, obj_t car, obj_t cdr) +{ + obj_t obj; + mps_addr_t addr; + size_t size = ALIGN_OBJ(sizeof(pair_s)); + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_pair"); + obj = addr; + obj->pair.type = TYPE_PAIR; + CAR(obj) = car; + CDR(obj) = cdr; + } while(!mps_commit(ap, addr, size)); + return obj; +} + +obj_t scheme_make_integer(mps_ap_t ap, long integer) +{ + obj_t obj; + mps_addr_t addr; + size_t size = ALIGN_OBJ(sizeof(integer_s)); + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_integer"); + obj = addr; + obj->integer.type = TYPE_INTEGER; + obj->integer.integer = integer; + } while(!mps_commit(ap, addr, size)); + return obj; +} + +obj_t scheme_make_symbol(mps_ap_t ap, size_t length, char string[]) +{ + obj_t obj; + mps_addr_t addr; + size_t size = ALIGN_OBJ(offsetof(symbol_s, string) + length+1); + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_symbol"); + obj = addr; + obj->symbol.type = TYPE_SYMBOL; + obj->symbol.length = length; + memcpy(obj->symbol.string, string, length+1); + } while(!mps_commit(ap, addr, size)); + return obj; +} + +obj_t scheme_make_string(mps_ap_t ap, size_t length, char string[]) +{ + obj_t obj; + mps_addr_t addr; + size_t size = ALIGN_OBJ(offsetof(string_s, string) + length+1); + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_string"); + obj = addr; + obj->string.type = TYPE_STRING; + obj->string.length = length; + if (string) memcpy(obj->string.string, string, length+1); + else memset(obj->string.string, 0, length+1); + } while(!mps_commit(ap, addr, size)); + return obj; +} + +obj_t scheme_make_special(mps_ap_t ap, char *string) +{ + obj_t obj; + mps_addr_t addr; + size_t size = ALIGN_OBJ(sizeof(special_s)); + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_special"); + obj = addr; + obj->special.type = TYPE_SPECIAL; + obj->special.name = string; + } while(!mps_commit(ap, addr, size)); + return obj; +} + +obj_t scheme_make_operator(mps_ap_t ap, char *name, + entry_t entry, obj_t arguments, + obj_t body, obj_t env, obj_t op_env) +{ + obj_t obj; + mps_addr_t addr; + size_t size = ALIGN_OBJ(sizeof(operator_s)); + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_operator"); + obj = addr; + obj->operator.type = TYPE_OPERATOR; + obj->operator.name = name; + obj->operator.entry = entry; + obj->operator.arguments = arguments; + obj->operator.body = body; + obj->operator.env = env; + obj->operator.op_env = op_env; + } while(!mps_commit(ap, addr, size)); + return obj; +} + +obj_t scheme_make_port(mps_ap_t ap, obj_t name, FILE *stream) +{ + mps_addr_t port_ref; + obj_t obj; + mps_addr_t addr; + size_t size = ALIGN_OBJ(sizeof(port_s)); + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_port"); + obj = addr; + obj->port.type = TYPE_PORT; + obj->port.name = name; + obj->port.stream = stream; + } while(!mps_commit(ap, addr, size)); + port_ref = obj; + mps_finalize(scheme_arena, &port_ref); + return obj; +} + +obj_t scheme_make_character(mps_ap_t ap, char c) +{ + obj_t obj; + mps_addr_t addr; + size_t size = ALIGN_OBJ(sizeof(character_s)); + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_character"); + obj = addr; + obj->character.type = TYPE_CHARACTER; + obj->character.c = c; + } while(!mps_commit(ap, addr, size)); + return obj; +} + +obj_t scheme_make_vector(mps_ap_t ap, size_t length, obj_t fill) +{ + obj_t obj; + mps_addr_t addr; + size_t size = ALIGN_OBJ(offsetof(vector_s, vector) + length * sizeof(obj_t)); + do { + size_t i; + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_vector"); + obj = addr; + obj->vector.type = TYPE_VECTOR; + obj->vector.length = length; + for(i = 0; i < length; ++i) + obj->vector.vector[i] = fill; + } while(!mps_commit(ap, addr, size)); + return obj; +} + +obj_t scheme_make_buckets(mps_ap_t ap, size_t length) +{ + obj_t obj; + mps_addr_t addr; + size_t size = ALIGN_OBJ(offsetof(buckets_s, bucket) + length * sizeof(obj->buckets.bucket[0])); + do { + size_t i; + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_buckets"); + obj = addr; + obj->buckets.type = TYPE_BUCKETS; + obj->buckets.length = length; + obj->buckets.used = 0; + obj->buckets.deleted = 0; + for(i = 0; i < length; ++i) { + obj->buckets.bucket[i].key = NULL; + obj->buckets.bucket[i].value = NULL; + } + } while(!mps_commit(ap, addr, size)); + return obj; +} + +obj_t scheme_make_table(mps_ap_t ap, size_t length, hash_t hashf, cmp_t cmpf) +{ + obj_t obj; + mps_addr_t addr; + size_t l, size = ALIGN_OBJ(sizeof(table_s)); + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in make_table"); + obj = addr; + obj->table.type = TYPE_TABLE; + obj->table.buckets = NULL; + } while(!mps_commit(ap, addr, size)); + obj->table.hash = hashf; + obj->table.cmp = cmpf; + /* round up to next power of 2 */ + for(l = 1; l < length; l *= 2); + obj->table.buckets = scheme_make_buckets(ap, l); + mps_ld_reset(&obj->table.ld, scheme_arena); + return obj; +} + + +/* MPS Format */ + +static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) +{ +#define FIX(ref) \ + do { \ + mps_addr_t _addr = (ref); /* copy to local to avoid type pun */ \ + mps_res_t res = MPS_FIX12(ss, &_addr); \ + if (res != MPS_RES_OK) return res; \ + (ref) = _addr; \ + } while(0) + + MPS_SCAN_BEGIN(ss) { + while (base < limit) { + obj_t obj = base; + switch (TYPE(obj)) { + case TYPE_PAIR: + case TYPE_PROMISE: + FIX(CAR(obj)); + FIX(CDR(obj)); + base = (char *)base + ALIGN_OBJ(sizeof(pair_s)); + break; + case TYPE_INTEGER: + base = (char *)base + ALIGN_OBJ(sizeof(integer_s)); + break; + case TYPE_SYMBOL: + base = (char *)base + + ALIGN_OBJ(offsetof(symbol_s, string) + obj->symbol.length + 1); + break; + case TYPE_SPECIAL: + base = (char *)base + ALIGN_OBJ(sizeof(special_s)); + break; + case TYPE_OPERATOR: + FIX(obj->operator.arguments); + FIX(obj->operator.body); + FIX(obj->operator.env); + FIX(obj->operator.op_env); + base = (char *)base + ALIGN_OBJ(sizeof(operator_s)); + break; + case TYPE_STRING: + base = (char *)base + + ALIGN_OBJ(offsetof(string_s, string) + obj->string.length + 1); + break; + case TYPE_PORT: + FIX(obj->port.name); + base = (char *)base + ALIGN_OBJ(sizeof(port_s)); + break; + case TYPE_CHARACTER: + base = (char *)base + ALIGN_OBJ(sizeof(character_s)); + break; + case TYPE_VECTOR: + { + size_t i; + for (i = 0; i < obj->vector.length; ++i) + FIX(obj->vector.vector[i]); + } + base = (char *)base + + ALIGN_OBJ(offsetof(vector_s, vector) + + obj->vector.length * sizeof(obj->vector.vector[0])); + break; + case TYPE_BUCKETS: + { + size_t i; + for (i = 0; i < obj->buckets.length; ++i) { + FIX(obj->buckets.bucket[i].key); + FIX(obj->buckets.bucket[i].value); + } + } + base = (char *)base + + ALIGN_OBJ(offsetof(buckets_s, bucket) + + obj->buckets.length * sizeof(obj->buckets.bucket[0])); + break; + case TYPE_TABLE: + FIX(obj->table.buckets); + base = (char *)base + ALIGN_OBJ(sizeof(table_s)); + break; + case TYPE_FWD2: + base = (char *)base + ALIGN_WORD(sizeof(fwd2_s)); + break; + case TYPE_FWD: + base = (char *)base + ALIGN_WORD(obj->fwd.size); + break; + case TYPE_PAD1: + base = (char *)base + ALIGN_WORD(sizeof(pad1_s)); + break; + case TYPE_PAD: + base = (char *)base + ALIGN_WORD(obj->pad.size); + break; + default: + error("Unexpected object on the heap\n"); + return MPS_RES_FAIL; + } + } + } MPS_SCAN_END(ss); + return MPS_RES_OK; +} + +static mps_addr_t obj_skip(mps_addr_t base) +{ + obj_t obj = base; + switch (TYPE(obj)) { + case TYPE_PAIR: + case TYPE_PROMISE: + base = (char *)base + ALIGN_OBJ(sizeof(pair_s)); + break; + case TYPE_INTEGER: + base = (char *)base + ALIGN_OBJ(sizeof(integer_s)); + break; + case TYPE_SYMBOL: + base = (char *)base + + ALIGN_OBJ(offsetof(symbol_s, string) + obj->symbol.length + 1); + break; + case TYPE_SPECIAL: + base = (char *)base + ALIGN_OBJ(sizeof(special_s)); + break; + case TYPE_OPERATOR: + base = (char *)base + ALIGN_OBJ(sizeof(operator_s)); + break; + case TYPE_STRING: + base = (char *)base + + ALIGN_OBJ(offsetof(string_s, string) + obj->string.length + 1); + break; + case TYPE_PORT: + base = (char *)base + ALIGN_OBJ(sizeof(port_s)); + break; + case TYPE_CHARACTER: + base = (char *)base + ALIGN_OBJ(sizeof(character_s)); + break; + case TYPE_VECTOR: + base = (char *)base + + ALIGN_OBJ(offsetof(vector_s, vector) + + obj->vector.length * sizeof(obj->vector.vector[0])); + break; + case TYPE_BUCKETS: + base = (char *)base + + ALIGN_OBJ(offsetof(buckets_s, bucket) + + obj->buckets.length * sizeof(obj->buckets.bucket[0])); + break; + case TYPE_TABLE: + base = (char *)base + ALIGN_OBJ(sizeof(table_s)); + break; + case TYPE_FWD2: + base = (char *)base + ALIGN_WORD(sizeof(fwd2_s)); + break; + case TYPE_FWD: + base = (char *)base + ALIGN_WORD(obj->fwd.size); + break; + case TYPE_PAD: + base = (char *)base + ALIGN_WORD(obj->pad.size); + break; + case TYPE_PAD1: + base = (char *)base + ALIGN_WORD(sizeof(pad1_s)); + break; + default: + error("Unexpected object on the heap\n"); + return NULL; + } + return base; +} + +static mps_addr_t obj_isfwd(mps_addr_t addr) +{ + obj_t obj = addr; + switch (TYPE(obj)) { + case TYPE_FWD2: + return obj->fwd2.fwd; + case TYPE_FWD: + return obj->fwd.fwd; + default: + return NULL; + } +} + +static void obj_fwd(mps_addr_t old, mps_addr_t new) +{ + obj_t obj = old; + mps_addr_t limit = obj_skip(old); + size_t size = (size_t)((char *)limit - (char *)old); + cdie(size >= ALIGN_WORD(sizeof(fwd2_s)), "bad size in obj_fwd"); + if (size == ALIGN_WORD(sizeof(fwd2_s))) { + TYPE(obj) = TYPE_FWD2; + obj->fwd2.fwd = new; + } else { + TYPE(obj) = TYPE_FWD; + obj->fwd.fwd = new; + obj->fwd.size = size; + } +} + +static void obj_pad(mps_addr_t addr, size_t size) +{ + obj_t obj = addr; + cdie(size >= ALIGN_WORD(sizeof(pad1_s)), "bad size in obj_pad"); + if (size == ALIGN_WORD(sizeof(pad1_s))) { + TYPE(obj) = TYPE_PAD1; + } else { + TYPE(obj) = TYPE_PAD; + obj->pad.size = size; + } +} + +void scheme_fmt(mps_fmt_t *fmt) +{ + mps_res_t res; + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, ALIGNMENT); + MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, obj_scan); + MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, obj_skip); + MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, obj_fwd); + MPS_ARGS_ADD(args, MPS_KEY_FMT_ISFWD, obj_isfwd); + MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, obj_pad); + res = mps_fmt_create_k(fmt, scheme_arena, args); + } MPS_ARGS_END(args); + if (res != MPS_RES_OK) error("Couldn't create obj format"); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2001-2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/fmtscheme.h b/code/fmtscheme.h new file mode 100644 index 0000000000..95a62a53ce --- /dev/null +++ b/code/fmtscheme.h @@ -0,0 +1,233 @@ +/* fmtscheme.h: SCHEME OBJECT FORMAT INTERFACE + * + * $Id: //info.ravenbrook.com/project/mps/branch/2014-01-15/nailboard/code/fmtdy.h#1 $ + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + */ + +#ifndef fmtscheme_h +#define fmtscheme_h + +#include +#include "mps.h" + +typedef union obj_u *obj_t; +typedef obj_t (*entry_t)(obj_t env, obj_t op_env, obj_t operator, obj_t rands); +typedef unsigned long (*hash_t)(obj_t obj, mps_ld_t ld); +typedef int (*cmp_t)(obj_t obj1, obj_t obj2); + +typedef int type_t; +enum { + TYPE_PAIR, + TYPE_INTEGER, + TYPE_SYMBOL, + TYPE_SPECIAL, + TYPE_OPERATOR, + TYPE_STRING, + TYPE_PORT, + TYPE_PROMISE, + TYPE_CHARACTER, + TYPE_VECTOR, + TYPE_TABLE, + TYPE_BUCKETS, + TYPE_FWD2, /* two-word forwarding object */ + TYPE_FWD, /* three words and up forwarding object */ + TYPE_PAD1, /* one-word padding object */ + TYPE_PAD /* two words and up padding object */ +}; + +typedef struct type_s { + type_t type; +} type_s; + +typedef struct pair_s { + type_t type; /* TYPE_PAIR */ + obj_t car, cdr; /* first and second projections */ +} pair_s; + +typedef struct symbol_s { + type_t type; /* TYPE_SYMBOL */ + size_t length; /* length of symbol string (excl. NUL) */ + char string[1]; /* symbol string, NUL terminated */ +} symbol_s; + +typedef struct integer_s { + type_t type; /* TYPE_INTEGER */ + long integer; /* the integer */ +} integer_s; + +typedef struct special_s { + type_t type; /* TYPE_SPECIAL */ + char *name; /* printed representation, NUL terminated */ +} special_s; + +typedef struct operator_s { + type_t type; /* TYPE_OPERATOR */ + char *name; /* printed name, NUL terminated */ + entry_t entry; /* entry point -- see eval() */ + obj_t arguments, body; /* function arguments and code */ + obj_t env, op_env; /* closure environments */ +} operator_s; + +typedef struct string_s { + type_t type; /* TYPE_STRING */ + size_t length; /* number of chars in string */ + char string[1]; /* string, NUL terminated */ +} string_s; + +typedef struct port_s { + type_t type; /* TYPE_PORT */ + obj_t name; /* name of stream */ + FILE *stream; +} port_s; + +typedef struct character_s { + type_t type; /* TYPE_CHARACTER */ + char c; /* the character */ +} character_s; + +typedef struct vector_s { + type_t type; /* TYPE_VECTOR */ + size_t length; /* number of elements */ + obj_t vector[1]; /* vector elements */ +} vector_s; + +typedef struct table_s { + type_t type; /* TYPE_TABLE */ + hash_t hash; /* hash function */ + cmp_t cmp; /* comparison function */ + mps_ld_s ld; /* location dependency */ + obj_t buckets; /* hash buckets */ +} table_s; + +typedef struct buckets_s { + type_t type; /* TYPE_BUCKETS */ + size_t length; /* number of buckets */ + size_t used; /* number of buckets in use */ + size_t deleted; /* number of deleted buckets */ + struct bucket_s { + obj_t key, value; + } bucket[1]; /* hash buckets */ +} buckets_s; + + +/* fwd2, fwd, pad1, pad -- MPS forwarding and padding objects */ + +typedef struct fwd2_s { + type_t type; /* TYPE_FWD2 */ + obj_t fwd; /* forwarded object */ +} fwd2_s; + +typedef struct fwd_s { + type_t type; /* TYPE_FWD */ + obj_t fwd; /* forwarded object */ + size_t size; /* total size of this object */ +} fwd_s; + +typedef struct pad1_s { + type_t type; /* TYPE_PAD1 */ +} pad1_s; + +typedef struct pad_s { + type_t type; /* TYPE_PAD */ + size_t size; /* total size of this object */ +} pad_s; + + +typedef union obj_u { + type_s type; /* one of TYPE_* */ + pair_s pair; + symbol_s symbol; + integer_s integer; + special_s special; + operator_s operator; + string_s string; + port_s port; + character_s character; + vector_s vector; + table_s table; + buckets_s buckets; + fwd2_s fwd2; + fwd_s fwd; + pad_s pad; +} obj_s; + + +/* structure macros */ + +#define TYPE(obj) ((obj)->type.type) +#define CAR(obj) ((obj)->pair.car) +#define CDR(obj) ((obj)->pair.cdr) +#define CAAR(obj) CAR(CAR(obj)) +#define CADR(obj) CAR(CDR(obj)) +#define CDAR(obj) CDR(CAR(obj)) +#define CDDR(obj) CDR(CDR(obj)) +#define CADDR(obj) CAR(CDDR(obj)) +#define CDDDR(obj) CDR(CDDR(obj)) +#define CDDAR(obj) CDR(CDAR(obj)) +#define CADAR(obj) CAR(CDAR(obj)) + + +extern obj_t scheme_make_bool(int condition); +extern obj_t scheme_make_pair(mps_ap_t ap, obj_t car, obj_t cdr); +extern obj_t scheme_make_integer(mps_ap_t ap, long integer); +extern obj_t scheme_make_symbol(mps_ap_t ap, size_t length, char string[]); +extern obj_t scheme_make_string(mps_ap_t ap, size_t length, char string[]); +extern obj_t scheme_make_special(mps_ap_t ap, char *string); +extern obj_t scheme_make_operator(mps_ap_t ap, char *name, entry_t entry, + obj_t arguments, obj_t body, obj_t env, + obj_t op_env); +extern obj_t scheme_make_port(mps_ap_t ap, obj_t name, FILE *stream); +extern obj_t scheme_make_character(mps_ap_t ap, char c); +extern obj_t scheme_make_vector(mps_ap_t ap, size_t length, obj_t fill); +extern obj_t scheme_make_buckets(mps_ap_t ap, size_t length); +extern obj_t scheme_make_table(mps_ap_t ap, size_t length, hash_t hashf, + cmp_t cmpf); +extern void scheme_fmt(mps_fmt_t *fmt); + +extern mps_arena_t scheme_arena; +extern mps_pool_t obj_pool; +extern mps_ap_t obj_ap; + +#endif /* fmtscheme_h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2001-2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/format.c b/code/format.c index fb72680535..07a8319082 100644 --- a/code/format.c +++ b/code/format.c @@ -1,7 +1,7 @@ /* format.c: OBJECT FORMATS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * DESIGN @@ -21,7 +21,7 @@ Bool FormatCheck(Format format) CHECKS(Format, format); CHECKU(Arena, format->arena); CHECKL(format->serial < format->arena->formatSerial); - CHECKL(RingCheck(&format->arenaRing)); + CHECKD_NOSIG(Ring, &format->arenaRing); CHECKL(AlignCheck(format->alignment)); /* TODO: Define the concept of the maximum alignment it is possible to request from the MPS, document and provide an interface to it, and then @@ -88,14 +88,14 @@ static mps_addr_t FormatDefaultClass(mps_addr_t object) /* FormatCreate -- create a format */ -ARG_DEFINE_KEY(fmt_align, Align); -ARG_DEFINE_KEY(fmt_scan, Fun); -ARG_DEFINE_KEY(fmt_skip, Fun); -ARG_DEFINE_KEY(fmt_fwd, Fun); -ARG_DEFINE_KEY(fmt_isfwd, Fun); -ARG_DEFINE_KEY(fmt_pad, Fun); -ARG_DEFINE_KEY(fmt_header_size, Size); -ARG_DEFINE_KEY(fmt_class, Fun); +ARG_DEFINE_KEY(FMT_ALIGN, Align); +ARG_DEFINE_KEY(FMT_SCAN, Fun); +ARG_DEFINE_KEY(FMT_SKIP, Fun); +ARG_DEFINE_KEY(FMT_FWD, Fun); +ARG_DEFINE_KEY(FMT_ISFWD, Fun); +ARG_DEFINE_KEY(FMT_PAD, Fun); +ARG_DEFINE_KEY(FMT_HEADER_SIZE, Size); +ARG_DEFINE_KEY(FMT_CLASS, Fun); Res FormatCreate(Format *formatReturn, Arena arena, ArgList args) { @@ -114,7 +114,7 @@ Res FormatCreate(Format *formatReturn, Arena arena, ArgList args) AVER(formatReturn != NULL); AVERT(Arena, arena); - AVER(ArgListCheck(args)); + AVERT(ArgList, args); if (ArgPick(&arg, args, MPS_KEY_FMT_ALIGN)) fmtAlign = arg.val.align; @@ -181,23 +181,22 @@ void FormatDestroy(Format format) /* FormatArena -- find the arena of a format * - * Must be thread-safe. See . */ + * Must be thread-safe. See . */ Arena FormatArena(Format format) { - /* Can't AVER format as that would not be thread-safe */ - /* AVERT(Format, format); */ + AVER(TESTT(Format, format)); return format->arena; } /* FormatDescribe -- describe a format */ -Res FormatDescribe(Format format, mps_lib_FILE *stream) +Res FormatDescribe(Format format, mps_lib_FILE *stream, Count depth) { Res res; - res = WriteF(stream, + res = WriteF(stream, depth, "Format $P ($U) {\n", (WriteFP)format, (WriteFU)format->serial, " arena $P ($U)\n", (WriteFP)format->arena, (WriteFU)format->arena->serial, @@ -207,9 +206,11 @@ Res FormatDescribe(Format format, mps_lib_FILE *stream) " move $F\n", (WriteFF)format->move, " isMoved $F\n", (WriteFF)format->isMoved, " pad $F\n", (WriteFF)format->pad, + " headerSize $W\n", (WriteFW)format->headerSize, "} Format $P ($U)\n", (WriteFP)format, (WriteFU)format->serial, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; return ResOK; } @@ -217,7 +218,7 @@ Res FormatDescribe(Format format, mps_lib_FILE *stream) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/fotest.c b/code/fotest.c index cab1d0f4e3..750883f61a 100644 --- a/code/fotest.c +++ b/code/fotest.c @@ -1,7 +1,7 @@ /* fotest.c: FAIL-OVER TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * This tests fail-over behaviour in low memory situations. The MVFF @@ -28,7 +28,7 @@ #include "mpmtypes.h" #include "poolmfs.h" -#include +#include /* printf */ #define testArenaSIZE ((((size_t)3)<<24) - 4) @@ -38,28 +38,38 @@ /* Accessors for the CBS used to implement a pool. */ -extern CBS _mps_mvff_cbs(mps_pool_t); -extern CBS _mps_mvt_cbs(mps_pool_t); +extern Land _mps_mvff_cbs(Pool); +extern Land _mps_mvt_cbs(Pool); /* "OOM" pool class -- dummy alloc/free pool class whose alloc() - * method always returns ResMEMORY */ + * method always fails and whose free method does nothing. */ -static Res OOMAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit) +static Res oomAlloc(Addr *pReturn, Pool pool, Size size, + Bool withReservoirPermit) { UNUSED(pReturn); UNUSED(pool); UNUSED(size); UNUSED(withReservoirPermit); - return ResMEMORY; + switch (rnd() % 3) { + case 0: + return ResRESOURCE; + case 1: + return ResMEMORY; + default: + return ResCOMMIT_LIMIT; + } } -extern PoolClass PoolClassOOM(void); +extern PoolClass OOMPoolClassGet(void); DEFINE_POOL_CLASS(OOMPoolClass, this) { - INHERIT_CLASS(this, AbstractAllocFreePoolClass); - this->alloc = OOMAlloc; + INHERIT_CLASS(this, AbstractPoolClass); + this->alloc = oomAlloc; + this->free = PoolTrivFree; + this->size = sizeof(PoolStruct); + AVERT(PoolClass, this); } @@ -81,16 +91,17 @@ static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size) /* set_oom -- set blockPool of CBS to OOM or MFS according to argument. */ -static void set_oom(CBS cbs, int oom) +static void set_oom(Land land, int oom) { - cbs->blockPool->class = oom ? EnsureOOMPoolClass() : PoolClassMFS(); + CBS cbs = PARENT(CBSStruct, landStruct, land); + cbs->blockPool->class = oom ? OOMPoolClassGet() : PoolClassMFS(); } /* stress -- create an allocation point and allocate in it */ static mps_res_t stress(size_t (*size)(unsigned long, mps_align_t), - mps_align_t alignment, mps_pool_t pool, CBS cbs) + mps_align_t alignment, mps_pool_t pool, Land cbs) { mps_res_t res = MPS_RES_OK; mps_ap_t ap; @@ -149,11 +160,6 @@ static mps_res_t stress(size_t (*size)(unsigned long, mps_align_t), } -#define max(a, b) (((a) > (b)) ? (a) : (b)) - -#define alignUp(w, a) (((w) + (a) - 1) & ~((size_t)(a) - 1)) - - /* randomSizeAligned -- produce sizes both large and small, * aligned by platform alignment */ @@ -170,12 +176,11 @@ int main(int argc, char *argv[]) mps_pool_t pool; mps_align_t alignment; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "mps_arena_create"); - alignment = (1 << (rnd() % 4)) * MPS_PF_ALIGN; + alignment = sizeof(void *) << (rnd() % 4); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, (64 + rnd() % 64) * 1024); MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, (1 + rnd() % 8) * 8); @@ -186,15 +191,15 @@ int main(int argc, char *argv[]) die(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), "create MVFF"); } MPS_ARGS_END(args); { - CBS cbs = _mps_mvff_cbs(pool); - die(stress(randomSizeAligned, alignment, pool, cbs), "stress MVFF"); + die(stress(randomSizeAligned, alignment, pool, _mps_mvff_cbs(pool)), + "stress MVFF"); } mps_pool_destroy(pool); mps_arena_destroy(arena); die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "mps_arena_create"); - alignment = (1 << (rnd() % 4)) * MPS_PF_ALIGN; + alignment = sizeof(void *) << (rnd() % 4); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_ALIGN, alignment); MPS_ARGS_ADD(args, MPS_KEY_MIN_SIZE, (1 + rnd() % 4) * 4); @@ -205,8 +210,8 @@ int main(int argc, char *argv[]) die(mps_pool_create_k(&pool, arena, mps_class_mvt(), args), "create MVFF"); } MPS_ARGS_END(args); { - CBS cbs = _mps_mvt_cbs(pool); - die(stress(randomSizeAligned, alignment, pool, cbs), "stress MVT"); + die(stress(randomSizeAligned, alignment, pool, _mps_mvt_cbs(pool)), + "stress MVT"); } mps_pool_destroy(pool); mps_arena_destroy(arena); @@ -218,7 +223,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/freelist.c b/code/freelist.c index 6260451ff5..6eddc3dff8 100644 --- a/code/freelist.c +++ b/code/freelist.c @@ -1,57 +1,90 @@ /* freelist.c: FREE LIST ALLOCATOR IMPLEMENTATION * * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2015 Ravenbrook Limited. See end of file for license. * * .sources: . */ -#include "cbs.h" #include "freelist.h" #include "mpm.h" +#include "range.h" SRCID(freelist, "$Id$"); +#define freelistOfLand(land) PARENT(FreelistStruct, landStruct, land) +#define freelistAlignment(fl) LandAlignment(FreelistLand(fl)) + + typedef union FreelistBlockUnion { - struct { + struct FreelistBlockSmall { FreelistBlock next; /* tagged with low bit 1 */ - /* limit is (char *)this + fl->alignment */ + /* limit is (char *)this + freelistAlignment(fl) */ } small; - struct { - FreelistBlock next; + struct FreelistBlockLarge { + FreelistBlock next; /* not tagged (low bit 0) */ Addr limit; } large; } FreelistBlockUnion; -/* See */ -#define freelistMinimumAlignment ((Align)sizeof(FreelistBlock)) +/* freelistEND -- the end of a list + * + * The end of a list should not be represented with NULL, as this is + * ambiguous. However, freelistEND is in fact a null pointer, for + * performance. To check whether you have it right, try temporarily + * defining freelistEND as ((FreelistBlock)2) or similar (it must be + * an even number because of the use of a tag). + */ + +#define freelistEND ((FreelistBlock)0) + + +/* freelistTag -- return the tag of word */ + +#define freelistTag(word) ((word) & 1) + + +/* freelistTagSet -- return word updated with the tag set */ + +#define freelistTagSet(word) ((FreelistBlock)((Word)(word) | 1)) + + +/* freelistTagReset -- return word updated with the tag reset */ + +#define freelistTagReset(word) ((FreelistBlock)((Word)(word) & ~(Word)1)) -#define FreelistTag(word) ((word) & 1) -#define FreelistTagSet(word) ((FreelistBlock)((Word)(word) | 1)) -#define FreelistTagReset(word) ((FreelistBlock)((Word)(word) & ~(Word)1)) -#define FreelistTagCopy(to, from) ((FreelistBlock)((Word)(to) | FreelistTag((Word)(from)))) +/* freelistTagCopy -- return 'to' updated to have the same tag as 'from' */ +#define freelistTagCopy(to, from) ((FreelistBlock)((Word)(to) | freelistTag((Word)(from)))) -/* FreelistBlockIsSmall -- return true if block is small, false if large */ -#define FreelistBlockIsSmall(block) FreelistTag((Word)((block)->small.next)) +/* freelistBlockIsSmall -- return true if block is small, false if large */ +#define freelistBlockIsSmall(block) freelistTag((Word)((block)->small.next)) -/* FreelistBlockBase -- return the base of a block. */ -#define FreelistBlockBase(block) ((Addr)(block)) +/* freelistBlockBase -- return the base of a block. */ +#define freelistBlockBase(block) ((Addr)(block)) -/* FreelistBlockLimit -- return the limit of a block. */ -static Addr FreelistBlockLimit(Freelist fl, FreelistBlock block) +/* freelistBlockNext -- return the next block in the list, or + * freelistEND if there are no more blocks. + */ + +#define freelistBlockNext(block) freelistTagReset((block)->small.next) + + +/* freelistBlockLimit -- return the limit of a block. */ + +static Addr freelistBlockLimit(Freelist fl, FreelistBlock block) { AVERT(Freelist, fl); - if (FreelistBlockIsSmall(block)) { - return AddrAdd(FreelistBlockBase(block), fl->alignment); + if (freelistBlockIsSmall(block)) { + return AddrAdd(freelistBlockBase(block), freelistAlignment(fl)); } else { return block->large.limit; } @@ -60,81 +93,76 @@ static Addr FreelistBlockLimit(Freelist fl, FreelistBlock block) /* FreelistBlockCheck -- check a block. */ +ATTRIBUTE_UNUSED static Bool FreelistBlockCheck(FreelistBlock block) { CHECKL(block != NULL); /* block list is address-ordered */ - CHECKL(FreelistTagReset(block->small.next) == NULL - || block < FreelistTagReset(block->small.next)); - CHECKL(FreelistBlockIsSmall(block) || (Addr)block < block->large.limit); + 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; } -/* FreelistBlockNext -- return the next block in the list, or NULL if - * there are no more blocks. - */ -static FreelistBlock FreelistBlockNext(FreelistBlock block) -{ - AVERT(FreelistBlock, block); - return FreelistTagReset(block->small.next); -} - +/* freelistBlockSize -- return the size of a block. */ -/* FreelistBlockSize -- return the size of a block. */ +#define freelistBlockSize(fl, block) \ + AddrOffset(freelistBlockBase(block), freelistBlockLimit(fl, block)) -#define FreelistBlockSize(fl, block) \ - AddrOffset(FreelistBlockBase(block), FreelistBlockLimit(fl, block)) +/* freelistBlockSetNext -- update the next block in the list */ -/* FreelistBlockSetNext -- update the next block in the list */ - -static void FreelistBlockSetNext(FreelistBlock block, FreelistBlock next) +static void freelistBlockSetNext(FreelistBlock block, FreelistBlock next) { AVERT(FreelistBlock, block); - block->small.next = FreelistTagCopy(next, block->small.next); + block->small.next = freelistTagCopy(next, block->small.next); } -/* FreelistBlockSetLimit -- update the limit of a block */ +/* freelistBlockSetLimit -- update the limit of a block */ -static void FreelistBlockSetLimit(Freelist fl, FreelistBlock block, Addr limit) +static void freelistBlockSetLimit(Freelist fl, FreelistBlock block, Addr limit) { Size size; AVERT(Freelist, fl); AVERT(FreelistBlock, block); - AVER(AddrIsAligned(limit, fl->alignment)); - AVER(FreelistBlockBase(block) < limit); + AVER(AddrIsAligned(limit, freelistAlignment(fl))); + AVER(freelistBlockBase(block) < limit); size = AddrOffset(block, limit); if (size >= sizeof(block->large)) { - block->large.next = FreelistTagReset(block->large.next); + block->large.next = freelistTagReset(block->large.next); block->large.limit = limit; } else { AVER(size >= sizeof(block->small)); - block->small.next = FreelistTagSet(block->small.next); + block->small.next = freelistTagSet(block->small.next); + AVER(freelistBlockSize(fl, block) == freelistAlignment(fl)); } - AVER(FreelistBlockLimit(fl, block) == limit); + AVER(freelistBlockLimit(fl, block) == limit); } -/* FreelistBlockInit -- initalize block storing the range [base, limit). */ +/* freelistBlockInit -- initalize block storing the range [base, limit). */ -static FreelistBlock FreelistBlockInit(Freelist fl, Addr base, Addr limit) +static FreelistBlock freelistBlockInit(Freelist fl, Addr base, Addr limit) { FreelistBlock block; AVERT(Freelist, fl); AVER(base != NULL); - AVER(AddrIsAligned(base, fl->alignment)); + AVER(AddrIsAligned(base, freelistAlignment(fl))); AVER(base < limit); - AVER(AddrIsAligned(limit, fl->alignment)); + AVER(AddrIsAligned(limit, freelistAlignment(fl))); block = (FreelistBlock)base; - block->small.next = FreelistTagSet(NULL); - FreelistBlockSetLimit(fl, block, limit); + block->small.next = freelistTagSet(freelistEND); + freelistBlockSetLimit(fl, block, limit); AVERT(FreelistBlock, block); return block; } @@ -142,23 +170,42 @@ static FreelistBlock FreelistBlockInit(Freelist fl, Addr base, Addr limit) Bool FreelistCheck(Freelist fl) { + Land land; 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(fl->alignment, freelistMinimumAlignment)); - CHECKL((fl->list == NULL) == (fl->listSize == 0)); + CHECKL(AlignIsAligned(freelistAlignment(fl), FreelistMinimumAlignment)); + CHECKL((fl->list == freelistEND) == (fl->listSize == 0)); + CHECKL((fl->list == freelistEND) == (fl->size == 0)); + CHECKL(SizeIsAligned(fl->size, freelistAlignment(fl))); + return TRUE; } -Res FreelistInit(Freelist fl, Align alignment) +static Res freelistInit(Land land, ArgList args) { + Freelist fl; + LandClass super; + Res res; + + AVERT(Land, land); + super = LAND_SUPERCLASS(FreelistLandClass); + res = (*super->init)(land, args); + if (res != ResOK) + return res; + /* See */ - if (!AlignIsAligned(alignment, freelistMinimumAlignment)) - return ResPARAM; + AVER(AlignIsAligned(LandAlignment(land), FreelistMinimumAlignment)); - fl->alignment = alignment; - fl->list = NULL; + fl = freelistOfLand(land); + fl->list = freelistEND; fl->listSize = 0; + fl->size = 0; fl->sig = FreelistSig; AVERT(Freelist, fl); @@ -166,31 +213,58 @@ Res FreelistInit(Freelist fl, Align alignment) } -void FreelistFinish(Freelist fl) +static void freelistFinish(Land land) { + Freelist fl; + + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); fl->sig = SigInvalid; - fl->list = NULL; + fl->list = freelistEND; +} + + +static Size freelistSize(Land land) +{ + Freelist fl; + + AVERT(Land, land); + fl = freelistOfLand(land); + AVERT(Freelist, fl); + return fl->size; } /* freelistBlockSetPrevNext -- update list of blocks - * If prev and next are both NULL, make the block list empty. - * Otherwise, if prev is NULL, make next the first block in the list. - * Otherwise, if next is NULL, make prev the last block in the list. + * + * If prev and next are both freelistEND, make the block list empty. + * Otherwise, if prev is freelistEND, make next the first block in the list. + * Otherwise, if next is freelistEND, make prev the last block in the list. * Otherwise, make next follow prev in the list. * Update the count of blocks by 'delta'. + * + * 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 (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, FreelistBlock next, int delta) { AVERT(Freelist, fl); - if (prev) { - AVER(next == NULL || FreelistBlockLimit(fl, prev) < FreelistBlockBase(next)); - FreelistBlockSetNext(prev, next); - } else { + if (prev == freelistEND) { fl->list = next; + } else { + /* Isolated range invariant (design.mps.freelist.impl.invariant). */ + AVER(next == freelistEND + || freelistBlockLimit(fl, prev) < freelistBlockBase(next)); + freelistBlockSetNext(prev, next); } if (delta < 0) { AVER(fl->listSize >= (Count)-delta); @@ -201,31 +275,34 @@ static void freelistBlockSetPrevNext(Freelist fl, FreelistBlock prev, } -Res FreelistInsert(Range rangeReturn, Freelist fl, Range range) +static Res freelistInsert(Range rangeReturn, Land land, Range range) { + Freelist fl; FreelistBlock prev, cur, next, new; Addr base, limit; Bool coalesceLeft, coalesceRight; AVER(rangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); AVERT(Range, range); - AVER(RangeIsAligned(range, fl->alignment)); + AVER(RangeIsAligned(range, freelistAlignment(fl))); base = RangeBase(range); limit = RangeLimit(range); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { - if (base < FreelistBlockLimit(fl, cur) && FreelistBlockBase(cur) < limit) + while (cur != freelistEND) { + if (base < freelistBlockLimit(fl, cur) && freelistBlockBase(cur) < limit) return ResFAIL; /* range overlaps with cur */ - if (limit <= FreelistBlockBase(cur)) + if (limit <= freelistBlockBase(cur)) break; - next = FreelistBlockNext(cur); - if (next) + next = freelistBlockNext(cur); + if (next != freelistEND) /* Isolated range invariant (design.mps.freelist.impl.invariant). */ - AVER(FreelistBlockLimit(fl, cur) < FreelistBlockBase(next)); + AVER(freelistBlockLimit(fl, cur) < freelistBlockBase(next)); prev = cur; cur = next; } @@ -234,44 +311,47 @@ Res FreelistInsert(Range rangeReturn, Freelist fl, Range range) * coalesces then it does so with prev on the left, and cur on the * right. */ - coalesceLeft = (prev && base == FreelistBlockLimit(fl, prev)); - coalesceRight = (cur && limit == FreelistBlockBase(cur)); + coalesceLeft = (prev != freelistEND && base == freelistBlockLimit(fl, prev)); + coalesceRight = (cur != freelistEND && limit == freelistBlockBase(cur)); if (coalesceLeft && coalesceRight) { - base = FreelistBlockBase(prev); - limit = FreelistBlockLimit(fl, cur); - FreelistBlockSetLimit(fl, prev, limit); - freelistBlockSetPrevNext(fl, prev, FreelistBlockNext(cur), -1); + base = freelistBlockBase(prev); + limit = freelistBlockLimit(fl, cur); + freelistBlockSetLimit(fl, prev, limit); + freelistBlockSetPrevNext(fl, prev, freelistBlockNext(cur), -1); } else if (coalesceLeft) { - base = FreelistBlockBase(prev); - FreelistBlockSetLimit(fl, prev, limit); + base = freelistBlockBase(prev); + freelistBlockSetLimit(fl, prev, limit); } else if (coalesceRight) { - next = FreelistBlockNext(cur); - limit = FreelistBlockLimit(fl, cur); - cur = FreelistBlockInit(fl, base, limit); - FreelistBlockSetNext(cur, next); + next = freelistBlockNext(cur); + limit = freelistBlockLimit(fl, cur); + cur = freelistBlockInit(fl, base, limit); + freelistBlockSetNext(cur, next); freelistBlockSetPrevNext(fl, prev, cur, 0); } else { /* failed to coalesce: add new block */ - new = FreelistBlockInit(fl, base, limit); - FreelistBlockSetNext(new, cur); + new = freelistBlockInit(fl, base, limit); + freelistBlockSetNext(new, cur); freelistBlockSetPrevNext(fl, prev, new, +1); } + fl->size += RangeSize(range); RangeInit(rangeReturn, base, limit); return ResOK; } -/* freelistDeleteFromBlock -- delete 'range' from 'block' (it is known - * to be a subset of that block); update 'rangeReturn' to the original - * range of 'block' and update the block list accordingly: 'prev' is - * the block on the list just before 'block', or NULL if 'block' is - * the first block on the list. +/* freelistDeleteFromBlock -- delete range from block + * + * range must be a subset of block. Update rangeReturn to be the + * original range of block and update the block list accordingly: prev + * is on the list just before block, or freelistEND if block is the + * first block on the list. */ + static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl, Range range, FreelistBlock prev, FreelistBlock block) @@ -282,17 +362,17 @@ static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl, AVER(rangeReturn != NULL); AVERT(Freelist, fl); AVERT(Range, range); - AVER(RangeIsAligned(range, fl->alignment)); - AVER(prev == NULL || FreelistBlockNext(prev) == block); + AVER(RangeIsAligned(range, freelistAlignment(fl))); + AVER(prev == freelistEND || freelistBlockNext(prev) == block); AVERT(FreelistBlock, block); - AVER(FreelistBlockBase(block) <= RangeBase(range)); - AVER(RangeLimit(range) <= FreelistBlockLimit(fl, block)); + AVER(freelistBlockBase(block) <= RangeBase(range)); + AVER(RangeLimit(range) <= freelistBlockLimit(fl, block)); base = RangeBase(range); limit = RangeLimit(range); - blockBase = FreelistBlockBase(block); - blockLimit = FreelistBlockLimit(fl, block); - next = FreelistBlockNext(block); + blockBase = freelistBlockBase(block); + blockLimit = freelistBlockLimit(fl, block); + next = freelistBlockNext(block); if (base == blockBase && limit == blockLimit) { /* No fragment at left; no fragment at right. */ @@ -300,44 +380,49 @@ static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl, } else if (base == blockBase) { /* No fragment at left; block at right. */ - block = FreelistBlockInit(fl, limit, blockLimit); - FreelistBlockSetNext(block, next); + block = freelistBlockInit(fl, limit, blockLimit); + freelistBlockSetNext(block, next); freelistBlockSetPrevNext(fl, prev, block, 0); } else if (limit == blockLimit) { /* Block at left; no fragment at right. */ - FreelistBlockSetLimit(fl, block, base); + freelistBlockSetLimit(fl, block, base); } else { /* Block at left; block at right. */ - FreelistBlockSetLimit(fl, block, base); - new = FreelistBlockInit(fl, limit, blockLimit); - FreelistBlockSetNext(new, next); + freelistBlockSetLimit(fl, block, base); + new = freelistBlockInit(fl, limit, blockLimit); + freelistBlockSetNext(new, next); freelistBlockSetPrevNext(fl, block, new, +1); } + AVER(fl->size >= RangeSize(range)); + fl->size -= RangeSize(range); RangeInit(rangeReturn, blockBase, blockLimit); } -Res FreelistDelete(Range rangeReturn, Freelist fl, Range range) +static Res freelistDelete(Range rangeReturn, Land land, Range range) { + Freelist fl; FreelistBlock prev, cur, next; Addr base, limit; AVER(rangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); AVERT(Range, range); base = RangeBase(range); limit = RangeLimit(range); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { + while (cur != freelistEND) { Addr blockBase, blockLimit; - blockBase = FreelistBlockBase(cur); - blockLimit = FreelistBlockLimit(fl, cur); + blockBase = freelistBlockBase(cur); + blockLimit = freelistBlockLimit(fl, cur); if (limit <= blockBase) return ResFAIL; /* not found */ @@ -348,7 +433,7 @@ Res FreelistDelete(Range rangeReturn, Freelist fl, Range range) return ResOK; } - next = FreelistBlockNext(cur); + next = freelistBlockNext(cur); prev = cur; cur = next; } @@ -358,43 +443,82 @@ Res FreelistDelete(Range rangeReturn, Freelist fl, Range range) } -void FreelistIterate(Freelist fl, FreelistIterateMethod iterate, - void *closureP, Size closureS) +static Bool freelistIterate(Land land, LandVisitor visitor, + void *closureP, Size closureS) { + Freelist fl; + FreelistBlock cur, next; + + AVERT(Land, land); + fl = freelistOfLand(land); + AVERT(Freelist, fl); + AVER(FUNCHECK(visitor)); + /* closureP and closureS are arbitrary */ + + for (cur = fl->list; cur != freelistEND; cur = next) { + RangeStruct range; + Bool cont; + /* .next.first: Take next before calling the visitor, in case the + * visitor touches the block. */ + next = freelistBlockNext(cur); + RangeInit(&range, freelistBlockBase(cur), freelistBlockLimit(fl, cur)); + cont = (*visitor)(land, &range, closureP, closureS); + if (!cont) + return FALSE; + } + return TRUE; +} + + +static Bool freelistIterateAndDelete(Land land, LandDeleteVisitor visitor, + void *closureP, Size closureS) +{ + Freelist fl; FreelistBlock prev, cur, next; + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); - AVER(FUNCHECK(iterate)); + AVER(FUNCHECK(visitor)); + /* closureP and closureS are arbitrary */ - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { + while (cur != freelistEND) { Bool delete = FALSE; RangeStruct range; Bool cont; - RangeInit(&range, FreelistBlockBase(cur), FreelistBlockLimit(fl, cur)); - cont = (*iterate)(&delete, &range, closureP, closureS); - next = FreelistBlockNext(cur); + Size size; + next = freelistBlockNext(cur); /* See .next.first. */ + size = freelistBlockSize(fl, cur); + RangeInit(&range, freelistBlockBase(cur), freelistBlockLimit(fl, cur)); + cont = (*visitor)(&delete, land, &range, closureP, closureS); if (delete) { freelistBlockSetPrevNext(fl, prev, next, -1); + AVER(fl->size >= size); + fl->size -= size; } else { prev = cur; } - cur = next; if (!cont) - break; + return FALSE; + cur = next; } + return TRUE; } -/* freelistFindDeleteFromBlock -- Find a chunk of 'size' bytes in - * 'block' (which is known to be at least that big) and possibly - * delete that chunk according to the instruction in 'findDelete'. - * Return the range of that chunk in 'rangeReturn'. Return the - * original range of the block in 'oldRangeReturn'. Update the block - * list accordingly, using 'prev' which is the previous block in the - * list, or NULL if 'block' is the first block in the list. +/* freelistFindDeleteFromBlock -- delete size bytes from block + * + * Find a chunk of size bytes in block (which is known to be at least + * that big) and possibly delete that chunk according to the + * instruction in findDelete. Return the range of that chunk in + * rangeReturn. Return the original range of the block in + * oldRangeReturn. Update the block list accordingly, using prev, + * which is previous in list or freelistEND if block is the first + * block in the list. */ + static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn, Freelist fl, Size size, FindDelete findDelete, @@ -406,14 +530,14 @@ static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn, AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); AVERT(Freelist, fl); - AVER(SizeIsAligned(size, fl->alignment)); + AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); - AVER(prev == NULL || FreelistBlockNext(prev) == block); + AVER(prev == freelistEND || freelistBlockNext(prev) == block); AVERT(FreelistBlock, block); - AVER(FreelistBlockSize(fl, block) >= size); + AVER(freelistBlockSize(fl, block) >= size); - base = FreelistBlockBase(block); - limit = FreelistBlockLimit(fl, block); + base = freelistBlockBase(block); + limit = freelistBlockLimit(fl, block); switch (findDelete) { case FindDeleteNONE: @@ -446,26 +570,29 @@ static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn, } -Bool FreelistFindFirst(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete) +static Bool freelistFindFirst(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + Freelist fl; FreelistBlock prev, cur, next; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); - AVER(SizeIsAligned(size, fl->alignment)); + AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { - if (FreelistBlockSize(fl, cur) >= size) { + while (cur != freelistEND) { + if (freelistBlockSize(fl, cur) >= size) { freelistFindDeleteFromBlock(rangeReturn, oldRangeReturn, fl, size, findDelete, prev, cur); return TRUE; } - next = FreelistBlockNext(cur); + next = freelistBlockNext(cur); prev = cur; cur = next; } @@ -474,28 +601,31 @@ Bool FreelistFindFirst(Range rangeReturn, Range oldRangeReturn, } -Bool FreelistFindLast(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete) +static Bool freelistFindLast(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + Freelist fl; Bool found = FALSE; FreelistBlock prev, cur, next; - FreelistBlock foundPrev = NULL, foundCur = NULL; + FreelistBlock foundPrev = freelistEND, foundCur = freelistEND; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); - AVER(SizeIsAligned(size, fl->alignment)); + AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { - if (FreelistBlockSize(fl, cur) >= size) { + while (cur != freelistEND) { + if (freelistBlockSize(fl, cur) >= size) { found = TRUE; foundPrev = prev; foundCur = cur; } - next = FreelistBlockNext(cur); + next = freelistBlockNext(cur); prev = cur; cur = next; } @@ -508,28 +638,31 @@ Bool FreelistFindLast(Range rangeReturn, Range oldRangeReturn, } -Bool FreelistFindLargest(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete) +static Bool freelistFindLargest(Range rangeReturn, Range oldRangeReturn, + Land land, Size size, FindDelete findDelete) { + Freelist fl; Bool found = FALSE; FreelistBlock prev, cur, next; - FreelistBlock bestPrev = NULL, bestCur = NULL; + FreelistBlock bestPrev = freelistEND, bestCur = freelistEND; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); AVERT(Freelist, fl); AVERT(FindDelete, findDelete); - prev = NULL; + prev = freelistEND; cur = fl->list; - while (cur) { - if (FreelistBlockSize(fl, cur) >= size) { + while (cur != freelistEND) { + if (freelistBlockSize(fl, cur) >= size) { found = TRUE; - size = FreelistBlockSize(fl, cur); + size = freelistBlockSize(fl, cur); bestPrev = prev; bestCur = cur; } - next = FreelistBlockNext(cur); + next = freelistBlockNext(cur); prev = cur; cur = next; } @@ -542,91 +675,157 @@ Bool FreelistFindLargest(Range rangeReturn, Range oldRangeReturn, } -/* freelistDescribeIterateMethod -- Iterate method for - * FreelistDescribe. Writes a decription of the range into the stream - * pointed to by 'closureP'. +static Res freelistFindInZones(Bool *foundReturn, Range rangeReturn, + Range oldRangeReturn, Land land, Size size, + ZoneSet zoneSet, Bool high) +{ + Freelist fl; + LandFindMethod landFind; + RangeInZoneSet search; + Bool found = FALSE; + FreelistBlock prev, cur, next; + FreelistBlock foundPrev = freelistEND, foundCur = freelistEND; + RangeStruct foundRange; + + AVER(FALSE); /* TODO: this code is completely untested! */ + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + fl = freelistOfLand(land); + AVERT(Freelist, fl); + /* AVERT(ZoneSet, zoneSet); */ + AVERT(Bool, high); + + landFind = high ? freelistFindLast : freelistFindFirst; + search = high ? RangeInZoneSetLast : RangeInZoneSetFirst; + + if (zoneSet == ZoneSetEMPTY) + goto fail; + if (zoneSet == ZoneSetUNIV) { + FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW; + *foundReturn = (*landFind)(rangeReturn, oldRangeReturn, land, size, fd); + return ResOK; + } + if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land))) + goto fail; + + prev = freelistEND; + cur = fl->list; + while (cur != freelistEND) { + Addr base, limit; + if ((*search)(&base, &limit, freelistBlockBase(cur), + freelistBlockLimit(fl, cur), + LandArena(land), zoneSet, size)) + { + found = TRUE; + foundPrev = prev; + foundCur = cur; + RangeInit(&foundRange, base, limit); + if (!high) + break; + } + next = freelistBlockNext(cur); + prev = cur; + cur = next; + } + + if (!found) + goto fail; + + freelistDeleteFromBlock(oldRangeReturn, fl, &foundRange, foundPrev, foundCur); + RangeCopy(rangeReturn, &foundRange); + *foundReturn = TRUE; + return ResOK; + +fail: + *foundReturn = FALSE; + return ResOK; +} + + +/* freelistDescribeVisitor -- visitor method for freelistDescribe + * + * Writes a decription of the range into the stream pointed to by + * closureP. */ -static Bool freelistDescribeIterateMethod(Bool *deleteReturn, Range range, - void *closureP, Size closureS) + +static Bool freelistDescribeVisitor(Land land, Range range, + void *closureP, Size closureS) { Res res; mps_lib_FILE *stream = closureP; + Count depth = closureS; - AVER(deleteReturn != NULL); - AVERT(Range, range); - AVER(stream != NULL); - UNUSED(closureS); + if (!TESTT(Land, land)) + return FALSE; + if (!RangeCheck(range)) + return FALSE; + if (stream == NULL) + return FALSE; - res = WriteF(stream, - " [$P,", (WriteFP)RangeBase(range), + res = WriteF(stream, depth, + "[$P,", (WriteFP)RangeBase(range), "$P)", (WriteFP)RangeLimit(range), " {$U}\n", (WriteFU)RangeSize(range), NULL); - *deleteReturn = FALSE; return res == ResOK; } -Res FreelistDescribe(Freelist fl, mps_lib_FILE *stream) +static Res freelistDescribe(Land land, mps_lib_FILE *stream, Count depth) { + Freelist fl; Res res; + Bool b; - if (!TESTT(Freelist, fl)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Land, land)) + return ResFAIL; + fl = freelistOfLand(land); + if (!TESTT(Freelist, fl)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = WriteF(stream, + res = WriteF(stream, depth, "Freelist $P {\n", (WriteFP)fl, - " alignment = $U\n", (WriteFU)fl->alignment, " listSize = $U\n", (WriteFU)fl->listSize, + " size = $U\n", (WriteFU)fl->size, NULL); - FreelistIterate(fl, freelistDescribeIterateMethod, stream, 0); + b = LandIterate(land, freelistDescribeVisitor, stream, depth + 2); + if (!b) + return ResFAIL; - res = WriteF(stream, "}\n", NULL); + res = WriteF(stream, depth, "} Freelist $P\n", (WriteFP)fl, NULL); return res; } -/* freelistFlushIterateMethod -- Iterate method for - * FreelistFlushToCBS. Attempst to insert the range into the CBS. - */ -static Bool freelistFlushIterateMethod(Bool *deleteReturn, Range range, - void *closureP, Size closureS) +DEFINE_LAND_CLASS(FreelistLandClass, class) { - Res res; - RangeStruct newRange; - CBS cbs; - - AVER(deleteReturn != NULL); - AVERT(Range, range); - AVER(closureP != NULL); - UNUSED(closureS); - - cbs = closureP; - res = CBSInsert(&newRange, cbs, range); - if (res == ResOK) { - *deleteReturn = TRUE; - return TRUE; - } else { - *deleteReturn = FALSE; - return FALSE; - } -} - - -void FreelistFlushToCBS(Freelist fl, CBS cbs) -{ - AVERT(Freelist, fl); - AVERT(CBS, cbs); - - FreelistIterate(fl, freelistFlushIterateMethod, cbs, 0); + INHERIT_CLASS(class, LandClass); + class->name = "FREELIST"; + class->size = sizeof(FreelistStruct); + class->init = freelistInit; + class->finish = freelistFinish; + class->sizeMethod = freelistSize; + class->insert = freelistInsert; + class->delete = freelistDelete; + class->iterate = freelistIterate; + class->iterateAndDelete = freelistIterateAndDelete; + class->findFirst = freelistFindFirst; + class->findLast = freelistFindLast; + class->findLargest = freelistFindLargest; + class->findInZones = freelistFindInZones; + class->describe = freelistDescribe; + AVERT(LandClass, class); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2013 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/code/freelist.h b/code/freelist.h index 1bb9840c8c..dab791c9c0 100644 --- a/code/freelist.h +++ b/code/freelist.h @@ -1,7 +1,7 @@ /* freelist.h: FREE LIST ALLOCATOR INTERFACE * * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. * * .source: . */ @@ -9,51 +9,25 @@ #ifndef freelist_h #define freelist_h -#include "cbs.h" #include "mpmtypes.h" -#include "range.h" - -#define FreelistSig ((Sig)0x519F6331) /* SIGnature FREEL */ typedef struct FreelistStruct *Freelist; -typedef union FreelistBlockUnion *FreelistBlock; - -typedef Bool (*FreelistIterateMethod)(Bool *deleteReturn, Range range, - void *closureP, Size closureS); - -typedef struct FreelistStruct { - Sig sig; - Align alignment; - FreelistBlock list; - Count listSize; -} FreelistStruct; - -extern Bool FreelistCheck(Freelist fl); -extern Res FreelistInit(Freelist fl, Align alignment); -extern void FreelistFinish(Freelist fl); -extern Res FreelistInsert(Range rangeReturn, Freelist fl, Range range); -extern Res FreelistDelete(Range rangeReturn, Freelist fl, Range range); -extern Res FreelistDescribe(Freelist fl, mps_lib_FILE *stream); +#define FreelistLand(fl) (&(fl)->landStruct) -extern void FreelistIterate(Freelist abq, FreelistIterateMethod iterate, - void *closureP, Size closureS); +extern Bool FreelistCheck(Freelist freelist); -extern Bool FreelistFindFirst(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete); -extern Bool FreelistFindLast(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete); -extern Bool FreelistFindLargest(Range rangeReturn, Range oldRangeReturn, - Freelist fl, Size size, FindDelete findDelete); +/* See */ +#define FreelistMinimumAlignment ((Align)sizeof(FreelistBlock)) -extern void FreelistFlushToCBS(Freelist fl, CBS cbs); +extern LandClass FreelistLandClassGet(void); #endif /* freelist.h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2013 Ravenbrook Limited . + * Copyright (C) 2013-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/fri3gc.gmk b/code/fri3gc.gmk index dd7f92d074..99d455e51f 100644 --- a/code/fri3gc.gmk +++ b/code/fri3gc.gmk @@ -3,33 +3,36 @@ # fri3gc.gmk: BUILD FOR FreeBSD/i386/GCC PLATFORM # # $Id$ -# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. PFM = fri3gc -MPMPF = lockix.c thix.c pthrdext.c vmix.c \ - protix.c protsgix.c prmcan.c prmci3fr.c ssixi3.c span.c +MPMPF = \ + lockix.c \ + prmcan.c \ + prmci3fr.c \ + protix.c \ + protsgix.c \ + pthrdext.c \ + span.c \ + ssixi3.c \ + thix.c \ + vmix.c LIBS = -lm -pthread include gc.gmk -# FIXME: We pun types through the MPS interface, setting off this warning. -# Can we avoid this? The puns might indeed be dangerous. -CFLAGSCOMPILER += -Wno-strict-aliasing - # For SQLite3. LINKFLAGS += -L/usr/local/lib CFLAGSCOMPILER += -I/usr/local/include -CC = cc - include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2013 Ravenbrook Limited . +# Copyright (C) 2001-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/code/gc.gmk b/code/gc.gmk index 8ff258c9cf..76716dc078 100644 --- a/code/gc.gmk +++ b/code/gc.gmk @@ -3,7 +3,7 @@ # gc.gmk: GNUMAKEFILE FRAGMENT FOR GNU CC # # $Id$ -# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. # # This file is included by platform makefiles that use the GNU CC # compiler. It defines the compiler-specific variables that the @@ -12,11 +12,22 @@ CC = gcc CFLAGSDEBUG = -O -g3 CFLAGSOPT = -O2 -g3 -CFLAGSCOMPILER := -Wall -Wextra -Werror -Wpointer-arith \ - -Wstrict-prototypes -Wmissing-prototypes \ - -Winline -Waggregate-return -Wnested-externs \ - -Wcast-qual -Wshadow -Wwrite-strings # -Wstrict-aliasing=2 -CFLAGSCOMPILERSTRICT := -ansi -pedantic -Wshadow +CFLAGSCOMPILER := \ + -Waggregate-return \ + -Wall \ + -Wcast-qual \ + -Werror \ + -Wextra \ + -Winline \ + -Wmissing-prototypes \ + -Wnested-externs \ + -Wpointer-arith \ + -Wshadow \ + -Wstrict-aliasing=2 \ + -Wstrict-prototypes \ + -Wswitch-default \ + -Wwrite-strings +CFLAGSCOMPILERSTRICT := -ansi -pedantic # A different set of compiler flags for less strict compilation, for # instance when we need to #include a third-party header file that @@ -30,7 +41,7 @@ CFLAGSCOMPILERLAX := # If interrupted, this is liable to leave a zero-length file behind. define gendep - $(SHELL) -ec "$(CC) $(CFLAGS) -MM $< | \ + $(SHELL) -ec "$(CC) $(CFLAGSSTRICT) -MM $< | \ sed '/:/s!$*.o!$(@D)/& $(@D)/$*.d!' > $@" [ -s $@ ] || rm -f $@ endef @@ -38,7 +49,7 @@ endef # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2013 Ravenbrook Limited . +# Copyright (C) 2001-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/code/gcbench.c b/code/gcbench.c index 7e0ccbfb32..6d7f333966 100644 --- a/code/gcbench.c +++ b/code/gcbench.c @@ -7,18 +7,21 @@ */ #include "mps.c" - -#include -#include -#include -#include -#include -#include -#include "getopt.h" #include "testlib.h" - +#include "testthr.h" #include "fmtdy.h" #include "fmtdytst.h" +#include "mpm.h" + +#ifdef MPS_OS_W3 +#include "getopt.h" +#else +#include +#endif + +#include /* fprintf, printf, putchars, sscanf, stderr, stdout */ +#include /* alloca, exit, EXIT_FAILURE, EXIT_SUCCESS, strtoul */ +#include /* clock, CLOCKS_PER_SEC */ #define RESMUST(expr) \ do { \ @@ -48,15 +51,17 @@ static double preuse = 0.2; /* probability of reuse */ static double pupdate = 0.1; /* probability of update */ static unsigned ngen = 0; /* number of generations specified */ static mps_gen_param_s gen[genLIMIT]; /* generation parameters */ -static size_t arenasize = 256ul * 1024 * 1024; /* arena size */ +static size_t arena_size = 256ul * 1024 * 1024; /* arena size */ +static size_t arena_grain_size = 1; /* arena grain size */ static unsigned pinleaf = FALSE; /* are leaf objects pinned at start */ +static mps_bool_t zoned = TRUE; /* arena allocates using zones */ typedef struct gcthread_s *gcthread_t; typedef void *(*gcthread_fn_t)(gcthread_t thread); struct gcthread_s { - pthread_t pthread; + testthr_t thread; mps_thr_t mps_thread; mps_root_t reg_root; mps_ap_t ap; @@ -83,7 +88,8 @@ static void aset(obj_t v, size_t i, obj_t val) { static obj_t mktree(mps_ap_t ap, unsigned d, obj_t leaf) { obj_t tree; size_t i; - if (d <= 0) return leaf; + if (d <= 0) + return leaf; tree = mkvector(ap, width); for (i = 0; i < width; ++i) { aset(tree, i, mktree(ap, d - 1, leaf)); @@ -189,23 +195,12 @@ static void weave(gcthread_fn_t fn) for (t = 0; t < nthreads; ++t) { gcthread_t thread = &threads[t]; - int err; thread->fn = fn; - err = pthread_create(&thread->pthread, NULL, start, thread); - if (err != 0) { - fprintf(stderr, "Unable to create thread: %d\n", err); - exit(EXIT_FAILURE); - } + testthr_create(&thread->thread, start, thread); } - for (t = 0; t < nthreads; ++t) { - gcthread_t thread = &threads[t]; - int err = pthread_join(thread->pthread, NULL); - if (err != 0) { - fprintf(stderr, "Unable to join thread: %d\n", err); - exit(EXIT_FAILURE); - } - } + for (t = 0; t < nthreads; ++t) + testthr_join(&threads[t].thread, NULL); } static void weave1(gcthread_fn_t fn) @@ -219,27 +214,29 @@ static void weave1(gcthread_fn_t fn) static void watch(gcthread_fn_t fn, const char *name) { - clock_t start, finish; + clock_t begin, end; - start = clock(); + begin = clock(); if (nthreads == 1) weave1(fn); else weave(fn); - finish = clock(); + end = clock(); - printf("%s: %g\n", name, (double)(finish - start) / CLOCKS_PER_SEC); + printf("%s: %g\n", name, (double)(end - begin) / CLOCKS_PER_SEC); } /* Setup MPS arena and call benchmark. */ static void arena_setup(gcthread_fn_t fn, - mps_class_t pool_class, + mps_pool_class_t pool_class, const char *name) { MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arenasize); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arena_size); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, zoned); RESMUST(mps_arena_create_k(&arena, mps_arena_class_vm(), args)); } MPS_ARGS_END(args); RESMUST(dylan_fmt(&format, arena)); @@ -255,6 +252,9 @@ static void arena_setup(gcthread_fn_t fn, RESMUST(mps_pool_create_k(&pool, arena, pool_class, args)); } MPS_ARGS_END(args); watch(fn, name); + mps_arena_park(arena); + printf("%u chunks\n", (unsigned)TreeDebugCount(ArenaChunkTree(arena), + ChunkCompare, ChunkKey)); mps_pool_destroy(pool); mps_fmt_destroy(format); if (ngen > 0) @@ -266,26 +266,28 @@ static void arena_setup(gcthread_fn_t fn, /* Command-line options definitions. See getopt_long(3). */ static struct option longopts[] = { - {"help", no_argument, NULL, 'h'}, - {"nthreads", required_argument, NULL, 't'}, - {"niter", required_argument, NULL, 'i'}, - {"npass", required_argument, NULL, 'p'}, - {"gen", required_argument, NULL, 'g'}, - {"arena-size",required_argument, NULL, 'm'}, - {"width", required_argument, NULL, 'w'}, - {"depth", required_argument, NULL, 'd'}, - {"preuse", required_argument, NULL, 'r'}, - {"pupdate", required_argument, NULL, 'u'}, - {"pin-leaf", no_argument, NULL, 'l'}, - {"seed", required_argument, NULL, 'x'}, - {NULL, 0, NULL, 0} + {"help", no_argument, NULL, 'h'}, + {"nthreads", required_argument, NULL, 't'}, + {"niter", required_argument, NULL, 'i'}, + {"npass", required_argument, NULL, 'p'}, + {"gen", required_argument, NULL, 'g'}, + {"arena-size", required_argument, NULL, 'm'}, + {"arena-grain-size", required_argument, NULL, 'a'}, + {"width", required_argument, NULL, 'w'}, + {"depth", required_argument, NULL, 'd'}, + {"preuse", required_argument, NULL, 'r'}, + {"pupdate", required_argument, NULL, 'u'}, + {"pin-leaf", no_argument, NULL, 'l'}, + {"seed", required_argument, NULL, 'x'}, + {"arena-unzoned", no_argument, NULL, 'z'}, + {NULL, 0, NULL, 0 } }; static struct { const char *name; gcthread_fn_t fn; - mps_class_t (*pool_class)(void); + mps_pool_class_t (*pool_class)(void); } pools[] = { {"amc", gc_tree, mps_class_amc}, {"ams", gc_tree, mps_class_ams}, @@ -307,7 +309,7 @@ int main(int argc, char *argv[]) { } putchar('\n'); - while ((ch = getopt_long(argc, argv, "ht:i:p:g:m:w:d:r:u:lx:", longopts, NULL)) != -1) + while ((ch = getopt_long(argc, argv, "ht:i:p:g:m:a:w:d:r:u:lx:z", longopts, NULL)) != -1) switch (ch) { case 't': nthreads = (unsigned)strtoul(optarg, NULL, 10); @@ -329,8 +331,8 @@ int main(int argc, char *argv[]) { double mort = 0.0; cap = (size_t)strtoul(optarg, &p, 10); switch(toupper(*p)) { - case 'G': cap *= 1024; - case 'M': cap *= 1024; + case 'G': cap <<= 20; p++; break; + case 'M': cap <<= 10; p++; break; case 'K': p++; break; default: cap = 0; break; } @@ -349,11 +351,29 @@ int main(int argc, char *argv[]) { break; case 'm': { char *p; - arenasize = (unsigned)strtoul(optarg, &p, 10); + arena_size = (unsigned)strtoul(optarg, &p, 10); switch(toupper(*p)) { - case 'G': arenasize *= 1024; - case 'M': arenasize *= 1024; - case 'K': arenasize *= 1024; break; + case 'G': arena_size <<= 30; break; + case 'M': arena_size <<= 20; break; + case 'K': arena_size <<= 10; break; + case '\0': break; + default: + fprintf(stderr, "Bad arena size %s\n", optarg); + return EXIT_FAILURE; + } + } + break; + case 'a': { + char *p; + arena_grain_size = (unsigned)strtoul(optarg, &p, 10); + switch(toupper(*p)) { + case 'G': arena_grain_size <<= 30; break; + case 'M': arena_grain_size <<= 20; break; + case 'K': arena_grain_size <<= 10; break; + case '\0': break; + default: + fprintf(stderr, "Bad arena grain size %s\n", optarg); + return EXIT_FAILURE; } } break; @@ -375,30 +395,37 @@ int main(int argc, char *argv[]) { case 'x': seed = strtoul(optarg, NULL, 10); break; + case 'z': + zoned = FALSE; + break; default: + /* This is printed in parts to keep within the 509 character + limit for string literals in portable standard C. */ fprintf(stderr, "Usage: %s [option...] [test...]\n" "Options:\n" + " -m n, --arena-size=n[KMG]?\n" + " Initial size of arena (default %lu).\n" + " -a n, --arena-grain-size=n[KMG]?\n" + " Arena grain size (default %lu).\n" " -t n, --nthreads=n\n" " Launch n threads each running the test (default %u).\n" " -i n, --niter=n\n" " Iterate each test n times (default %u).\n" " -p n, --npass=n\n" - " Pass over the tree n times (default %u).\n" - " -g c,m, --gen=c[KMG],m\n" - " Generation with capacity c (in Kb) and mortality m\n" - " Use multiple times for multiple generations.\n" - " -m n, --arena-size=n[KMG]?\n" - " Initial size of arena (default %lu).\n" - " -w n, --width=n\n" - " Width of tree nodes made (default %lu)\n", + " Pass over the tree n times (default %u).\n", argv[0], + (unsigned long)arena_size, + (unsigned long)arena_grain_size, nthreads, niter, - npass, - (unsigned long)arenasize, - (unsigned long)width); + npass); fprintf(stderr, + " -g c,m, --gen=c[KMG],m\n" + " Generation with capacity c (in Kb) and mortality m\n" + " Use multiple times for multiple generations.\n" + " -w n, --width=n\n" + " Width of tree nodes made (default %lu)\n" " -d n, --depth=n\n" " Depth of tree made (default %u)\n" " -r p, --preuse=p\n" @@ -408,27 +435,33 @@ int main(int argc, char *argv[]) { " -l --pin-leaf\n" " Make a pinned object to use for leaves.\n" " -x n, --seed=n\n" - " Random number seed (default from entropy)\n" - "Tests:\n" - " amc pool class AMC\n" - " ams pool class AMS\n", + " Random number seed (default from entropy)\n", + (unsigned long)width, depth, preuse, pupdate); + fprintf(stderr, + " -z, --arena-unzoned\n" + " Disable zoned allocation in the arena\n" + "Tests:\n" + " amc pool class AMC\n" + " ams pool class AMS\n"); return EXIT_FAILURE; } argc -= optind; argv += optind; printf("seed: %lu\n", seed); - + (void)fflush(stdout); + while (argc > 0) { - for (i = 0; i < sizeof(pools) / sizeof(pools[0]); ++i) + for (i = 0; i < NELEMS(pools); ++i) if (strcmp(argv[0], pools[i].name) == 0) goto found; fprintf(stderr, "unknown pool test \"%s\"\n", argv[0]); return EXIT_FAILURE; found: + (void)mps_lib_assert_fail_install(assert_die); rnd_state_set(seed); arena_setup(pools[i].fn, pools[i].pool_class(), pools[i].name); --argc; diff --git a/code/getopt.h b/code/getopt.h index ad2a956608..fb9c789d23 100644 --- a/code/getopt.h +++ b/code/getopt.h @@ -48,8 +48,6 @@ #ifndef _GETOPT_H_ #define _GETOPT_H_ -#include - /* * GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension. * getopt() is declared here too for GNU programs. @@ -72,7 +70,6 @@ struct option { int val; }; -__BEGIN_DECLS int getopt_long(int, char * const *, const char *, const struct option *, int *); int getopt_long_only(int, char * const *, const char *, @@ -88,6 +85,5 @@ extern int optind, opterr, optopt; #define _OPTRESET_DECLARED extern int optreset; /* getopt(3) external variable */ #endif -__END_DECLS #endif /* !_GETOPT_H_ */ diff --git a/code/global.c b/code/global.c index ca0946ae9a..354adb54a9 100644 --- a/code/global.c +++ b/code/global.c @@ -1,7 +1,7 @@ /* global.c: ARENA-GLOBAL INTERFACES * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .sources: See . design.mps.thread-safety is relevant @@ -37,10 +37,6 @@ static Bool arenaRingInit = FALSE; static RingStruct arenaRing; /* */ static Serial arenaSerial; /* */ -/* forward declarations */ -void arenaEnterLock(Arena, int); -void arenaLeaveLock(Arena, int); - /* arenaClaimRingLock, arenaReleaseRingLock -- lock/release the arena ring * @@ -119,7 +115,7 @@ Bool GlobalsCheck(Globals arenaGlobals) CHECKS(Globals, arenaGlobals); arena = GlobalsArena(arenaGlobals); CHECKL(arena->serial < arenaSerial); - CHECKL(RingCheck(&arenaGlobals->globalRing)); + CHECKD_NOSIG(Ring, &arenaGlobals->globalRing); CHECKL(MPSVersion() == arenaGlobals->mpsVersionString); @@ -138,16 +134,17 @@ Bool GlobalsCheck(Globals arenaGlobals) CHECKL(arenaGlobals->emptyInternalSize >= 0.0); CHECKL(BoolCheck(arenaGlobals->bufferLogging)); - CHECKL(RingCheck(&arenaGlobals->poolRing)); - CHECKL(RingCheck(&arenaGlobals->rootRing)); - CHECKL(RingCheck(&arenaGlobals->rememberedSummaryRing)); + CHECKD_NOSIG(Ring, &arenaGlobals->poolRing); + CHECKD_NOSIG(Ring, &arenaGlobals->rootRing); + CHECKD_NOSIG(Ring, &arenaGlobals->rememberedSummaryRing); CHECKL(arenaGlobals->rememberedSummaryIndex < RememberedSummaryBLOCK); /* RingIsSingle imples index == 0 */ CHECKL(!RingIsSingle(&arenaGlobals->rememberedSummaryRing) || arenaGlobals->rememberedSummaryIndex == 0); - CHECKL(RingCheck(&arena->formatRing)); - CHECKL(RingCheck(&arena->messageRing)); - /* Don't check enabledMessageTypes */ + CHECKD_NOSIG(Ring, &arena->formatRing); + CHECKD_NOSIG(Ring, &arena->messageRing); + if (arena->enabledMessageTypes != NULL) + CHECKD_NOSIG(BT, arena->enabledMessageTypes); CHECKL(BoolCheck(arena->isFinalPool)); if (arena->isFinalPool) { CHECKD(Pool, arena->finalPool); @@ -155,7 +152,8 @@ Bool GlobalsCheck(Globals arenaGlobals) CHECKL(arena->finalPool == NULL); } - CHECKL(RingCheck(&arena->threadRing)); + CHECKD_NOSIG(Ring, &arena->threadRing); + CHECKD_NOSIG(Ring, &arena->deadRing); CHECKL(BoolCheck(arena->insideShield)); CHECKL(arena->shCacheLimit <= ShieldCacheSIZE); @@ -188,9 +186,9 @@ Bool GlobalsCheck(Globals arenaGlobals) CHECKL(TraceIdMessagesCheck(arena, ti)); TRACE_SET_ITER_END(ti, trace, TraceSetUNIV, arena); - for(rank = 0; rank < RankLIMIT; ++rank) - CHECKL(RingCheck(&arena->greyRing[rank])); - CHECKL(RingCheck(&arena->chainRing)); + for(rank = RankMIN; rank < RankLIMIT; ++rank) + CHECKD_NOSIG(Ring, &arena->greyRing[rank]); + CHECKD_NOSIG(Ring, &arena->chainRing); CHECKL(arena->tracedSize >= 0.0); CHECKL(arena->tracedTime >= 0.0); @@ -212,6 +210,8 @@ Bool GlobalsCheck(Globals arenaGlobals) /* we also check the statics now. */ CHECKL(BoolCheck(arenaRingInit)); + /* Can't CHECKD_NOSIG here because &arenaRing is never NULL and GCC + * will warn about a constant comparison. */ CHECKL(RingCheck(&arenaRing)); CHECKL(BoolCheck(arena->emergency)); @@ -278,6 +278,7 @@ Res GlobalsInit(Globals arenaGlobals) arenaGlobals->rememberedSummaryIndex = 0; RingInit(&arena->threadRing); + RingInit(&arena->deadRing); arena->threadSerial = (Serial)0; RingInit(&arena->formatRing); arena->formatSerial = (Serial)0; @@ -309,7 +310,7 @@ Res GlobalsInit(Globals arenaGlobals) arena->tMessage[ti] = NULL; } - for(rank = 0; rank < RankLIMIT; ++rank) + for(rank = RankMIN; rank < RankLIMIT; ++rank) RingInit(&arena->greyRing[rank]); STATISTIC(arena->writeBarrierHitCount = 0); RingInit(&arena->chainRing); @@ -394,25 +395,7 @@ void GlobalsFinish(Globals arenaGlobals) Arena arena; Rank rank; - /* Check that the tear-down is complete: that the client has - * destroyed all data structures associated with the arena. We do - * this *before* calling AVERT(Globals, arenaGlobals) because the - * AVERT will crash if there are any remaining data structures, and - * it is politer to assert than to crash. (The crash would happen - * because by this point in the code the control pool has been - * destroyed and so the address space containing all these rings has - * potentially been unmapped, and so RingCheck dereferences a - * pointer into that unmapped memory.) See job000652. */ arena = GlobalsArena(arenaGlobals); - AVER(RingIsSingle(&arena->formatRing)); - AVER(RingIsSingle(&arena->chainRing)); - AVER(RingIsSingle(&arena->messageRing)); - AVER(RingIsSingle(&arena->threadRing)); - for(rank = 0; rank < RankLIMIT; ++rank) - AVER(RingIsSingle(&arena->greyRing[rank])); - AVER(RingIsSingle(&arenaGlobals->poolRing)); - AVER(RingIsSingle(&arenaGlobals->rootRing)); - AVERT(Globals, arenaGlobals); STATISTIC_STAT(EVENT2(ArenaWriteFaults, arena, @@ -424,7 +407,8 @@ void GlobalsFinish(Globals arenaGlobals) RingFinish(&arena->chainRing); RingFinish(&arena->messageRing); RingFinish(&arena->threadRing); - for(rank = 0; rank < RankLIMIT; ++rank) + RingFinish(&arena->deadRing); + for(rank = RankMIN; rank < RankLIMIT; ++rank) RingFinish(&arena->greyRing[rank]); RingFinish(&arenaGlobals->rootRing); RingFinish(&arenaGlobals->poolRing); @@ -441,20 +425,28 @@ void GlobalsPrepareToDestroy(Globals arenaGlobals) Arena arena; TraceId ti; Trace trace; + Chain defaultChain; + Rank rank; AVERT(Globals, arenaGlobals); + /* Park the arena before destroying the default chain, to ensure + * that there are no traces using that chain. */ + ArenaPark(arenaGlobals); + arena = GlobalsArena(arenaGlobals); arenaDenounce(arena); - ChainDestroy(arenaGlobals->defaultChain); + defaultChain = arenaGlobals->defaultChain; arenaGlobals->defaultChain = NULL; + ChainDestroy(defaultChain); - LockReleaseMPM(arenaGlobals->lock); + LockRelease(arenaGlobals->lock); /* Theoretically, another thread could grab the lock here, but it's */ /* not worth worrying about, since an attempt after the lock has been */ /* destroyed would lead to a crash just the same. */ LockFinish(arenaGlobals->lock); + arenaGlobals->lock = NULL; TRACE_SET_ITER(ti, trace, TraceSetUNIV, arena) /* */ @@ -493,6 +485,32 @@ void GlobalsPrepareToDestroy(Globals arenaGlobals) arena->finalPool = NULL; PoolDestroy(pool); } + + /* Check that the tear-down is complete: that the client has + * destroyed all data structures associated with the arena. We do + * this here rather than in GlobalsFinish because by the time that + * is called, the control pool has been destroyed and so the address + * space containing all these rings has potentially been unmapped, + * and so RingCheck dereferences a pointer into that unmapped memory + * and we get a crash instead of an assertion. See job000652. + */ + AVER(RingIsSingle(&arena->formatRing)); + AVER(RingIsSingle(&arena->chainRing)); + AVER(RingIsSingle(&arena->messageRing)); + AVER(RingIsSingle(&arena->threadRing)); + AVER(RingIsSingle(&arena->deadRing)); + AVER(RingIsSingle(&arenaGlobals->rootRing)); + for(rank = RankMIN; rank < RankLIMIT; ++rank) + AVER(RingIsSingle(&arena->greyRing[rank])); + + /* At this point the following pools still exist: + * 0. arena->freeCBSBlockPoolStruct + * 1. arena->reservoirStruct + * 2. arena->controlPoolStruct + * 3. arena->controlPoolStruct.blockPoolStruct + * 4. arena->controlPoolStruct.spanPoolStruct + */ + AVER(RingLength(&arenaGlobals->poolRing) == 5); } @@ -506,26 +524,15 @@ Ring GlobalsRememberedSummaryRing(Globals global) /* ArenaEnter -- enter the state where you can look at the arena */ -/* TODO: The THREAD_SINGLE and PROTECTION_NONE build configs aren't regularly - tested, though they might well be useful for embedded custom targets. - Should test them. RB 2012-09-03 */ - -#if defined(THREAD_SINGLE) && defined(PROTECTION_NONE) void (ArenaEnter)(Arena arena) { - /* Don't need to lock, just check. */ AVERT(Arena, arena); + ArenaEnter(arena); } -#else -void ArenaEnter(Arena arena) -{ - arenaEnterLock(arena, 0); -} -#endif /* The recursive argument specifies whether to claim the lock recursively or not. */ -void arenaEnterLock(Arena arena, int recursive) +void ArenaEnterLock(Arena arena, Bool recursive) { Lock lock; @@ -545,7 +552,7 @@ void arenaEnterLock(Arena arena, int recursive) } else { LockClaim(lock); } - AVERT(Arena, arena); /* can't AVER it until we've got the lock */ + AVERT(Arena, arena); /* can't AVERT it until we've got the lock */ if(recursive) { /* already in shield */ } else { @@ -560,25 +567,18 @@ void arenaEnterLock(Arena arena, int recursive) void ArenaEnterRecursive(Arena arena) { - arenaEnterLock(arena, 1); + ArenaEnterLock(arena, TRUE); } /* ArenaLeave -- leave the state where you can look at MPM data structures */ -#if defined(THREAD_SINGLE) && defined(PROTECTION_NONE) void (ArenaLeave)(Arena arena) { - /* Don't need to lock, just check. */ AVERT(Arena, arena); + ArenaLeave(arena); } -#else -void ArenaLeave(Arena arena) -{ - arenaLeaveLock(arena, 0); -} -#endif -void arenaLeaveLock(Arena arena, int recursive) +void ArenaLeaveLock(Arena arena, Bool recursive) { Lock lock; @@ -595,14 +595,14 @@ void arenaLeaveLock(Arena arena, int recursive) if(recursive) { LockReleaseRecursive(lock); } else { - LockReleaseMPM(lock); + LockRelease(lock); } return; } void ArenaLeaveRecursive(Arena arena) { - arenaLeaveLock(arena, 1); + ArenaLeaveLock(arena, TRUE); } /* mps_exception_info -- pointer to exception info @@ -611,6 +611,7 @@ void ArenaLeaveRecursive(Arena arena) * version. The format is platform-specific. We won't necessarily * publish this. */ +extern MutatorFaultContext mps_exception_info; MutatorFaultContext mps_exception_info = NULL; @@ -629,7 +630,7 @@ Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context) arenaClaimRingLock(); /* */ mps_exception_info = context; - AVER(RingCheck(&arenaRing)); + AVERT(Ring, &arenaRing); RING_FOR(node, &arenaRing, nextNode) { Globals arenaGlobals = RING_ELT(Globals, globalRing, node); @@ -656,7 +657,8 @@ Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context) res = PoolAccess(SegPool(seg), seg, addr, mode, context); AVER(res == ResOK); /* Mutator can't continue unless this succeeds */ } else { - /* Protection was already cleared: nothing to do now. */ + /* Protection was already cleared, for example by another thread + or a fault in a nested exception handler: nothing to do now. */ } EVENT4(ArenaAccess, arena, count, addr, mode); ArenaLeave(arena); @@ -702,20 +704,12 @@ Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context) * series of manual steps for looking around. This might be worthwhile * if we introduce background activities other than tracing. */ -#ifdef MPS_PROD_EPCORE void (ArenaPoll)(Globals globals) -{ - /* Don't poll, just check. */ - AVERT(Globals, globals); -} -#else -void ArenaPoll(Globals globals) { Arena arena; Clock start; Count quanta; Size tracedSize; - double nextPollThreshold = 0.0; AVERT(Globals, globals); @@ -723,93 +717,37 @@ void ArenaPoll(Globals globals) return; if (globals->insidePoll) return; - if(globals->fillMutatorSize < globals->pollThreshold) + arena = GlobalsArena(globals); + if (!PolicyPoll(arena)) return; globals->insidePoll = TRUE; /* fillMutatorSize has advanced; call TracePoll enough to catch up. */ - arena = GlobalsArena(globals); start = ClockNow(); quanta = 0; EVENT3(ArenaPoll, arena, start, 0); - while(globals->pollThreshold <= globals->fillMutatorSize) { + do { tracedSize = TracePoll(globals); - - if(tracedSize == 0) { - /* No work to do. Sleep until NOW + a bit. */ - nextPollThreshold = globals->fillMutatorSize + ArenaPollALLOCTIME; - } else { - /* We did one quantum of work; consume one unit of 'time'. */ + if (tracedSize > 0) { quanta += 1; arena->tracedSize += tracedSize; - nextPollThreshold = globals->pollThreshold + ArenaPollALLOCTIME; } - - /* Advance pollThreshold; check: enough precision? */ - AVER(nextPollThreshold > globals->pollThreshold); - globals->pollThreshold = nextPollThreshold; - } + } while (PolicyPollAgain(arena, start, tracedSize)); /* Don't count time spent checking for work, if there was no work to do. */ if(quanta > 0) { arena->tracedTime += (ClockNow() - start) / (double) ClocksPerSec(); } - AVER(globals->fillMutatorSize < globals->pollThreshold); + AVER(!PolicyPoll(arena)); EVENT3(ArenaPoll, arena, start, quanta); globals->insidePoll = FALSE; } -#endif - -/* Work out whether we have enough time here to collect the world, - * and whether much time has passed since the last time we did that - * opportunistically. */ -static Bool arenaShouldCollectWorld(Arena arena, - double interval, - double multiplier, - Clock now, - Clock clocks_per_sec) -{ - double scanRate; - Size arenaSize; - double arenaScanTime; - double sinceLastWorldCollect; - - /* don't collect the world if we're not given any time */ - if ((interval > 0.0) && (multiplier > 0.0)) { - /* don't collect the world if we're already collecting. */ - if (arena->busyTraces == TraceSetEMPTY) { - /* don't collect the world if it's very small */ - arenaSize = ArenaCommitted(arena) - ArenaSpareCommitted(arena); - if (arenaSize > 1000000) { - /* how long would it take to collect the world? */ - if ((arena->tracedSize > 1000000.0) && - (arena->tracedTime > 1.0)) - scanRate = arena->tracedSize / arena->tracedTime; - else - scanRate = 25000000.0; /* a reasonable default. */ - arenaScanTime = arenaSize / scanRate; - arenaScanTime += 0.1; /* for overheads. */ - - /* how long since we last collected the world? */ - sinceLastWorldCollect = ((now - arena->lastWorldCollect) / - (double) clocks_per_sec); - /* have to be offered enough time, and it has to be a long time - * since we last did it. */ - if ((interval * multiplier > arenaScanTime) && - sinceLastWorldCollect > arenaScanTime * 10.0) { - return TRUE; - } - } - } - } - return FALSE; -} Bool ArenaStep(Globals globals, double interval, double multiplier) { @@ -832,8 +770,8 @@ Bool ArenaStep(Globals globals, double interval, double multiplier) stepped = FALSE; - if (arenaShouldCollectWorld(arena, interval, multiplier, - start, clocks_per_sec)) + if (PolicyShouldCollectWorld(arena, interval, multiplier, + start, clocks_per_sec)) { Res res; Trace trace; @@ -868,17 +806,19 @@ Bool ArenaStep(Globals globals, double interval, double multiplier) Res ArenaFinalize(Arena arena, Ref obj) { Res res; + Pool refpool; AVERT(Arena, arena); - AVER(ArenaHasAddr(arena, (Addr)obj)); + AVER(PoolOfAddr(&refpool, arena, (Addr)obj)); + AVER(PoolHasAttr(refpool, AttrGC)); if (!arena->isFinalPool) { - Pool pool; + Pool finalpool; - res = PoolCreate(&pool, arena, PoolClassMRG(), argsNone); + res = PoolCreate(&finalpool, arena, PoolClassMRG(), argsNone); if (res != ResOK) return res; - arena->finalPool = pool; + arena->finalPool = finalpool; arena->isFinalPool = TRUE; } @@ -1019,88 +959,111 @@ Ref ArenaRead(Arena arena, Ref *p) /* GlobalsDescribe -- describe the arena globals */ -Res GlobalsDescribe(Globals arenaGlobals, mps_lib_FILE *stream) +Res GlobalsDescribe(Globals arenaGlobals, mps_lib_FILE *stream, Count depth) { Res res; Arena arena; Ring node, nextNode; Index i; + TraceId ti; + Trace trace; - if (!TESTT(Globals, arenaGlobals)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Globals, arenaGlobals)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; arena = GlobalsArena(arenaGlobals); - res = WriteF(stream, - " mpsVersion $S\n", arenaGlobals->mpsVersionString, - " lock $P\n", (WriteFP)arenaGlobals->lock, - " pollThreshold $U kB\n", + res = WriteF(stream, depth, + "mpsVersion $S\n", (WriteFS)arenaGlobals->mpsVersionString, + "lock $P\n", (WriteFP)arenaGlobals->lock, + "pollThreshold $U kB\n", (WriteFU)(arenaGlobals->pollThreshold / 1024), - arenaGlobals->insidePoll ? "inside poll\n" : "outside poll\n", + arenaGlobals->insidePoll ? "inside" : "outside", " poll\n", arenaGlobals->clamped ? "clamped\n" : "released\n", - " fillMutatorSize $U kB\n", - (WriteFU)(arenaGlobals->fillMutatorSize / 1024), - " emptyMutatorSize $U kB\n", - (WriteFU)(arenaGlobals->emptyMutatorSize / 1024), - " allocMutatorSize $U kB\n", - (WriteFU)(arenaGlobals->allocMutatorSize / 1024), - " fillInternalSize $U kB\n", - (WriteFU)(arenaGlobals->fillInternalSize / 1024), - " emptyInternalSize $U kB\n", - (WriteFU)(arenaGlobals->emptyInternalSize / 1024), - " poolSerial $U\n", (WriteFU)arenaGlobals->poolSerial, - " rootSerial $U\n", (WriteFU)arenaGlobals->rootSerial, - " formatSerial $U\n", (WriteFU)arena->formatSerial, - " threadSerial $U\n", (WriteFU)arena->threadSerial, - arena->insideShield ? "inside shield\n" : "outside shield\n", - " busyTraces $B\n", (WriteFB)arena->busyTraces, - " flippedTraces $B\n", (WriteFB)arena->flippedTraces, - /* @@@@ no TraceDescribe function */ - " epoch $U\n", (WriteFU)arena->epoch, + "fillMutatorSize $U kB\n", + (WriteFU)(arenaGlobals->fillMutatorSize / 1024), + "emptyMutatorSize $U kB\n", + (WriteFU)(arenaGlobals->emptyMutatorSize / 1024), + "allocMutatorSize $U kB\n", + (WriteFU)(arenaGlobals->allocMutatorSize / 1024), + "fillInternalSize $U kB\n", + (WriteFU)(arenaGlobals->fillInternalSize / 1024), + "emptyInternalSize $U kB\n", + (WriteFU)(arenaGlobals->emptyInternalSize / 1024), + "poolSerial $U\n", (WriteFU)arenaGlobals->poolSerial, + "rootSerial $U\n", (WriteFU)arenaGlobals->rootSerial, + "formatSerial $U\n", (WriteFU)arena->formatSerial, + "threadSerial $U\n", (WriteFU)arena->threadSerial, + arena->insideShield ? "inside" : "outside", " shield\n", + "busyTraces $B\n", (WriteFB)arena->busyTraces, + "flippedTraces $B\n", (WriteFB)arena->flippedTraces, + "epoch $U\n", (WriteFU)arena->epoch, + "prehistory = $B\n", (WriteFB)arena->prehistory, + "history {\n", + " [note: indices are raw, not rotated]\n", NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; for(i=0; i < LDHistoryLENGTH; ++ i) { - res = WriteF(stream, - " history[$U] = $B\n", i, arena->history[i], + res = WriteF(stream, depth + 2, + "[$U] = $B\n", (WriteFU)i, (WriteFB)arena->history[i], NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; } - res = WriteF(stream, - " [note: indices are raw, not rotated]\n" - " prehistory = $B\n", (WriteFB)arena->prehistory, - NULL); - if (res != ResOK) return res; - - res = WriteF(stream, - " suspended $S\n", arena->suspended ? "YES" : "NO", - " shDepth $U\n", arena->shDepth, - " shCacheI $U\n", arena->shCacheI, + res = WriteF(stream, depth, + "} history\n", + "suspended $S\n", WriteFYesNo(arena->suspended), + "shDepth $U\n", (WriteFU)arena->shDepth, + "shCacheI $U\n", (WriteFU)arena->shCacheI, /* @@@@ should SegDescribe the cached segs? */ NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; - res = RootsDescribe(arenaGlobals, stream); - if (res != ResOK) return res; + res = RootsDescribe(arenaGlobals, stream, depth); + if (res != ResOK) + return res; RING_FOR(node, &arenaGlobals->poolRing, nextNode) { Pool pool = RING_ELT(Pool, arenaRing, node); - res = PoolDescribe(pool, stream); - if (res != ResOK) return res; + res = PoolDescribe(pool, stream, depth); + if (res != ResOK) + return res; } RING_FOR(node, &arena->formatRing, nextNode) { Format format = RING_ELT(Format, arenaRing, node); - res = FormatDescribe(format, stream); - if (res != ResOK) return res; + res = FormatDescribe(format, stream, depth); + if (res != ResOK) + return res; } RING_FOR(node, &arena->threadRing, nextNode) { Thread thread = ThreadRingThread(node); - res = ThreadDescribe(thread, stream); - if (res != ResOK) return res; + res = ThreadDescribe(thread, stream, depth); + if (res != ResOK) + return res; } + RING_FOR(node, &arena->chainRing, nextNode) { + Chain chain = RING_ELT(Chain, chainRing, node); + res = ChainDescribe(chain, stream, depth); + if (res != ResOK) + return res; + } + + TRACE_SET_ITER(ti, trace, TraceSetUNIV, arena) + if (TraceSetIsMember(arena->busyTraces, trace)) { + res = TraceDescribe(trace, stream, depth); + if (res != ResOK) + return res; + } + TRACE_SET_ITER_END(ti, trace, TraceSetUNIV, arena); + /* @@@@ What about grey rings? */ return res; } @@ -1125,7 +1088,7 @@ void ArenaSetEmergency(Arena arena, Bool emergency) AVERT(Arena, arena); AVERT(Bool, emergency); - EVENT2(ArenaSetEmergency, arena, emergency); + EVENT2(ArenaSetEmergency, arena, BOOLOF(emergency)); arena->emergency = emergency; } @@ -1140,7 +1103,7 @@ Bool ArenaEmergency(Arena arena) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/land.c b/code/land.c new file mode 100644 index 0000000000..18d446288f --- /dev/null +++ b/code/land.c @@ -0,0 +1,646 @@ +/* land.c: LAND (COLLECTION OF ADDRESS RANGES) IMPLEMENTATION + * + * $Id$ + * Copyright (c) 2014-2015 Ravenbrook Limited. See end of file for license. + * + * .design: + */ + +#include "mpm.h" +#include "range.h" + +SRCID(land, "$Id$"); + + +/* FindDeleteCheck -- check method for a FindDelete value */ + +Bool FindDeleteCheck(FindDelete findDelete) +{ + CHECKL(findDelete == FindDeleteNONE + || findDelete == FindDeleteLOW + || findDelete == FindDeleteHIGH + || findDelete == FindDeleteENTIRE); + UNUSED(findDelete); /* */ + + return TRUE; +} + + +/* landEnter, landLeave -- Avoid re-entrance + * + * .enter-leave: The visitor functions passed to LandIterate and + * LandIterateAndDelete are not allowed to call methods of that land. + * These functions enforce this. + * + * .enter-leave.simple: Some simple queries are fine to call from + * visitor functions. These are marked with the tag of this comment. + */ + +static void landEnter(Land land) +{ + /* Don't need to check as always called from interface function. */ + AVER(!land->inLand); + land->inLand = TRUE; + return; +} + +static void landLeave(Land land) +{ + /* Don't need to check as always called from interface function. */ + AVER(land->inLand); + land->inLand = FALSE; + return; +} + + +/* LandCheck -- check land */ + +Bool LandCheck(Land land) +{ + /* .enter-leave.simple */ + CHECKS(Land, land); + CHECKD(LandClass, land->class); + CHECKU(Arena, land->arena); + CHECKL(AlignCheck(land->alignment)); + CHECKL(BoolCheck(land->inLand)); + return TRUE; +} + + +/* LandInit -- initialize land + * + * See + */ + +Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args) +{ + Res res; + + AVER(land != NULL); + AVERT(LandClass, class); + AVERT(Align, alignment); + + land->inLand = TRUE; + land->alignment = alignment; + land->arena = arena; + land->class = class; + land->sig = LandSig; + + AVERT(Land, land); + + res = (*class->init)(land, args); + if (res != ResOK) + goto failInit; + + EVENT2(LandInit, land, owner); + landLeave(land); + return ResOK; + + failInit: + land->sig = SigInvalid; + return res; +} + + +/* LandCreate -- allocate and initialize land + * + * See + */ + +Res LandCreate(Land *landReturn, Arena arena, LandClass class, Align alignment, void *owner, ArgList args) +{ + Res res; + Land land; + void *p; + + AVER(landReturn != NULL); + AVERT(Arena, arena); + AVERT(LandClass, class); + + res = ControlAlloc(&p, arena, class->size, + /* withReservoirPermit */ FALSE); + if (res != ResOK) + goto failAlloc; + land = p; + + res = LandInit(land, class, arena, alignment, owner, args); + if (res != ResOK) + goto failInit; + + *landReturn = land; + return ResOK; + +failInit: + ControlFree(arena, land, class->size); +failAlloc: + return res; +} + + +/* LandDestroy -- finish and deallocate land + * + * See + */ + +void LandDestroy(Land land) +{ + Arena arena; + LandClass class; + + AVERT(Land, land); + arena = land->arena; + class = land->class; + AVERT(LandClass, class); + LandFinish(land); + ControlFree(arena, land, class->size); +} + + +/* LandFinish -- finish land + * + * See + */ + +void LandFinish(Land land) +{ + AVERT(Land, land); + landEnter(land); + + (*land->class->finish)(land); + + land->sig = SigInvalid; +} + + +/* LandSize -- return the total size of ranges in land + * + * See + */ + +Size LandSize(Land land) +{ + /* .enter-leave.simple */ + AVERT(Land, land); + + return (*land->class->sizeMethod)(land); +} + + +/* LandInsert -- insert range of addresses into land + * + * See + */ + +Res LandInsert(Range rangeReturn, Land land, Range range) +{ + Res res; + + AVER(rangeReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + AVER(RangeIsAligned(range, land->alignment)); + landEnter(land); + + res = (*land->class->insert)(rangeReturn, land, range); + + landLeave(land); + return res; +} + + +/* LandDelete -- delete range of addresses from land + * + * See + */ + +Res LandDelete(Range rangeReturn, Land land, Range range) +{ + Res res; + + AVER(rangeReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + AVER(RangeIsAligned(range, land->alignment)); + landEnter(land); + + res = (*land->class->delete)(rangeReturn, land, range); + + landLeave(land); + return res; +} + + +/* LandIterate -- iterate over isolated ranges of addresses in land + * + * See + */ + +Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +{ + Bool b; + AVERT(Land, land); + AVER(FUNCHECK(visitor)); + landEnter(land); + + b = (*land->class->iterate)(land, visitor, closureP, closureS); + + landLeave(land); + return b; +} + + +/* LandIterateAndDelete -- iterate over isolated ranges of addresses + * in land, deleting some of them + * + * See + */ + +Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS) +{ + Bool b; + AVERT(Land, land); + AVER(FUNCHECK(visitor)); + landEnter(land); + + b = (*land->class->iterateAndDelete)(land, visitor, closureP, closureS); + + landLeave(land); + return b; +} + + +/* LandFindFirst -- find first range of given size + * + * See + */ + +Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + Bool b; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + AVER(SizeIsAligned(size, land->alignment)); + AVERT(FindDelete, findDelete); + landEnter(land); + + b = (*land->class->findFirst)(rangeReturn, oldRangeReturn, land, size, + findDelete); + + landLeave(land); + return b; +} + + +/* LandFindLast -- find last range of given size + * + * See + */ + +Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + Bool b; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + AVER(SizeIsAligned(size, land->alignment)); + AVERT(FindDelete, findDelete); + landEnter(land); + + b = (*land->class->findLast)(rangeReturn, oldRangeReturn, land, size, + findDelete); + + landLeave(land); + return b; +} + + +/* LandFindLargest -- find largest range of at least given size + * + * See + */ + +Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + Bool b; + + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + AVER(SizeIsAligned(size, land->alignment)); + AVERT(FindDelete, findDelete); + landEnter(land); + + b = (*land->class->findLargest)(rangeReturn, oldRangeReturn, land, size, + findDelete); + + landLeave(land); + return b; +} + + +/* LandFindInSize -- find range of given size in set of zones + * + * See + */ + +Res LandFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) +{ + Res res; + + AVER(foundReturn != NULL); + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + AVER(SizeIsAligned(size, land->alignment)); + /* AVER(ZoneSet, zoneSet); */ + AVERT(Bool, high); + landEnter(land); + + res = (*land->class->findInZones)(foundReturn, rangeReturn, oldRangeReturn, + land, size, zoneSet, high); + + landLeave(land); + return res; +} + + +/* LandDescribe -- describe land for debugging + * + * See + */ + +Res LandDescribe(Land land, mps_lib_FILE *stream, Count depth) +{ + Res res; + + if (!TESTT(Land, land)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "Land $P {\n", (WriteFP)land, + " class $P", (WriteFP)land->class, + " (\"$S\")\n", (WriteFS)land->class->name, + " arena $P\n", (WriteFP)land->arena, + " align $U\n", (WriteFU)land->alignment, + " inLand $S\n", WriteFYesNo(land->inLand), + NULL); + if (res != ResOK) + return res; + + res = (*land->class->describe)(land, stream, depth + 2); + if (res != ResOK) + return res; + + res = WriteF(stream, depth, "} Land $P\n", (WriteFP)land, NULL); + return ResOK; +} + + +/* landFlushVisitor -- visitor for LandFlush. + * + * closureP argument is the destination Land. Attempt to insert the + * range into the destination. + */ +static Bool landFlushVisitor(Bool *deleteReturn, Land land, Range range, + void *closureP, Size closureS) +{ + Res res; + RangeStruct newRange; + Land dest; + + AVER(deleteReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + AVER(closureP != NULL); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); + + dest = closureP; + res = LandInsert(&newRange, dest, range); + if (res == ResOK) { + *deleteReturn = TRUE; + return TRUE; + } else { + *deleteReturn = FALSE; + return FALSE; + } +} + + +/* LandFlush -- move ranges from src to dest + * + * See + */ + +Bool LandFlush(Land dest, Land src) +{ + AVERT(Land, dest); + AVERT(Land, src); + + return LandIterateAndDelete(src, landFlushVisitor, dest, UNUSED_SIZE); +} + + +/* LandClassCheck -- check land class */ + +Bool LandClassCheck(LandClass class) +{ + CHECKL(ProtocolClassCheck(&class->protocol)); + CHECKL(class->name != NULL); /* Should be <=6 char C identifier */ + CHECKL(class->size >= sizeof(LandStruct)); + CHECKL(FUNCHECK(class->init)); + CHECKL(FUNCHECK(class->finish)); + CHECKL(FUNCHECK(class->insert)); + CHECKL(FUNCHECK(class->delete)); + CHECKL(FUNCHECK(class->findFirst)); + CHECKL(FUNCHECK(class->findLast)); + CHECKL(FUNCHECK(class->findLargest)); + CHECKL(FUNCHECK(class->findInZones)); + CHECKL(FUNCHECK(class->describe)); + CHECKS(LandClass, class); + return TRUE; +} + + +static Res landTrivInit(Land land, ArgList args) +{ + AVERT(Land, land); + AVERT(ArgList, args); + UNUSED(args); + return ResOK; +} + +static void landTrivFinish(Land land) +{ + AVERT(Land, land); + NOOP; +} + +static Size landNoSize(Land land) +{ + UNUSED(land); + NOTREACHED; + return 0; +} + +/* LandSlowSize -- generic size method but slow */ + +static Bool landSizeVisitor(Land land, Range range, + void *closureP, Size closureS) +{ + Size *size; + + AVERT(Land, land); + AVERT(Range, range); + AVER(closureP != NULL); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); + + size = closureP; + *size += RangeSize(range); + + return TRUE; +} + +Size LandSlowSize(Land land) +{ + Size size = 0; + Bool b = LandIterate(land, landSizeVisitor, &size, UNUSED_SIZE); + AVER(b); + return size; +} + +static Res landNoInsert(Range rangeReturn, Land land, Range range) +{ + AVER(rangeReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + return ResUNIMPL; +} + +static Res landNoDelete(Range rangeReturn, Land land, Range range) +{ + AVER(rangeReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + return ResUNIMPL; +} + +static Bool landNoIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +{ + AVERT(Land, land); + AVER(visitor != NULL); + UNUSED(closureP); + UNUSED(closureS); + return FALSE; +} + +static Bool landNoIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS) +{ + AVERT(Land, land); + AVER(visitor != NULL); + UNUSED(closureP); + UNUSED(closureS); + return FALSE; +} + +static Bool landNoFind(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +{ + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + UNUSED(size); + AVERT(FindDelete, findDelete); + return ResUNIMPL; +} + +static Res landNoFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) +{ + AVER(foundReturn != NULL); + AVER(rangeReturn != NULL); + AVER(oldRangeReturn != NULL); + AVERT(Land, land); + UNUSED(size); + UNUSED(zoneSet); + AVERT(Bool, high); + return ResUNIMPL; +} + +static Res landTrivDescribe(Land land, mps_lib_FILE *stream, Count depth) +{ + if (!TESTT(Land, land)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + UNUSED(depth); + /* dispatching function does it all */ + return ResOK; +} + +DEFINE_CLASS(LandClass, class) +{ + INHERIT_CLASS(&class->protocol, ProtocolClass); + class->name = "LAND"; + class->size = sizeof(LandStruct); + class->init = landTrivInit; + class->sizeMethod = landNoSize; + class->finish = landTrivFinish; + class->insert = landNoInsert; + class->delete = landNoDelete; + class->iterate = landNoIterate; + class->iterateAndDelete = landNoIterateAndDelete; + class->findFirst = landNoFind; + class->findLast = landNoFind; + class->findLargest = landNoFind; + class->findInZones = landNoFindInZones; + class->describe = landTrivDescribe; + class->sig = LandClassSig; + AVERT(LandClass, class); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014-2015 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/landtest.c b/code/landtest.c new file mode 100644 index 0000000000..eb4229f65e --- /dev/null +++ b/code/landtest.c @@ -0,0 +1,587 @@ +/* landtest.c: LAND TEST + * + * $Id$ + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * + * Test all three Land implementations against duplicate operations on + * a bit-table. + */ + +#include "cbs.h" +#include "failover.h" +#include "freelist.h" +#include "mpm.h" +#include "mps.h" +#include "mpsavm.h" +#include "mpstd.h" +#include "poolmfs.h" +#include "testlib.h" + +#include /* printf */ + +SRCID(landtest, "$Id$"); + + +#define ArraySize ((Size)123456) + +/* CBS is much faster than Freelist, so we apply more operations to + * the former. */ +#define nCBSOperations ((Size)125000) +#define nFLOperations ((Size)12500) +#define nFOOperations ((Size)12500) + +static Count NAllocateTried, NAllocateSucceeded, NDeallocateTried, + NDeallocateSucceeded; + +static int verbose = 0; + +typedef struct TestStateStruct { + Align align; + BT allocTable; + Addr block; + Size size; + Land land; +} TestStateStruct, *TestState; + +typedef struct CheckTestClosureStruct { + TestState state; + Addr limit; + Addr oldLimit; +} CheckTestClosureStruct, *CheckTestClosure; + + +static Addr (addrOfIndex)(TestState state, Index i) +{ + return AddrAdd(state->block, (i * state->align)); +} + + +static Index (indexOfAddr)(TestState state, Addr a) +{ + return (Index)(AddrOffset(state->block, a) / state->align); +} + + +static void describe(TestState state) { + die(LandDescribe(state->land, mps_lib_get_stdout(), 0), "LandDescribe"); +} + + +static Bool checkVisitor(Land land, Range range, void *closureP, Size closureS) +{ + Addr base, limit; + CheckTestClosure cl = closureP; + + testlib_unused(land); + Insist(closureS == UNUSED_SIZE); + Insist(cl != NULL); + + base = RangeBase(range); + limit = RangeLimit(range); + + if (base > cl->oldLimit) { + Insist(BTIsSetRange(cl->state->allocTable, + indexOfAddr(cl->state, cl->oldLimit), + indexOfAddr(cl->state, base))); + } else { /* must be at start of table */ + Insist(base == cl->oldLimit); + Insist(cl->oldLimit == cl->state->block); + } + + Insist(BTIsResRange(cl->state->allocTable, + indexOfAddr(cl->state, base), + indexOfAddr(cl->state, limit))); + + cl->oldLimit = limit; + + return TRUE; +} + +static void check(TestState state) +{ + CheckTestClosureStruct closure; + Bool b; + + closure.state = state; + closure.limit = addrOfIndex(state, state->size); + closure.oldLimit = state->block; + + b = LandIterate(state->land, checkVisitor, &closure, UNUSED_SIZE); + Insist(b); + + if (closure.oldLimit == state->block) + Insist(BTIsSetRange(state->allocTable, 0, + indexOfAddr(state, closure.limit))); + else if (closure.limit > closure.oldLimit) + Insist(BTIsSetRange(state->allocTable, + indexOfAddr(state, closure.oldLimit), + indexOfAddr(state, closure.limit))); + else + Insist(closure.oldLimit == closure.limit); +} + + +static Word fbmRnd(Word limit) +{ + /* Not very uniform, but never mind. */ + return (Word)rnd() % limit; +} + + +/* nextEdge -- Finds the next transition in the bit table + * + * Returns the index greater than such that the + * range [, ) has the same value in the bit table, + * and has a different value or does not exist. + */ + +static Index nextEdge(BT bt, Size size, Index base) +{ + Index end; + Bool baseValue; + + Insist(bt != NULL); + Insist(base < size); + + baseValue = BTGet(bt, base); + + for(end = base + 1; end < size && BTGet(bt, end) == baseValue; end++) + NOOP; + + return end; +} + + +/* lastEdge -- Finds the previous transition in the bit table + * + * Returns the index less than such that the range + * [, ] has the same value in the bit table, + * and -1 has a different value or does not exist. + */ + +static Index lastEdge(BT bt, Size size, Index base) +{ + Index end; + Bool baseValue; + + Insist(bt != NULL); + Insist(base < size); + + baseValue = BTGet(bt, base); + + for(end = base; end > (Index)0 && BTGet(bt, end - 1) == baseValue; end--) + NOOP; + + return end; +} + + +/* randomRange -- picks random range within table + * + * The function first picks a uniformly distributed within the table. + * + * It then scans forward a binary exponentially distributed number of + * "edges" in the table (that is, transitions between set and reset) + * to get . Note that there is a 50% chance that will be + * the next edge, a 25% chance it will be the edge after, etc., until + * the end of the table. + * + * Finally it picks a uniformly distributed in the range + * [base+1, limit]. + * + * Hence there is a somewhat better than 50% chance that the range will be + * all either set or reset. + */ + +static void randomRange(Addr *baseReturn, Addr *limitReturn, TestState state) +{ + Index base; /* the start of our range */ + Index end; /* an edge (i.e. different from its predecessor) */ + /* after base */ + Index limit; /* a randomly chosen value in (base, limit]. */ + + base = fbmRnd(state->size); + + do { + end = nextEdge(state->allocTable, state->size, base); + } while (end < state->size && fbmRnd(2) == 0); /* p=0.5 exponential */ + + Insist(end > base); + + limit = base + 1 + fbmRnd(end - base); + + *baseReturn = addrOfIndex(state, base); + *limitReturn = addrOfIndex(state, limit); +} + + +static void allocate(TestState state, Addr base, Addr limit) +{ + Res res; + Index ib, il; /* Indexed for base and limit */ + Bool isFree; + RangeStruct range, oldRange; + Addr outerBase, outerLimit; /* interval containing [ib, il) */ + + ib = indexOfAddr(state, base); + il = indexOfAddr(state, limit); + + isFree = BTIsResRange(state->allocTable, ib, il); + + NAllocateTried++; + + if (isFree) { + outerBase = + addrOfIndex(state, lastEdge(state->allocTable, state->size, ib)); + outerLimit = + addrOfIndex(state, nextEdge(state->allocTable, state->size, il - 1)); + } else { + outerBase = outerLimit = NULL; + } + + RangeInit(&range, base, limit); + res = LandDelete(&oldRange, state->land, &range); + + if (verbose) { + printf("allocate: [%p,%p) -- %s\n", + (void *)base, (void *)limit, isFree ? "succeed" : "fail"); + describe(state); + } + + if (!isFree) { + die_expect((mps_res_t)res, MPS_RES_FAIL, + "Succeeded in deleting allocated block"); + } else { /* isFree */ + die_expect((mps_res_t)res, MPS_RES_OK, + "failed to delete free block"); + Insist(RangeBase(&oldRange) == outerBase); + Insist(RangeLimit(&oldRange) == outerLimit); + NAllocateSucceeded++; + BTSetRange(state->allocTable, ib, il); + } +} + + +static void deallocate(TestState state, Addr base, Addr limit) +{ + Res res; + Index ib, il; + Bool isAllocated; + Addr outerBase = base, outerLimit = limit; /* interval containing [ib, il) */ + RangeStruct range, freeRange; /* interval returned by the manager */ + + ib = indexOfAddr(state, base); + il = indexOfAddr(state, limit); + + isAllocated = BTIsSetRange(state->allocTable, ib, il); + + NDeallocateTried++; + + if (isAllocated) { + /* Find the free blocks adjacent to the allocated block */ + if (ib > 0 && !BTGet(state->allocTable, ib - 1)) { + outerBase = + addrOfIndex(state, lastEdge(state->allocTable, state->size, ib - 1)); + } else { + outerBase = base; + } + + if (il < state->size && !BTGet(state->allocTable, il)) { + outerLimit = + addrOfIndex(state, nextEdge(state->allocTable, state->size, il)); + } else { + outerLimit = limit; + } + } + + RangeInit(&range, base, limit); + res = LandInsert(&freeRange, state->land, &range); + + if (verbose) { + printf("deallocate: [%p,%p) -- %s\n", + (void *)base, (void *)limit, isAllocated ? "succeed" : "fail"); + describe(state); + } + + if (!isAllocated) { + die_expect((mps_res_t)res, MPS_RES_FAIL, + "succeeded in inserting non-allocated block"); + } else { /* isAllocated */ + die_expect((mps_res_t)res, MPS_RES_OK, + "failed to insert allocated block"); + + NDeallocateSucceeded++; + BTResRange(state->allocTable, ib, il); + Insist(RangeBase(&freeRange) == outerBase); + Insist(RangeLimit(&freeRange) == outerLimit); + } +} + + +static void find(TestState state, Size size, Bool high, FindDelete findDelete) +{ + Bool expected, found; + Index expectedBase, expectedLimit; + RangeStruct foundRange, oldRange; + Addr origBase, origLimit; + + origBase = origLimit = NULL; + expected = (high ? BTFindLongResRangeHigh : BTFindLongResRange) + (&expectedBase, &expectedLimit, state->allocTable, + (Index)0, (Index)state->size, (Count)size); + + if (expected) { + origBase = addrOfIndex(state, expectedBase); + origLimit = addrOfIndex(state, expectedLimit); + + switch(findDelete) { + case FindDeleteNONE: + /* do nothing */ + break; + case FindDeleteENTIRE: + break; + case FindDeleteLOW: + expectedLimit = expectedBase + size; + break; + case FindDeleteHIGH: + expectedBase = expectedLimit - size; + break; + default: + cdie(0, "invalid findDelete"); + break; + } + } + + found = (high ? LandFindLast : LandFindFirst) + (&foundRange, &oldRange, state->land, size * state->align, findDelete); + + if (verbose) { + printf("find %s %lu: ", high ? "last" : "first", + (unsigned long)(size * state->align)); + if (expected) { + printf("expecting [%p,%p)\n", + (void *)addrOfIndex(state, expectedBase), + (void *)addrOfIndex(state, expectedLimit)); + } else { + printf("expecting this not to be found\n"); + } + if (found) { + printf(" found [%p,%p)\n", (void *)RangeBase(&foundRange), + (void *)RangeLimit(&foundRange)); + } else { + printf(" not found\n"); + } + } + + Insist(found == expected); + + if (found) { + Insist(expectedBase == indexOfAddr(state, RangeBase(&foundRange))); + Insist(expectedLimit == indexOfAddr(state, RangeLimit(&foundRange))); + + if (findDelete != FindDeleteNONE) { + Insist(RangeBase(&oldRange) == origBase); + Insist(RangeLimit(&oldRange) == origLimit); + BTSetRange(state->allocTable, expectedBase, expectedLimit); + } + } + + return; +} + +static void test(TestState state, unsigned n) { + Addr base, limit; + unsigned i; + Size size; + Bool high; + FindDelete findDelete = FindDeleteNONE; + + BTSetRange(state->allocTable, 0, state->size); /* Initially all allocated */ + check(state); + for(i = 0; i < n; i++) { + switch(fbmRnd(3)) { + case 0: + randomRange(&base, &limit, state); + allocate(state, base, limit); + break; + case 1: + randomRange(&base, &limit, state); + deallocate(state, base, limit); + break; + case 2: + size = fbmRnd(state->size / 10) + 1; + high = fbmRnd(2) ? TRUE : FALSE; + switch(fbmRnd(6)) { + default: findDelete = FindDeleteNONE; break; + case 3: findDelete = FindDeleteLOW; break; + case 4: findDelete = FindDeleteHIGH; break; + case 5: findDelete = FindDeleteENTIRE; break; + } + find(state, size, high, findDelete); + break; + default: + cdie(0, "invalid rnd(3)"); + return; + } + if ((i + 1) % 1000 == 0) + check(state); + } +} + +#define testArenaSIZE (((size_t)4)<<20) + +extern int main(int argc, char *argv[]) +{ + mps_arena_t mpsArena; + Arena arena; + TestStateStruct state; + void *p; + MFSStruct blockPool; + CBSStruct cbsStruct; + FreelistStruct flStruct; + FailoverStruct foStruct; + Land cbs = CBSLand(&cbsStruct); + Land fl = FreelistLand(&flStruct); + Land fo = FailoverLand(&foStruct); + Pool mfs = MFSPool(&blockPool); + int i; + + testlib_init(argc, argv); + state.size = ArraySize; + state.align = (1 << rnd() % 4) * MPS_PF_ALIGN; + + NAllocateTried = NAllocateSucceeded = NDeallocateTried = + NDeallocateSucceeded = 0; + + die(mps_arena_create(&mpsArena, mps_arena_class_vm(), testArenaSIZE), + "mps_arena_create"); + arena = (Arena)mpsArena; /* avoid pun */ + + die((mps_res_t)BTCreate(&state.allocTable, arena, state.size), + "failed to create alloc table"); + + die((mps_res_t)ControlAlloc(&p, arena, (state.size + 1) * state.align, + /* withReservoirPermit */ FALSE), + "failed to allocate block"); + state.block = AddrAlignUp(p, state.align); + + if (verbose) { + printf("Allocated block [%p,%p)\n", (void *)state.block, + (void *)AddrAdd(state.block, state.size)); + } + + /* 1. Test CBS */ + + MPS_ARGS_BEGIN(args) { + die((mps_res_t)LandInit(cbs, CBSFastLandClassGet(), arena, state.align, + NULL, args), + "failed to initialise CBS"); + } MPS_ARGS_END(args); + state.land = cbs; + test(&state, nCBSOperations); + LandFinish(cbs); + + /* 2. Test Freelist */ + + die((mps_res_t)LandInit(fl, FreelistLandClassGet(), arena, state.align, + NULL, mps_args_none), + "failed to initialise Freelist"); + state.land = fl; + test(&state, nFLOperations); + LandFinish(fl); + + /* 3. Test CBS-failing-over-to-Freelist (always failing over on + * first iteration, never failing over on second; see fotest.c for a + * test case that randomly switches fail-over on and off) + */ + + for (i = 0; i < 2; ++i) { + MPS_ARGS_BEGIN(piArgs) { + MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSFastBlockStruct)); + MPS_ARGS_ADD(piArgs, MPS_KEY_EXTEND_BY, ArenaGrainSize(arena)); + MPS_ARGS_ADD(piArgs, MFSExtendSelf, i); + die(PoolInit(mfs, arena, PoolClassMFS(), piArgs), "PoolInit"); + } MPS_ARGS_END(piArgs); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, CBSBlockPool, mfs); + die((mps_res_t)LandInit(cbs, CBSFastLandClassGet(), arena, state.align, + NULL, args), + "failed to initialise CBS"); + } MPS_ARGS_END(args); + + die((mps_res_t)LandInit(fl, FreelistLandClassGet(), arena, state.align, + NULL, mps_args_none), + "failed to initialise Freelist"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, FailoverPrimary, cbs); + MPS_ARGS_ADD(args, FailoverSecondary, fl); + die((mps_res_t)LandInit(fo, FailoverLandClassGet(), arena, state.align, + NULL, args), + "failed to initialise Failover"); + } MPS_ARGS_END(args); + + state.land = fo; + test(&state, nFOOperations); + LandFinish(fo); + LandFinish(fl); + LandFinish(cbs); + PoolFinish(mfs); + } + + ControlFree(arena, p, (state.size + 1) * state.align); + mps_arena_destroy(arena); + + printf("\nNumber of allocations attempted: %"PRIuLONGEST"\n", + (ulongest_t)NAllocateTried); + printf("Number of allocations succeeded: %"PRIuLONGEST"\n", + (ulongest_t)NAllocateSucceeded); + printf("Number of deallocations attempted: %"PRIuLONGEST"\n", + (ulongest_t)NDeallocateTried); + printf("Number of deallocations succeeded: %"PRIuLONGEST"\n", + (ulongest_t)NDeallocateSucceeded); + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); + return 0; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (c) 2001-2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/ld.c b/code/ld.c index e34479c629..c9e79f8a76 100644 --- a/code/ld.c +++ b/code/ld.c @@ -1,7 +1,7 @@ /* ld.c: LOCATION DEPENDENCY IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * * .def: A location dependency records the fact that the bit-patterns * of some references will be used directly (most likely for @@ -92,11 +92,21 @@ void LDReset(mps_ld_t ld, Arena arena) * occured since the epoch recorded in the dependency. If the location * were used first only the new location of the reference would end up * in the set. + * + * .add.no-arena-check: Add does not check that the address belongs to + * the arena because this would require taking the arena lock. We + * would rather that this function be lock-free even if some errors + * are not detected. + * + * .add.no-align-check: Add does not check that the address is + * aligned, for the same reason as .add.check: it can't find out which + * pool the address belongs to without taking the lock. */ void LDAdd(mps_ld_t ld, Arena arena, Addr addr) { + AVER(ld != NULL); + AVER(TESTT(Arena, arena)); /* see .add.lock-free */ AVER(ld->_epoch <= arena->epoch); - /* AVERT(Arena, arena) -- see .add.lock-free */ ld->_rs = RefSetAdd(arena, ld->_rs, addr); } @@ -126,8 +136,9 @@ Bool LDIsStaleAny(mps_ld_t ld, Arena arena) { RefSet rs; + AVER(ld != NULL); + AVER(TESTT(Arena, arena)); /* .stale.thread-safe */ AVER(ld->_epoch <= arena->epoch); - /* AVERT(Arena, arena) -- .stale.thread-safe */ if (arena->epoch == ld->_epoch) /* .stale.current */ return FALSE; @@ -151,6 +162,10 @@ Bool LDIsStaleAny(mps_ld_t ld, Arena arena) * .stale.conservative: In fact we just ignore the address and test if * any dependency is stale. This is conservatively correct (no false * negatives) but provides a hook for future improvement. + * + * .stale.no-arena-check: See .add.no-arena-check. + * + * .stale.no-align-check: See .add.no-align-check. */ Bool LDIsStale(mps_ld_t ld, Arena arena, Addr addr) { @@ -204,8 +219,8 @@ void LDAge(Arena arena, RefSet rs) */ void LDMerge(mps_ld_t ld, Arena arena, mps_ld_t from) { - /* AVERT(Arena, arena); -- .merge.lock-free */ AVER(ld != NULL); + AVER(TESTT(Arena, arena)); /* .merge.lock-free */ AVER(ld->_epoch <= arena->epoch); AVER(from != NULL); AVER(from->_epoch <= arena->epoch); @@ -223,7 +238,7 @@ void LDMerge(mps_ld_t ld, Arena arena, mps_ld_t from) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/lii3gc.gmk b/code/lii3gc.gmk index b205028358..00be40c673 100644 --- a/code/lii3gc.gmk +++ b/code/lii3gc.gmk @@ -1,30 +1,33 @@ # -*- makefile -*- # -# lii3gc.gmk: BUILD FOR LINUX/INTEL/GCC PLATFORM +# lii3gc.gmk: BUILD FOR LINUX/x86/GCC PLATFORM # # $Id$ -# Copyright (c) 2001 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. PFM = lii3gc -THREADSRC = lockli.c thix.c pthrdext.c -THREADLIB = -lpthread +MPMPF = \ + lockli.c \ + prmci3li.c \ + proti3.c \ + protix.c \ + protli.c \ + pthrdext.c \ + span.c \ + ssixi3.c \ + thix.c \ + vmix.c -MPMPF = ${THREADSRC} vmix.c \ - protix.c protli.c proti3.c prmci3li.c ssixi3.c span.c - -LIBS = -lm ${THREADLIB} +LIBS = -lm -lpthread include gc.gmk - -CC = cc - include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2002 Ravenbrook Limited . +# Copyright (C) 2001-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/code/lii6gc.gmk b/code/lii6gc.gmk index b913578a89..91f8f5d906 100644 --- a/code/lii6gc.gmk +++ b/code/lii6gc.gmk @@ -3,28 +3,31 @@ # lii6gc.gmk: BUILD FOR LINUX/x64/GCC PLATFORM # # $Id$ -# Copyright (c) 2001 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. PFM = lii6gc -THREADSRC = lockli.c thix.c pthrdext.c -THREADLIB = -lpthread +MPMPF = \ + lockli.c \ + prmci6li.c \ + proti6.c \ + protix.c \ + protli.c \ + pthrdext.c \ + span.c \ + ssixi6.c \ + thix.c \ + vmix.c -MPMPF = ${THREADSRC} vmix.c \ - protix.c protli.c proti6.c prmci6li.c ssixi6.c span.c - -LIBS = -lm ${THREADLIB} +LIBS = -lm -lpthread include gc.gmk - -CC = cc - include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2002 Ravenbrook Limited . +# Copyright (C) 2001-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/code/lii6ll.gmk b/code/lii6ll.gmk index 75b298b156..5988b0c0b1 100644 --- a/code/lii6ll.gmk +++ b/code/lii6ll.gmk @@ -3,17 +3,23 @@ # lii6ll.gmk: BUILD FOR LINUX/x64/Clang PLATFORM # # $Id$ -# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. PFM = lii6ll -THREADSRC = lockli.c thix.c pthrdext.c -THREADLIB = -lpthread +MPMPF = \ + lockli.c \ + prmci6li.c \ + proti6.c \ + protix.c \ + protli.c \ + pthrdext.c \ + span.c \ + ssixi6.c \ + thix.c \ + vmix.c -MPMPF = ${THREADSRC} vmix.c \ - protix.c protli.c proti6.c prmci6li.c ssixi6.c span.c - -LIBS = -lm ${THREADLIB} +LIBS = -lm -lpthread include ll.gmk include comm.gmk @@ -21,7 +27,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2013 Ravenbrook Limited . +# Copyright (C) 2001-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/code/ll.gmk b/code/ll.gmk index fd91e62fe4..787380fb3e 100644 --- a/code/ll.gmk +++ b/code/ll.gmk @@ -3,20 +3,36 @@ # ll.gmk: GNUMAKEFILE FRAGMENT FOR CLANG/LLVM # # $Id$ -# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. # # This file is included by platform makefiles that use the Clang/LLVM # compiler. It defines the compiler-specific variables that the # common makefile fragment () requires. CC = clang -CFLAGSDEBUG = -O -g3 +CFLAGSDEBUG = -O0 -g3 CFLAGSOPT = -O2 -g3 -CFLAGSCOMPILER := -Wall -Werror -Wpointer-arith \ - -Wstrict-prototypes -Wmissing-prototypes \ - -Winline -Waggregate-return -Wnested-externs \ - -Wcast-qual -Wshadow # -Wstrict-aliasing=2 -CFLAGSCOMPILERSTRICT := -pedantic -Wno-extended-offsetof +CFLAGSCOMPILER := \ + -pedantic \ + -Waggregate-return \ + -Wall \ + -Wcast-qual \ + -Wconversion \ + -Wduplicate-enum \ + -Werror \ + -Wextra \ + -Winline \ + -Wmissing-prototypes \ + -Wmissing-variable-declarations \ + -Wnested-externs \ + -Wno-extended-offsetof \ + -Wpointer-arith \ + -Wshadow \ + -Wstrict-aliasing=2 \ + -Wstrict-prototypes \ + -Wunreachable-code \ + -Wwrite-strings +CFLAGSCOMPILERSTRICT := # A different set of compiler flags for less strict compilation, for # instance when we need to #include a third-party header file that @@ -30,7 +46,7 @@ CFLAGSCOMPILERLAX := # If interrupted, this is liable to leave a zero-length file behind. define gendep - $(SHELL) -ec "$(CC) $(CFLAGS) -MM $< | \ + $(SHELL) -ec "$(CC) $(CFLAGSSTRICT) -MM $< | \ sed '/:/s!$*.o!$(@D)/& $(@D)/$*.d!' > $@" [ -s $@ ] || rm -f $@ endef @@ -38,7 +54,7 @@ endef # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2013 Ravenbrook Limited . +# Copyright (C) 2001-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/code/locbwcss.c b/code/locbwcss.c index 5f9d609562..dbf54e7e88 100644 --- a/code/locbwcss.c +++ b/code/locbwcss.c @@ -1,7 +1,7 @@ /* locbwcss.c: LOCUS BACKWARDS COMPATIBILITY STRESS TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "mpscmvff.h" @@ -11,8 +11,7 @@ #include "mpslib.h" #include "mps.h" -#include -#include +#include /* printf */ /* some constants */ @@ -110,7 +109,7 @@ static void poolStatInit(PoolStat stat, mps_pool_t pool, size_t objSize) static void allocMultiple(PoolStat stat) { mps_addr_t objects[allocsPerIteration]; - int i; + size_t i; /* allocate a few objects, and record stats for them */ for (i = 0; i < allocsPerIteration; i++) { @@ -129,10 +128,9 @@ static void allocMultiple(PoolStat stat) /* reportResults - print a report on a PoolStat */ -static void reportResults(PoolStat stat, char *name) +static void reportResults(PoolStat stat, const char *name) { - printf("\nResults for "); - fputs(name, stdout); + printf("\nResults for %s\n", name); printf("\n"); printf(" Allocated %"PRIuLONGEST" objects\n", (ulongest_t)stat->aCount); printf(" Freed %"PRIuLONGEST" objects\n", (ulongest_t)stat->fCount); @@ -193,11 +191,14 @@ int main(int argc, char *argv[]) { mps_arena_t arena; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); - die(mps_arena_create(&arena, mps_arena_class_vmnz(), testArenaSIZE), - "mps_arena_create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), + "mps_arena_create"); + } MPS_ARGS_END(args); testInArena(arena); @@ -210,7 +211,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/lock.h b/code/lock.h index 1431bbacd8..35d0ed6db4 100644 --- a/code/lock.h +++ b/code/lock.h @@ -1,79 +1,7 @@ /* lock.h: RECURSIVE LOCKS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. - * - * .description: [@@@@ Should be combined with ] - * This defines the type Lock, which supports simple recursive - * locking. Locking ensures that only a single thread may be running - * with a lock held. By claiming a lock in some code, this ensures - * that only one thread can be running in that code at a time. This - * in turn can be used to protect different threads from trying to - * read or update data structures which are in a transitional state. - * - * At most one thread may own a lock at a time. A lock is initialised - * without an owner. A lock should not have an owner when it is - * finished. Claiming the lock will wait until the lock is not owned - * by another thread and then cause the current thread to become the - * owner. Releasing the the lock will relinquish ownership if the - * number of releases matches the number of claims. - * - * To use a lock a structure of type LockStruct must be allocated. - * This is defined in . Sources which allocate such a - * structure will need to include "lockst.h". A lock of type Lock is - * a pointer to such an allocated structure. - * - * A lock must be Inited before use and should be Finished after use, - * using LockInit and LockFinish. - * - * LockClaimRecursive & LockReleaseRecursive are for claiming and - * releasing the lock. These may be used recursively. - * - * There is a limit on the number of recursive claims which - * depends on the implementation. See issue.lock-claim-limit. - * - * LockClaim and LockReleaseMPM are the same as the Recursive versions, - * except that LockClaim may only be used by a thread that doesn't - * already own the lock, and LockReleaseMPM may only be used to release - * a lock with one claim. LockClaim and LockReleaseMPM if used, must - * be used symmetrically in pairs. - * - * There are two intended uses. Here is an example: - * #include "lock.h" - * #include "lockst.h" - * static LockStruct lockStruct; - * binaryUse() - * { ;; lock not owned by this thread. - * LockClaim(&lockStruct); - * ;; lock owned by this thread. - * ;; Cannot call binaryUse() at this point. - * ;; only one thread at a time may be at this point. - * LockReleaseMPM(&lockStruct); - * ;; lock not owned by this thread. - * } - * - * recursiveUse() - * { ;; lock may already be owned by this thread. - * LockClaimRecursive(&lockStruct); - * ;; lock held by this thread. - * ;; only one thread at a time may be at this point. - * LockReleaseRecursive(&lockStruct); - * ;; lock owned by this thread if it was before. - * } - * LockInit(&lockStruct) must be called before calling binaryUse() - * or recursiveUse(). - * LockFinish(&lockStruct) should be called when lock is no longer - * needed. - * recursiveUse() may be called by both functions. - * binaryUse() may only be called where lock is known not to be - * already owned by this thread. In particular, it may not be - * called by recursiveUse(). - * - * LockClaimGlobalRecursive & LockReleaseGlobalRecursive are - * similar to LockClaimRecursive & LockReleaseRecursive - * except that they lock an implicit global lock. This may be - * used for locking access to data structures which are global, - * such as class objects. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef lock_h @@ -85,9 +13,6 @@ #define LockSig ((Sig)0x51970CC9) /* SIGnature LOCK */ -#if defined(THREAD_MULTI) - - /* LockSize -- Return the size of a LockStruct * * Supports allocation of locks. @@ -132,20 +57,20 @@ extern void LockReleaseRecursive(Lock lock); * This may only be used when the lock is not already owned by * the calling thread. * When used it behaves like LockClaimRecursive, but must be - * matched by a call to LockReleaseMPM. + * matched by a call to LockRelease. */ extern void LockClaim(Lock lock); -/* LockReleaseMPM +/* LockRelease * * This must only be used to release a Lock symmetrically * with LockClaim. It therefore should only be called with * a single claim. */ -extern void LockReleaseMPM(Lock lock); +extern void LockRelease(Lock lock); /* LockCheck -- Validation */ @@ -198,28 +123,24 @@ extern void LockClaimGlobal(void); extern void LockReleaseGlobal(void); -#elif defined(THREAD_SINGLE) - - +#if defined(LOCK) +/* Nothing to do: functions declared in all lock configurations. */ +#elif defined(LOCK_NONE) #define LockSize() MPS_PF_ALIGN #define LockInit(lock) UNUSED(lock) #define LockFinish(lock) UNUSED(lock) #define LockClaimRecursive(lock) UNUSED(lock) #define LockReleaseRecursive(lock) UNUSED(lock) #define LockClaim(lock) UNUSED(lock) -#define LockReleaseMPM(lock) UNUSED(lock) +#define LockRelease(lock) UNUSED(lock) #define LockCheck(lock) ((void)lock, TRUE) #define LockClaimGlobalRecursive() #define LockReleaseGlobalRecursive() #define LockClaimGlobal() #define LockReleaseGlobal() - - #else - -#error "No threading defined." - -#endif +#error "No lock configuration." +#endif /* LOCK */ #endif /* lock_h */ @@ -227,7 +148,7 @@ extern void LockReleaseGlobal(void); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/lockan.c b/code/lockan.c index f720e22755..fe5082a6eb 100644 --- a/code/lockan.c +++ b/code/lockan.c @@ -1,7 +1,7 @@ /* lockan.c: ANSI RECURSIVE LOCKS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This is a trivial implementation of recursive locks * that assumes we are not running in a multi-threaded environment. @@ -58,7 +58,7 @@ void (LockClaim)(Lock lock) lock->claims = 1; } -void (LockReleaseMPM)(Lock lock) +void (LockRelease)(Lock lock) { AVERT(Lock, lock); AVER(lock->claims == 1); @@ -118,13 +118,13 @@ void (LockClaimGlobal)(void) void (LockReleaseGlobal)(void) { - LockReleaseMPM(globalLock); + LockRelease(globalLock); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/lockcov.c b/code/lockcov.c index 28c398e4bb..84866046d8 100644 --- a/code/lockcov.c +++ b/code/lockcov.c @@ -1,20 +1,39 @@ /* lockcov.c: LOCK COVERAGE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ +#include "mps.h" +#include "mpsavm.h" +#include "mpscmfs.h" #include "mpm.h" #include "testlib.h" #include "mpslib.h" -#include /* for malloc & free */ + +#include /* printf */ int main(int argc, char *argv[]) { - Lock a = malloc(LockSize()); - Lock b = malloc(LockSize()); - testlib_unused(argc); + mps_arena_t arena; + mps_pool_t pool; + mps_addr_t p; + Lock a, b; + + testlib_init(argc, argv); + + die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), + "arena_create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, LockSize()); + die(mps_pool_create_k(&pool, arena, mps_class_mfs(), args), "pool_create"); + } MPS_ARGS_END(args); + + die(mps_alloc(&p, pool, LockSize()), "alloc a"); + a = p; + die(mps_alloc(&p, pool, LockSize()), "alloc b"); + b = p; Insist(a != NULL); Insist(b != NULL); @@ -27,7 +46,7 @@ int main(int argc, char *argv[]) LockClaimGlobalRecursive(); LockReleaseGlobal(); LockClaimGlobal(); - LockReleaseMPM(a); + LockRelease(a); LockClaimGlobalRecursive(); LockReleaseGlobal(); LockClaimRecursive(b); @@ -40,11 +59,14 @@ int main(int argc, char *argv[]) LockClaimRecursive(a); LockReleaseGlobalRecursive(); LockReleaseRecursive(a); - LockReleaseMPM(a); + LockRelease(a); LockFinish(a); LockReleaseGlobalRecursive(); - free(a); - free(b); + + mps_free(pool, a, LockSize()); + mps_free(pool, b, LockSize()); + mps_pool_destroy(pool); + mps_arena_destroy(arena); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; @@ -53,7 +75,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/lockix.c b/code/lockix.c index c32361e856..c982bf0cb1 100644 --- a/code/lockix.c +++ b/code/lockix.c @@ -1,7 +1,7 @@ /* lockix.c: RECURSIVE LOCKS FOR POSIX SYSTEMS * * $Id$ - * Copyright (c) 2001,2007 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .posix: The implementation uses a POSIX interface, and should be reusable * for many Unix-like operating systems. @@ -58,7 +58,7 @@ typedef struct LockStruct { /* LockSize -- size of a LockStruct */ -size_t LockSize(void) +size_t (LockSize)(void) { return sizeof(LockStruct); } @@ -66,7 +66,7 @@ size_t LockSize(void) /* LockCheck -- check a lock */ -Bool LockCheck(Lock lock) +Bool (LockCheck)(Lock lock) { CHECKS(Lock, lock); /* While claims can't be very large, I don't dare to put a limit on it. */ @@ -77,7 +77,7 @@ Bool LockCheck(Lock lock) /* LockInit -- initialize a lock */ -void LockInit(Lock lock) +void (LockInit)(Lock lock) { pthread_mutexattr_t attr; int res; @@ -99,7 +99,7 @@ void LockInit(Lock lock) /* LockFinish -- finish a lock */ -void LockFinish(Lock lock) +void (LockFinish)(Lock lock) { int res; @@ -114,7 +114,7 @@ void LockFinish(Lock lock) /* LockClaim -- claim a lock (non-recursive) */ -void LockClaim(Lock lock) +void (LockClaim)(Lock lock) { int res; @@ -131,9 +131,9 @@ void LockClaim(Lock lock) } -/* LockReleaseMPM -- release a lock (non-recursive) */ +/* LockRelease -- release a lock (non-recursive) */ -void LockReleaseMPM(Lock lock) +void (LockRelease)(Lock lock) { int res; @@ -148,7 +148,7 @@ void LockReleaseMPM(Lock lock) /* LockClaimRecursive -- claim a lock (recursive) */ -void LockClaimRecursive(Lock lock) +void (LockClaimRecursive)(Lock lock) { int res; @@ -168,7 +168,7 @@ void LockClaimRecursive(Lock lock) /* LockReleaseRecursive -- release a lock (recursive) */ -void LockReleaseRecursive(Lock lock) +void (LockReleaseRecursive)(Lock lock) { int res; @@ -203,7 +203,7 @@ static void globalLockInit(void) /* LockClaimGlobalRecursive -- claim the global recursive lock */ -void LockClaimGlobalRecursive(void) +void (LockClaimGlobalRecursive)(void) { int res; @@ -216,7 +216,7 @@ void LockClaimGlobalRecursive(void) /* LockReleaseGlobalRecursive -- release the global recursive lock */ -void LockReleaseGlobalRecursive(void) +void (LockReleaseGlobalRecursive)(void) { LockReleaseRecursive(globalRecLock); } @@ -224,7 +224,7 @@ void LockReleaseGlobalRecursive(void) /* LockClaimGlobal -- claim the global non-recursive lock */ -void LockClaimGlobal(void) +void (LockClaimGlobal)(void) { int res; @@ -237,15 +237,15 @@ void LockClaimGlobal(void) /* LockReleaseGlobal -- release the global non-recursive lock */ -void LockReleaseGlobal(void) +void (LockReleaseGlobal)(void) { - LockReleaseMPM(globalLock); + LockRelease(globalLock); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/lockli.c b/code/lockli.c index 06437b5b53..0dc98fb8a2 100644 --- a/code/lockli.c +++ b/code/lockli.c @@ -1,7 +1,7 @@ /* lockli.c: RECURSIVE LOCKS FOR POSIX SYSTEMS * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .linux: This implementation currently just supports LinuxThreads * (platform MPS_OS_LI), Single Unix i/f. @@ -72,7 +72,7 @@ typedef struct LockStruct { /* LockSize -- size of a LockStruct */ -size_t LockSize(void) +size_t (LockSize)(void) { return sizeof(LockStruct); } @@ -80,7 +80,7 @@ size_t LockSize(void) /* LockCheck -- check a lock */ -Bool LockCheck(Lock lock) +Bool (LockCheck)(Lock lock) { CHECKS(Lock, lock); /* While claims can't be very large, I don't dare to put a limit on it. */ @@ -91,7 +91,7 @@ Bool LockCheck(Lock lock) /* LockInit -- initialize a lock */ -void LockInit(Lock lock) +void (LockInit)(Lock lock) { pthread_mutexattr_t attr; int res; @@ -113,7 +113,7 @@ void LockInit(Lock lock) /* LockFinish -- finish a lock */ -void LockFinish(Lock lock) +void (LockFinish)(Lock lock) { int res; @@ -128,7 +128,7 @@ void LockFinish(Lock lock) /* LockClaim -- claim a lock (non-recursive) */ -void LockClaim(Lock lock) +void (LockClaim)(Lock lock) { int res; @@ -145,9 +145,9 @@ void LockClaim(Lock lock) } -/* LockReleaseMPM -- release a lock (non-recursive) */ +/* LockRelease -- release a lock (non-recursive) */ -void LockReleaseMPM(Lock lock) +void (LockRelease)(Lock lock) { int res; @@ -162,7 +162,7 @@ void LockReleaseMPM(Lock lock) /* LockClaimRecursive -- claim a lock (recursive) */ -void LockClaimRecursive(Lock lock) +void (LockClaimRecursive)(Lock lock) { int res; @@ -182,7 +182,7 @@ void LockClaimRecursive(Lock lock) /* LockReleaseRecursive -- release a lock (recursive) */ -void LockReleaseRecursive(Lock lock) +void (LockReleaseRecursive)(Lock lock) { int res; @@ -217,7 +217,7 @@ static void globalLockInit(void) /* LockClaimGlobalRecursive -- claim the global recursive lock */ -void LockClaimGlobalRecursive(void) +void (LockClaimGlobalRecursive)(void) { int res; @@ -230,7 +230,7 @@ void LockClaimGlobalRecursive(void) /* LockReleaseGlobalRecursive -- release the global recursive lock */ -void LockReleaseGlobalRecursive(void) +void (LockReleaseGlobalRecursive)(void) { LockReleaseRecursive(globalRecLock); } @@ -238,7 +238,7 @@ void LockReleaseGlobalRecursive(void) /* LockClaimGlobal -- claim the global non-recursive lock */ -void LockClaimGlobal(void) +void (LockClaimGlobal)(void) { int res; @@ -251,15 +251,15 @@ void LockClaimGlobal(void) /* LockReleaseGlobal -- release the global non-recursive lock */ -void LockReleaseGlobal(void) +void (LockReleaseGlobal)(void) { - LockReleaseMPM(globalLock); + LockRelease(globalLock); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/lockutw3.c b/code/lockut.c similarity index 73% rename from code/lockutw3.c rename to code/lockut.c index c66db7edd7..a6e592988f 100644 --- a/code/lockutw3.c +++ b/code/lockut.c @@ -1,28 +1,26 @@ -/* lockutw3.c: LOCK UTILIZATION TEST +/* lockut.c: LOCK UTILIZATION TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ +#include "mps.h" +#include "mpsavm.h" +#include "mpscmfs.h" #include "mpm.h" #include "testlib.h" -#include "mpslib.h" +#include "testthr.h" -#include "mpswin.h" - - -#ifndef MPS_OS_W3 -#error "Relies on Win32 threads" -#endif +#include /* printf */ #define nTHREADS 4 static Lock lock; -unsigned long shared, tmp; +static unsigned long shared, tmp; -void incR(unsigned long i) +static void incR(unsigned long i) { LockClaimRecursive(lock); if (i < 100) { @@ -38,7 +36,7 @@ void incR(unsigned long i) } -void inc(unsigned long i) +static void inc(unsigned long i) { incR( (i+1) >>1); i >>= 1; @@ -51,27 +49,39 @@ void inc(unsigned long i) tmp = shared; shared = tmp+1; i--; - LockReleaseMPM(lock); + LockRelease(lock); } } #define COUNT 100000l -DWORD WINAPI thread0(void *p) +static void *thread0(void *p) { - (void)p; + testlib_unused(p); inc(COUNT); - return 0; + return NULL; } int main(int argc, char *argv[]) { - DWORD id; - HANDLE t[10]; + mps_arena_t arena; + mps_pool_t pool; + mps_addr_t p; + testthr_t t[10]; unsigned i; - lock = malloc(LockSize()); + testlib_init(argc, argv); + + die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), + "arena_create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, LockSize()); + die(mps_pool_create_k(&pool, arena, mps_class_mfs(), args), "pool_create"); + } MPS_ARGS_END(args); + + die(mps_alloc(&p, pool, LockSize()), "alloc"); + lock = p; Insist(lock != NULL); LockInit(lock); @@ -80,15 +90,19 @@ int main(int argc, char *argv[]) shared = 0; for(i = 0; i < nTHREADS; i++) - t[i] = CreateThread(NULL, 0, thread0, NULL, 0, &id); + testthr_create(&t[i], thread0, NULL); for(i = 0; i < nTHREADS; i++) - WaitForSingleObject(t[i], INFINITE); + testthr_join(&t[i], NULL); Insist(shared == nTHREADS*COUNT); LockFinish(lock); + mps_free(pool, lock, LockSize()); + mps_pool_destroy(pool); + mps_arena_destroy(arena); + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; } @@ -96,7 +110,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/lockw3.c b/code/lockw3.c index 258b31bff4..53da970aed 100644 --- a/code/lockw3.c +++ b/code/lockw3.c @@ -1,7 +1,7 @@ /* lockw3.c: RECURSIVE LOCKS IN WIN32 * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .design: These are implemented using critical sections. * See the section titled "Synchronization functions" in the Groups @@ -40,18 +40,18 @@ typedef struct LockStruct { } LockStruct; -size_t LockSize(void) +size_t (LockSize)(void) { return sizeof(LockStruct); } -Bool LockCheck(Lock lock) +Bool (LockCheck)(Lock lock) { CHECKS(Lock, lock); return TRUE; } -void LockInit(Lock lock) +void (LockInit)(Lock lock) { AVER(lock != NULL); lock->claims = 0; @@ -60,7 +60,7 @@ void LockInit(Lock lock) AVERT(Lock, lock); } -void LockFinish(Lock lock) +void (LockFinish)(Lock lock) { AVERT(Lock, lock); /* Lock should not be finished while held */ @@ -69,7 +69,7 @@ void LockFinish(Lock lock) lock->sig = SigInvalid; } -void LockClaim(Lock lock) +void (LockClaim)(Lock lock) { AVERT(Lock, lock); EnterCriticalSection(&lock->cs); @@ -79,7 +79,7 @@ void LockClaim(Lock lock) lock->claims = 1; } -void LockReleaseMPM(Lock lock) +void (LockRelease)(Lock lock) { AVERT(Lock, lock); AVER(lock->claims == 1); /* The lock should only be held once */ @@ -87,7 +87,7 @@ void LockReleaseMPM(Lock lock) LeaveCriticalSection(&lock->cs); } -void LockClaimRecursive(Lock lock) +void (LockClaimRecursive)(Lock lock) { AVERT(Lock, lock); EnterCriticalSection(&lock->cs); @@ -95,7 +95,7 @@ void LockClaimRecursive(Lock lock) AVER(lock->claims > 0); } -void LockReleaseRecursive(Lock lock) +void (LockReleaseRecursive)(Lock lock) { AVERT(Lock, lock); AVER(lock->claims > 0); @@ -129,36 +129,36 @@ static void lockEnsureGlobalLock(void) } } -void LockClaimGlobalRecursive(void) +void (LockClaimGlobalRecursive)(void) { lockEnsureGlobalLock(); AVER(globalLockInit); LockClaimRecursive(globalRecLock); } -void LockReleaseGlobalRecursive(void) +void (LockReleaseGlobalRecursive)(void) { AVER(globalLockInit); LockReleaseRecursive(globalRecLock); } -void LockClaimGlobal(void) +void (LockClaimGlobal)(void) { lockEnsureGlobalLock(); AVER(globalLockInit); LockClaim(globalLock); } -void LockReleaseGlobal(void) +void (LockReleaseGlobal)(void) { AVER(globalLockInit); - LockReleaseMPM(globalLock); + LockRelease(globalLock); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/locus.c b/code/locus.c index aa18a276fc..e17ec3137c 100644 --- a/code/locus.c +++ b/code/locus.c @@ -1,12 +1,13 @@ /* locus.c: LOCUS MANAGER * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * DESIGN * * See and for basic locus stuff. - * See for chains. + * See for chains. See for the + * collection strategy. */ #include "chain.h" @@ -18,94 +19,111 @@ SRCID(locus, "$Id$"); -/* SegPrefCheck -- check the consistency of a segment preference */ +/* LocusPrefCheck -- check the consistency of a locus preference */ -Bool SegPrefCheck(SegPref pref) +Bool LocusPrefCheck(LocusPref pref) { - CHECKS(SegPref, pref); + CHECKS(LocusPref, pref); CHECKL(BoolCheck(pref->high)); /* zones can't be checked because it's arbitrary. */ - CHECKL(BoolCheck(pref->isCollected)); + /* avoid can't be checked because it's arbitrary. */ return TRUE; } -/* SegPrefDefault -- return a segment preference representing the defaults */ +/* LocusPrefDefault -- return a locus preference representing the defaults */ -static SegPrefStruct segPrefDefault = SegPrefDEFAULT; +static LocusPrefStruct locusPrefDefault = LocusPrefDEFAULT; -SegPref SegPrefDefault(void) +LocusPref LocusPrefDefault(void) { - return &segPrefDefault; + return &locusPrefDefault; } -/* SegPrefInit -- initialise a segment preference to the defaults */ +/* LocusPrefInit -- initialise a locus preference to the defaults */ -void SegPrefInit(SegPref pref) +void LocusPrefInit(LocusPref pref) { - mps_lib_memcpy(pref, &segPrefDefault, sizeof(SegPrefStruct)); + (void)mps_lib_memcpy(pref, &locusPrefDefault, sizeof(LocusPrefStruct)); } -/* SegPrefExpress -- express a segment preference */ +/* LocusPrefExpress -- express a locus preference */ -void SegPrefExpress(SegPref pref, SegPrefKind kind, void *p) +void LocusPrefExpress(LocusPref pref, LocusPrefKind kind, void *p) { - AVERT(SegPref, pref); - AVER(pref != &segPrefDefault); + AVERT(LocusPref, pref); + AVER(pref != &locusPrefDefault); switch(kind) { - case SegPrefHigh: + case LocusPrefHIGH: AVER(p == NULL); pref->high = TRUE; break; - case SegPrefLow: + case LocusPrefLOW: AVER(p == NULL); pref->high = FALSE; break; - case SegPrefZoneSet: + case LocusPrefZONESET: AVER(p != NULL); pref->zones = *(ZoneSet *)p; break; - case SegPrefCollected: - AVER(p == NULL); - pref->isCollected = TRUE; - break; - default: /* Unknown kinds are ignored for binary compatibility. */ - /* See design.mps.pref. */ break; } } +/* LocusPrefDescribe -- describe a locus preference */ + +Res LocusPrefDescribe(LocusPref pref, mps_lib_FILE *stream, Count depth) +{ + Res res; + + if (!TESTT(LocusPref, pref)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "LocusPref $P {\n", (WriteFP)pref, + " high $S\n", WriteFYesNo(pref->high), + " zones $B\n", (WriteFB)pref->zones, + " avoid $B\n", (WriteFB)pref->avoid, + "} LocusPref $P\n", (WriteFP)pref, + NULL); + return res; +} + + /* GenDescCheck -- check a GenDesc */ -static Bool GenDescCheck(GenDesc gen) +ATTRIBUTE_UNUSED +Bool GenDescCheck(GenDesc gen) { CHECKS(GenDesc, gen); /* nothing to check for zones */ /* nothing to check for capacity */ CHECKL(gen->mortality >= 0.0); CHECKL(gen->mortality <= 1.0); - CHECKL(gen->proflow >= 0.0); - CHECKL(gen->proflow <= 1.0); - CHECKL(RingCheck(&gen->locusRing)); + CHECKD_NOSIG(Ring, &gen->locusRing); return TRUE; } /* GenDescNewSize -- return effective size of generation */ -static Size GenDescNewSize(GenDesc gen) +Size GenDescNewSize(GenDesc gen) { Size size = 0; Ring node, nextNode; + AVERT(GenDesc, gen); + RING_FOR(node, &gen->locusRing, nextNode) { PoolGen pgen = RING_ELT(PoolGen, genRing, node); AVERT(PoolGen, pgen); @@ -117,11 +135,13 @@ static Size GenDescNewSize(GenDesc gen) /* GenDescTotalSize -- return total size of generation */ -static Size GenDescTotalSize(GenDesc gen) +Size GenDescTotalSize(GenDesc gen) { Size size = 0; Ring node, nextNode; + AVERT(GenDesc, gen); + RING_FOR(node, &gen->locusRing, nextNode) { PoolGen pgen = RING_ELT(PoolGen, genRing, node); AVERT(PoolGen, pgen); @@ -131,6 +151,39 @@ static Size GenDescTotalSize(GenDesc gen) } +/* GenDescDescribe -- describe a generation in a chain */ + +Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth) +{ + Res res; + Ring node, nextNode; + + if (!TESTT(GenDesc, gen)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "GenDesc $P {\n", (WriteFP)gen, + " zones $B\n", (WriteFB)gen->zones, + " capacity $W\n", (WriteFW)gen->capacity, + " mortality $D\n", (WriteFD)gen->mortality, + NULL); + if (res != ResOK) + return res; + + RING_FOR(node, &gen->locusRing, nextNode) { + PoolGen pgen = RING_ELT(PoolGen, genRing, node); + res = PoolGenDescribe(pgen, stream, depth + 2); + if (res != ResOK) + return res; + } + + res = WriteF(stream, depth, "} GenDesc $P\n", (WriteFP)gen, NULL); + return res; +} + + /* ChainCreate -- create a generation chain */ Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount, @@ -161,9 +214,9 @@ Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount, gens[i].zones = ZoneSetEMPTY; gens[i].capacity = params[i].capacity; gens[i].mortality = params[i].mortality; - gens[i].proflow = 1.0; /* @@@@ temporary */ RingInit(&gens[i].locusRing); gens[i].sig = GenDescSig; + AVERT(GenDesc, &gens[i]); } res = ControlAlloc(&p, arena, sizeof(ChainStruct), FALSE); @@ -197,7 +250,7 @@ Bool ChainCheck(Chain chain) CHECKS(Chain, chain); CHECKU(Arena, chain->arena); - CHECKL(RingCheck(&chain->chainRing)); + CHECKD_NOSIG(Ring, &chain->chainRing); CHECKL(TraceSetCheck(chain->activeTraces)); CHECKL(chain->genCount > 0); for (i = 0; i < chain->genCount; ++i) { @@ -216,8 +269,10 @@ void ChainDestroy(Chain chain) size_t i; AVERT(Chain, chain); + AVER(chain->activeTraces == TraceSetEMPTY); - arena = chain->arena; genCount = chain->genCount; + arena = chain->arena; + genCount = chain->genCount; RingRemove(&chain->chainRing); chain->sig = SigInvalid; for (i = 0; i < genCount; ++i) { @@ -239,232 +294,431 @@ size_t ChainGens(Chain chain) } -/* ChainAlloc -- allocate tracts in a generation */ +/* ChainGen -- return a generation in a chain, or the arena top generation */ -Res ChainAlloc(Seg *segReturn, Chain chain, Serial genNr, SegClass class, - Size size, Pool pool, Bool withReservoirPermit, - ArgList args) +GenDesc ChainGen(Chain chain, Index gen) { - SegPrefStruct pref; + AVERT(Chain, chain); + AVER(gen <= chain->genCount); + + if (gen < chain->genCount) + return &chain->gens[gen]; + else + return &chain->arena->topGen; +} + + +/* PoolGenAlloc -- allocate a segment in a pool generation and update + * accounting + */ + +Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class, Size size, + Bool withReservoirPermit, ArgList args) +{ + LocusPrefStruct pref; Res res; Seg seg; ZoneSet zones, moreZones; Arena arena; + GenDesc gen; - AVERT(Chain, chain); - AVER(genNr <= chain->genCount); - - arena = chain->arena; - if (genNr < chain->genCount) - zones = chain->gens[genNr].zones; - else - zones = arena->topGen.zones; - - SegPrefInit(&pref); - SegPrefExpress(&pref, SegPrefCollected, NULL); - SegPrefExpress(&pref, SegPrefZoneSet, &zones); - res = SegAlloc(&seg, class, &pref, size, pool, withReservoirPermit, args); + AVER(segReturn != NULL); + AVERT(PoolGen, pgen); + AVERT(SegClass, class); + AVER(size > 0); + AVERT(Bool, withReservoirPermit); + AVERT(ArgList, args); + + arena = PoolArena(pgen->pool); + gen = pgen->gen; + zones = gen->zones; + + LocusPrefInit(&pref); + pref.high = FALSE; + pref.zones = zones; + pref.avoid = ZoneSetBlacklist(arena); + res = SegAlloc(&seg, class, &pref, size, pgen->pool, withReservoirPermit, + args); if (res != ResOK) return res; moreZones = ZoneSetUnion(zones, ZoneSetOfSeg(arena, seg)); + gen->zones = moreZones; if (!ZoneSetSuper(zones, moreZones)) { - /* Tracking the whole zoneset for each generation number gives - * more understandable telemetry than just reporting the added + /* Tracking the whole zoneset for each generation gives more + * understandable telemetry than just reporting the added * zones. */ - EVENT3(ArenaGenZoneAdd, arena, genNr, moreZones); + EVENT3(ArenaGenZoneAdd, arena, gen, moreZones); } - if (genNr < chain->genCount) - chain->gens[genNr].zones = moreZones; - else - chain->arena->topGen.zones = moreZones; - + size = SegSize(seg); + pgen->totalSize += size; + STATISTIC_STAT ({ + ++ pgen->segs; + pgen->freeSize += size; + }); *segReturn = seg; return ResOK; } - /* ChainDeferral -- time until next ephemeral GC for this chain */ double ChainDeferral(Chain chain) { + double time = DBL_MAX; + size_t i; + AVERT(Chain, chain); - if (chain->activeTraces != TraceSetEMPTY) - return DBL_MAX; - else - return chain->gens[0].capacity * 1024.0 - - (double)GenDescNewSize(&chain->gens[0]); + if (chain->activeTraces == TraceSetEMPTY) { + for (i = 0; i < chain->genCount; ++i) { + double genTime = chain->gens[i].capacity * 1024.0 + - (double)GenDescNewSize(&chain->gens[i]); + if (genTime < time) + time = genTime; + } + } + + return time; } -/* ChainCondemnAuto -- condemn approriate parts of this chain - * - * This is only called if ChainDeferral returned a value sufficiently - * low that the tracer decided to start the collection. (Usually - * such values are less than zero; see ) - */ -Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace) -{ - Res res; - Serial topCondemnedGenSerial, currGenSerial; - GenDesc gen; - ZoneSet condemnedSet = ZoneSetEMPTY; - Size condemnedSize = 0, survivorSize = 0, genNewSize, genTotalSize; +/* ChainStartGC -- called to notify start of GC for this chain */ +void ChainStartGC(Chain chain, Trace trace) +{ AVERT(Chain, chain); AVERT(Trace, trace); - /* Find lowest gen within its capacity, set topCondemnedGenSerial to the */ - /* preceeding one. */ - currGenSerial = 0; - gen = &chain->gens[0]; - AVERT(GenDesc, gen); - genNewSize = GenDescNewSize(gen); - do { /* At this point, we've decided to collect currGenSerial. */ - topCondemnedGenSerial = currGenSerial; - condemnedSet = ZoneSetUnion(condemnedSet, gen->zones); - genTotalSize = GenDescTotalSize(gen); - condemnedSize += genTotalSize; - survivorSize += (Size)(genNewSize * (1.0 - gen->mortality)) - /* predict survivors will survive again */ - + (genTotalSize - genNewSize); - - /* is there another one to consider? */ - currGenSerial += 1; - if (currGenSerial >= chain->genCount) - break; /* reached the top */ - gen = &chain->gens[currGenSerial]; - AVERT(GenDesc, gen); - genNewSize = GenDescNewSize(gen); - } while (genNewSize >= gen->capacity * (Size)1024); - - AVER(condemnedSet != ZoneSetEMPTY || condemnedSize == 0); - EVENT3(ChainCondemnAuto, chain, topCondemnedGenSerial, chain->genCount); - UNUSED(topCondemnedGenSerial); /* only used for EVENT */ - - /* Condemn everything in these zones. */ - if (condemnedSet != ZoneSetEMPTY) { - res = TraceCondemnZones(trace, condemnedSet); - if (res != ResOK) - return res; - } + chain->activeTraces = TraceSetAdd(chain->activeTraces, trace); +} - *mortalityReturn = 1.0 - (double)survivorSize / condemnedSize; - return ResOK; + +/* ChainEndGC -- called to notify end of GC for this chain */ + +void ChainEndGC(Chain chain, Trace trace) +{ + AVERT(Chain, chain); + AVERT(Trace, trace); + + chain->activeTraces = TraceSetDel(chain->activeTraces, trace); } -/* ChainCondemnAll -- condemn everything in the chain */ +/* ChainDescribe -- describe a chain */ -Res ChainCondemnAll(Chain chain, Trace trace) +Res ChainDescribe(Chain chain, mps_lib_FILE *stream, Count depth) { - Ring node, nextNode; - Bool haveWhiteSegs = FALSE; Res res; + size_t i; - /* Condemn every segment in every pool using this chain. */ - /* Finds the pools by iterating over the PoolGens in gen 0. */ - RING_FOR(node, &chain->gens[0].locusRing, nextNode) { - PoolGen nursery = RING_ELT(PoolGen, genRing, node); - Pool pool = nursery->pool; - Ring segNode, nextSegNode; - - AVERT(Pool, pool); - AVER((pool->class->attr & AttrGC) != 0); - RING_FOR(segNode, PoolSegRing(pool), nextSegNode) { - Seg seg = SegOfPoolRing(segNode); - - res = TraceAddWhite(trace, seg); - if (res != ResOK) - goto failBegin; - haveWhiteSegs = TRUE; - } + if (!TESTT(Chain, chain)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "Chain $P {\n", (WriteFP)chain, + " arena $P\n", (WriteFP)chain->arena, + " activeTraces $B\n", (WriteFB)chain->activeTraces, + NULL); + if (res != ResOK) + return res; + + for (i = 0; i < chain->genCount; ++i) { + res = GenDescDescribe(&chain->gens[i], stream, depth + 2); + if (res != ResOK) + return res; } - - return ResOK; -failBegin: - AVER(!haveWhiteSegs); /* Would leave white sets inconsistent. */ + res = WriteF(stream, depth, + "} Chain $P\n", (WriteFP)chain, + NULL); return res; } -/* ChainStartGC -- called to notify start of GC for this chain */ +/* PoolGenInit -- initialize a PoolGen */ -void ChainStartGC(Chain chain, Trace trace) +Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool) { - AVERT(Chain, chain); - AVERT(Trace, trace); + /* Can't check pgen, because it's not been initialized. */ + AVER(pgen != NULL); + AVERT(GenDesc, gen); + AVERT(Pool, pool); + AVER(PoolHasAttr(pool, AttrGC)); + + pgen->pool = pool; + pgen->gen = gen; + RingInit(&pgen->genRing); + STATISTIC(pgen->segs = 0); + pgen->totalSize = 0; + STATISTIC(pgen->freeSize = 0); + pgen->newSize = 0; + STATISTIC(pgen->oldSize = 0); + pgen->newDeferredSize = 0; + STATISTIC(pgen->oldDeferredSize = 0); + pgen->sig = PoolGenSig; + AVERT(PoolGen, pgen); + + RingAppend(&gen->locusRing, &pgen->genRing); + return ResOK; +} - chain->activeTraces = TraceSetAdd(chain->activeTraces, trace); + +/* PoolGenFinish -- finish a PoolGen */ + +void PoolGenFinish(PoolGen pgen) +{ + AVERT(PoolGen, pgen); + AVER(pgen->totalSize == 0); + AVER(pgen->newSize == 0); + AVER(pgen->newDeferredSize == 0); + STATISTIC_STAT ({ + AVER(pgen->segs == 0); + AVER(pgen->freeSize == 0); + AVER(pgen->oldSize == 0); + AVER(pgen->oldDeferredSize == 0); + }); + + pgen->sig = SigInvalid; + RingRemove(&pgen->genRing); } -/* ChainEndGC -- called to notify end of GC for this chain */ +/* PoolGenCheck -- check a PoolGen */ -void ChainEndGC(Chain chain, Trace trace) +Bool PoolGenCheck(PoolGen pgen) { - AVERT(Chain, chain); - AVERT(Trace, trace); + CHECKS(PoolGen, pgen); + /* nothing to check about serial */ + CHECKU(Pool, pgen->pool); + CHECKU(GenDesc, pgen->gen); + CHECKD_NOSIG(Ring, &pgen->genRing); + STATISTIC_STAT ({ + CHECKL((pgen->totalSize == 0) == (pgen->segs == 0)); + CHECKL(pgen->totalSize >= pgen->segs * ArenaGrainSize(PoolArena(pgen->pool))); + CHECKL(pgen->totalSize == pgen->freeSize + pgen->newSize + pgen->oldSize + + pgen->newDeferredSize + pgen->oldDeferredSize); + }); + return TRUE; +} - chain->activeTraces = TraceSetDel(chain->activeTraces, trace); + +/* PoolGenAccountForFill -- accounting for allocation + * + * Call this when the pool allocates memory to the client program via + * BufferFill. The deferred flag indicates whether the accounting of + * this memory (for the purpose of scheduling collections) should be + * deferred until later. + * + * See + */ + +void PoolGenAccountForFill(PoolGen pgen, Size size, Bool deferred) +{ + AVERT(PoolGen, pgen); + AVERT(Bool, deferred); + + STATISTIC_STAT ({ + AVER(pgen->freeSize >= size); + pgen->freeSize -= size; + }); + if (deferred) + pgen->newDeferredSize += size; + else + pgen->newSize += size; } -/* PoolGenInit -- initialize a PoolGen */ +/* PoolGenAccountForEmpty -- accounting for emptying a buffer + * + * Call this when the client program returns memory (that was never + * condemned) to the pool via BufferEmpty. The deferred flag is as for + * PoolGenAccountForFill. + * + * See + */ -Res PoolGenInit(PoolGen gen, Chain chain, Serial nr, Pool pool) +void PoolGenAccountForEmpty(PoolGen pgen, Size unused, Bool deferred) { - /* Can't check gen, because it's not been initialized. */ - AVERT(Chain, chain); - AVER(nr <= chain->genCount); - AVERT(Pool, pool); + AVERT(PoolGen, pgen); + AVERT(Bool, deferred); + + if (deferred) { + AVER(pgen->newDeferredSize >= unused); + pgen->newDeferredSize -= unused; + } else { + AVER(pgen->newSize >= unused); + pgen->newSize -= unused; + } + STATISTIC(pgen->freeSize += unused); +} + - gen->nr = nr; - gen->pool = pool; - gen->chain = chain; - RingInit(&gen->genRing); - gen->totalSize = (Size)0; - gen->newSize = (Size)0; - gen->sig = PoolGenSig; +/* PoolGenAccountForAge -- accounting for condemning + * + * Call this when memory is condemned via PoolWhiten. The size + * parameter should be the amount of memory that is being condemned + * for the first time. The deferred flag is as for PoolGenAccountForFill. + * + * See + */ - if(nr != chain->genCount) { - RingAppend(&chain->gens[nr].locusRing, &gen->genRing); +void PoolGenAccountForAge(PoolGen pgen, Size size, Bool deferred) +{ + AVERT(PoolGen, pgen); + + if (deferred) { + AVER(pgen->newDeferredSize >= size); + pgen->newDeferredSize -= size; + STATISTIC(pgen->oldDeferredSize += size); } else { - /* Dynamic generation is linked to the arena, not the chain. */ - RingAppend(&chain->arena->topGen.locusRing, &gen->genRing); + AVER(pgen->newSize >= size); + pgen->newSize -= size; + STATISTIC(pgen->oldSize += size); } - AVERT(PoolGen, gen); - return ResOK; } -/* PoolGenFinish -- finish a PoolGen */ +/* PoolGenAccountForReclaim -- accounting for reclaiming + * + * Call this when reclaiming memory, passing the amount of memory that + * was reclaimed. The deferred flag is as for PoolGenAccountForFill. + * + * See + */ -void PoolGenFinish(PoolGen gen) +void PoolGenAccountForReclaim(PoolGen pgen, Size reclaimed, Bool deferred) { - AVERT(PoolGen, gen); + AVERT(PoolGen, pgen); + AVERT(Bool, deferred); + + STATISTIC_STAT ({ + if (deferred) { + AVER(pgen->oldDeferredSize >= reclaimed); + pgen->oldDeferredSize -= reclaimed; + } else { + AVER(pgen->oldSize >= reclaimed); + pgen->oldSize -= reclaimed; + } + pgen->freeSize += reclaimed; + }); +} - gen->sig = SigInvalid; - RingRemove(&gen->genRing); + +/* PoolGenUndefer -- finish deferring accounting + * + * Call this when exiting ramp mode, passing the amount of old + * (condemned at least once) and new (never condemned) memory whose + * accounting was deferred (for example, during a ramp). + * + * See + */ + +void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize) +{ + AVERT(PoolGen, pgen); + STATISTIC_STAT ({ + AVER(pgen->oldDeferredSize >= oldSize); + pgen->oldDeferredSize -= oldSize; + pgen->oldSize += oldSize; + }); + AVER(pgen->newDeferredSize >= newSize); + pgen->newDeferredSize -= newSize; + pgen->newSize += newSize; } -/* PoolGenCheck -- check a PoolGen */ +/* PoolGenAccountForSegSplit -- accounting for splitting a segment */ -Bool PoolGenCheck(PoolGen gen) +void PoolGenAccountForSegSplit(PoolGen pgen) { - CHECKS(PoolGen, gen); - /* nothing to check about serial */ - CHECKU(Pool, gen->pool); - CHECKU(Chain, gen->chain); - CHECKL(RingCheck(&gen->genRing)); - CHECKL(gen->newSize <= gen->totalSize); - return TRUE; + AVERT(PoolGen, pgen); + STATISTIC_STAT ({ + AVER(pgen->segs >= 1); /* must be at least one segment to split */ + ++ pgen->segs; + }); +} + + +/* PoolGenAccountForSegMerge -- accounting for merging a segment */ + +void PoolGenAccountForSegMerge(PoolGen pgen) +{ + AVERT(PoolGen, pgen); + STATISTIC_STAT ({ + AVER(pgen->segs >= 2); /* must be at least two segments to merge */ + -- pgen->segs; + }); +} + + +/* PoolGenFree -- free a segment and update accounting + * + * Pass the amount of memory in the segment that is accounted as free, + * old, or new, respectively. The deferred flag is as for + * PoolGenAccountForFill. + * + * See + */ + +void PoolGenFree(PoolGen pgen, Seg seg, Size freeSize, Size oldSize, + Size newSize, Bool deferred) +{ + Size size; + + AVERT(PoolGen, pgen); + AVERT(Seg, seg); + + size = SegSize(seg); + AVER(freeSize + oldSize + newSize == size); + + /* Pretend to age and reclaim the contents of the segment to ensure + * that the entire segment is accounted as free. */ + PoolGenAccountForAge(pgen, newSize, deferred); + PoolGenAccountForReclaim(pgen, oldSize + newSize, deferred); + + AVER(pgen->totalSize >= size); + pgen->totalSize -= size; + STATISTIC_STAT ({ + AVER(pgen->segs > 0); + -- pgen->segs; + AVER(pgen->freeSize >= size); + pgen->freeSize -= size; + }); + SegFree(seg); +} + + +/* PoolGenDescribe -- describe a PoolGen */ + +Res PoolGenDescribe(PoolGen pgen, mps_lib_FILE *stream, Count depth) +{ + Res res; + + if (!TESTT(PoolGen, pgen)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "PoolGen $P {\n", (WriteFP)pgen, + " pool $P ($U) \"$S\"\n", + (WriteFP)pgen->pool, (WriteFU)pgen->pool->serial, + (WriteFS)pgen->pool->class->name, + " segs $U\n", (WriteFU)pgen->segs, + " totalSize $U\n", (WriteFU)pgen->totalSize, + " freeSize $U\n", (WriteFU)pgen->freeSize, + " oldSize $U\n", (WriteFU)pgen->oldSize, + " oldDeferredSize $U\n", (WriteFU)pgen->oldDeferredSize, + " newSize $U\n", (WriteFU)pgen->newSize, + " newDeferredSize $U\n", (WriteFU)pgen->newDeferredSize, + "} PoolGen $P\n", (WriteFP)pgen, + NULL); + return res; } @@ -481,9 +735,9 @@ void LocusInit(Arena arena) gen->zones = ZoneSetEMPTY; gen->capacity = 0; /* unused */ gen->mortality = 0.51; - gen->proflow = 0.0; RingInit(&gen->locusRing); gen->sig = GenDescSig; + AVERT(GenDesc, gen); } @@ -505,14 +759,14 @@ void LocusFinish(Arena arena) Bool LocusCheck(Arena arena) { /* Can't check arena, because this is part of ArenaCheck. */ - CHECKL(GenDescCheck(&arena->topGen)); + CHECKD(GenDesc, &arena->topGen); return TRUE; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/locusss.c b/code/locusss.c index c9a86fa37d..b3769dd674 100644 --- a/code/locusss.c +++ b/code/locusss.c @@ -1,7 +1,7 @@ /* locusss.c: LOCUS STRESS TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "mpscmvff.h" @@ -12,8 +12,7 @@ #include "mpslib.h" #include "mps.h" -#include -#include +#include /* printf */ /* some constants */ @@ -110,7 +109,7 @@ static void poolStatInit(PoolStat stat, mps_pool_t pool, size_t objSize) static mps_res_t allocMultiple(PoolStat stat) { mps_addr_t objects[contigAllocs]; - int i; + size_t i; /* allocate a few objects, and record stats for them */ for (i = 0; i < contigAllocs; i++) { @@ -133,7 +132,7 @@ static mps_res_t allocMultiple(PoolStat stat) /* reportResults - print a report on a PoolStat */ -static void reportResults(PoolStat stat, char *name) +static void reportResults(PoolStat stat, const char *name) { printf("\nResults for "); printf("%s", name); @@ -217,11 +216,13 @@ static void runArenaTest(size_t size, { mps_arena_t arena; - die(mps_arena_create(&arena, mps_arena_class_vmnz(), size), - "mps_arena_create"); - - die(mps_arena_commit_limit_set(arena, size - chunkSize), - "mps_arena_commit_limit_set"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, size); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE); + MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, size - chunkSize); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), + "mps_arena_create"); + } MPS_ARGS_END(args); testInArena(arena, failcase, usefulFailcase); @@ -232,9 +233,7 @@ static void runArenaTest(size_t size, int main(int argc, char *argv[]) { - - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); printf("\nRunning test with no information about peak usage.\n"); runArenaTest(smallArenaSize, FALSE, FALSE); @@ -251,7 +250,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/locv.c b/code/locv.c index 29881f6c54..06d2722dac 100644 --- a/code/locv.c +++ b/code/locv.c @@ -1,7 +1,7 @@ /* locv.c: LEAF OBJECT POOL CLASS COVERAGE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * This is (not much of) a coverage test for the Leaf Object * pool (PoolClassLO). @@ -13,6 +13,8 @@ #include "mpsclo.h" #include "mpsavm.h" +#include /* printf */ + #define testArenaSIZE ((size_t)16<<20) @@ -48,7 +50,8 @@ int main(int argc, char *argv[]) mps_ap_t ap; mps_addr_t p; mps_root_t root; - testlib_unused(argc); + + testlib_init(argc, argv); locv_fmt.align = sizeof(void *); /* .fmt.align.delayed */ @@ -169,7 +172,7 @@ static void stepper(mps_addr_t addr, mps_fmt_t fmt, mps_pool_t pool, /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/message.c b/code/message.c index 75b01d328d..eba9418283 100644 --- a/code/message.c +++ b/code/message.c @@ -1,7 +1,7 @@ /* message.c: MPS/CLIENT MESSAGES * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -53,7 +53,7 @@ Bool MessageCheck(Message message) CHECKS(Message, message); CHECKU(Arena, message->arena); CHECKD(MessageClass, message->class); - CHECKL(RingCheck(&message->queueRing)); + CHECKD_NOSIG(Ring, &message->queueRing); /* postedClock is uncheckable for clocked message types, */ /* but must be 0 for unclocked message types: */ CHECKL(MessageIsClocked(message) || (message->postedClock == 0)); @@ -186,7 +186,7 @@ void MessageEmpty(Arena arena) static Bool MessageTypeEnabled(Arena arena, MessageType type) { AVERT(Arena, arena); - AVER(MessageTypeCheck(type)); + AVERT(MessageType, type); return BTGet(arena->enabledMessageTypes, type); } @@ -194,7 +194,7 @@ static Bool MessageTypeEnabled(Arena arena, MessageType type) void MessageTypeEnable(Arena arena, MessageType type) { AVERT(Arena, arena); - AVER(MessageTypeCheck(type)); + AVERT(MessageType, type); BTSet(arena->enabledMessageTypes, type); } @@ -204,7 +204,7 @@ void MessageTypeDisable(Arena arena, MessageType type) Message message; AVERT(Arena, arena); - AVER(MessageTypeCheck(type)); + AVERT(MessageType, type); /* Flush existing messages of this type */ while(MessageGet(&message, arena, type)) { @@ -252,7 +252,7 @@ Bool MessageGet(Message *messageReturn, Arena arena, MessageType type) AVER(messageReturn != NULL); AVERT(Arena, arena); - AVER(MessageTypeCheck(type)); + AVERT(MessageType, type); RING_FOR(node, &arena->messageRing, next) { Message message = RING_ELT(Message, queueRing, node); @@ -427,7 +427,7 @@ const char *MessageNoGCStartWhy(Message message) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002, 2008 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/messtest.c b/code/messtest.c index b7e178b483..96deeebfc5 100644 --- a/code/messtest.c +++ b/code/messtest.c @@ -1,7 +1,7 @@ /* messtest.c: MESSAGE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" @@ -10,8 +10,7 @@ #include "testlib.h" #include "mpslib.h" -#include -#include +#include /* printf */ SRCID(messtest, "$Id$"); @@ -261,8 +260,7 @@ extern int main(int argc, char *argv[]) mps_arena_t mpsArena; Arena arena; - testlib_unused(argc); - testlib_unused(argv); + testlib_init(argc, argv); die(mps_arena_create(&mpsArena, mps_arena_class_vm(), testArenaSIZE), "mps_arena_create"); @@ -279,7 +277,7 @@ extern int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/meter.c b/code/meter.c index 92937c1c94..c2c8bccbe8 100644 --- a/code/meter.c +++ b/code/meter.c @@ -1,7 +1,7 @@ /* meter.c: METERS * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * TRANSGRESSIONS * @@ -64,30 +64,30 @@ void MeterAccumulate(Meter meter, Size amount) /* MeterWrite -- describe method for meters */ -Res MeterWrite(Meter meter, mps_lib_FILE *stream) +Res MeterWrite(Meter meter, mps_lib_FILE *stream, Count depth) { Res res = ResOK; - res = WriteF(stream, - "meter $S {", meter->name, - "count: $U", meter->count, + res = WriteF(stream, depth, + "meter \"$S\" {", (WriteFS)meter->name, + "count: $U", (WriteFU)meter->count, NULL); if (res != ResOK) return res; if (meter->count > 0) { double mean = meter->total / (double)meter->count; - res = WriteF(stream, - ", total: $D", meter->total, - ", max: $U", meter->max, - ", min: $U", meter->min, - ", mean: $D", mean, - ", mean^2: $D", meter->meanSquared, + res = WriteF(stream, 0, + ", total $D", (WriteFD)meter->total, + ", max $U", (WriteFU)meter->max, + ", min $U", (WriteFU)meter->min, + ", mean $D", (WriteFD)mean, + ", meanSquared $D", (WriteFD)meter->meanSquared, NULL); if (res != ResOK) return res; } - res = WriteF(stream, "}\n", NULL); + res = WriteF(stream, 0, "}\n", NULL); return res; } @@ -98,13 +98,13 @@ Res MeterWrite(Meter meter, mps_lib_FILE *stream) void MeterEmit(Meter meter) { EVENT6(MeterValues, meter, meter->total, meter->meanSquared, - meter->count, meter->max, meter->min); + meter->count, meter->max, meter->min); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/meter.h b/code/meter.h index 7a7f8266e8..9ac571cae2 100644 --- a/code/meter.h +++ b/code/meter.h @@ -1,7 +1,7 @@ /* meter.h: METER INTERFACE * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .sources: mps.design.metrics. * @@ -35,7 +35,7 @@ typedef struct MeterStruct extern void MeterInit(Meter meter, const char *name, void *owner); extern void MeterAccumulate(Meter meter, Size amount); -extern Res MeterWrite(Meter meter, mps_lib_FILE *stream); +extern Res MeterWrite(Meter meter, mps_lib_FILE *stream, Count depth); extern void MeterEmit(Meter meter); #define METER_DECL(meter) STATISTIC_DECL(struct MeterStruct meter) @@ -45,9 +45,12 @@ extern void MeterEmit(Meter meter); #define METER_ACC(meter, delta) \ STATISTIC(MeterAccumulate(&(meter), delta)) #if defined(STATISTICS) -#define METER_WRITE(meter, stream) MeterWrite(&(meter), stream) +#define METER_WRITE(meter, stream, depth) BEGIN \ + Res _res = MeterWrite(&(meter), (stream), (depth)); \ + if (_res != ResOK) return _res; \ + END #elif defined(STATISTICS_NONE) -#define METER_WRITE(meter, stream) (ResOK) +#define METER_WRITE(meter, stream, depth) NOOP #else #error "No statistics configured." #endif @@ -59,7 +62,7 @@ extern void MeterEmit(Meter meter); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/misc.h b/code/misc.h index 0557c9af6f..9b22522558 100644 --- a/code/misc.h +++ b/code/misc.h @@ -1,7 +1,7 @@ /* misc.h: MISCELLANEOUS DEFINITIONS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2001 Global Graphics Software. * * Small general things which are useful for C but aren't part of the @@ -13,8 +13,6 @@ #ifndef misc_h #define misc_h -#include - typedef int Bool; /* */ enum BoolEnum { @@ -23,6 +21,14 @@ enum BoolEnum { }; +typedef int Compare; +enum CompareEnum { + CompareLESS = -1, + CompareEQUAL = 0, + CompareGREATER = 1 +}; + + /* SrcId -- source identification * * Every C source file should start with a SRCID declaration to @@ -42,6 +48,7 @@ typedef const struct SrcIdStruct { #define SRCID(id, scmid) \ static SrcIdStruct id ## FileSrcIdStruct = \ {__FILE__, (scmid), __DATE__, __TIME__}; \ + extern SrcId id ## SrcId; \ SrcId id ## SrcId = &id ## FileSrcIdStruct @@ -95,8 +102,8 @@ typedef const struct SrcIdStruct { * NELEMS(a) expands into an expression that is the number * of elements in the array a. * - * WARNING: expands a more than once (you'd have to write obviously - * perverse code for this to matter though). + * WARNING: expands a more than once (but only in the context of + * sizeof, so does not cause double evaluation). */ #define NELEMS(a) (sizeof(a)/sizeof((a)[0])) @@ -145,6 +152,18 @@ typedef const struct SrcIdStruct { #define UNUSED(param) ((void)param) +/* UNUSED_POINTER, UNUSED_SIZE -- values for unused arguments + * + * Use these values for unused pointer, size closure arguments and + * check them in the callback or visitor. + * + * Ensure that they have high bits set on 64-bit platforms for maximum + * unusability. + */ +#define UNUSED_POINTER (Pointer)((Word)~0xFFFFFFFF | (Word)0xB60405ED) /* PointeR UNUSED */ +#define UNUSED_SIZE ((Size)~0xFFFFFFFF | (Size)0x520405ED) /* SiZe UNUSED */ + + /* PARENT -- parent structure * * Given a pointer to a field of a structure this returns a pointer to @@ -162,6 +181,29 @@ typedef const struct SrcIdStruct { ((type *)(void *)((char *)(p) - offsetof(type, field))) + +/* BOOLFIELD -- declare a Boolean bitfield + * + * A Boolean bitfield needs to be unsigned (not Bool), so that its + * values are 0 and 1 (not 0 and -1), in order to avoid a sign + * conversion (which would be a compiler error) when assigning TRUE to + * the field. + * + * See + */ +#define BOOLFIELD(name) unsigned name : 1 + + +/* BITFIELD -- coerce a value into a bitfield + * + * This coerces value to the given width and type in a way that avoids + * warnings from gcc -Wconversion about possible loss of data. + */ + +#define BITFIELD(type, value, width) ((type)value & (((type)1 << (width)) - 1)) +#define BOOLOF(v) BITFIELD(unsigned, (v), 1) + + /* Bit Sets -- sets of integers in [0,N-1]. * * Can be used on any unsigned integral type, ty. These definitions @@ -183,6 +225,7 @@ typedef const struct SrcIdStruct { #define BS_SUB(s1, s2) BS_SUPER((s2), (s1)) #define BS_IS_SINGLE(s) ( ((s) != 0) && (((s) & ((s)-1)) == 0) ) #define BS_SYM_DIFF(s1, s2) ((s1) ^ (s2)) +#define BS_BITFIELD(ty, s) BITFIELD(ty ## Set, (s), ty ## LIMIT) #endif /* misc_h */ @@ -190,7 +233,7 @@ typedef const struct SrcIdStruct { /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpm.c b/code/mpm.c index 5436327c7e..aba1ac2f5c 100644 --- a/code/mpm.c +++ b/code/mpm.c @@ -1,7 +1,7 @@ /* mpm.c: GENERAL MPM SUPPORT * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * * .purpose: Miscellaneous support for the implementation of the MPM * and pool classes. @@ -10,6 +10,8 @@ #include "check.h" #include "mpm.h" +#include "vm.h" + #include /* Get some floating constants for WriteDouble */ #include @@ -77,7 +79,16 @@ Bool MPMCheck(void) CHECKL(-(DBL_MIN_10_EXP) <= DBL_MAX_10_EXP); } - return TRUE; + /* The granularity of memory mapping must be a multiple of the + * granularity of protection (or we might not be able to protect an + * arena grain). */ + CHECKL(PageSize() % ProtGranularity() == 0); + + /* StackProbe mustn't skip over the stack guard page. See + * . */ + CHECKL(StackProbeDEPTH * sizeof(Word) < PageSize()); + + return TRUE; } @@ -117,7 +128,8 @@ Bool AttrCheck(Attr attr) Bool AlignCheck(Align align) { - CHECKL(align > 0 && (align & (align - 1)) == 0); + CHECKL(align > 0); + CHECKL((align & (align - 1)) == 0); /* .check.unused: Check methods for signatureless types don't use */ /* their argument in hot varieties, so UNUSED is needed. */ UNUSED(align); @@ -125,6 +137,16 @@ Bool AlignCheck(Align align) } +/* AccessSetCheck -- check that an access set is valid */ + +Bool AccessSetCheck(AccessSet mode) +{ + CHECKL(mode < ((ULongest)1 << AccessLIMIT)); + UNUSED(mode); /* see .check.unused */ + return TRUE; +} + + #endif /* defined(AVER_AND_CHECK) */ @@ -132,7 +154,7 @@ Bool AlignCheck(Align align) Bool (WordIsAligned)(Word word, Align align) { - AVER(AlignCheck(align)); + AVERT(Align, align); return WordIsAligned(word, align); } @@ -141,7 +163,7 @@ Bool (WordIsAligned)(Word word, Align align) Word (WordAlignUp)(Word word, Align align) { - AVER(AlignCheck(align)); + AVERT(Align, align); return WordAlignUp(word, align); } @@ -167,23 +189,23 @@ Word (WordRoundUp)(Word word, Size modulus) Word (WordAlignDown)(Word word, Align alignment) { - AVER(AlignCheck(alignment)); + AVERT(Align, alignment); return WordAlignDown(word, alignment); } /* SizeIsP2 -- test whether a size is a power of two */ -Bool SizeIsP2(Size size) +Bool (SizeIsP2)(Size size) { - return WordIsP2((Word)size); + return SizeIsP2(size); } /* WordIsP2 -- tests whether a word is a power of two */ -Bool WordIsP2(Word word) +Bool (WordIsP2)(Word word) { - return word > 0 && (word & (word - 1)) == 0; + return WordIsP2(word); } @@ -212,7 +234,7 @@ Shift SizeLog2(Size size) Addr (AddrAlignDown)(Addr addr, Align alignment) { - AVER(AlignCheck(alignment)); + AVERT(Align, alignment); return AddrAlignDown(addr, alignment); } @@ -430,34 +452,35 @@ static Res WriteDouble(mps_lib_FILE *stream, double d) * .writef.check: See .check.writef. */ -Res WriteF(mps_lib_FILE *stream, ...) +Res WriteF(mps_lib_FILE *stream, Count depth, ...) { Res res; va_list args; - va_start(args, stream); - res = WriteF_v(stream, args); + va_start(args, depth); + res = WriteF_v(stream, depth, args); va_end(args); return res; } -Res WriteF_v(mps_lib_FILE *stream, va_list args) +Res WriteF_v(mps_lib_FILE *stream, Count depth, va_list args) { const char *firstformat; Res res; firstformat = va_arg(args, const char *); - res = WriteF_firstformat_v(stream, firstformat, args); + res = WriteF_firstformat_v(stream, depth, firstformat, args); return res; } -Res WriteF_firstformat_v(mps_lib_FILE *stream, +Res WriteF_firstformat_v(mps_lib_FILE *stream, Count depth, const char *firstformat, va_list args) { const char *format; int r; size_t i; Res res; + Bool start_of_line = TRUE; AVER(stream != NULL); @@ -468,9 +491,19 @@ Res WriteF_firstformat_v(mps_lib_FILE *stream, break; while(*format != '\0') { + if (start_of_line) { + for (i = 0; i < depth; ++i) { + mps_lib_fputc(' ', stream); + } + start_of_line = FALSE; + } if (*format != '$') { r = mps_lib_fputc(*format, stream); /* Could be more efficient */ - if (r == mps_lib_EOF) return ResIO; + if (r == mps_lib_EOF) + return ResIO; + if (*format == '\n') { + start_of_line = TRUE; + } } else { ++format; AVER(*format != '\0'); @@ -480,75 +513,86 @@ Res WriteF_firstformat_v(mps_lib_FILE *stream, WriteFA addr = va_arg(args, WriteFA); res = WriteULongest(stream, (ULongest)addr, 16, (sizeof(WriteFA) * CHAR_BIT + 3) / 4); - if (res != ResOK) return res; + if (res != ResOK) + return res; } break; case 'P': { /* pointer, see .writef.p */ WriteFP p = va_arg(args, WriteFP); res = WriteULongest(stream, (ULongest)p, 16, (sizeof(WriteFP) * CHAR_BIT + 3)/ 4); - if (res != ResOK) return res; + if (res != ResOK) + return res; } break; case 'F': { /* function */ WriteFF f = va_arg(args, WriteFF); Byte *b = (Byte *)&f; - /* ISO C forbits casting function pointers to integer, so + /* ISO C forbids casting function pointers to integer, so decode bytes (see design.writef.f). TODO: Be smarter about endianness. */ for(i=0; i < sizeof(WriteFF); i++) { res = WriteULongest(stream, (ULongest)(b[i]), 16, (CHAR_BIT + 3) / 4); - if (res != ResOK) return res; + if (res != ResOK) + return res; } } break; case 'S': { /* string */ WriteFS s = va_arg(args, WriteFS); r = mps_lib_fputs((const char *)s, stream); - if (r == mps_lib_EOF) return ResIO; + if (r == mps_lib_EOF) + return ResIO; } break; case 'C': { /* character */ WriteFC c = va_arg(args, WriteFC); /* promoted */ r = mps_lib_fputc((int)c, stream); - if (r == mps_lib_EOF) return ResIO; + if (r == mps_lib_EOF) + return ResIO; } break; case 'W': { /* word */ WriteFW w = va_arg(args, WriteFW); res = WriteULongest(stream, (ULongest)w, 16, (sizeof(WriteFW) * CHAR_BIT + 3) / 4); - if (res != ResOK) return res; + if (res != ResOK) + return res; } break; case 'U': { /* decimal, see .writef.p */ WriteFU u = va_arg(args, WriteFU); res = WriteULongest(stream, (ULongest)u, 10, 0); - if (res != ResOK) return res; + if (res != ResOK) + return res; } break; case '3': { /* decimal for thousandths */ WriteFU u = va_arg(args, WriteFU); res = WriteULongest(stream, (ULongest)u, 10, 3); - if (res != ResOK) return res; + if (res != ResOK) + return res; } break; case 'B': { /* binary, see .writef.p */ WriteFB b = va_arg(args, WriteFB); res = WriteULongest(stream, (ULongest)b, 2, sizeof(WriteFB) * CHAR_BIT); - if (res != ResOK) return res; + if (res != ResOK) + return res; } break; case '$': { /* dollar char */ r = mps_lib_fputc('$', stream); - if (r == mps_lib_EOF) return ResIO; + if (r == mps_lib_EOF) + return ResIO; } break; case 'D': { /* double */ WriteFD d = va_arg(args, WriteFD); res = WriteDouble(stream, d); - if (res != ResOK) return res; + if (res != ResOK) + return res; } break; default: @@ -604,7 +648,7 @@ Bool StringEqual(const char *s1, const char *s2) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpm.h b/code/mpm.h index b5cbabfb04..583adc06ee 100644 --- a/code/mpm.h +++ b/code/mpm.h @@ -1,7 +1,7 @@ /* mpm.h: MEMORY POOL MANAGER DEFINITIONS * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .trans.bufferinit: The Buffer data structure has an Init field and @@ -18,6 +18,8 @@ #include "event.h" #include "lock.h" +#include "prot.h" +#include "sp.h" #include "th.h" #include "ss.h" #include "mpslib.h" @@ -44,6 +46,7 @@ extern Bool FunCheck(Fun f); extern Bool ShiftCheck(Shift shift); extern Bool AttrCheck(Attr attr); extern Bool RootVarCheck(RootVar rootVar); +extern Bool AccessSetCheck(AccessSet mode); /* Address/Size Interface -- see */ @@ -87,6 +90,9 @@ extern Addr (AddrAlignDown)(Addr addr, Align align); #define AddrIsAligned(p, a) WordIsAligned((Word)(p), a) #define AddrAlignUp(p, a) ((Addr)WordAlignUp((Word)(p), a)) +#define AddrRoundUp(p, r) ((Addr)WordRoundUp((Word)(p), r)) + +#define ReadonlyAddrAdd(p, s) ((ReadonlyAddr)((const char *)(p) + (s))) #define SizeIsAligned(s, a) WordIsAligned((Word)(s), a) #define SizeAlignUp(s, a) ((Size)WordAlignUp((Word)(s), a)) @@ -143,18 +149,21 @@ extern Bool ResIsAllocFailure(Res res); * SizeFloorLog2 returns the floor of the logarithm in base 2 of size. * size can be any positive non-zero value. */ -extern Bool SizeIsP2(Size size); +extern Bool (SizeIsP2)(Size size); +#define SizeIsP2(size) WordIsP2((Word)size) extern Shift SizeLog2(Size size); extern Shift SizeFloorLog2(Size size); -extern Bool WordIsP2(Word word); +extern Bool (WordIsP2)(Word word); +#define WordIsP2(word) ((word) > 0 && ((word) & ((word) - 1)) == 0) /* Formatted Output -- see , */ -extern Res WriteF(mps_lib_FILE *stream, ...); -extern Res WriteF_v(mps_lib_FILE *stream, va_list args); -extern Res WriteF_firstformat_v(mps_lib_FILE *stream, +extern Res WriteF(mps_lib_FILE *stream, Count depth, ...); +extern Res WriteF_v(mps_lib_FILE *stream, Count depth, va_list args); +extern Res WriteF_firstformat_v(mps_lib_FILE *stream, Count depth, const char *firstformat, va_list args); +#define WriteFYesNo(condition) ((WriteFS)((condition) ? "YES" : "NO")) /* Miscellaneous support -- see */ @@ -176,13 +185,15 @@ extern Res PoolInit(Pool pool, Arena arena, PoolClass class, ArgList args); extern void PoolFinish(Pool pool); extern Bool PoolClassCheck(PoolClass class); extern Bool PoolCheck(Pool pool); -extern Res PoolDescribe(Pool pool, mps_lib_FILE *stream); +extern Res PoolDescribe(Pool pool, mps_lib_FILE *stream, Count depth); +/* Must be thread-safe. See . */ #define PoolArena(pool) ((pool)->arena) #define PoolAlignment(pool) ((pool)->alignment) #define PoolSegRing(pool) (&(pool)->segRing) #define PoolArenaRing(pool) (&(pool)->arenaRing) #define PoolOfArenaRing(node) RING_ELT(Pool, arenaRing, node) +#define PoolHasAttr(pool, Attr) (((pool)->class->attr & (Attr)) != 0) extern Bool PoolFormat(Format *formatReturn, Pool pool); @@ -211,12 +222,15 @@ extern Res (PoolFix)(Pool pool, ScanState ss, Seg seg, Addr *refIO); #define PoolFix(pool, ss, seg, refIO) \ ((*(pool)->fix)(pool, ss, seg, refIO)) extern Res PoolFixEmergency(Pool pool, ScanState ss, Seg seg, Addr *refIO); -extern void PoolReclaim(Pool pool, Trace trace, Seg seg); +extern Bool PoolReclaim(Pool pool, Trace trace, Seg seg); extern void PoolTraceEnd(Pool pool, Trace trace); extern Res PoolAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr); -extern void PoolWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, +extern void PoolWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, void *v, size_t s); -extern void PoolFreeWalk(Pool pool, FreeBlockStepMethod f, void *p); +extern void PoolFreeWalk(Pool pool, FreeBlockVisitor f, void *p); +extern Size PoolTotalSize(Pool pool); +extern Size PoolFreeSize(Pool pool); + extern Res PoolTrivInit(Pool pool, ArgList arg); extern void PoolTrivFinish(Pool pool); extern Res PoolNoAlloc(Addr *pReturn, Pool pool, Size size, @@ -235,7 +249,7 @@ extern void PoolNoBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit); extern void PoolTrivBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit); -extern Res PoolTrivDescribe(Pool pool, mps_lib_FILE *stream); +extern Res PoolTrivDescribe(Pool pool, mps_lib_FILE *stream, Count depth); extern Res PoolNoTraceBegin(Pool pool, Trace trace); extern Res PoolTrivTraceBegin(Pool pool, Trace trace); extern Res PoolNoAccess(Pool pool, Seg seg, Addr addr, @@ -252,7 +266,7 @@ extern void PoolNoBlacken(Pool pool, TraceSet traceSet, Seg seg); extern void PoolTrivBlacken(Pool pool, TraceSet traceSet, Seg seg); extern Res PoolNoScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg); extern Res PoolNoFix(Pool pool, ScanState ss, Seg seg, Ref *refIO); -extern void PoolNoReclaim(Pool pool, Trace trace, Seg seg); +extern Bool PoolNoReclaim(Pool pool, Trace trace, Seg seg); extern void PoolTrivTraceEnd(Pool pool, Trace trace); extern void PoolNoRampBegin(Pool pool, Buffer buf, Bool collectAll); extern void PoolTrivRampBegin(Pool pool, Buffer buf, Bool collectAll); @@ -263,12 +277,14 @@ extern Res PoolTrivFramePush(AllocFrame *frameReturn, Pool pool, Buffer buf); extern Res PoolNoFramePop(Pool pool, Buffer buf, AllocFrame frame); extern Res PoolTrivFramePop(Pool pool, Buffer buf, AllocFrame frame); extern void PoolNoFramePopPending(Pool pool, Buffer buf, AllocFrame frame); +extern void PoolTrivFramePopPending(Pool pool, Buffer buf, AllocFrame frame); extern Res PoolNoAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr); -extern void PoolNoWalk(Pool pool, Seg seg, FormattedObjectsStepMethod step, +extern void PoolNoWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, void *p, size_t s); -extern void PoolNoFreeWalk(Pool pool, FreeBlockStepMethod f, void *p); +extern void PoolTrivFreeWalk(Pool pool, FreeBlockVisitor f, void *p); extern PoolDebugMixin PoolNoDebugMixin(Pool pool); extern BufferClass PoolNoBufferClass(void); +extern Size PoolNoSize(Pool pool); #define ClassOfPool(pool) ((pool)->class) #define SuperclassOfPool(pool) \ @@ -276,13 +292,11 @@ extern BufferClass PoolNoBufferClass(void); /* Abstract Pool Classes Interface -- see */ -extern void PoolClassMixInAllocFree(PoolClass class); extern void PoolClassMixInBuffer(PoolClass class); extern void PoolClassMixInScan(PoolClass class); extern void PoolClassMixInFormat(PoolClass class); extern void PoolClassMixInCollect(PoolClass class); extern AbstractPoolClass AbstractPoolClassGet(void); -extern AbstractAllocFreePoolClass AbstractAllocFreePoolClassGet(void); extern AbstractBufferPoolClass AbstractBufferPoolClassGet(void); extern AbstractBufferPoolClass AbstractSegBufPoolClassGet(void); extern AbstractScanPoolClass AbstractScanPoolClassGet(void); @@ -390,8 +404,9 @@ extern Size TracePoll(Globals globals); extern Rank TraceRankForAccess(Arena arena, Seg seg); extern void TraceSegAccess(Arena arena, Seg seg, AccessSet mode); -extern void TraceQuantum(Trace trace); +extern void TraceAdvance(Trace trace); extern Res TraceStartCollectAll(Trace *traceReturn, Arena arena, int why); +extern Res TraceDescribe(Trace trace, mps_lib_FILE *stream, Count depth); /* traceanc.c -- Trace Ancillary */ @@ -482,61 +497,81 @@ extern AbstractArenaClass AbstractArenaClassGet(void); extern Bool ArenaClassCheck(ArenaClass class); extern Bool ArenaCheck(Arena arena); -extern Res ArenaCreate(Arena *arenaReturn, ArenaClass class, mps_arg_s args[]); +extern Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args); extern void ArenaDestroy(Arena arena); -extern Res ArenaInit(Arena arena, ArenaClass class); +extern Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, + ArgList args); extern void ArenaFinish(Arena arena); -extern Res ArenaDescribe(Arena arena, mps_lib_FILE *stream); -extern Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream); +extern Res ArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth); +extern Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream, Count depth); extern Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context); +extern Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit); +extern void ArenaFreeLandDelete(Arena arena, Addr base, Addr limit); + extern Bool GlobalsCheck(Globals arena); extern Res GlobalsInit(Globals arena); extern void GlobalsFinish(Globals arena); extern Res GlobalsCompleteCreate(Globals arenaGlobals); extern void GlobalsPrepareToDestroy(Globals arenaGlobals); -extern Res GlobalsDescribe(Globals arena, mps_lib_FILE *stream); +extern Res GlobalsDescribe(Globals arena, mps_lib_FILE *stream, Count depth); extern Ring GlobalsRememberedSummaryRing(Globals); #define ArenaGlobals(arena) (&(arena)->globals) #define GlobalsArena(glob) PARENT(ArenaStruct, globals, glob) #define ArenaThreadRing(arena) (&(arena)->threadRing) +#define ArenaDeadRing(arena) (&(arena)->deadRing) #define ArenaEpoch(arena) ((arena)->epoch) /* .epoch.ts */ #define ArenaTrace(arena, ti) (&(arena)->trace[ti]) #define ArenaZoneShift(arena) ((arena)->zoneShift) -#define ArenaAlign(arena) ((arena)->alignment) +#define ArenaStripeSize(arena) ((Size)1 << ArenaZoneShift(arena)) +#define ArenaGrainSize(arena) ((arena)->grainSize) #define ArenaGreyRing(arena, rank) (&(arena)->greyRing[rank]) #define ArenaPoolRing(arena) (&ArenaGlobals(arena)->poolRing) +#define ArenaChunkTree(arena) RVALUE((arena)->chunkTree) +#define ArenaSegSplay(arena) (&(arena)->segSplayTreeStruct) + +extern Bool ArenaGrainSizeCheck(Size size); +#define AddrArenaGrainUp(addr, arena) AddrAlignUp(addr, ArenaGrainSize(arena)) +#define AddrArenaGrainDown(addr, arena) AddrAlignDown(addr, ArenaGrainSize(arena)) +#define AddrIsArenaGrain(addr, arena) AddrIsAligned(addr, ArenaGrainSize(arena)) +#define SizeArenaGrains(size, arena) SizeAlignUp(size, ArenaGrainSize(arena)) +#define SizeIsArenaGrains(size, arena) SizeIsAligned(size, ArenaGrainSize(arena)) + +extern void ArenaEnterLock(Arena arena, Bool recursive); +extern void ArenaLeaveLock(Arena arena, Bool recursive); extern void (ArenaEnter)(Arena arena); extern void (ArenaLeave)(Arena arena); +extern void (ArenaPoll)(Globals globals); -#if defined(THREAD_SINGLE) && defined(PROTECTION_NONE) +#if defined(SHIELD) +#define ArenaEnter(arena) ArenaEnterLock(arena, FALSE) +#define ArenaLeave(arena) ArenaLeaveLock(arena, FALSE) +#elif defined(SHIELD_NONE) #define ArenaEnter(arena) UNUSED(arena) -#define ArenaLeave(arena) UNUSED(arena) -#endif +#define ArenaLeave(arena) AVER(arena->busyTraces == TraceSetEMPTY) +#define ArenaPoll(globals) UNUSED(globals) +#else +#error "No shield configuration." +#endif /* SHIELD */ extern void ArenaEnterRecursive(Arena arena); extern void ArenaLeaveRecursive(Arena arena); -extern void (ArenaPoll)(Globals globals); -#ifdef MPS_PROD_EPCORE -#define ArenaPoll(globals) UNUSED(globals) -#endif -/* .nogc.why: ScriptWorks doesn't use MM-provided incremental GC, so */ -/* doesn't need to poll when allocating. */ - extern Bool (ArenaStep)(Globals globals, double interval, double multiplier); extern void ArenaClamp(Globals globals); extern void ArenaRelease(Globals globals); extern void ArenaPark(Globals globals); -extern void ArenaExposeRemember(Globals globals, int remember); +extern void ArenaExposeRemember(Globals globals, Bool remember); extern void ArenaRestoreProtection(Globals globals); extern Res ArenaStartCollect(Globals globals, int why); extern Res ArenaCollect(Globals globals, int why); 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 ArenaSetEmergency(Arena arena, Bool emergency); extern Bool ArenaEmergency(Arena arean); @@ -546,7 +581,7 @@ extern void ControlFinish(Arena arena); extern Res ControlAlloc(void **baseReturn, Arena arena, size_t size, Bool withReservoirPermit); extern void ControlFree(Arena arena, void *base, size_t size); -extern Res ControlDescribe(Arena arena, mps_lib_FILE *stream); +extern Res ControlDescribe(Arena arena, mps_lib_FILE *stream, Count depth); /* Peek/Poke @@ -589,9 +624,10 @@ extern Res ArenaSetCommitLimit(Arena arena, Size limit); extern Size ArenaSpareCommitLimit(Arena arena); extern void ArenaSetSpareCommitLimit(Arena arena, Size limit); extern Size ArenaNoPurgeSpare(Arena arena, Size size); +extern Res ArenaNoGrow(Arena arena, LocusPref pref, Size size); -extern double ArenaMutatorAllocSize(Arena arena); extern Size ArenaAvail(Arena arena); +extern Size ArenaCollectable(Arena arena); extern Res ArenaExtend(Arena, Addr base, Size size); @@ -600,9 +636,8 @@ extern void ArenaCompact(Arena arena, Trace trace); extern Res ArenaFinalize(Arena arena, Ref obj); extern Res ArenaDefinalize(Arena arena, Ref obj); -extern Bool ArenaIsReservedAddr(Arena arena, Addr addr); - #define ArenaReservoir(arena) (&(arena)->reservoirStruct) +#define ReservoirPool(reservoir) (&(reservoir)->poolStruct) extern Bool ReservoirCheck(Reservoir reservoir); extern Res ReservoirInit(Reservoir reservoir, Arena arena); @@ -611,23 +646,38 @@ extern Size ReservoirLimit(Reservoir reservoir); extern void ReservoirSetLimit(Reservoir reservoir, Size size); extern Size ReservoirAvailable(Reservoir reservoir); extern Res ReservoirEnsureFull(Reservoir reservoir); -extern void ReservoirDeposit(Reservoir reservoir, Addr base, Size size); +extern Bool ReservoirDeposit(Reservoir reservoir, Addr *baseIO, Size *sizeIO); extern Res ReservoirWithdraw(Addr *baseReturn, Tract *baseTractReturn, Reservoir reservoir, Size size, Pool pool); -extern Res ArenaAlloc(Addr *baseReturn, SegPref pref, +extern Res ArenaAlloc(Addr *baseReturn, LocusPref pref, Size size, Pool pool, Bool withReservoirPermit); +extern Res ArenaFreeLandAlloc(Tract *tractReturn, Arena arena, ZoneSet zones, + Bool high, Size size, Pool pool); extern void ArenaFree(Addr base, Size size, Pool pool); extern Res ArenaNoExtend(Arena arena, Addr base, Size size); +/* Policy interface */ + +extern Res PolicyAlloc(Tract *tractReturn, Arena arena, LocusPref pref, + Size size, Pool pool); +extern Bool PolicyShouldCollectWorld(Arena arena, double interval, + double multiplier, Clock now, + Clock clocks_per_sec); +extern Bool PolicyStartTrace(Trace *traceReturn, Arena arena); +extern Bool PolicyPoll(Arena arena); +extern Bool PolicyPollAgain(Arena arena, Clock start, Size tracedSize); + + /* Locus interface */ -extern Bool SegPrefCheck(SegPref pref); -extern SegPref SegPrefDefault(void); -extern void SegPrefInit(SegPref pref); -extern void SegPrefExpress(SegPref pref, SegPrefKind kind, void *p); +extern Bool LocusPrefCheck(LocusPref pref); +extern LocusPref LocusPrefDefault(void); +extern void LocusPrefInit(LocusPref pref); +extern void LocusPrefExpress(LocusPref pref, LocusPrefKind kind, void *p); +extern Res LocusPrefDescribe(LocusPref pref, mps_lib_FILE *stream, Count depth); extern void LocusInit(Arena arena); extern void LocusFinish(Arena arena); @@ -636,15 +686,19 @@ extern Bool LocusCheck(Arena arena); /* Segment interface */ -extern Res SegAlloc(Seg *segReturn, SegClass class, SegPref pref, +extern Res SegAlloc(Seg *segReturn, SegClass class, LocusPref pref, Size size, Pool pool, Bool withReservoirPermit, ArgList args); extern void SegFree(Seg seg); extern Bool SegOfAddr(Seg *segReturn, Arena arena, Addr addr); +typedef Bool (*SegVisitor)(Seg seg, void *closureP, Size closureS); +extern Bool SegTraverse(Arena arena, SegVisitor visit, + void *closureP, Size closureS); +extern void SegTraverseAndDelete(Arena arena, SegVisitor visit, + void *closureP, Size closureS); extern Bool SegFirst(Seg *segReturn, Arena arena); extern Bool SegNext(Seg *segReturn, Arena arena, Seg seg); extern Bool SegNextOfRing(Seg *segReturn, Arena arena, Pool pool, Ring next); -extern Bool SegFindAboveAddr(Seg *segReturn, Arena arena, Addr addr); extern void SegSetWhite(Seg seg, TraceSet white); extern void SegSetGrey(Seg seg, TraceSet grey); extern void SegSetRankSet(Seg seg, RankSet rankSet); @@ -653,7 +707,7 @@ extern Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi, Bool withReservoirPermit); extern Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at, Bool withReservoirPermit); -extern Res SegDescribe(Seg seg, mps_lib_FILE *stream); +extern Res SegDescribe(Seg seg, mps_lib_FILE *stream, Count depth); extern void SegSetSummary(Seg seg, RefSet summary); extern Buffer SegBuffer(Seg seg); extern void SegSetBuffer(Seg seg, Buffer buffer); @@ -663,6 +717,8 @@ extern Bool SegClassCheck(SegClass class); extern SegClass SegClassGet(void); extern SegClass GCSegClassGet(void); extern void SegClassMixInNoSplitMerge(SegClass class); +extern Compare SegCompare(Tree tree, TreeKey key); +extern TreeKey SegKey(Tree tree); /* DEFINE_SEG_CLASS -- define a segment class */ @@ -693,16 +749,17 @@ extern Addr (SegLimit)(Seg seg); #define SegWhite(seg) ((TraceSet)(seg)->white) #define SegNailed(seg) ((TraceSet)(seg)->nailed) #define SegPoolRing(seg) (&(seg)->poolRing) +#define SegTree(seg) (&(seg)->treeStruct) #define SegOfPoolRing(node) (RING_ELT(Seg, poolRing, (node))) #define SegOfGreyRing(node) (&(RING_ELT(GCSeg, greyRing, (node)) \ ->segStruct)) #define SegSummary(seg) (((GCSeg)(seg))->summary) -#define SegSetPM(seg, mode) ((void)((seg)->pm = (mode))) -#define SegSetSM(seg, mode) ((void)((seg)->sm = (mode))) -#define SegSetDepth(seg, d) ((void)((seg)->depth = (d))) -#define SegSetNailed(seg, ts) ((void)((seg)->nailed = (ts))) +#define SegSetPM(seg, mode) ((void)((seg)->pm = BS_BITFIELD(Access, (mode)))) +#define SegSetSM(seg, mode) ((void)((seg)->sm = BS_BITFIELD(Access, (mode)))) +#define SegSetDepth(seg, d) ((void)((seg)->depth = BITFIELD(unsigned, (d), ShieldDepthWIDTH))) +#define SegSetNailed(seg, ts) ((void)((seg)->nailed = BS_BITFIELD(Trace, (ts)))) /* Buffer Interface -- see */ @@ -712,7 +769,7 @@ extern Res BufferCreate(Buffer *bufferReturn, BufferClass class, extern void BufferDestroy(Buffer buffer); extern Bool BufferCheck(Buffer buffer); extern Bool SegBufCheck(SegBuf segbuf); -extern Res BufferDescribe(Buffer buffer, mps_lib_FILE *stream); +extern Res BufferDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth); extern Res BufferReserve(Addr *pReturn, Buffer buffer, Size size, Bool withReservoirPermit); /* macro equivalent for BufferReserve, keep in sync with */ @@ -800,7 +857,7 @@ extern AllocPattern AllocPatternRamp(void); extern AllocPattern AllocPatternRampCollectAll(void); -/* FindDelete -- see and */ +/* FindDelete -- see */ extern Bool FindDeleteCheck(FindDelete findDelete); @@ -811,7 +868,7 @@ extern Bool FormatCheck(Format format); extern Res FormatCreate(Format *formatReturn, Arena arena, ArgList args); extern void FormatDestroy(Format format); extern Arena FormatArena(Format format); -extern Res FormatDescribe(Format format, mps_lib_FILE *stream); +extern Res FormatDescribe(Format format, mps_lib_FILE *stream, Count depth); /* Reference Interface -- see */ @@ -848,6 +905,7 @@ extern Bool RankSetCheck(RankSet rankSet); BS_ADD(ZoneSet, zs, AddrZone(arena, addr)) #define ZoneSetHasAddr(arena, zs, addr) \ BS_IS_MEMBER(zs, AddrZone(arena, addr)) +#define ZoneSetIsSingle(zs) BS_IS_SINGLE(zs) #define ZoneSetSub(zs1, zs2) BS_SUB(zs1, zs2) #define ZoneSetSuper(zs1, zs2) BS_SUPER(zs1, zs2) #define ZoneSetComp(zs) BS_COMP(zs) @@ -856,6 +914,16 @@ extern Bool RankSetCheck(RankSet rankSet); extern ZoneSet ZoneSetOfRange(Arena arena, Addr base, Addr limit); extern ZoneSet ZoneSetOfSeg(Arena arena, Seg seg); +typedef Bool (*RangeInZoneSet)(Addr *baseReturn, Addr *limitReturn, + Addr base, Addr limit, + Arena arena, ZoneSet zoneSet, Size size); +extern Bool RangeInZoneSetFirst(Addr *baseReturn, Addr *limitReturn, + Addr base, Addr limit, + Arena arena, ZoneSet zoneSet, Size size); +extern Bool RangeInZoneSetLast(Addr *baseReturn, Addr *limitReturn, + Addr base, Addr limit, + Arena arena, ZoneSet zoneSet, Size size); +extern ZoneSet ZoneSetBlacklist(Arena arena); /* Shield Interface -- see */ @@ -870,7 +938,9 @@ extern void (ShieldSuspend)(Arena arena); extern void (ShieldResume)(Arena arena); extern void (ShieldFlush)(Arena arena); -#if defined(THREAD_SINGLE) && defined(PROTECTION_NONE) +#if defined(SHIELD) +/* Nothing to do: functions declared in all shield configurations. */ +#elif defined(SHIELD_NONE) #define ShieldRaise(arena, seg, mode) \ BEGIN UNUSED(arena); UNUSED(seg); UNUSED(mode); END #define ShieldLower(arena, seg, mode) \ @@ -884,32 +954,9 @@ extern void (ShieldFlush)(Arena arena); #define ShieldSuspend(arena) BEGIN UNUSED(arena); END #define ShieldResume(arena) BEGIN UNUSED(arena); END #define ShieldFlush(arena) BEGIN UNUSED(arena); END -#endif - - -/* Protection Interface - * - * See for the design of the generic interface including - * the contracts for these functions. - * - * This interface has several different implementations, typically one - * per platform, see * for the various implementations, and - * * for the corresponding designs. */ - -extern void ProtSetup(void); - -extern void ProtSet(Addr base, Addr limit, AccessSet mode); -extern void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), - void *p, size_t s); -extern void ProtSync(Arena arena); -extern Bool ProtCanStepInstruction(MutatorFaultContext context); -extern Res ProtStepInstruction(MutatorFaultContext context); - - -/* Mutator Fault Context */ - -extern Addr MutatorFaultContextSP(MutatorFaultContext mfc); -extern Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc); +#else +#error "No shield configuration." +#endif /* SHIELD */ /* Location Dependency -- see */ @@ -945,8 +992,8 @@ extern Res RootCreateFun(Root *rootReturn, Arena arena, extern void RootDestroy(Root root); extern Bool RootModeCheck(RootMode mode); extern Bool RootCheck(Root root); -extern Res RootDescribe(Root root, mps_lib_FILE *stream); -extern Res RootsDescribe(Globals arenaGlobals, mps_lib_FILE *stream); +extern Res RootDescribe(Root root, mps_lib_FILE *stream, Count depth); +extern Res RootsDescribe(Globals arenaGlobals, mps_lib_FILE *stream, Count depth); extern Rank RootRank(Root root); extern AccessSet RootPM(Root root); extern RefSet RootSummary(Root root); @@ -959,24 +1006,35 @@ typedef Res (*RootIterateFn)(Root root, void *p); extern Res RootsIterate(Globals arena, RootIterateFn f, void *p); -/* VM Interface -- see * */ - -extern Align VMAlign(VM vm); -extern Bool VMCheck(VM vm); -extern Res VMParamFromArgs(void *params, size_t paramSize, ArgList args); -extern Res VMCreate(VM *VMReturn, Size size, void *params); -extern void VMDestroy(VM vm); -extern Addr VMBase(VM vm); -extern Addr VMLimit(VM vm); -extern Res VMMap(VM vm, Addr base, Addr limit); -extern void VMUnmap(VM vm, Addr base, Addr limit); -extern Size VMReserved(VM vm); -extern Size VMMapped(VM vm); - - -/* Stack Probe */ - -extern void StackProbe(Size depth); +/* Land Interface -- see */ + +extern Bool LandCheck(Land land); +#define LandArena(land) ((land)->arena) +#define LandAlignment(land) ((land)->alignment) +extern Size LandSize(Land land); +extern Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args); +extern Res LandCreate(Land *landReturn, Arena arena, LandClass class, Align alignment, void *owner, ArgList args); +extern void LandDestroy(Land land); +extern void LandFinish(Land land); +extern Res LandInsert(Range rangeReturn, Land land, Range range); +extern Res LandDelete(Range rangeReturn, Land land, Range range); +extern Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS); +extern Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS); +extern Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete); +extern Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete); +extern Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete); +extern Res LandFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high); +extern Res LandDescribe(Land land, mps_lib_FILE *stream, Count depth); +extern Bool LandFlush(Land dest, Land src); + +extern Size LandSlowSize(Land land); +extern Bool LandClassCheck(LandClass class); +extern LandClass LandClassGet(void); +#define LAND_SUPERCLASS(className) ((LandClass)SUPERCLASS(className)) +#define DEFINE_LAND_CLASS(className, var) \ + DEFINE_ALIAS_CLASS(className, LandClass, var) +#define IsLandSubclass(land, className) \ + IsSubclassPoly((land)->class, className ## Get()) /* STATISTIC -- gather statistics (in some varieties) @@ -990,9 +1048,6 @@ extern void StackProbe(Size depth); * STATISTIC_WRITE is inserted in WriteF arguments to output the values * of statistic fields. * - * STATISTIC_BEGIN and STATISTIC_END can be used around a block of - * statements. - * * .statistic.whitehot: The implementation of STATISTIC for * non-statistical varieties passes the parameter to DISCARD to ensure * the parameter is syntactically an expression. The parameter is @@ -1004,16 +1059,12 @@ extern void StackProbe(Size depth); #define STATISTIC(gather) BEGIN (gather); END #define STATISTIC_STAT(gather) BEGIN gather; END #define STATISTIC_WRITE(format, arg) (format), (arg), -#define STATISTIC_BEGIN BEGIN -#define STATISTIC_END END #elif defined(STATISTICS_NONE) #define STATISTIC(gather) DISCARD(((gather), 0)) #define STATISTIC_STAT DISCARD_STAT #define STATISTIC_WRITE(format, arg) -#define STATISTIC_BEGIN BEGIN if (0) { -#define STATISTIC_END } END #else /* !defined(STATISTICS) && !defined(STATISTICS_NONE) */ @@ -1026,7 +1077,7 @@ extern void StackProbe(Size depth); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpmss.c b/code/mpmss.c index c18c4fe07d..2c746b7e34 100644 --- a/code/mpmss.c +++ b/code/mpmss.c @@ -1,25 +1,21 @@ /* mpmss.c: MPM STRESS TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. */ +#include "mpm.h" +#include "mps.h" +#include "mpsavm.h" +#include "mpscmfs.h" #include "mpscmv.h" #include "mpscmvff.h" #include "mpslib.h" -#include "mpsavm.h" -#include "testlib.h" #include "mpslib.h" -#include "mps.h" -#include -#include - +#include "testlib.h" -/* TODO: Decide whether we should support the MPS pool class externally, - create mpscmfs.h, and replace this extern with proper use of its - interface. */ -extern mps_class_t PoolClassMFS(void); +#include /* printf */ #define testArenaSIZE ((((size_t)64)<<20) - 4) @@ -28,21 +24,34 @@ extern mps_class_t PoolClassMFS(void); #define testLOOPS 10 +/* check_allocated_size -- check the allocated size of the pool */ + +static void check_allocated_size(mps_pool_t pool, size_t allocated) +{ + size_t total_size = mps_pool_total_size(pool); + size_t free_size = mps_pool_free_size(pool); + Insist(total_size - free_size == allocated); +} + + /* stress -- create a pool of the requested type and allocate in it */ -static mps_res_t stress(mps_class_t class, size_t (*size)(int i), - mps_arena_t arena, ...) +static mps_res_t stress(mps_arena_t arena, mps_pool_debug_option_s *options, + size_t (*size)(size_t i), mps_align_t align, + const char *name, mps_pool_class_t pool_class, + mps_arg_s *args) { mps_res_t res; mps_pool_t pool; - va_list arg; - int i, k; + size_t i, k; int *ps[testSetSIZE]; size_t ss[testSetSIZE]; + size_t allocated = 0; /* Total allocated memory */ + size_t debugOverhead = options ? 2 * alignUp(options->fence_size, align) : 0; - va_start(arg, arena); - res = mps_pool_create_v(&pool, arena, class, arg); - va_end(arg); + printf("Pool class %s, alignment %u\n", name, (unsigned)align); + + res = mps_pool_create_k(&pool, arena, pool_class, args); if (res != MPS_RES_OK) return res; @@ -53,8 +62,10 @@ static mps_res_t stress(mps_class_t class, size_t (*size)(int i), res = mps_alloc((mps_addr_t *)&ps[i], pool, ss[i]); if (res != MPS_RES_OK) return res; + allocated += alignUp(ss[i], align) + debugOverhead; if (ss[i] >= sizeof(ps[i])) *ps[i] = 1; /* Write something, so it gets swap. */ + check_allocated_size(pool, allocated); } mps_pool_check_fenceposts(pool); @@ -62,7 +73,7 @@ static mps_res_t stress(mps_class_t class, size_t (*size)(int i), for (k=0; k (b)) ? (a) : (b)) - -#define alignUp(w, a) (((w) + (a) - 1) & ~((size_t)(a) - 1)) - - -/* randomSize -- produce sizes both latge and small */ +/* randomSize -- produce sizes both large and small */ -static size_t randomSize(int i) +static size_t randomSize(size_t i) { /* Make the range large enough to span three pages in the segment table: */ /* 160 segments/page, page size max 0x2000. */ @@ -109,9 +121,9 @@ static size_t randomSize(int i) } -/* randomSize8 -- produce sizes both latge and small, 8-byte aligned */ +/* randomSize8 -- produce sizes both large and small, 8-byte aligned */ -static size_t randomSize8(int i) +static size_t randomSize8(size_t i) { size_t maxSize = 2 * 160 * 0x2000; /* Reduce by a factor of 2 every 10 cycles. Total allocation about 40 MB. */ @@ -123,83 +135,102 @@ static size_t randomSize8(int i) static size_t fixedSizeSize = 0; -static size_t fixedSize(int i) +static size_t fixedSize(size_t i) { testlib_unused(i); return fixedSizeSize; } -static mps_pool_debug_option_s bothOptions8 = { - /* .fence_template = */ (const void *)"postpost", - /* .fence_size = */ 8, - /* .free_template = */ (const void *)"DEAD", - /* .free_size = */ 4 -}; - -static mps_pool_debug_option_s bothOptions16 = { - /* .fence_template = */ (const void *)"postpostpostpost", - /* .fence_size = */ 16, - /* .free_template = */ (const void *)"DEAD", +static mps_pool_debug_option_s bothOptions = { + /* .fence_template = */ "post", + /* .fence_size = */ 4, + /* .free_template = */ "DEAD", /* .free_size = */ 4 }; static mps_pool_debug_option_s fenceOptions = { - /* .fence_template = */ (const void *)"\0XXX ''\"\"'' XXX\0", - /* .fence_size = */ 16, + /* .fence_template = */ "123456789abcdef", + /* .fence_size = */ 15, /* .free_template = */ NULL, /* .free_size = */ 0 }; /* testInArena -- test all the pool classes in the given arena */ -static int testInArena(mps_arena_t arena, mps_pool_debug_option_s *options) +static void testInArena(mps_arena_class_t arena_class, mps_arg_s *arena_args, + mps_pool_debug_option_s *options) { - /* IWBN to test MVFFDebug, but the MPS doesn't support debugging */ - /* cross-segment allocation (possibly MVFF ought not to). */ - printf("MVFF\n"); - die(stress(mps_class_mvff(), randomSize8, arena, - (size_t)65536, (size_t)32, (mps_align_t)MPS_PF_ALIGN, TRUE, TRUE, TRUE), - "stress MVFF"); - printf("MV debug\n"); - die(stress(mps_class_mv_debug(), randomSize, arena, - options, (size_t)65536, (size_t)32, (size_t)65536), - "stress MV debug"); - - printf("MFS\n"); - fixedSizeSize = 13; - die(stress(PoolClassMFS(), - fixedSize, arena, (size_t)100000, fixedSizeSize), - "stress MFS"); - - printf("MV\n"); - die(stress(mps_class_mv(), randomSize, arena, - (size_t)65536, (size_t)32, (size_t)65536), - "stress MV"); + mps_arena_t arena; - return 0; + die(mps_arena_create_k(&arena, arena_class, arena_args), + "mps_arena_create"); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = sizeof(void *) << (rnd() % 4); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_SPARE, rnd_double()); + die(stress(arena, NULL, randomSize8, align, "MVFF", + mps_class_mvff(), args), "stress MVFF"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = sizeof(void *) << (rnd() % 4); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_SPARE, rnd_double()); + MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, options); + die(stress(arena, options, randomSize8, align, "MVFF debug", + mps_class_mvff_debug(), args), "stress MVFF debug"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = (mps_align_t)1 << (rnd() % 6); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + die(stress(arena, NULL, randomSize, align, "MV", + mps_class_mv(), args), "stress MV"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = (mps_align_t)1 << (rnd() % 6); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, options); + die(stress(arena, options, randomSize, align, "MV debug", + mps_class_mv_debug(), args), "stress MV debug"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + fixedSizeSize = 1 + rnd() % 64; + MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, fixedSizeSize); + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, 100000); + die(stress(arena, NULL, fixedSize, MPS_PF_ALIGN, "MFS", + mps_class_mfs(), args), "stress MFS"); + } MPS_ARGS_END(args); + + mps_arena_destroy(arena); } int main(int argc, char *argv[]) { - mps_arena_t arena; - mps_pool_debug_option_s *bothOptions; - - bothOptions = MPS_PF_ALIGN == 8 ? &bothOptions8 : &bothOptions16; + testlib_init(argc, argv); - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE)); + testInArena(mps_arena_class_vm(), args, &bothOptions); + } MPS_ARGS_END(args); - die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), - "mps_arena_create"); - testInArena(arena, bothOptions); - mps_arena_destroy(arena); - - die(mps_arena_create(&arena, mps_arena_class_vm(), smallArenaSIZE), - "mps_arena_create"); - testInArena(arena, &fenceOptions); - mps_arena_destroy(arena); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, smallArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(smallArenaSIZE)); + testInArena(mps_arena_class_vm(), args, &fenceOptions); + } MPS_ARGS_END(args); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; @@ -208,7 +239,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpmst.h b/code/mpmst.h index d0d3c12fbe..7dc9f8b26b 100644 --- a/code/mpmst.h +++ b/code/mpmst.h @@ -1,7 +1,7 @@ /* mpmst.h: MEMORY POOL MANAGER DATA STRUCTURES * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2001 Global Graphics Software. * * .design: This header file crosses module boundaries. The relevant @@ -27,6 +27,10 @@ #include "protocol.h" #include "ring.h" #include "chain.h" +#include "table.h" +#include "splay.h" +#include "meter.h" +#include "tree.h" /* PoolClassStruct -- pool class structure @@ -46,7 +50,7 @@ #define PoolClassSig ((Sig)0x519C7A55) /* SIGnature pool CLASS */ -typedef struct mps_class_s { +typedef struct mps_pool_class_s { ProtocolClassStruct protocol; const char *name; /* class name string */ size_t size; /* size of outer structure */ @@ -79,6 +83,8 @@ typedef struct mps_class_s { PoolBufferClassMethod bufferClass; /* default BufferClass of pool */ PoolDescribeMethod describe; /* describe the contents of the pool */ PoolDebugMixinMethod debugMixin; /* find the debug mixin, if any */ + PoolSizeMethod totalSize; /* total memory allocated from arena */ + PoolSizeMethod freeSize; /* free memory (unused by client program) */ Bool labelled; /* whether it has been EventLabelled */ Sig sig; /* .class.end-sig */ } PoolClassStruct; @@ -107,10 +113,6 @@ typedef struct mps_pool_s { /* generic structure */ Align alignment; /* alignment for units */ Format format; /* format only if class->attr&AttrFMT */ PoolFixMethod fix; /* fix method */ - double fillMutatorSize; /* bytes filled, mutator buffers */ - double emptyMutatorSize; /* bytes emptied, mutator buffers */ - double fillInternalSize; /* bytes filled, internal buffers */ - double emptyInternalSize; /* bytes emptied, internal buffers */ } PoolStruct; @@ -131,10 +133,12 @@ typedef struct MFSStruct { /* MFS outer structure */ PoolStruct poolStruct; /* generic structure */ Size unroundedUnitSize; /* the unit size requested */ Size extendBy; /* arena alloc size rounded using unitSize */ + Bool extendSelf; /* whether to allocate tracts */ Size unitSize; /* rounded for management purposes */ - Word unitsPerExtent; /* number of units per arena alloc */ struct MFSHeaderStruct *freeList; /* head of the free list */ - Tract tractList; /* the first tract */ + Size total; /* total size allocated from arena */ + Size free; /* free space in pool */ + Addr extentList; /* linked list of extents */ Sig sig; /* */ } MFSStruct; @@ -156,7 +160,7 @@ typedef struct MVStruct { /* MV pool outer structure */ Size extendBy; /* segment size to extend pool by */ Size avgSize; /* client estimate of allocation size */ Size maxSize; /* client estimate of maximum size */ - Size space; /* total free space in pool */ + Size free; /* free space in pool */ Size lost; /* */ RingStruct spans; /* span chain */ Sig sig; /* */ @@ -272,9 +276,10 @@ typedef struct SegStruct { /* segment structure */ Tract firstTract; /* first tract of segment */ RingStruct poolRing; /* link in list of segs in pool */ Addr limit; /* limit of segment */ + TreeStruct treeStruct; /* tree of all segments by address */ unsigned depth : ShieldDepthWIDTH; /* see */ - AccessSet pm : AccessSetWIDTH; /* protection mode, */ - AccessSet sm : AccessSetWIDTH; /* shield mode, */ + AccessSet pm : AccessLIMIT; /* protection mode, */ + AccessSet sm : AccessLIMIT; /* shield mode, */ TraceSet grey : TraceLIMIT; /* traces for which seg is grey */ TraceSet white : TraceLIMIT; /* traces for which seg is white */ TraceSet nailed : TraceLIMIT; /* traces for which seg has nailed objects */ @@ -298,22 +303,21 @@ typedef struct GCSegStruct { /* GC segment structure */ } GCSegStruct; -/* SegPrefStruct -- segment preference structure +/* LocusPrefStruct -- locus preference structure * - * .seg-pref: arena memory users (pool class code) need a way of - * expressing preferences about the segments they allocate. - * - * .seg-pref.misleading: The name is historical and misleading. SegPref - * objects need have nothing to do with segments. @@@@ */ + * .locus-pref: arena memory users (pool class code) need a way of + * expressing preferences about the locus of the segments they + * allocate. See . + */ -#define SegPrefSig ((Sig)0x5195E9B6) /* SIGnature SEG PRef */ +#define LocusPrefSig ((Sig)0x51970CB6) /* SIGnature LOCus PRef */ -typedef struct SegPrefStruct { /* segment placement preferences */ +typedef struct LocusPrefStruct { /* locus placement preferences */ Sig sig; /* */ Bool high; /* high or low */ ZoneSet zones; /* preferred zones */ - Bool isCollected; /* whether segment will be collected */ -} SegPrefStruct; + ZoneSet avoid; /* zones to avoid */ +} LocusPrefStruct; /* BufferClassStruct -- buffer class structure @@ -447,6 +451,7 @@ typedef struct ScanStateStruct { PoolFixMethod fix; /* third stage fix function */ void *fixClosure; /* closure data for fix */ TraceSet traces; /* traces to scan for */ + Table whiteTable; Rank rank; /* reference rank of scanning */ Bool wasMarked; /* design.mps.fix.protocol.was-ready */ RefSet fixedSummary; /* accumulated summary of fixed references */ @@ -474,6 +479,7 @@ typedef struct TraceStruct { Arena arena; /* owning arena */ int why; /* why the trace began */ ZoneSet white; /* zones in the white set */ + Table whiteTable; /* table of white segments */ ZoneSet mayMove; /* zones containing possibly moving objs */ TraceState state; /* current state of trace */ Rank band; /* current band */ @@ -513,18 +519,6 @@ typedef struct TraceStruct { } TraceStruct; -/* ChunkCacheEntryStruct -- cache entry in the chunk cache */ - -#define ChunkCacheEntrySig ((Sig)0x519C80CE) /* SIGnature CHUnk Cache Entry */ - -typedef struct ChunkCacheEntryStruct { - Sig sig; - Chunk chunk; - Addr base; - Addr limit; -} ChunkCacheEntryStruct; - - /* ArenaClassStruct -- generic arena class interface */ #define ArenaClassSig ((Sig)0x519A6C1A) /* SIGnature ARena CLAss */ @@ -537,15 +531,15 @@ typedef struct mps_arena_class_s { ArenaVarargsMethod varargs; ArenaInitMethod init; ArenaFinishMethod finish; - ArenaReservedMethod reserved; ArenaPurgeSpareMethod purgeSpare; ArenaExtendMethod extend; - ArenaAllocMethod alloc; + ArenaGrowMethod grow; ArenaFreeMethod free; ArenaChunkInitMethod chunkInit; ArenaChunkFinishMethod chunkFinish; ArenaCompactMethod compact; ArenaDescribeMethod describe; + ArenaPagesMarkAllocatedMethod pagesMarkAllocated; Sig sig; } ArenaClassStruct; @@ -601,6 +595,112 @@ typedef struct GlobalsStruct { } GlobalsStruct; +/* LandClassStruct -- land class structure + * + * See . + */ + +#define LandClassSig ((Sig)0x5197A4DC) /* SIGnature LAND Class */ + +typedef struct LandClassStruct { + ProtocolClassStruct protocol; + const char *name; /* class name string */ + size_t size; /* size of outer structure */ + LandSizeMethod sizeMethod; /* total size of ranges in land */ + LandInitMethod init; /* initialize the land */ + LandFinishMethod finish; /* finish the land */ + LandInsertMethod insert; /* insert a range into the land */ + LandDeleteMethod delete; /* delete a range from the land */ + LandIterateMethod iterate; /* iterate over ranges in the land */ + LandIterateAndDeleteMethod iterateAndDelete; /* iterate and maybe delete */ + LandFindMethod findFirst; /* find first range of given size */ + LandFindMethod findLast; /* find last range of given size */ + LandFindMethod findLargest; /* find largest range */ + LandFindInZonesMethod findInZones; /* find first range of given size in zone set */ + LandDescribeMethod describe; /* describe the land */ + Sig sig; /* .class.end-sig */ +} LandClassStruct; + + +/* LandStruct -- generic land structure + * + * See , + */ + +#define LandSig ((Sig)0x5197A4D9) /* SIGnature LAND */ + +typedef struct LandStruct { + Sig sig; /* */ + LandClass class; /* land class structure */ + Arena arena; /* owning arena */ + Align alignment; /* alignment of addresses */ + Bool inLand; /* prevent reentrance */ +} LandStruct; + + +/* CBSStruct -- coalescing block structure + * + * CBS is a Land implementation that maintains a collection of + * disjoint ranges in a splay tree. + * + * See . + */ + +#define CBSSig ((Sig)0x519CB599) /* SIGnature CBS */ + +typedef struct CBSStruct { + LandStruct landStruct; /* superclass fields come first */ + SplayTreeStruct splayTreeStruct; + STATISTIC_DECL(Count treeSize); + Pool blockPool; /* pool that manages blocks */ + Size blockStructSize; /* size of block structure */ + Bool ownPool; /* did we create blockPool? */ + Size size; /* total size of ranges in CBS */ + /* meters for sizes of search structures at each op */ + METER_DECL(treeSearch); + Sig sig; /* .class.end-sig */ +} CBSStruct; + + +/* FailoverStruct -- fail over from one land to another + * + * Failover is a Land implementation that combines two other Lands, + * using primary until it fails, and then using secondary. + * + * See . + */ + +#define FailoverSig ((Sig)0x519FA170) /* SIGnature FAILOver */ + +typedef struct FailoverStruct { + LandStruct landStruct; /* superclass fields come first */ + Land primary; /* use this land normally */ + Land secondary; /* but use this one if primary fails */ + Sig sig; /* .class.end-sig */ +} FailoverStruct; + + +/* FreelistStruct -- address-ordered freelist + * + * Freelist is a subclass of Land that maintains a collection of + * disjoint ranges in an address-ordered freelist. + * + * See . + */ + +#define FreelistSig ((Sig)0x519F6331) /* SIGnature FREEL */ + +typedef union FreelistBlockUnion *FreelistBlock; + +typedef struct FreelistStruct { + LandStruct landStruct; /* superclass fields come first */ + FreelistBlock list; /* first block in list or NULL if empty */ + Count listSize; /* number of blocks in list */ + Size size; /* total size of ranges in list */ + Sig sig; /* .class.end-sig */ +} FreelistStruct; + + /* ArenaStruct -- generic arena * * See . */ @@ -618,22 +718,31 @@ typedef struct mps_arena_s { ReservoirStruct reservoirStruct; /* */ - Size committed; /* amount of committed RAM */ + Size reserved; /* total reserved address space */ + Size committed; /* total committed memory */ Size commitLimit; /* client-configurable commit limit */ Size spareCommitted; /* Amount of memory in hysteresis fund */ Size spareCommitLimit; /* Limit on spareCommitted */ Shift zoneShift; /* see also */ - Align alignment; /* minimum alignment of tracts */ + Size grainSize; /* */ Tract lastTract; /* most recently allocated tract */ Addr lastTractBase; /* base address of lastTract */ Chunk primary; /* the primary chunk */ - RingStruct chunkRing; /* all the chunks */ + RingStruct chunkRing; /* all the chunks, in a ring for iteration */ + Tree chunkTree; /* all the chunks, in a tree for fast lookup */ Serial chunkSerial; /* next chunk number */ - ChunkCacheEntryStruct chunkCache; /* just one entry */ + + Bool hasFreeLand; /* Is freeLand available? */ + MFSStruct freeCBSBlockPoolStruct; + CBSStruct freeLandStruct; + ZoneSet freeZones; /* zones not yet allocated */ + Bool zoned; /* use zoned allocation? */ + + SplayTreeStruct segSplayTreeStruct; /* tree of all segments */ /* locus fields () */ GenDescStruct topGen; /* generation descriptor for dynamic gen */ @@ -653,6 +762,7 @@ typedef struct mps_arena_s { /* thread fields () */ RingStruct threadRing; /* ring of attached threads */ + RingStruct deadRing; /* ring of dead threads */ Serial threadSerial; /* serial of next thread */ /* shield fields () */ @@ -706,7 +816,7 @@ typedef struct AllocPatternStruct { /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpmtypes.h b/code/mpmtypes.h index e7d01fc2e9..1e8aa542c3 100644 --- a/code/mpmtypes.h +++ b/code/mpmtypes.h @@ -1,7 +1,7 @@ /* mpmtypes.h: MEMORY POOL MANAGER TYPES * * $Id$ - * Copyright (c) 2001-2002, 2006 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2001 Global Graphics Software. * * .design: @@ -33,6 +33,7 @@ typedef void (*Fun)(void); /* */ typedef MPS_T_WORD Word; /* */ typedef unsigned char Byte; /* */ typedef struct AddrStruct *Addr; /* */ +typedef const struct AddrStruct *ReadonlyAddr; /* */ typedef Word Size; /* */ typedef Word Count; /* */ typedef Word Index; /* */ @@ -41,7 +42,7 @@ typedef unsigned Shift; /* */ typedef unsigned Serial; /* */ typedef Addr Ref; /* */ typedef void *Pointer; /* */ -typedef Word Clock; /* processor time */ +typedef Word Clock; /* */ typedef MPS_T_ULONGEST ULongest; /* */ typedef mps_arg_s ArgStruct; @@ -60,7 +61,6 @@ typedef unsigned TraceSet; /* */ typedef unsigned TraceState; /* */ typedef unsigned AccessSet; /* */ typedef unsigned Attr; /* */ -typedef unsigned FormatVariety; typedef int RootVar; /* */ typedef Word *BT; /* */ @@ -75,9 +75,8 @@ typedef unsigned FrameState; /* */ typedef struct mps_fmt_s *Format; /* design.mps.format */ typedef struct LockStruct *Lock; /* * */ typedef struct mps_pool_s *Pool; /* */ -typedef struct mps_class_s *PoolClass; /* */ +typedef struct mps_pool_class_s *PoolClass; /* */ typedef PoolClass AbstractPoolClass; /* */ -typedef PoolClass AbstractAllocFreePoolClass; /* */ typedef PoolClass AbstractBufferPoolClass; /* */ typedef PoolClass AbstractSegBufPoolClass; /* */ typedef PoolClass AbstractScanPoolClass; /* */ @@ -93,8 +92,8 @@ typedef struct SegStruct *Seg; /* */ typedef struct GCSegStruct *GCSeg; /* */ typedef struct SegClassStruct *SegClass; /* */ typedef SegClass GCSegClass; /* */ -typedef struct SegPrefStruct *SegPref; /* design.mps.pref, */ -typedef int SegPrefKind; /* design.mps.pref, */ +typedef struct LocusPrefStruct *LocusPref; /* , */ +typedef int LocusPrefKind; /* , */ typedef struct mps_arena_class_s *ArenaClass; /* */ typedef ArenaClass AbstractArenaClass; /* */ typedef struct mps_arena_s *Arena; /* */ @@ -109,7 +108,10 @@ typedef struct AllocPatternStruct *AllocPattern; typedef struct AllocFrameStruct *AllocFrame; /* */ typedef struct ReservoirStruct *Reservoir; /* */ typedef struct StackContextStruct *StackContext; -typedef unsigned FindDelete; /* */ +typedef struct RangeStruct *Range; /* */ +typedef struct LandStruct *Land; /* */ +typedef struct LandClassStruct *LandClass; /* */ +typedef unsigned FindDelete; /* */ /* Arena*Method -- see */ @@ -118,16 +120,18 @@ typedef void (*ArenaVarargsMethod)(ArgStruct args[], va_list varargs); typedef Res (*ArenaInitMethod)(Arena *arenaReturn, ArenaClass class, ArgList args); typedef void (*ArenaFinishMethod)(Arena arena); -typedef Size (*ArenaReservedMethod)(Arena arena); typedef Size (*ArenaPurgeSpareMethod)(Arena arena, Size size); typedef Res (*ArenaExtendMethod)(Arena arena, Addr base, Size size); -typedef Res (*ArenaAllocMethod)(Addr *baseReturn, Tract *baseTractReturn, - SegPref pref, Size size, Pool pool); +typedef Res (*ArenaGrowMethod)(Arena arena, LocusPref pref, Size size); typedef void (*ArenaFreeMethod)(Addr base, Size size, Pool pool); typedef Res (*ArenaChunkInitMethod)(Chunk chunk, BootBlock boot); typedef void (*ArenaChunkFinishMethod)(Chunk chunk); typedef void (*ArenaCompactMethod)(Arena arena, Trace trace); -typedef Res (*ArenaDescribeMethod)(Arena arena, mps_lib_FILE *stream); +typedef Res (*ArenaDescribeMethod)(Arena arena, mps_lib_FILE *stream, Count depth); +typedef Res (*ArenaPagesMarkAllocatedMethod)(Arena arena, Chunk chunk, + Index baseIndex, Count pages, + Pool pool); + /* These are not generally exposed and public, but are part of a commercial extension to the MPS. */ @@ -143,11 +147,11 @@ typedef Res (*TraceFixMethod)(ScanState ss, Ref *refIO); /* Heap Walker */ /* This type is used by the PoolClass method Walk */ -typedef void (*FormattedObjectsStepMethod)(Addr obj, Format fmt, Pool pool, +typedef void (*FormattedObjectsVisitor)(Addr obj, Format fmt, Pool pool, void *v, size_t s); /* This type is used by the PoolClass method Walk */ -typedef void (*FreeBlockStepMethod)(Addr base, Addr limit, Pool pool, void *p); +typedef void (*FreeBlockVisitor)(Addr base, Addr limit, Pool pool, void *p); /* Seg*Method -- see */ @@ -163,7 +167,7 @@ typedef void (*SegSetRankSummaryMethod)(Seg seg, RankSet rankSet, typedef void (*SegSetSummaryMethod)(Seg seg, RefSet summary); typedef Buffer (*SegBufferMethod)(Seg seg); typedef void (*SegSetBufferMethod)(Seg seg, Buffer buffer); -typedef Res (*SegDescribeMethod)(Seg seg, mps_lib_FILE *stream); +typedef Res (*SegDescribeMethod)(Seg seg, mps_lib_FILE *stream, Count depth); typedef Res (*SegMergeMethod)(Seg seg, Seg segHi, Addr base, Addr mid, Addr limit, Bool withReservoirPermit); @@ -183,7 +187,7 @@ typedef Seg (*BufferSegMethod)(Buffer buffer); typedef RankSet (*BufferRankSetMethod)(Buffer buffer); typedef void (*BufferSetRankSetMethod)(Buffer buffer, RankSet rankSet); typedef void (*BufferReassignSegMethod)(Buffer buffer, Seg seg); -typedef Res (*BufferDescribeMethod)(Buffer buffer, mps_lib_FILE *stream); +typedef Res (*BufferDescribeMethod)(Buffer buffer, mps_lib_FILE *stream, Count depth); /* Pool*Method -- see */ @@ -213,7 +217,7 @@ typedef Res (*PoolFixMethod)(Pool pool, ScanState ss, Seg seg, Ref *refIO); typedef Res (*PoolFixEmergencyMethod)(Pool pool, ScanState ss, Seg seg, Ref *refIO); -typedef void (*PoolReclaimMethod)(Pool pool, Trace trace, Seg seg); +typedef Bool (*PoolReclaimMethod)(Pool pool, Trace trace, Seg seg); typedef void (*PoolTraceEndMethod)(Pool pool, Trace trace); typedef void (*PoolRampBeginMethod)(Pool pool, Buffer buf, Bool collectAll); typedef void (*PoolRampEndMethod)(Pool pool, Buffer buf); @@ -225,13 +229,13 @@ typedef void (*PoolFramePopPendingMethod)(Pool pool, Buffer buf, AllocFrame frame); typedef Res (*PoolAddrObjectMethod)(Addr *pReturn, Pool pool, Seg seg, Addr addr); -typedef void (*PoolWalkMethod)(Pool pool, Seg seg, - FormattedObjectsStepMethod f, +typedef void (*PoolWalkMethod)(Pool pool, Seg seg, FormattedObjectsVisitor f, void *v, size_t s); -typedef void (*PoolFreeWalkMethod)(Pool pool, FreeBlockStepMethod f, void *p); +typedef void (*PoolFreeWalkMethod)(Pool pool, FreeBlockVisitor f, void *p); typedef BufferClass (*PoolBufferClassMethod)(void); -typedef Res (*PoolDescribeMethod)(Pool pool, mps_lib_FILE *stream); +typedef Res (*PoolDescribeMethod)(Pool pool, mps_lib_FILE *stream, Count depth); typedef PoolDebugMixin (*PoolDebugMixinMethod)(Pool pool); +typedef Size (*PoolSizeMethod)(Pool pool); /* Messages @@ -259,6 +263,22 @@ typedef struct TraceStartMessageStruct *TraceStartMessage; typedef struct TraceMessageStruct *TraceMessage; /* trace end */ +/* Land*Method -- see */ + +typedef Res (*LandInitMethod)(Land land, ArgList args); +typedef void (*LandFinishMethod)(Land land); +typedef Size (*LandSizeMethod)(Land land); +typedef Res (*LandInsertMethod)(Range rangeReturn, Land land, Range range); +typedef Res (*LandDeleteMethod)(Range rangeReturn, Land land, Range range); +typedef Bool (*LandVisitor)(Land land, Range range, void *closureP, Size closureS); +typedef Bool (*LandDeleteVisitor)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS); +typedef Bool (*LandIterateMethod)(Land land, LandVisitor visitor, void *closureP, Size closureS); +typedef Bool (*LandIterateAndDeleteMethod)(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS); +typedef Bool (*LandFindMethod)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete); +typedef Res (*LandFindInZonesMethod)(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high); +typedef Res (*LandDescribeMethod)(Land land, mps_lib_FILE *stream, Count depth); + + /* CONSTANTS */ @@ -269,7 +289,7 @@ typedef struct TraceMessageStruct *TraceMessage; /* trace end */ #define AccessSetEMPTY ((AccessSet)0) /* */ #define AccessREAD ((AccessSet)(1<<0)) #define AccessWRITE ((AccessSet)(1<<1)) -#define AccessSetWIDTH (2) +#define AccessLIMIT (2) #define RefSetEMPTY BS_EMPTY(RefSet) #define RefSetUNIV BS_UNIV(RefSet) #define ZoneSetEMPTY BS_EMPTY(ZoneSet) @@ -279,41 +299,17 @@ typedef struct TraceMessageStruct *TraceMessage; /* trace end */ #define RankSetEMPTY BS_EMPTY(RankSet) #define RankSetUNIV ((RankSet)((1u << RankLIMIT) - 1)) #define AttrFMT ((Attr)(1<<0)) /* */ -#define AttrSCAN ((Attr)(1<<1)) -#define AttrPM_NO_READ ((Attr)(1<<2)) -#define AttrPM_NO_WRITE ((Attr)(1<<3)) -#define AttrALLOC ((Attr)(1<<4)) -#define AttrFREE ((Attr)(1<<5)) -#define AttrBUF ((Attr)(1<<6)) -#define AttrBUF_RESERVE ((Attr)(1<<7)) -#define AttrBUF_ALLOC ((Attr)(1<<8)) -#define AttrGC ((Attr)(1<<9)) -#define AttrINCR_RB ((Attr)(1<<10)) -#define AttrINCR_WB ((Attr)(1<<11)) -#define AttrMOVINGGC ((Attr)(1<<12)) -#define AttrMASK (AttrFMT | AttrSCAN | AttrPM_NO_READ | \ - AttrPM_NO_WRITE | AttrALLOC | AttrFREE | \ - AttrBUF | AttrBUF_RESERVE | AttrBUF_ALLOC | \ - AttrGC | AttrINCR_RB | AttrINCR_WB | AttrMOVINGGC) - - -/* Format varieties */ -enum { - FormatVarietyA = 1, - FormatVarietyB, - FormatVarietyAutoHeader, - FormatVarietyFixed, - FormatVarietyLIMIT -}; +#define AttrGC ((Attr)(1<<1)) +#define AttrMOVINGGC ((Attr)(1<<2)) +#define AttrMASK (AttrFMT | AttrGC | AttrMOVINGGC) -/* Segment preferences */ +/* Locus preferences */ enum { - SegPrefHigh = 1, - SegPrefLow, - SegPrefZoneSet, - SegPrefCollected, - SegPrefLIMIT + LocusPrefHIGH = 1, + LocusPrefLOW, + LocusPrefZONESET, + LocusPrefLIMIT }; @@ -337,6 +333,7 @@ enum { /* This is checked by . */ enum { + RankMIN = 0, RankAMBIG = 0, RankEXACT = 1, RankFINAL = 2, @@ -416,7 +413,7 @@ enum { }; -/* FindDelete operations -- see and */ +/* FindDelete operations -- see */ enum { FindDeleteNONE = 1, /* don't delete after finding */ @@ -463,7 +460,7 @@ typedef double WriteFD; /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002, 2006 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mps.c b/code/mps.c index 329c67de9a..2f2e4f248e 100644 --- a/code/mps.c +++ b/code/mps.c @@ -64,6 +64,7 @@ #include "dbgpooli.c" #include "boot.c" #include "meter.c" +#include "tree.c" #include "splay.c" #include "cbs.c" #include "ss.c" @@ -74,6 +75,11 @@ #include "range.c" #include "freelist.c" #include "sa.c" +#include "nailboard.c" +#include "land.c" +#include "failover.c" +#include "vm.c" +#include "policy.c" /* Additional pool classes */ @@ -83,20 +89,31 @@ #include "poolawl.c" #include "poollo.c" #include "poolsnc.c" -#include "pooln.c" #include "poolmv2.c" #include "poolmvff.c" /* ANSI Plinth */ -#if !defined(PLINTH_NONE) /* see CONFIG_PLINTH_NONE in config.h */ +#if defined(PLINTH) /* see CONFIG_PLINTH_NONE in config.h */ #include "mpsliban.c" #include "mpsioan.c" #endif +/* Generic ("ANSI") platform */ + +#if defined(PLATFORM_ANSI) + +#include "lockan.c" /* generic locks */ +#include "than.c" /* generic threads manager */ +#include "vman.c" /* malloc-based pseudo memory mapping */ +#include "protan.c" /* generic memory protection */ +#include "prmcan.c" /* generic protection mutator context */ +#include "span.c" /* generic stack probe */ +#include "ssan.c" /* generic stack scanner */ + /* Mac OS X on 32-bit Intel built with Clang or GCC */ -#if defined(MPS_PF_XCI3LL) || defined(MPS_PF_XCI3GC) +#elif defined(MPS_PF_XCI3LL) || defined(MPS_PF_XCI3GC) #include "lockix.c" /* Posix locks */ #include "thxc.c" /* OS X Mach threading */ @@ -193,12 +210,11 @@ #include "protw3.c" /* Windows protection */ #include "proti3.c" /* 32-bit Intel mutator context decoding */ #include "prmci3w3.c" /* Windows on 32-bit Intel mutator context */ -#include "ssw3i3mv.c" /* Windows on 32-bit stack scan for Microsoft C */ -#include "spw3i3mv.c" /* Windows on 32-bit stack probe for Microsoft C */ +#include "ssw3i3mv.c" /* Windows on 32-bit Intel stack scan for Microsoft C */ +#include "spw3i3.c" /* Windows on 32-bit Intel stack probe */ #include "mpsiw3.c" /* Windows interface layer extras */ /* Windows on 64-bit Intel with Microsoft Visual Studio */ -/* ssw3i6.asm is also required, but can't be included here */ #elif defined(MPS_PF_W3I6MV) @@ -209,8 +225,38 @@ #include "protw3.c" /* Windows protection */ #include "proti6.c" /* 64-bit Intel mutator context decoding */ #include "prmci6w3.c" /* Windows on 64-bit Intel mutator context */ -#include "ssw3i6mv.c" /* Windows on 64-bit stack scan for Microsoft C */ -#include "spw3i6mv.c" /* Windows on 64-bit stack probe for Microsoft C */ +#include "ssw3i6mv.c" /* Windows on 64-bit Intel stack scan for Microsoft C */ +#include "spw3i6.c" /* Windows on 64-bit Intel stack probe */ +#include "mpsiw3.c" /* Windows interface layer extras */ + +/* Windows on 32-bit Intel with Pelles C */ + +#elif defined(MPS_PF_W3I3PC) + +#include "lockw3.c" /* Windows locks */ +#include "thw3.c" /* Windows threading */ +#include "thw3i3.c" /* Windows on 32-bit Intel thread stack scan */ +#include "vmw3.c" /* Windows virtual memory */ +#include "protw3.c" /* Windows protection */ +#include "proti3.c" /* 32-bit Intel mutator context decoding */ +#include "prmci3w3.c" /* Windows on 32-bit Intel mutator context */ +#include "ssw3i3pc.c" /* Windows on 32-bit stack scan for Pelles C */ +#include "spw3i3.c" /* 32-bit Intel stack probe */ +#include "mpsiw3.c" /* Windows interface layer extras */ + +/* Windows on 64-bit Intel with Pelles C */ + +#elif defined(MPS_PF_W3I6PC) + +#include "lockw3.c" /* Windows locks */ +#include "thw3.c" /* Windows threading */ +#include "thw3i6.c" /* Windows on 64-bit Intel thread stack scan */ +#include "vmw3.c" /* Windows virtual memory */ +#include "protw3.c" /* Windows protection */ +#include "proti6.c" /* 64-bit Intel mutator context decoding */ +#include "prmci6w3.c" /* Windows on 64-bit Intel mutator context */ +#include "ssw3i6pc.c" /* Windows on 64-bit stack scan for Pelles C */ +#include "spw3i6.c" /* 64-bit Intel stack probe */ #include "mpsiw3.c" /* Windows interface layer extras */ #else diff --git a/code/mps.h b/code/mps.h index a767da3b29..d9e750417e 100644 --- a/code/mps.h +++ b/code/mps.h @@ -1,7 +1,7 @@ /* mps.h: RAVENBROOK MEMORY POOL SYSTEM C INTERFACE * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * THIS HEADER IS NOT DOCUMENTATION. @@ -13,7 +13,7 @@ * `MPS_` or `_mps_` and may use any identifiers with these prefixes in * future. * - * .naming.internal: Any idenfitier beginning with underscore is for + * .naming.internal: Any identifier beginning with an underscore is for * internal use within the interface and may change or be withdrawn without * warning. * @@ -51,7 +51,8 @@ typedef struct mps_pool_s *mps_pool_t; /* pool */ typedef struct mps_chain_s *mps_chain_t; /* chain */ typedef struct mps_fmt_s *mps_fmt_t; /* object format */ typedef struct mps_root_s *mps_root_t; /* root */ -typedef struct mps_class_s *mps_class_t; /* pool class */ +typedef struct mps_pool_class_s *mps_pool_class_t; /* pool class */ +typedef mps_pool_class_t mps_class_t; /* deprecated alias */ typedef struct mps_thr_s *mps_thr_t; /* thread registration */ typedef struct mps_ap_s *mps_ap_t; /* allocation point */ typedef struct mps_ld_s *mps_ld_t; /* location dependency */ @@ -146,94 +147,115 @@ typedef struct mps_arg_s { mps_fmt_isfwd_t fmt_isfwd; mps_fmt_pad_t fmt_pad; mps_fmt_class_t fmt_class; + mps_pool_t pool; } val; } mps_arg_s; -extern const struct mps_key_s _mps_key_args_end; -#define MPS_KEY_ARGS_END (&_mps_key_args_end) +extern const struct mps_key_s _mps_key_ARGS_END; +#define MPS_KEY_ARGS_END (&_mps_key_ARGS_END) extern mps_arg_s mps_args_none[]; -extern const struct mps_key_s _mps_key_arena_size; -#define MPS_KEY_ARENA_SIZE (&_mps_key_arena_size) +extern const struct mps_key_s _mps_key_ARENA_GRAIN_SIZE; +#define MPS_KEY_ARENA_GRAIN_SIZE (&_mps_key_ARENA_GRAIN_SIZE) +#define MPS_KEY_ARENA_GRAIN_SIZE_FIELD size +extern const struct mps_key_s _mps_key_ARENA_SIZE; +#define MPS_KEY_ARENA_SIZE (&_mps_key_ARENA_SIZE) #define MPS_KEY_ARENA_SIZE_FIELD size -extern const struct mps_key_s _mps_key_format; -#define MPS_KEY_FORMAT (&_mps_key_format) +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_FORMAT; +#define MPS_KEY_FORMAT (&_mps_key_FORMAT) #define MPS_KEY_FORMAT_FIELD format -extern const struct mps_key_s _mps_key_chain; -#define MPS_KEY_CHAIN (&_mps_key_chain) +extern const struct mps_key_s _mps_key_CHAIN; +#define MPS_KEY_CHAIN (&_mps_key_CHAIN) #define MPS_KEY_CHAIN_FIELD chain -extern const struct mps_key_s _mps_key_gen; -#define MPS_KEY_GEN (&_mps_key_gen) +extern const struct mps_key_s _mps_key_GEN; +#define MPS_KEY_GEN (&_mps_key_GEN) #define MPS_KEY_GEN_FIELD u -extern const struct mps_key_s _mps_key_rank; -#define MPS_KEY_RANK (&_mps_key_rank) +extern const struct mps_key_s _mps_key_RANK; +#define MPS_KEY_RANK (&_mps_key_RANK) #define MPS_KEY_RANK_FIELD rank - -extern const struct mps_key_s _mps_key_extend_by; -#define MPS_KEY_EXTEND_BY (&_mps_key_extend_by) +extern const struct mps_key_s _mps_key_COMMIT_LIMIT; +#define MPS_KEY_COMMIT_LIMIT (&_mps_key_COMMIT_LIMIT) +#define MPS_KEY_COMMIT_LIMIT_FIELD size +extern const struct mps_key_s _mps_key_SPARE_COMMIT_LIMIT; +#define MPS_KEY_SPARE_COMMIT_LIMIT (&_mps_key_SPARE_COMMIT_LIMIT) +#define MPS_KEY_SPARE_COMMIT_LIMIT_FIELD size + +extern const struct mps_key_s _mps_key_EXTEND_BY; +#define MPS_KEY_EXTEND_BY (&_mps_key_EXTEND_BY) #define MPS_KEY_EXTEND_BY_FIELD size -extern const struct mps_key_s _mps_key_min_size; -#define MPS_KEY_MIN_SIZE (&_mps_key_min_size) +extern const struct mps_key_s _mps_key_LARGE_SIZE; +#define MPS_KEY_LARGE_SIZE (&_mps_key_LARGE_SIZE) +#define MPS_KEY_LARGE_SIZE_FIELD size +extern const struct mps_key_s _mps_key_MIN_SIZE; +#define MPS_KEY_MIN_SIZE (&_mps_key_MIN_SIZE) #define MPS_KEY_MIN_SIZE_FIELD size -extern const struct mps_key_s _mps_key_mean_size; -#define MPS_KEY_MEAN_SIZE (&_mps_key_mean_size) +extern const struct mps_key_s _mps_key_MEAN_SIZE; +#define MPS_KEY_MEAN_SIZE (&_mps_key_MEAN_SIZE) #define MPS_KEY_MEAN_SIZE_FIELD size -extern const struct mps_key_s _mps_key_max_size; -#define MPS_KEY_MAX_SIZE (&_mps_key_max_size) +extern const struct mps_key_s _mps_key_MAX_SIZE; +#define MPS_KEY_MAX_SIZE (&_mps_key_MAX_SIZE) #define MPS_KEY_MAX_SIZE_FIELD size -extern const struct mps_key_s _mps_key_align; -#define MPS_KEY_ALIGN (&_mps_key_align) +extern const struct mps_key_s _mps_key_ALIGN; +#define MPS_KEY_ALIGN (&_mps_key_ALIGN) #define MPS_KEY_ALIGN_FIELD align -extern const struct mps_key_s _mps_key_cbs_extend_by; -#define MPS_KEY_CBS_EXTEND_BY (&_mps_key_cbs_extend_by) -#define MPS_KEY_CBS_EXTEND_BY_FIELD size - -extern const struct mps_key_s _mps_key_vmw3_top_down; -#define MPS_KEY_VMW3_TOP_DOWN (&_mps_key_vmw3_top_down) +extern const struct mps_key_s _mps_key_SPARE; +#define MPS_KEY_SPARE (&_mps_key_SPARE) +#define MPS_KEY_SPARE_FIELD d +extern const struct mps_key_s _mps_key_INTERIOR; +#define MPS_KEY_INTERIOR (&_mps_key_INTERIOR) +#define MPS_KEY_INTERIOR_FIELD b + +extern const struct mps_key_s _mps_key_VMW3_TOP_DOWN; +#define MPS_KEY_VMW3_TOP_DOWN (&_mps_key_VMW3_TOP_DOWN) #define MPS_KEY_VMW3_TOP_DOWN_FIELD b -extern const struct mps_key_s _mps_key_fmt_align; -#define MPS_KEY_FMT_ALIGN (&_mps_key_fmt_align) +extern const struct mps_key_s _mps_key_FMT_ALIGN; +#define MPS_KEY_FMT_ALIGN (&_mps_key_FMT_ALIGN) #define MPS_KEY_FMT_ALIGN_FIELD align -extern const struct mps_key_s _mps_key_fmt_header_size; -#define MPS_KEY_FMT_HEADER_SIZE (&_mps_key_fmt_header_size) +extern const struct mps_key_s _mps_key_FMT_HEADER_SIZE; +#define MPS_KEY_FMT_HEADER_SIZE (&_mps_key_FMT_HEADER_SIZE) #define MPS_KEY_FMT_HEADER_SIZE_FIELD size -extern const struct mps_key_s _mps_key_fmt_scan; -#define MPS_KEY_FMT_SCAN (&_mps_key_fmt_scan) +extern const struct mps_key_s _mps_key_FMT_SCAN; +#define MPS_KEY_FMT_SCAN (&_mps_key_FMT_SCAN) #define MPS_KEY_FMT_SCAN_FIELD fmt_scan -extern const struct mps_key_s _mps_key_fmt_skip; -#define MPS_KEY_FMT_SKIP (&_mps_key_fmt_skip) +extern const struct mps_key_s _mps_key_FMT_SKIP; +#define MPS_KEY_FMT_SKIP (&_mps_key_FMT_SKIP) #define MPS_KEY_FMT_SKIP_FIELD fmt_skip -extern const struct mps_key_s _mps_key_fmt_fwd; -#define MPS_KEY_FMT_FWD (&_mps_key_fmt_fwd) +extern const struct mps_key_s _mps_key_FMT_FWD; +#define MPS_KEY_FMT_FWD (&_mps_key_FMT_FWD) #define MPS_KEY_FMT_FWD_FIELD fmt_fwd -extern const struct mps_key_s _mps_key_fmt_isfwd; -#define MPS_KEY_FMT_ISFWD (&_mps_key_fmt_isfwd) +extern const struct mps_key_s _mps_key_FMT_ISFWD; +#define MPS_KEY_FMT_ISFWD (&_mps_key_FMT_ISFWD) #define MPS_KEY_FMT_ISFWD_FIELD fmt_isfwd -extern const struct mps_key_s _mps_key_fmt_pad; -#define MPS_KEY_FMT_PAD (&_mps_key_fmt_pad) +extern const struct mps_key_s _mps_key_FMT_PAD; +#define MPS_KEY_FMT_PAD (&_mps_key_FMT_PAD) #define MPS_KEY_FMT_PAD_FIELD fmt_pad -extern const struct mps_key_s _mps_key_fmt_class; -#define MPS_KEY_FMT_CLASS (&_mps_key_fmt_class) +extern const struct mps_key_s _mps_key_FMT_CLASS; +#define MPS_KEY_FMT_CLASS (&_mps_key_FMT_CLASS) #define MPS_KEY_FMT_CLASS_FIELD fmt_class /* Maximum length of a keyword argument list. */ #define MPS_ARGS_MAX 32 +extern void _mps_args_set_key(mps_arg_s args[MPS_ARGS_MAX], unsigned i, + mps_key_t key); + #define MPS_ARGS_BEGIN(_var) \ MPS_BEGIN \ mps_arg_s _var[MPS_ARGS_MAX]; \ unsigned _var##_i = 0; \ - _var[_var##_i].key = MPS_KEY_ARGS_END; \ + _mps_args_set_key(_var, _var##_i, MPS_KEY_ARGS_END); \ MPS_BEGIN #define MPS_ARGS_ADD_FIELD(_var, _key, _field, _val) \ MPS_BEGIN \ - /* TODO: AVER(_var##_i + 1 < MPS_ARGS_MAX); */ \ - _var[_var##_i].key = (_key); \ + _mps_args_set_key(_var, _var##_i, _key); \ _var[_var##_i].val._field = (_val); \ ++_var##_i; \ - _var[_var##_i].key = MPS_KEY_ARGS_END; \ + _mps_args_set_key(_var, _var##_i, MPS_KEY_ARGS_END); \ MPS_END #define MPS_ARGS_ADD(_var, _key, _val) \ @@ -241,9 +263,8 @@ extern const struct mps_key_s _mps_key_fmt_class; #define MPS_ARGS_DONE(_var) \ MPS_BEGIN \ - /* TODO: AVER(_var##_i < MPS_ARGS_MAX); */ \ - _var[_var##_i].key = MPS_KEY_ARGS_END; \ - /* TODO: _var##_i = MPS_ARGS_MAX; */ \ + _mps_args_set_key(_var, _var##_i, MPS_KEY_ARGS_END); \ + _var##_i = MPS_ARGS_MAX; \ MPS_END #define MPS_ARGS_END(_var) \ @@ -279,12 +300,12 @@ extern mps_rank_t mps_rank_weak(void); /* Root Modes */ /* .rm: Keep in sync with */ -#define MPS_RM_CONST (((mps_rm_t)1<<0)) -#define MPS_RM_PROT (((mps_rm_t)1<<1)) +#define MPS_RM_CONST (((mps_rm_t)1<<0)) +#define MPS_RM_PROT (((mps_rm_t)1<<1)) +#define MPS_RM_PROT_INNER (((mps_rm_t)1<<1)) /* Allocation Point */ -/* .ap: Keep in sync with . */ typedef struct mps_ap_s { /* allocation point descriptor */ mps_addr_t init; /* limit of initialized memory */ @@ -318,9 +339,9 @@ typedef struct _mps_sac_s { /* .sacc: Keep in sync with . */ typedef struct mps_sac_class_s { - size_t _block_size; - size_t _cached_count; - unsigned _frequency; + size_t mps_block_size; + size_t mps_cached_count; + unsigned mps_frequency; } mps_sac_class_s; #define mps_sac_classes_s mps_sac_class_s @@ -458,12 +479,17 @@ extern void mps_fmt_destroy(mps_fmt_t); /* Pools */ extern mps_res_t mps_pool_create(mps_pool_t *, mps_arena_t, - mps_class_t, ...); + mps_pool_class_t, ...); extern mps_res_t mps_pool_create_v(mps_pool_t *, mps_arena_t, - mps_class_t, va_list); + mps_pool_class_t, va_list); extern mps_res_t mps_pool_create_k(mps_pool_t *, mps_arena_t, - mps_class_t, mps_arg_s []); + mps_pool_class_t, mps_arg_s []); extern void mps_pool_destroy(mps_pool_t); +extern size_t mps_pool_total_size(mps_pool_t); +extern size_t mps_pool_free_size(mps_pool_t); + + +/* Chains */ /* .gen-param: This structure must match . */ typedef struct mps_gen_param_s { @@ -475,6 +501,9 @@ extern mps_res_t mps_chain_create(mps_chain_t *, mps_arena_t, size_t, mps_gen_param_s *); extern void mps_chain_destroy(mps_chain_t); + +/* Manual Allocation */ + extern mps_res_t mps_alloc(mps_addr_t *, mps_pool_t, size_t); extern mps_res_t mps_alloc_v(mps_addr_t *, mps_pool_t, size_t, va_list); extern void mps_free(mps_pool_t, mps_addr_t, size_t); @@ -752,8 +781,8 @@ typedef struct mps_pool_debug_option_s { size_t free_size; } mps_pool_debug_option_s; -extern const struct mps_key_s _mps_key_pool_debug_options; -#define MPS_KEY_POOL_DEBUG_OPTIONS (&_mps_key_pool_debug_options) +extern const struct mps_key_s _mps_key_POOL_DEBUG_OPTIONS; +#define MPS_KEY_POOL_DEBUG_OPTIONS (&_mps_key_POOL_DEBUG_OPTIONS) #define MPS_KEY_POOL_DEBUG_OPTIONS_FIELD pool_debug_options extern void mps_pool_check_fenceposts(mps_pool_t); @@ -805,7 +834,7 @@ extern mps_res_t _mps_fix2(mps_ss_t, mps_addr_t *); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mps.xcodeproj/project.pbxproj b/code/mps.xcodeproj/project.pbxproj index 9524465561..418ae2ecc2 100644 --- a/code/mps.xcodeproj/project.pbxproj +++ b/code/mps.xcodeproj/project.pbxproj @@ -7,6 +7,54 @@ objects = { /* Begin PBXAggregateTarget section */ + 2215A9A9192A47BB00E9E2CE /* testci */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 2215A9AD192A47BB00E9E2CE /* Build configuration list for PBXAggregateTarget "testci" */; + buildPhases = ( + 2215A9AC192A47BB00E9E2CE /* ShellScript */, + ); + dependencies = ( + 2215A9AA192A47BB00E9E2CE /* PBXTargetDependency */, + ); + name = testci; + productName = testrun; + }; + 2215A9B1192A47C500E9E2CE /* testansi */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 2215A9B5192A47C500E9E2CE /* Build configuration list for PBXAggregateTarget "testansi" */; + buildPhases = ( + 2215A9B4192A47C500E9E2CE /* ShellScript */, + ); + dependencies = ( + 2215A9B2192A47C500E9E2CE /* PBXTargetDependency */, + ); + name = testansi; + productName = testrun; + }; + 2215A9B9192A47CE00E9E2CE /* testall */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 2215A9BD192A47CE00E9E2CE /* Build configuration list for PBXAggregateTarget "testall" */; + buildPhases = ( + 2215A9BC192A47CE00E9E2CE /* ShellScript */, + ); + dependencies = ( + 2215A9BA192A47CE00E9E2CE /* PBXTargetDependency */, + ); + name = testall; + productName = testrun; + }; + 2215A9C1192A47D500E9E2CE /* testpollnone */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 2215A9C5192A47D500E9E2CE /* Build configuration list for PBXAggregateTarget "testpollnone" */; + buildPhases = ( + 2215A9C4192A47D500E9E2CE /* ShellScript */, + ); + dependencies = ( + 2215A9C2192A47D500E9E2CE /* PBXTargetDependency */, + ); + name = testpollnone; + productName = testrun; + }; 22CDE8EF16E9E97D00366D0A /* testrun */ = { isa = PBXAggregateTarget; buildConfigurationList = 22CDE8F016E9E97E00366D0A /* Build configuration list for PBXAggregateTarget "testrun" */; @@ -27,6 +75,7 @@ dependencies = ( 3104AFF6156D37BC000A585A /* PBXTargetDependency */, 3114A644156E94FB001E0AA3 /* PBXTargetDependency */, + 22FACEF1188809B5000FDBC1 /* PBXTargetDependency */, 3104AFF8156D37BE000A585A /* PBXTargetDependency */, 3104B004156D37CD000A585A /* PBXTargetDependency */, 22FA177916E8DB0C0098B23F /* PBXTargetDependency */, @@ -42,17 +91,21 @@ 22B2BC3D18B643B300C33E63 /* PBXTargetDependency */, 2291A5E6175CB207001D4920 /* PBXTargetDependency */, 2291A5E8175CB20E001D4920 /* PBXTargetDependency */, - 3114A65B156E95B4001E0AA3 /* PBXTargetDependency */, 3114A5CC156E932C001E0AA3 /* PBXTargetDependency */, 3114A5EA156E93C4001E0AA3 /* PBXTargetDependency */, 224CC79D175E187C002FF81B /* PBXTargetDependency */, 22B2BC3F18B643B700C33E63 /* PBXTargetDependency */, + 3114A65B156E95B4001E0AA3 /* PBXTargetDependency */, + 2231BB6D18CA986B002D6322 /* PBXTargetDependency */, 31D60034156D3D5A00337B26 /* PBXTargetDependency */, + 2286E4C918F4389E004111E2 /* PBXTargetDependency */, + 2231BB6F18CA986D002D6322 /* PBXTargetDependency */, 3114A5A0156E915A001E0AA3 /* PBXTargetDependency */, 3114A6A7156E9739001E0AA3 /* PBXTargetDependency */, 3104AFFE156D37C6000A585A /* PBXTargetDependency */, 3104B000156D37C8000A585A /* PBXTargetDependency */, 3114A68D156E9686001E0AA3 /* PBXTargetDependency */, + 22C2ACB218BE4056006B3677 /* PBXTargetDependency */, 31D6004F156D3EF700337B26 /* PBXTargetDependency */, 3114A5B6156E92DC001E0AA3 /* PBXTargetDependency */, 3104B002156D37CB000A585A /* PBXTargetDependency */, @@ -74,10 +127,21 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 2215A9C9192A495F00E9E2CE /* pooln.c in Sources */ = {isa = PBXBuildFile; fileRef = 22FACEDE18880933000FDBC1 /* pooln.c */; }; + 2231BB5118CA97D8002D6322 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; + 2231BB5318CA97D8002D6322 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 2231BB5F18CA97DC002D6322 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; + 2231BB6118CA97DC002D6322 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 2231BB6A18CA984F002D6322 /* locusss.c in Sources */ = {isa = PBXBuildFile; fileRef = 2231BB6918CA983C002D6322 /* locusss.c */; }; + 2231BB6B18CA9861002D6322 /* locbwcss.c in Sources */ = {isa = PBXBuildFile; fileRef = 2231BB6818CA9834002D6322 /* locbwcss.c */; }; 224CC791175E1821002FF81B /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; 224CC793175E1821002FF81B /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; 224CC79F175E321C002FF81B /* mv2test.c in Sources */ = {isa = PBXBuildFile; fileRef = 3114A686156E9674001E0AA3 /* mv2test.c */; }; 224CC7A0175E322C002FF81B /* fotest.c in Sources */ = {isa = PBXBuildFile; fileRef = 224CC79E175E3202002FF81B /* fotest.c */; }; + 22561A9818F4265D00372C66 /* testthrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 22561A9718F4263300372C66 /* testthrix.c */; }; + 22561A9918F4266600372C66 /* testthrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 22561A9718F4263300372C66 /* testthrix.c */; }; + 22561A9A18F426BB00372C66 /* testthrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 22561A9718F4263300372C66 /* testthrix.c */; }; + 22561A9B18F426F300372C66 /* testthrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 22561A9718F4263300372C66 /* testthrix.c */; }; 2291A5B1175CAB2F001D4920 /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; }; 2291A5B2175CAB2F001D4920 /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; }; 2291A5B3175CAB2F001D4920 /* fmthe.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAE4156BE6D500753214 /* fmthe.c */; }; @@ -97,9 +161,16 @@ 2291A5DB175CB05F001D4920 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; 2291A5DD175CB05F001D4920 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; 2291A5E4175CB076001D4920 /* exposet0.c in Sources */ = {isa = PBXBuildFile; fileRef = 2291A5AA175CAA9B001D4920 /* exposet0.c */; }; - 2291A5ED175CB5E2001D4920 /* fbmtest.c in Sources */ = {isa = PBXBuildFile; fileRef = 2291A5E9175CB4EC001D4920 /* fbmtest.c */; }; + 2291A5ED175CB5E2001D4920 /* landtest.c in Sources */ = {isa = PBXBuildFile; fileRef = 2291A5E9175CB4EC001D4920 /* landtest.c */; }; 22B2BC2E18B6434F00C33E63 /* mps.c in Sources */ = {isa = PBXBuildFile; fileRef = 31A47BA3156C1E130039B1C2 /* mps.c */; }; 22B2BC3718B6437C00C33E63 /* scheme-advanced.c in Sources */ = {isa = PBXBuildFile; fileRef = 22B2BC2B18B6434000C33E63 /* scheme-advanced.c */; }; + 22C2ACA718BE400A006B3677 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; + 22C2ACA918BE400A006B3677 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 22C2ACB018BE4049006B3677 /* nailboardtest.c in Sources */ = {isa = PBXBuildFile; fileRef = 22C2ACA018BE3FEC006B3677 /* nailboardtest.c */; }; + 22F846B518F437B900982BA7 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; + 22F846B718F437B900982BA7 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 22F846BE18F437D700982BA7 /* lockut.c in Sources */ = {isa = PBXBuildFile; fileRef = 22F846AF18F4379C00982BA7 /* lockut.c */; }; + 22F846BF18F437E000982BA7 /* testthrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 22561A9718F4263300372C66 /* testthrix.c */; }; 22FA176916E8D6FC0098B23F /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; }; 22FA176A16E8D6FC0098B23F /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; }; 22FA176B16E8D6FC0098B23F /* fmthe.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAE4156BE6D500753214 /* fmthe.c */; }; @@ -107,6 +178,10 @@ 22FA176D16E8D6FC0098B23F /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; 22FA176F16E8D6FC0098B23F /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; 22FA177716E8D7A80098B23F /* amcssth.c in Sources */ = {isa = PBXBuildFile; fileRef = 22FA177616E8D7A80098B23F /* amcssth.c */; }; + 22FACEE518880983000FDBC1 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; + 22FACEE718880983000FDBC1 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 22FACEEE188809A3000FDBC1 /* fmtscheme.c in Sources */ = {isa = PBXBuildFile; fileRef = 22FACED6188807FF000FDBC1 /* fmtscheme.c */; }; + 22FACEEF188809A7000FDBC1 /* airtest.c in Sources */ = {isa = PBXBuildFile; fileRef = 22FACED1188807FF000FDBC1 /* airtest.c */; }; 2D07B97A1636FCCE00DB751B /* eventsql.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D07B96C1636FC7200DB751B /* eventsql.c */; }; 2D07B97C163705E400DB751B /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D07B97B163705E400DB751B /* libsqlite3.dylib */; }; 2D53F2E716515A63009A1829 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; @@ -210,7 +285,6 @@ 3124CAFB156BE82000753214 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; 3124CAFC156BE82900753214 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; 3150AE53156ABA2500A6E22A /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; - 318DA8D21892B13B0089718C /* getoptl.c in Sources */ = {isa = PBXBuildFile; fileRef = 318DA8D11892B13B0089718C /* getoptl.c */; }; 318DA8D31892B27E0089718C /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; 31A47BA4156C1E130039B1C2 /* mps.c in Sources */ = {isa = PBXBuildFile; fileRef = 31A47BA3156C1E130039B1C2 /* mps.c */; }; 31D60007156D3C6200337B26 /* segsmss.c in Sources */ = {isa = PBXBuildFile; fileRef = 31D60006156D3C5F00337B26 /* segsmss.c */; }; @@ -252,7 +326,6 @@ 31FCAE161769244F008C034C /* mps.c in Sources */ = {isa = PBXBuildFile; fileRef = 31A47BA3156C1E130039B1C2 /* mps.c */; }; 31FCAE19176924D4008C034C /* scheme.c in Sources */ = {isa = PBXBuildFile; fileRef = 31FCAE18176924D4008C034C /* scheme.c */; }; 6313D46918A400B200EB03EF /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; - 6313D46A18A400B200EB03EF /* getoptl.c in Sources */ = {isa = PBXBuildFile; fileRef = 318DA8D11892B13B0089718C /* getoptl.c */; }; 6313D47318A4028E00EB03EF /* djbench.c in Sources */ = {isa = PBXBuildFile; fileRef = 318DA8CE1892B1210089718C /* djbench.c */; }; 6313D47418A4029200EB03EF /* gcbench.c in Sources */ = {isa = PBXBuildFile; fileRef = 6313D46618A3FDC900EB03EF /* gcbench.c */; }; 6313D47518A40C6300EB03EF /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; }; @@ -261,6 +334,62 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 2215A9AB192A47BB00E9E2CE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3104AFF1156D37A0000A585A; + remoteInfo = all; + }; + 2215A9B3192A47C500E9E2CE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3104AFF1156D37A0000A585A; + remoteInfo = all; + }; + 2215A9BB192A47CE00E9E2CE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3104AFF1156D37A0000A585A; + remoteInfo = all; + }; + 2215A9C3192A47D500E9E2CE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3104AFF1156D37A0000A585A; + remoteInfo = all; + }; + 2231BB4E18CA97D8002D6322 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 31EEABFA156AAF9D00714D05; + remoteInfo = mps; + }; + 2231BB5C18CA97DC002D6322 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 31EEABFA156AAF9D00714D05; + remoteInfo = mps; + }; + 2231BB6C18CA986B002D6322 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2231BB4C18CA97D8002D6322; + remoteInfo = locbwcss; + }; + 2231BB6E18CA986D002D6322 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2231BB5A18CA97DC002D6322; + remoteInfo = locusss; + }; 224CC78E175E1821002FF81B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -282,6 +411,13 @@ remoteGlobalIDString = 2D604B9B16514B1A003AAF46; remoteInfo = mpseventtxt; }; + 2286E4C818F4389E004111E2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 22F846B018F437B900982BA7; + remoteInfo = lockut; + }; 2291A5AE175CAB2F001D4920 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -352,6 +488,20 @@ remoteGlobalIDString = 6313D46718A400B200EB03EF; remoteInfo = gcbench; }; + 22C2ACA418BE400A006B3677 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 31EEABFA156AAF9D00714D05; + remoteInfo = mps; + }; + 22C2ACB118BE4056006B3677 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 22C2ACA218BE400A006B3677; + remoteInfo = nailboardtest; + }; 22CDE92D16E9EB9300366D0A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -359,6 +509,13 @@ remoteGlobalIDString = 3104AFF1156D37A0000A585A; remoteInfo = all; }; + 22F846B218F437B900982BA7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 31EEABFA156AAF9D00714D05; + remoteInfo = mps; + }; 22FA176616E8D6FC0098B23F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -373,6 +530,20 @@ remoteGlobalIDString = 22FA176416E8D6FC0098B23F; remoteInfo = amcssth; }; + 22FACEE218880983000FDBC1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 31EEABFA156AAF9D00714D05; + remoteInfo = mps; + }; + 22FACEF0188809B5000FDBC1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 22FACEE018880983000FDBC1; + remoteInfo = airtest; + }; 2D07B9781636FCBD00DB751B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -623,7 +794,7 @@ containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; proxyType = 1; remoteGlobalIDString = 3114A64B156E9596001E0AA3; - remoteInfo = fbmtest; + remoteInfo = landtest; }; 3114A674156E9619001E0AA3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -810,6 +981,24 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 2231BB5418CA97D8002D6322 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 2231BB6218CA97DC002D6322 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 224CC794175E1821002FF81B /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -855,6 +1044,24 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 22C2ACAA18BE400A006B3677 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 22F846B818F437B900982BA7 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 22FA177016E8D6FC0098B23F /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -864,6 +1071,15 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 22FACEE818880983000FDBC1 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 2D07B96F1636FC9900DB751B /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1182,8 +1398,16 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2231BB5918CA97D8002D6322 /* locbwcss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locbwcss; sourceTree = BUILT_PRODUCTS_DIR; }; + 2231BB6718CA97DC002D6322 /* locusss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locusss; sourceTree = BUILT_PRODUCTS_DIR; }; + 2231BB6818CA9834002D6322 /* locbwcss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = locbwcss.c; sourceTree = ""; }; + 2231BB6918CA983C002D6322 /* locusss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = locusss.c; sourceTree = ""; }; + 223475CB194CA09500C69128 /* vm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vm.c; sourceTree = ""; }; + 223475CC194CA09500C69128 /* vm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = vm.h; sourceTree = ""; }; 224CC799175E1821002FF81B /* fotest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fotest; sourceTree = BUILT_PRODUCTS_DIR; }; 224CC79E175E3202002FF81B /* fotest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fotest.c; sourceTree = ""; }; + 22561A9618F4263300372C66 /* testthr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testthr.h; sourceTree = ""; }; + 22561A9718F4263300372C66 /* testthrix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testthrix.c; sourceTree = ""; }; 2291A5A8175CAA51001D4920 /* poolmv2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = poolmv2.h; sourceTree = ""; }; 2291A5A9175CAA9B001D4920 /* awlutth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = awlutth.c; sourceTree = ""; }; 2291A5AA175CAA9B001D4920 /* exposet0.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exposet0.c; sourceTree = ""; }; @@ -1191,7 +1415,7 @@ 2291A5BD175CAB2F001D4920 /* awlutth */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = awlutth; sourceTree = BUILT_PRODUCTS_DIR; }; 2291A5D1175CAFCA001D4920 /* expt825 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = expt825; sourceTree = BUILT_PRODUCTS_DIR; }; 2291A5E3175CB05F001D4920 /* exposet0 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = exposet0; sourceTree = BUILT_PRODUCTS_DIR; }; - 2291A5E9175CB4EC001D4920 /* fbmtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fbmtest.c; sourceTree = ""; }; + 2291A5E9175CB4EC001D4920 /* landtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = landtest.c; sourceTree = ""; }; 2291A5EA175CB503001D4920 /* abq.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = abq.h; sourceTree = ""; }; 2291A5EB175CB53E001D4920 /* range.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = range.c; sourceTree = ""; }; 2291A5EC175CB53E001D4920 /* range.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = range.h; sourceTree = ""; }; @@ -1200,8 +1424,33 @@ 2291A5F0175CB7A4001D4920 /* testlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testlib.h; sourceTree = ""; }; 22B2BC2B18B6434000C33E63 /* scheme-advanced.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "scheme-advanced.c"; path = "../example/scheme/scheme-advanced.c"; sourceTree = ""; }; 22B2BC3618B6434F00C33E63 /* scheme-advanced */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "scheme-advanced"; sourceTree = BUILT_PRODUCTS_DIR; }; + 22C2ACA018BE3FEC006B3677 /* nailboardtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nailboardtest.c; sourceTree = ""; }; + 22C2ACAF18BE400A006B3677 /* nailboardtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nailboardtest; sourceTree = BUILT_PRODUCTS_DIR; }; + 22C5C99A18EC6AEC004C63D4 /* failover.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = failover.c; sourceTree = ""; }; + 22C5C99B18EC6AEC004C63D4 /* failover.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = failover.h; sourceTree = ""; }; + 22C5C99C18EC6AEC004C63D4 /* land.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = land.c; sourceTree = ""; }; + 22DD93E118ED815F00240DD2 /* failover.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = failover.txt; path = ../design/failover.txt; sourceTree = ""; }; + 22DD93E218ED815F00240DD2 /* land.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = land.txt; path = ../design/land.txt; sourceTree = ""; }; + 22E30E821886FF1400D98EA9 /* nailboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nailboard.c; sourceTree = ""; }; + 22E30E831886FF1400D98EA9 /* nailboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nailboard.h; sourceTree = ""; }; + 22F846AF18F4379C00982BA7 /* lockut.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lockut.c; sourceTree = ""; }; + 22F846BD18F437B900982BA7 /* lockut */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lockut; sourceTree = BUILT_PRODUCTS_DIR; }; 22FA177516E8D6FC0098B23F /* amcssth */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = amcssth; sourceTree = BUILT_PRODUCTS_DIR; }; 22FA177616E8D7A80098B23F /* amcssth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amcssth.c; sourceTree = ""; }; + 22FACED1188807FF000FDBC1 /* airtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = airtest.c; sourceTree = ""; }; + 22FACED2188807FF000FDBC1 /* fmtdy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmtdy.h; sourceTree = ""; }; + 22FACED3188807FF000FDBC1 /* fmtdytst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmtdytst.h; sourceTree = ""; }; + 22FACED4188807FF000FDBC1 /* fmthe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmthe.h; sourceTree = ""; }; + 22FACED5188807FF000FDBC1 /* fmtno.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmtno.h; sourceTree = ""; }; + 22FACED6188807FF000FDBC1 /* fmtscheme.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fmtscheme.c; sourceTree = ""; }; + 22FACED7188807FF000FDBC1 /* fmtscheme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmtscheme.h; sourceTree = ""; }; + 22FACEDA1888088A000FDBC1 /* ss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ss.c; sourceTree = ""; }; + 22FACEDB188808D5000FDBC1 /* mpscmfs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpscmfs.h; sourceTree = ""; }; + 22FACEDC18880933000FDBC1 /* poolmfs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = poolmfs.h; sourceTree = ""; }; + 22FACEDD18880933000FDBC1 /* poolmrg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = poolmrg.h; sourceTree = ""; }; + 22FACEDE18880933000FDBC1 /* pooln.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pooln.c; sourceTree = ""; }; + 22FACEDF18880933000FDBC1 /* pooln.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pooln.h; sourceTree = ""; }; + 22FACEED18880983000FDBC1 /* airtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = airtest; sourceTree = BUILT_PRODUCTS_DIR; }; 2D07B96C1636FC7200DB751B /* eventsql.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = eventsql.c; sourceTree = ""; }; 2D07B9711636FC9900DB751B /* mpseventsql */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mpseventsql; sourceTree = BUILT_PRODUCTS_DIR; }; 2D07B97B163705E400DB751B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; @@ -1220,6 +1469,8 @@ 3104B02F156D39F2000A585A /* amssshe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amssshe.c; sourceTree = ""; }; 3104B03D156D3AD7000A585A /* segsmss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = segsmss; sourceTree = BUILT_PRODUCTS_DIR; }; 3107DC4E173B03D100F705C8 /* arg.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = arg.h; sourceTree = ""; }; + 310F5D7118B6675F007EFCBC /* tree.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tree.c; sourceTree = ""; }; + 310F5D7218B6675F007EFCBC /* tree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tree.h; sourceTree = ""; }; 3112ED3A18ABC57F00CC531A /* sa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sa.h; sourceTree = ""; }; 3112ED3B18ABC75200CC531A /* sa.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sa.c; sourceTree = ""; }; 3114A590156E913C001E0AA3 /* locv */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locv; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1239,7 +1490,7 @@ 3114A633156E94DB001E0AA3 /* abqtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = abqtest; sourceTree = BUILT_PRODUCTS_DIR; }; 3114A63D156E94EA001E0AA3 /* abqtest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = abqtest.c; sourceTree = ""; }; 3114A645156E9525001E0AA3 /* abq.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = abq.c; sourceTree = ""; }; - 3114A64C156E9596001E0AA3 /* fbmtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fbmtest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3114A64C156E9596001E0AA3 /* landtest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = landtest; sourceTree = BUILT_PRODUCTS_DIR; }; 3114A662156E95D9001E0AA3 /* btcv */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = btcv; sourceTree = BUILT_PRODUCTS_DIR; }; 3114A66C156E95EB001E0AA3 /* btcv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = btcv.c; sourceTree = ""; }; 3114A67C156E9668001E0AA3 /* mv2test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mv2test; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1348,7 +1599,6 @@ 311F2F6717398B3B00C15B6A /* mpsio.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mpsio.h; sourceTree = ""; }; 311F2F6817398B3B00C15B6A /* mpslib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mpslib.h; sourceTree = ""; }; 311F2F6917398B3B00C15B6A /* mpstd.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mpstd.h; sourceTree = ""; }; - 311F2F6A17398B4C00C15B6A /* mpsw3.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mpsw3.h; sourceTree = ""; }; 311F2F6B17398B4C00C15B6A /* mpswin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mpswin.h; sourceTree = ""; }; 311F2F6D17398B6300C15B6A /* prmci3.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = prmci3.h; sourceTree = ""; }; 311F2F6E17398B6300C15B6A /* prmci6.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = prmci6.h; sourceTree = ""; }; @@ -1383,8 +1633,6 @@ 317B3C2A1731830100F9A469 /* arg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = arg.c; sourceTree = ""; }; 318DA8CD1892B0F30089718C /* djbench */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = djbench; sourceTree = BUILT_PRODUCTS_DIR; }; 318DA8CE1892B1210089718C /* djbench.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = djbench.c; sourceTree = ""; }; - 318DA8D01892B13B0089718C /* getopt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = getopt.h; sourceTree = ""; }; - 318DA8D11892B13B0089718C /* getoptl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = getoptl.c; sourceTree = ""; }; 31A47BA3156C1E130039B1C2 /* mps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mps.c; sourceTree = ""; }; 31A47BA5156C1E5E0039B1C2 /* ssixi3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ssixi3.c; sourceTree = ""; }; 31C83ADD1786281C0031A0DB /* protxc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = protxc.h; sourceTree = ""; }; @@ -1405,6 +1653,8 @@ 31D6007B156D3FCC00337B26 /* zmess.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = zmess.c; sourceTree = ""; }; 31D6008C156D402900337B26 /* steptest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = steptest; sourceTree = BUILT_PRODUCTS_DIR; }; 31D60098156D403C00337B26 /* steptest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = steptest.c; sourceTree = ""; }; + 31EA4B5D18C17CE8006B0D77 /* table.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = table.c; sourceTree = ""; }; + 31EA4B5E18C17CE8006B0D77 /* table.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = ""; }; 31EEABF5156AAF7C00714D05 /* mpsi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpsi.c; sourceTree = ""; }; 31EEABFB156AAF9D00714D05 /* libmps.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libmps.a; sourceTree = BUILT_PRODUCTS_DIR; }; 31EEAC01156AB21B00714D05 /* mpm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpm.c; sourceTree = ""; }; @@ -1467,6 +1717,22 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 2231BB5218CA97D8002D6322 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2231BB5318CA97D8002D6322 /* libmps.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2231BB6018CA97DC002D6322 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2231BB6118CA97DC002D6322 /* libmps.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 224CC792175E1821002FF81B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1506,6 +1772,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 22C2ACA818BE400A006B3677 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 22C2ACA918BE400A006B3677 /* libmps.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 22F846B618F437B900982BA7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 22F846B718F437B900982BA7 /* libmps.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 22FA176E16E8D6FC0098B23F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1514,6 +1796,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 22FACEE618880983000FDBC1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 22FACEE718880983000FDBC1 /* libmps.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2D07B96E1636FC9900DB751B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1851,6 +2141,7 @@ 31160D9C1899540D0071EB17 /* config.txt */, 31160D9D1899540D0071EB17 /* critical-path.txt */, 31160D9E1899540D0071EB17 /* diag.txt */, + 22DD93E118ED815F00240DD2 /* failover.txt */, 31160D9F1899540D0071EB17 /* finalize.txt */, 31160DA01899540D0071EB17 /* fix.txt */, 31160DA11899540D0071EB17 /* freelist.txt */, @@ -1860,6 +2151,7 @@ 31160DA51899540D0071EB17 /* interface-c.txt */, 31160DA61899540D0071EB17 /* io.txt */, 31160DA71899540D0071EB17 /* keyword-arguments.txt */, + 22DD93E218ED815F00240DD2 /* land.txt */, 31160DA81899540D0071EB17 /* lib.txt */, 31160DA91899540D0071EB17 /* lock.txt */, 31160DAA1899540D0071EB17 /* locus.txt */, @@ -1913,7 +2205,9 @@ 3124CAB3156BE1B700753214 /* Tests */ = { isa = PBXGroup; children = ( + 22F846AF18F4379C00982BA7 /* lockut.c */, 3114A63D156E94EA001E0AA3 /* abqtest.c */, + 22FACED1188807FF000FDBC1 /* airtest.c */, 3124CAF5156BE81100753214 /* amcss.c */, 3104AFEB156D36A5000A585A /* amcsshe.c */, 22FA177616E8D7A80098B23F /* amcssth.c */, @@ -1928,28 +2222,39 @@ 3114A613156E944A001E0AA3 /* bttest.c */, 2291A5AA175CAA9B001D4920 /* exposet0.c */, 2291A5AB175CAA9B001D4920 /* expt825.c */, - 2291A5E9175CB4EC001D4920 /* fbmtest.c */, 3114A5CD156E9369001E0AA3 /* finalcv.c */, 3114A5E5156E93B9001E0AA3 /* finaltest.c */, 3124CAC6156BE48D00753214 /* fmtdy.c */, + 22FACED2188807FF000FDBC1 /* fmtdy.h */, 3124CAC7156BE48D00753214 /* fmtdytst.c */, + 22FACED3188807FF000FDBC1 /* fmtdytst.h */, 3124CAE4156BE6D500753214 /* fmthe.c */, + 22FACED4188807FF000FDBC1 /* fmthe.h */, 3124CACC156BE4C200753214 /* fmtno.c */, + 22FACED5188807FF000FDBC1 /* fmtno.h */, + 22FACED6188807FF000FDBC1 /* fmtscheme.c */, + 22FACED7188807FF000FDBC1 /* fmtscheme.h */, 224CC79E175E3202002FF81B /* fotest.c */, + 2291A5E9175CB4EC001D4920 /* landtest.c */, + 2231BB6818CA9834002D6322 /* locbwcss.c */, 31D60036156D3E0200337B26 /* lockcov.c */, + 2231BB6918CA983C002D6322 /* locusss.c */, 3114A5A1156E9168001E0AA3 /* locv.c */, 3114A69F156E9725001E0AA3 /* messtest.c */, 31EEAC74156AB58E00714D05 /* mpmss.c */, 3124CADE156BE65900753214 /* mpsicv.c */, 3114A686156E9674001E0AA3 /* mv2test.c */, + 22C2ACA018BE3FEC006B3677 /* nailboardtest.c */, 31D6004A156D3EE600337B26 /* poolncv.c */, 3114A5B7156E92F0001E0AA3 /* qs.c */, 3104AFD6156D3602000A585A /* sacss.c */, 31D60006156D3C5F00337B26 /* segsmss.c */, 31D60098156D403C00337B26 /* steptest.c */, 3114A628156E949A001E0AA3 /* teletest.c */, - 2291A5F0175CB7A4001D4920 /* testlib.h */, 31EEAC9E156AB73400714D05 /* testlib.c */, + 2291A5F0175CB7A4001D4920 /* testlib.h */, + 22561A9618F4263300372C66 /* testthr.h */, + 22561A9718F4263300372C66 /* testthrix.c */, 3114A6BA156E9768001E0AA3 /* walkt0.c */, 31D6005E156D3F4A00337B26 /* zcoll.c */, 31D6007B156D3FCC00337B26 /* zmess.c */, @@ -1960,8 +2265,6 @@ 318DA8C21892B0B20089718C /* Benchmarks */ = { isa = PBXGroup; children = ( - 318DA8D01892B13B0089718C /* getopt.h */, - 318DA8D11892B13B0089718C /* getoptl.c */, 318DA8CE1892B1210089718C /* djbench.c */, 6313D46618A3FDC900EB03EF /* gcbench.c */, ); @@ -2022,7 +2325,7 @@ 3114A605156E9430001E0AA3 /* bttest */, 3114A61C156E9485001E0AA3 /* teletest */, 3114A633156E94DB001E0AA3 /* abqtest */, - 3114A64C156E9596001E0AA3 /* fbmtest */, + 3114A64C156E9596001E0AA3 /* landtest */, 3114A662156E95D9001E0AA3 /* btcv */, 3114A67C156E9668001E0AA3 /* mv2test */, 3114A695156E971B001E0AA3 /* messtest */, @@ -2039,6 +2342,11 @@ 318DA8CD1892B0F30089718C /* djbench */, 6313D47218A400B200EB03EF /* gcbench */, 22B2BC3618B6434F00C33E63 /* scheme-advanced */, + 2231BB5918CA97D8002D6322 /* locbwcss */, + 2231BB6718CA97DC002D6322 /* locusss */, + 22FACEED18880983000FDBC1 /* airtest */, + 22C2ACAF18BE400A006B3677 /* nailboardtest */, + 22F846BD18F437B900982BA7 /* lockut */, ); name = Products; sourceTree = ""; @@ -2072,10 +2380,13 @@ 311F2F5917398AE900C15B6A /* eventcom.h */, 311F2F5A17398AE900C15B6A /* eventdef.h */, 311F2F5C17398AE900C15B6A /* eventrep.h */, + 22C5C99A18EC6AEC004C63D4 /* failover.c */, + 22C5C99B18EC6AEC004C63D4 /* failover.h */, 31EEAC1A156AB2B200714D05 /* format.c */, 2291A5EE175CB768001D4920 /* freelist.c */, 2291A5EF175CB768001D4920 /* freelist.h */, 31EEAC07156AB27B00714D05 /* global.c */, + 22C5C99C18EC6AEC004C63D4 /* land.c */, 31EEAC2B156AB2F200714D05 /* ld.c */, 311F2F5E17398B0E00C15B6A /* lock.h */, 31EEAC08156AB27B00714D05 /* locus.c */, @@ -2090,19 +2401,25 @@ 311F2F6417398B1A00C15B6A /* mps.h */, 311F2F6517398B3B00C15B6A /* mpsacl.h */, 311F2F6617398B3B00C15B6A /* mpsavm.h */, + 22FACEDB188808D5000FDBC1 /* mpscmfs.h */, 311F2F7C17398E9A00C15B6A /* mpscmv.h */, 31EEABF5156AAF7C00714D05 /* mpsi.c */, 311F2F6717398B3B00C15B6A /* mpsio.h */, 311F2F6817398B3B00C15B6A /* mpslib.h */, 311F2F6917398B3B00C15B6A /* mpstd.h */, - 311F2F6A17398B4C00C15B6A /* mpsw3.h */, 311F2F6B17398B4C00C15B6A /* mpswin.h */, + 22E30E821886FF1400D98EA9 /* nailboard.c */, + 22E30E831886FF1400D98EA9 /* nailboard.h */, 31EEAC09156AB27B00714D05 /* pool.c */, 31EEAC0A156AB27B00714D05 /* poolabs.c */, 31EEAC2D156AB2F200714D05 /* poolmfs.c */, + 22FACEDC18880933000FDBC1 /* poolmfs.h */, 31EEAC2E156AB2F200714D05 /* poolmrg.c */, + 22FACEDD18880933000FDBC1 /* poolmrg.h */, 31EEAC2F156AB2F200714D05 /* poolmv.c */, 311F2F7B17398E7600C15B6A /* poolmv.h */, + 22FACEDE18880933000FDBC1 /* pooln.c */, + 22FACEDF18880933000FDBC1 /* pooln.h */, 311F2F6D17398B6300C15B6A /* prmci3.h */, 311F2F6E17398B6300C15B6A /* prmci6.h */, 311F2F6F17398B6300C15B6A /* prmcix.h */, @@ -2117,6 +2434,8 @@ 31EEAC30156AB2F200714D05 /* ring.c */, 311F2F7317398B7100C15B6A /* ring.h */, 31EEAC1C156AB2B200714D05 /* root.c */, + 3112ED3B18ABC75200CC531A /* sa.c */, + 3112ED3A18ABC57F00CC531A /* sa.h */, 31EEAC31156AB2F200714D05 /* sac.c */, 311F2F7417398B7100C15B6A /* sac.h */, 311F2F7517398B8E00C15B6A /* sc.h */, @@ -2124,17 +2443,22 @@ 31EEAC32156AB2F200714D05 /* shield.c */, 31EEAC43156AB32500714D05 /* splay.c */, 311F2F7617398B8E00C15B6A /* splay.h */, + 22FACEDA1888088A000FDBC1 /* ss.c */, 311F2F7717398B8E00C15B6A /* ss.h */, + 31EA4B5D18C17CE8006B0D77 /* table.c */, + 31EA4B5E18C17CE8006B0D77 /* table.h */, 311F2F7817398B8E00C15B6A /* th.h */, 311F2F7917398B8E00C15B6A /* thw3.h */, 31EEAC1E156AB2B200714D05 /* trace.c */, 31EEAC1F156AB2B200714D05 /* traceanc.c */, 31EEAC0D156AB27B00714D05 /* tract.c */, 311F2F7A17398B8E00C15B6A /* tract.h */, + 310F5D7118B6675F007EFCBC /* tree.c */, + 310F5D7218B6675F007EFCBC /* tree.h */, 31EEAC44156AB32500714D05 /* version.c */, + 223475CB194CA09500C69128 /* vm.c */, + 223475CC194CA09500C69128 /* vm.h */, 31EEAC0E156AB27B00714D05 /* walk.c */, - 3112ED3A18ABC57F00CC531A /* sa.h */, - 3112ED3B18ABC75200CC531A /* sa.c */, ); name = "MPM Core"; sourceTree = ""; @@ -2214,6 +2538,42 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 2231BB4C18CA97D8002D6322 /* locbwcss */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2231BB5518CA97D8002D6322 /* Build configuration list for PBXNativeTarget "locbwcss" */; + buildPhases = ( + 2231BB4F18CA97D8002D6322 /* Sources */, + 2231BB5218CA97D8002D6322 /* Frameworks */, + 2231BB5418CA97D8002D6322 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 2231BB4D18CA97D8002D6322 /* PBXTargetDependency */, + ); + name = locbwcss; + productName = lockcov; + productReference = 2231BB5918CA97D8002D6322 /* locbwcss */; + productType = "com.apple.product-type.tool"; + }; + 2231BB5A18CA97DC002D6322 /* locusss */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2231BB6318CA97DC002D6322 /* Build configuration list for PBXNativeTarget "locusss" */; + buildPhases = ( + 2231BB5D18CA97DC002D6322 /* Sources */, + 2231BB6018CA97DC002D6322 /* Frameworks */, + 2231BB6218CA97DC002D6322 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 2231BB5B18CA97DC002D6322 /* PBXTargetDependency */, + ); + name = locusss; + productName = lockcov; + productReference = 2231BB6718CA97DC002D6322 /* locusss */; + productType = "com.apple.product-type.tool"; + }; 224CC78C175E1821002FF81B /* fotest */ = { isa = PBXNativeTarget; buildConfigurationList = 224CC795175E1821002FF81B /* Build configuration list for PBXNativeTarget "fotest" */; @@ -2303,6 +2663,42 @@ productReference = 22B2BC3618B6434F00C33E63 /* scheme-advanced */; productType = "com.apple.product-type.tool"; }; + 22C2ACA218BE400A006B3677 /* nailboardtest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 22C2ACAB18BE400A006B3677 /* Build configuration list for PBXNativeTarget "nailboardtest" */; + buildPhases = ( + 22C2ACA518BE400A006B3677 /* Sources */, + 22C2ACA818BE400A006B3677 /* Frameworks */, + 22C2ACAA18BE400A006B3677 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 22C2ACA318BE400A006B3677 /* PBXTargetDependency */, + ); + name = nailboardtest; + productName = mv2test; + productReference = 22C2ACAF18BE400A006B3677 /* nailboardtest */; + productType = "com.apple.product-type.tool"; + }; + 22F846B018F437B900982BA7 /* lockut */ = { + isa = PBXNativeTarget; + buildConfigurationList = 22F846B918F437B900982BA7 /* Build configuration list for PBXNativeTarget "lockut" */; + buildPhases = ( + 22F846B318F437B900982BA7 /* Sources */, + 22F846B618F437B900982BA7 /* Frameworks */, + 22F846B818F437B900982BA7 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 22F846B118F437B900982BA7 /* PBXTargetDependency */, + ); + name = lockut; + productName = lockcov; + productReference = 22F846BD18F437B900982BA7 /* lockut */; + productType = "com.apple.product-type.tool"; + }; 22FA176416E8D6FC0098B23F /* amcssth */ = { isa = PBXNativeTarget; buildConfigurationList = 22FA177116E8D6FC0098B23F /* Build configuration list for PBXNativeTarget "amcssth" */; @@ -2321,6 +2717,24 @@ productReference = 22FA177516E8D6FC0098B23F /* amcssth */; productType = "com.apple.product-type.tool"; }; + 22FACEE018880983000FDBC1 /* airtest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 22FACEE918880983000FDBC1 /* Build configuration list for PBXNativeTarget "airtest" */; + buildPhases = ( + 22FACEE318880983000FDBC1 /* Sources */, + 22FACEE618880983000FDBC1 /* Frameworks */, + 22FACEE818880983000FDBC1 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 22FACEE118880983000FDBC1 /* PBXTargetDependency */, + ); + name = airtest; + productName = airtest; + productReference = 22FACEED18880983000FDBC1 /* airtest */; + productType = "com.apple.product-type.tool"; + }; 2D07B9701636FC9900DB751B /* mpseventsql */ = { isa = PBXNativeTarget; buildConfigurationList = 2D07B9741636FC9900DB751B /* Build configuration list for PBXNativeTarget "mpseventsql" */; @@ -2607,9 +3021,9 @@ productReference = 3114A633156E94DB001E0AA3 /* abqtest */; productType = "com.apple.product-type.tool"; }; - 3114A64B156E9596001E0AA3 /* fbmtest */ = { + 3114A64B156E9596001E0AA3 /* landtest */ = { isa = PBXNativeTarget; - buildConfigurationList = 3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "fbmtest" */; + buildConfigurationList = 3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "landtest" */; buildPhases = ( 3114A648156E9596001E0AA3 /* Sources */, 3114A649156E9596001E0AA3 /* Frameworks */, @@ -2620,9 +3034,9 @@ dependencies = ( 3114A659156E95B1001E0AA3 /* PBXTargetDependency */, ); - name = fbmtest; - productName = fbmtest; - productReference = 3114A64C156E9596001E0AA3 /* fbmtest */; + name = landtest; + productName = landtest; + productReference = 3114A64C156E9596001E0AA3 /* landtest */; productType = "com.apple.product-type.tool"; }; 3114A661156E95D9001E0AA3 /* btcv */ = { @@ -2969,7 +3383,7 @@ 31EEABDA156AAE9E00714D05 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0460; + LastUpgradeCheck = 0510; }; buildConfigurationList = 31EEABDD156AAE9E00714D05 /* Build configuration list for PBXProject "mps" */; compatibilityVersion = "Xcode 3.2"; @@ -2984,9 +3398,14 @@ projectRoot = ""; targets = ( 3104AFF1156D37A0000A585A /* all */, + 2215A9B9192A47CE00E9E2CE /* testall */, + 2215A9B1192A47C500E9E2CE /* testansi */, + 2215A9A9192A47BB00E9E2CE /* testci */, + 2215A9C1192A47D500E9E2CE /* testpollnone */, 22CDE8EF16E9E97D00366D0A /* testrun */, 31EEABFA156AAF9D00714D05 /* mps */, 3114A632156E94DB001E0AA3 /* abqtest */, + 22FACEE018880983000FDBC1 /* airtest */, 3124CAEA156BE7F300753214 /* amcss */, 3104AFDC156D3681000A585A /* amcsshe */, 22FA176416E8D6FC0098B23F /* amcssth */, @@ -3002,17 +3421,21 @@ 318DA8C31892B0F30089718C /* djbench */, 2291A5D3175CB05F001D4920 /* exposet0 */, 2291A5C1175CAFCA001D4920 /* expt825 */, - 3114A64B156E9596001E0AA3 /* fbmtest */, 3114A5BC156E9315001E0AA3 /* finalcv */, 3114A5D5156E93A0001E0AA3 /* finaltest */, 224CC78C175E1821002FF81B /* fotest */, 6313D46718A400B200EB03EF /* gcbench */, + 3114A64B156E9596001E0AA3 /* landtest */, + 2231BB4C18CA97D8002D6322 /* locbwcss */, 31D60026156D3D3E00337B26 /* lockcov */, + 2231BB5A18CA97DC002D6322 /* locusss */, + 22F846B018F437B900982BA7 /* lockut */, 3114A58F156E913C001E0AA3 /* locv */, 3114A694156E971B001E0AA3 /* messtest */, 31EEAC64156AB52600714D05 /* mpmss */, 3124CAD3156BE64A00753214 /* mpsicv */, 3114A67B156E9668001E0AA3 /* mv2test */, + 22C2ACA218BE400A006B3677 /* nailboardtest */, 31D6003D156D3EC700337B26 /* poolncv */, 3114A5A6156E92C0001E0AA3 /* qs */, 3104AFC7156D35E2000A585A /* sacss */, @@ -3032,7 +3455,7 @@ /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ - 22CDE8F416E9E9D400366D0A /* ShellScript */ = { + 2215A9AC192A47BB00E9E2CE /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -3043,35 +3466,110 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Not listed here:\n# awlutth -- fails (job003506).\n# bttest and teletest -- interactive and so cannot be run unattended.\n# zcoll -- takes too long to be useful as a regularly run smoke test.\nTESTCASES=\"abqtest amcss amcsshe amcssth amsss amssshe apss arenacv \\\n awlut awlutth awluthe btcv expt825 exposet0 fbmtest finalcv \\\n finaltest fotest lockcov locv messtest mpmss mpsicv mv2test \\\n poolncv qs sacss segsmss steptest walkt0 zmess\"\n\n../tool/testrun.sh $(for TEST in $TESTCASES; do echo $TARGET_BUILD_DIR/$TEST; done)\n\n# Coverage\nif [ \"$CONFIGURATION\" == \"Debug\" ]; then\n (cd xc/$PROJECT.build/$CONFIGURATION/$PROJECT.build/Objects-normal/x86_64 &&\n gcov mps.c 2> /dev/null) | ../tool/gcovfmt.py\nfi"; + shellScript = "../tool/testrun.sh -s \"$TARGET_NAME\" \"$TARGET_BUILD_DIR\"\n"; showEnvVarsInLog = 0; }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 224CC78F175E1821002FF81B /* Sources */ = { - isa = PBXSourcesBuildPhase; + 2215A9B4192A47C500E9E2CE /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( - 224CC7A0175E322C002FF81B /* fotest.c in Sources */, - 224CC791175E1821002FF81B /* testlib.c in Sources */, + ); + inputPaths = ( + ); + outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "../tool/testrun.sh -s \"$TARGET_NAME\" \"$TARGET_BUILD_DIR\"\n"; + showEnvVarsInLog = 0; }; - 2291A5AF175CAB2F001D4920 /* Sources */ = { - isa = PBXSourcesBuildPhase; + 2215A9BC192A47CE00E9E2CE /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( - 2291A5BE175CAB4E001D4920 /* awlutth.c in Sources */, - 2291A5B1175CAB2F001D4920 /* fmtdy.c in Sources */, - 2291A5B2175CAB2F001D4920 /* fmtdytst.c in Sources */, - 2291A5B3175CAB2F001D4920 /* fmthe.c in Sources */, - 2291A5B4175CAB2F001D4920 /* fmtno.c in Sources */, - 2291A5B5175CAB2F001D4920 /* testlib.c in Sources */, ); - runOnlyForDeploymentPostprocessing = 0; - }; - 2291A5C4175CAFCA001D4920 /* Sources */ = { + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "../tool/testrun.sh -s \"$TARGET_NAME\" \"$TARGET_BUILD_DIR\"\n"; + showEnvVarsInLog = 0; + }; + 2215A9C4192A47D500E9E2CE /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "../tool/testrun.sh -s \"$TARGET_NAME\" \"$TARGET_BUILD_DIR\"\n"; + showEnvVarsInLog = 0; + }; + 22CDE8F416E9E9D400366D0A /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "../tool/testrun.sh -s \"$TARGET_NAME\" \"$TARGET_BUILD_DIR\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2231BB4F18CA97D8002D6322 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2231BB6B18CA9861002D6322 /* locbwcss.c in Sources */, + 2231BB5118CA97D8002D6322 /* testlib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2231BB5D18CA97DC002D6322 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2231BB6A18CA984F002D6322 /* locusss.c in Sources */, + 2231BB5F18CA97DC002D6322 /* testlib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 224CC78F175E1821002FF81B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 224CC7A0175E322C002FF81B /* fotest.c in Sources */, + 224CC791175E1821002FF81B /* testlib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2291A5AF175CAB2F001D4920 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2291A5BE175CAB4E001D4920 /* awlutth.c in Sources */, + 2291A5B1175CAB2F001D4920 /* fmtdy.c in Sources */, + 2291A5B2175CAB2F001D4920 /* fmtdytst.c in Sources */, + 2291A5B3175CAB2F001D4920 /* fmthe.c in Sources */, + 2291A5B4175CAB2F001D4920 /* fmtno.c in Sources */, + 2291A5B5175CAB2F001D4920 /* testlib.c in Sources */, + 22561A9918F4266600372C66 /* testthrix.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2291A5C4175CAFCA001D4920 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -3104,6 +3602,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 22C2ACA518BE400A006B3677 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 22C2ACB018BE4049006B3677 /* nailboardtest.c in Sources */, + 22C2ACA718BE400A006B3677 /* testlib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 22F846B318F437B900982BA7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 22F846BE18F437D700982BA7 /* lockut.c in Sources */, + 22F846B518F437B900982BA7 /* testlib.c in Sources */, + 22F846BF18F437E000982BA7 /* testthrix.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 22FA176716E8D6FC0098B23F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -3114,6 +3631,17 @@ 22FA176B16E8D6FC0098B23F /* fmthe.c in Sources */, 22FA176C16E8D6FC0098B23F /* fmtno.c in Sources */, 22FA176D16E8D6FC0098B23F /* testlib.c in Sources */, + 22561A9818F4265D00372C66 /* testthrix.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 22FACEE318880983000FDBC1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 22FACEEF188809A7000FDBC1 /* airtest.c in Sources */, + 22FACEEE188809A3000FDBC1 /* fmtscheme.c in Sources */, + 22FACEE518880983000FDBC1 /* testlib.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3283,7 +3811,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2291A5ED175CB5E2001D4920 /* fbmtest.c in Sources */, + 2291A5ED175CB5E2001D4920 /* landtest.c in Sources */, 3114A672156E95F6001E0AA3 /* testlib.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3379,7 +3907,7 @@ files = ( 318DA8D31892B27E0089718C /* testlib.c in Sources */, 6313D47318A4028E00EB03EF /* djbench.c in Sources */, - 318DA8D21892B13B0089718C /* getoptl.c in Sources */, + 22561A9A18F426BB00372C66 /* testthrix.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3409,8 +3937,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 31D60048156D3ECF00337B26 /* testlib.c in Sources */, + 2215A9C9192A495F00E9E2CE /* pooln.c in Sources */, 31D6004B156D3EE600337B26 /* poolncv.c in Sources */, + 31D60048156D3ECF00337B26 /* testlib.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3486,13 +4015,53 @@ 6313D47418A4029200EB03EF /* gcbench.c in Sources */, 6313D47518A40C6300EB03EF /* fmtdytst.c in Sources */, 6313D47618A40C7B00EB03EF /* fmtdy.c in Sources */, - 6313D46A18A400B200EB03EF /* getoptl.c in Sources */, + 22561A9B18F426F300372C66 /* testthrix.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 2215A9AA192A47BB00E9E2CE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3104AFF1156D37A0000A585A /* all */; + targetProxy = 2215A9AB192A47BB00E9E2CE /* PBXContainerItemProxy */; + }; + 2215A9B2192A47C500E9E2CE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3104AFF1156D37A0000A585A /* all */; + targetProxy = 2215A9B3192A47C500E9E2CE /* PBXContainerItemProxy */; + }; + 2215A9BA192A47CE00E9E2CE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3104AFF1156D37A0000A585A /* all */; + targetProxy = 2215A9BB192A47CE00E9E2CE /* PBXContainerItemProxy */; + }; + 2215A9C2192A47D500E9E2CE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3104AFF1156D37A0000A585A /* all */; + targetProxy = 2215A9C3192A47D500E9E2CE /* PBXContainerItemProxy */; + }; + 2231BB4D18CA97D8002D6322 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 31EEABFA156AAF9D00714D05 /* mps */; + targetProxy = 2231BB4E18CA97D8002D6322 /* PBXContainerItemProxy */; + }; + 2231BB5B18CA97DC002D6322 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 31EEABFA156AAF9D00714D05 /* mps */; + targetProxy = 2231BB5C18CA97DC002D6322 /* PBXContainerItemProxy */; + }; + 2231BB6D18CA986B002D6322 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2231BB4C18CA97D8002D6322 /* locbwcss */; + targetProxy = 2231BB6C18CA986B002D6322 /* PBXContainerItemProxy */; + }; + 2231BB6F18CA986D002D6322 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2231BB5A18CA97DC002D6322 /* locusss */; + targetProxy = 2231BB6E18CA986D002D6322 /* PBXContainerItemProxy */; + }; 224CC78D175E1821002FF81B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 31EEABFA156AAF9D00714D05 /* mps */; @@ -3508,6 +4077,11 @@ target = 2D604B9B16514B1A003AAF46 /* mpseventtxt */; targetProxy = 2275798816C5422900B662B0 /* PBXContainerItemProxy */; }; + 2286E4C918F4389E004111E2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 22F846B018F437B900982BA7 /* lockut */; + targetProxy = 2286E4C818F4389E004111E2 /* PBXContainerItemProxy */; + }; 2291A5AD175CAB2F001D4920 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 31EEABFA156AAF9D00714D05 /* mps */; @@ -3558,11 +4132,26 @@ target = 6313D46718A400B200EB03EF /* gcbench */; targetProxy = 22B2BC3E18B643B700C33E63 /* PBXContainerItemProxy */; }; + 22C2ACA318BE400A006B3677 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 31EEABFA156AAF9D00714D05 /* mps */; + targetProxy = 22C2ACA418BE400A006B3677 /* PBXContainerItemProxy */; + }; + 22C2ACB218BE4056006B3677 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 22C2ACA218BE400A006B3677 /* nailboardtest */; + targetProxy = 22C2ACB118BE4056006B3677 /* PBXContainerItemProxy */; + }; 22CDE92E16E9EB9300366D0A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 3104AFF1156D37A0000A585A /* all */; targetProxy = 22CDE92D16E9EB9300366D0A /* PBXContainerItemProxy */; }; + 22F846B118F437B900982BA7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 31EEABFA156AAF9D00714D05 /* mps */; + targetProxy = 22F846B218F437B900982BA7 /* PBXContainerItemProxy */; + }; 22FA176516E8D6FC0098B23F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 31EEABFA156AAF9D00714D05 /* mps */; @@ -3573,6 +4162,16 @@ target = 22FA176416E8D6FC0098B23F /* amcssth */; targetProxy = 22FA177816E8DB0C0098B23F /* PBXContainerItemProxy */; }; + 22FACEE118880983000FDBC1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 31EEABFA156AAF9D00714D05 /* mps */; + targetProxy = 22FACEE218880983000FDBC1 /* PBXContainerItemProxy */; + }; + 22FACEF1188809B5000FDBC1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 22FACEE018880983000FDBC1 /* airtest */; + targetProxy = 22FACEF0188809B5000FDBC1 /* PBXContainerItemProxy */; + }; 2D07B9791636FCBD00DB751B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 2D07B9701636FC9900DB751B /* mpseventsql */; @@ -3750,7 +4349,7 @@ }; 3114A65B156E95B4001E0AA3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 3114A64B156E9596001E0AA3 /* fbmtest */; + target = 3114A64B156E9596001E0AA3 /* landtest */; targetProxy = 3114A65A156E95B4001E0AA3 /* PBXContainerItemProxy */; }; 3114A675156E9619001E0AA3 /* PBXTargetDependency */ = { @@ -3886,11 +4485,135 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 2215A9AE192A47BB00E9E2CE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 2215A9AF192A47BB00E9E2CE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 2215A9B0192A47BB00E9E2CE /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; + 2215A9B6192A47C500E9E2CE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 2215A9B7192A47C500E9E2CE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 2215A9B8192A47C500E9E2CE /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; + 2215A9BE192A47CE00E9E2CE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 2215A9BF192A47CE00E9E2CE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 2215A9C0192A47CE00E9E2CE /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; + 2215A9C6192A47D500E9E2CE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 2215A9C7192A47D500E9E2CE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 2215A9C8192A47D500E9E2CE /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; + 2231BB5618CA97D8002D6322 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 2231BB5718CA97D8002D6322 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 2231BB5818CA97D8002D6322 /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; + 2231BB6418CA97DC002D6322 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 2231BB6518CA97DC002D6322 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 2231BB6618CA97DC002D6322 /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; 224CC796175E1821002FF81B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -3898,8 +4621,6 @@ 224CC797175E1821002FF81B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -3907,8 +4628,6 @@ 2291A5BA175CAB2F001D4920 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -3916,8 +4635,6 @@ 2291A5BB175CAB2F001D4920 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -3925,8 +4642,6 @@ 2291A5CE175CAFCA001D4920 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -3934,8 +4649,6 @@ 2291A5CF175CAFCA001D4920 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -3943,8 +4656,6 @@ 2291A5E0175CB05F001D4920 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -3952,8 +4663,6 @@ 2291A5E1175CB05F001D4920 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -3961,24 +4670,49 @@ 22B2BC3318B6434F00C33E63 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - PRODUCT_NAME = "scheme-advanced"; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; 22B2BC3418B6434F00C33E63 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - PRODUCT_NAME = "scheme-advanced"; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; 22B2BC3518B6434F00C33E63 /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - PRODUCT_NAME = "scheme-advanced"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; + 22C2ACA118BE3FEC006B3677 /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; + 22C2ACAC18BE400A006B3677 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 22C2ACAD18BE400A006B3677 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 22C2ACAE18BE400A006B3677 /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; }; @@ -3996,11 +4730,30 @@ }; name = Release; }; + 22F846BA18F437B900982BA7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 22F846BB18F437B900982BA7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 22F846BC18F437B900982BA7 /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; 22FA177216E8D6FC0098B23F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4008,8 +4761,20 @@ 22FA177316E8D6FC0098B23F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 22FACEEA18880983000FDBC1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 22FACEEB18880983000FDBC1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4018,8 +4783,6 @@ isa = XCBuildConfiguration; buildSettings = { GCC_C_LANGUAGE_STANDARD = c99; - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4035,8 +4798,6 @@ 2D604B9F16514B1A003AAF46 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4051,8 +4812,6 @@ 3104AFBA156D357B000A585A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4060,8 +4819,6 @@ 3104AFBB156D357B000A585A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4069,8 +4826,6 @@ 3104AFD0156D35E2000A585A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4078,8 +4833,6 @@ 3104AFD1156D35E2000A585A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4087,8 +4840,6 @@ 3104AFE5156D3682000A585A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4096,8 +4847,6 @@ 3104AFE6156D3682000A585A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4105,7 +4854,6 @@ 3104AFF3156D37A0000A585A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4113,7 +4861,6 @@ 3104AFF4156D37A0000A585A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4121,8 +4868,6 @@ 3104B011156D38F3000A585A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4130,8 +4875,6 @@ 3104B012156D38F3000A585A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4139,8 +4882,6 @@ 3104B02A156D39D4000A585A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4148,8 +4889,6 @@ 3104B02B156D39D4000A585A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4157,8 +4896,6 @@ 3104B045156D3AD8000A585A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4166,8 +4903,6 @@ 3104B046156D3AD8000A585A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4175,8 +4910,6 @@ 3114A597156E913C001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4184,8 +4917,6 @@ 3114A598156E913C001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4193,8 +4924,6 @@ 3114A5AF156E92C0001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4202,8 +4931,6 @@ 3114A5B0156E92C0001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4211,8 +4938,6 @@ 3114A5C5156E9315001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4220,8 +4945,6 @@ 3114A5C6156E9315001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4229,8 +4952,6 @@ 3114A5DE156E93A0001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4238,8 +4959,6 @@ 3114A5DF156E93A0001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4247,8 +4966,6 @@ 3114A5F7156E93E7001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4256,8 +4973,6 @@ 3114A5F8156E93E7001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4265,8 +4980,6 @@ 3114A60D156E9430001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4274,8 +4987,6 @@ 3114A60E156E9430001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4283,8 +4994,6 @@ 3114A624156E9485001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4292,8 +5001,6 @@ 3114A625156E9485001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4301,8 +5008,6 @@ 3114A63B156E94DB001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4310,8 +5015,6 @@ 3114A63C156E94DB001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4319,8 +5022,6 @@ 3114A654156E9596001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4328,8 +5029,6 @@ 3114A655156E9596001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4337,8 +5036,6 @@ 3114A66A156E95D9001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4346,8 +5043,6 @@ 3114A66B156E95D9001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4355,8 +5050,6 @@ 3114A684156E9669001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4364,8 +5057,6 @@ 3114A685156E9669001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4373,8 +5064,6 @@ 3114A69D156E971B001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4382,8 +5071,6 @@ 3114A69E156E971B001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4391,8 +5078,6 @@ 3114A6B4156E9759001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4400,8 +5085,6 @@ 3114A6B5156E9759001E0AA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4409,8 +5092,6 @@ 3114A6CE156E9815001E0AA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4425,8 +5106,6 @@ 3124CAC0156BE3EC00753214 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4434,8 +5113,6 @@ 3124CAC1156BE3EC00753214 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4443,8 +5120,6 @@ 3124CADC156BE64A00753214 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4452,8 +5127,6 @@ 3124CADD156BE64A00753214 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4461,8 +5134,6 @@ 3124CAF3156BE7F300753214 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4470,8 +5141,6 @@ 3124CAF4156BE7F300753214 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4479,16 +5148,14 @@ 318DA8CA1892B0F30089718C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - PRODUCT_NAME = djbench; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; 318DA8CB1892B0F30089718C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - PRODUCT_NAME = djbench; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; @@ -4496,6 +5163,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -4516,6 +5184,7 @@ GCC_WARN_PEDANTIC = YES; GCC_WARN_SHADOW = YES; GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; @@ -4523,29 +5192,24 @@ GCC_WARN_UNUSED_PARAMETER = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.4; - OTHER_CFLAGS = ( - "-pedantic", - "-Wall", - "-Wextra", - "-Wwrite-strings", - "-Wno-extended-offsetof", - ); SDKROOT = macosx; SYMROOT = xc; WARNING_CFLAGS = ( "-pedantic", - "-Wpointer-arith", - "-Wstrict-prototypes", - "-Wmissing-prototypes", - "-Winline", "-Waggregate-return", - "-Wnested-externs", - "-Wcast-qual", - "-Wshadow", "-Wall", + "-Wcast-qual", "-Wextra", - "-Wwrite-strings", + "-Winline", + "-Wmissing-prototypes", + "-Wnested-externs", "-Wno-extended-offsetof", + "-Wpointer-arith", + "-Wshadow", + "-Wstrict-aliasing=2", + "-Wstrict-prototypes", + "-Wunreachable-code", + "-Wwrite-strings", ); }; name = RASH; @@ -4553,7 +5217,6 @@ 318DA8D51892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - COMBINE_HIDPI_IMAGES = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4562,7 +5225,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - COMBINE_HIDPI_IMAGES = YES; EXECUTABLE_PREFIX = lib; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -4571,8 +5233,6 @@ 318DA8D71892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4580,8 +5240,6 @@ 318DA8D81892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4589,8 +5247,6 @@ 318DA8D91892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4598,8 +5254,6 @@ 318DA8DA1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4607,8 +5261,6 @@ 318DA8DB1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4616,8 +5268,6 @@ 318DA8DC1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4625,8 +5275,6 @@ 318DA8DD1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4634,8 +5282,6 @@ 318DA8DE1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4643,8 +5289,6 @@ 318DA8DF1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4652,8 +5296,6 @@ 318DA8E01892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4661,8 +5303,6 @@ 318DA8E11892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4670,8 +5310,6 @@ 318DA8E21892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4679,8 +5317,6 @@ 318DA8E31892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4688,8 +5324,6 @@ 318DA8E41892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4697,8 +5331,6 @@ 318DA8E51892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4706,8 +5338,6 @@ 318DA8E61892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4715,8 +5345,6 @@ 318DA8E71892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4724,8 +5352,6 @@ 318DA8E81892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4733,8 +5359,6 @@ 318DA8E91892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4742,8 +5366,6 @@ 318DA8EA1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4751,8 +5373,6 @@ 318DA8EB1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4760,8 +5380,6 @@ 318DA8EC1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4769,8 +5387,6 @@ 318DA8ED1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4778,8 +5394,6 @@ 318DA8EE1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4787,8 +5401,6 @@ 318DA8EF1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4796,8 +5408,6 @@ 318DA8F01892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4805,8 +5415,6 @@ 318DA8F11892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4814,8 +5422,6 @@ 318DA8F21892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4823,8 +5429,6 @@ 318DA8F31892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4832,8 +5436,6 @@ 318DA8F41892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4841,8 +5443,6 @@ 318DA8F51892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4850,8 +5450,6 @@ 318DA8F61892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4859,8 +5457,6 @@ 318DA8F71892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4868,8 +5464,6 @@ 318DA8F81892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4906,7 +5500,6 @@ 318DA8FD1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; @@ -4914,16 +5507,13 @@ 318DA8FE1892C0D00089718C /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - PRODUCT_NAME = djbench; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; }; 31D60015156D3CB200337B26 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4931,8 +5521,6 @@ 31D60016156D3CB200337B26 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4940,8 +5528,6 @@ 31D6002F156D3D3F00337B26 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4949,8 +5535,6 @@ 31D60030156D3D3F00337B26 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4958,8 +5542,6 @@ 31D60046156D3EC700337B26 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4967,8 +5549,6 @@ 31D60047156D3EC700337B26 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4976,8 +5556,6 @@ 31D6005C156D3F3500337B26 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -4985,8 +5563,6 @@ 31D6005D156D3F3500337B26 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -4994,8 +5570,6 @@ 31D60079156D3FBC00337B26 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -5003,8 +5577,6 @@ 31D6007A156D3FBC00337B26 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -5012,8 +5584,6 @@ 31D60094156D402900337B26 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -5021,8 +5591,6 @@ 31D60095156D402900337B26 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -5031,6 +5599,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -5051,6 +5620,7 @@ GCC_WARN_PEDANTIC = YES; GCC_WARN_SHADOW = YES; GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; @@ -5059,29 +5629,24 @@ GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.4; ONLY_ACTIVE_ARCH = YES; - OTHER_CFLAGS = ( - "-pedantic", - "-Wall", - "-Wextra", - "-Wwrite-strings", - "-Wno-extended-offsetof", - ); SDKROOT = macosx; SYMROOT = xc; WARNING_CFLAGS = ( "-pedantic", - "-Wpointer-arith", - "-Wstrict-prototypes", - "-Wmissing-prototypes", - "-Winline", "-Waggregate-return", - "-Wnested-externs", - "-Wcast-qual", - "-Wshadow", "-Wall", + "-Wcast-qual", "-Wextra", - "-Wwrite-strings", + "-Winline", + "-Wmissing-prototypes", + "-Wnested-externs", "-Wno-extended-offsetof", + "-Wpointer-arith", + "-Wshadow", + "-Wstrict-aliasing=2", + "-Wstrict-prototypes", + "-Wunreachable-code", + "-Wwrite-strings", ); }; name = Debug; @@ -5090,6 +5655,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -5110,6 +5676,7 @@ GCC_WARN_PEDANTIC = YES; GCC_WARN_SHADOW = YES; GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; @@ -5117,29 +5684,24 @@ GCC_WARN_UNUSED_PARAMETER = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.4; - OTHER_CFLAGS = ( - "-pedantic", - "-Wall", - "-Wextra", - "-Wwrite-strings", - "-Wno-extended-offsetof", - ); SDKROOT = macosx; SYMROOT = xc; WARNING_CFLAGS = ( "-pedantic", - "-Wpointer-arith", - "-Wstrict-prototypes", - "-Wmissing-prototypes", - "-Winline", "-Waggregate-return", - "-Wnested-externs", - "-Wcast-qual", - "-Wshadow", "-Wall", + "-Wcast-qual", "-Wextra", - "-Wwrite-strings", + "-Winline", + "-Wmissing-prototypes", + "-Wnested-externs", "-Wno-extended-offsetof", + "-Wpointer-arith", + "-Wshadow", + "-Wstrict-aliasing=2", + "-Wstrict-prototypes", + "-Wunreachable-code", + "-Wwrite-strings", ); }; name = Release; @@ -5148,10 +5710,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - COMBINE_HIDPI_IMAGES = YES; EXECUTABLE_PREFIX = lib; - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -5160,7 +5719,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - COMBINE_HIDPI_IMAGES = YES; EXECUTABLE_PREFIX = lib; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -5169,8 +5727,6 @@ 31EEAC6D156AB52600714D05 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = YES; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -5178,8 +5734,6 @@ 31EEAC6E156AB52600714D05 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_GENERATE_TEST_COVERAGE_FILES = NO; - GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -5187,7 +5741,6 @@ 31FCAE1017692403008C034C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -5195,7 +5748,6 @@ 31FCAE1117692403008C034C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -5203,30 +5755,87 @@ 6313D46F18A400B200EB03EF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - PRODUCT_NAME = gcbench; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; 6313D47018A400B200EB03EF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - PRODUCT_NAME = gcbench; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; 6313D47118A400B200EB03EF /* RASH */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - PRODUCT_NAME = gcbench; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = RASH; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 2215A9AD192A47BB00E9E2CE /* Build configuration list for PBXAggregateTarget "testci" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2215A9AE192A47BB00E9E2CE /* Debug */, + 2215A9AF192A47BB00E9E2CE /* Release */, + 2215A9B0192A47BB00E9E2CE /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2215A9B5192A47C500E9E2CE /* Build configuration list for PBXAggregateTarget "testansi" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2215A9B6192A47C500E9E2CE /* Debug */, + 2215A9B7192A47C500E9E2CE /* Release */, + 2215A9B8192A47C500E9E2CE /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2215A9BD192A47CE00E9E2CE /* Build configuration list for PBXAggregateTarget "testall" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2215A9BE192A47CE00E9E2CE /* Debug */, + 2215A9BF192A47CE00E9E2CE /* Release */, + 2215A9C0192A47CE00E9E2CE /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2215A9C5192A47D500E9E2CE /* Build configuration list for PBXAggregateTarget "testpollnone" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2215A9C6192A47D500E9E2CE /* Debug */, + 2215A9C7192A47D500E9E2CE /* Release */, + 2215A9C8192A47D500E9E2CE /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2231BB5518CA97D8002D6322 /* Build configuration list for PBXNativeTarget "locbwcss" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2231BB5618CA97D8002D6322 /* Debug */, + 2231BB5718CA97D8002D6322 /* Release */, + 2231BB5818CA97D8002D6322 /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2231BB6318CA97DC002D6322 /* Build configuration list for PBXNativeTarget "locusss" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2231BB6418CA97DC002D6322 /* Debug */, + 2231BB6518CA97DC002D6322 /* Release */, + 2231BB6618CA97DC002D6322 /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 224CC795175E1821002FF81B /* Build configuration list for PBXNativeTarget "fotest" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -5277,6 +5886,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 22C2ACAB18BE400A006B3677 /* Build configuration list for PBXNativeTarget "nailboardtest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 22C2ACAC18BE400A006B3677 /* Debug */, + 22C2ACAD18BE400A006B3677 /* Release */, + 22C2ACAE18BE400A006B3677 /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 22CDE8F016E9E97E00366D0A /* Build configuration list for PBXAggregateTarget "testrun" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -5287,6 +5906,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 22F846B918F437B900982BA7 /* Build configuration list for PBXNativeTarget "lockut" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 22F846BA18F437B900982BA7 /* Debug */, + 22F846BB18F437B900982BA7 /* Release */, + 22F846BC18F437B900982BA7 /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 22FA177116E8D6FC0098B23F /* Build configuration list for PBXNativeTarget "amcssth" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -5297,6 +5926,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 22FACEE918880983000FDBC1 /* Build configuration list for PBXNativeTarget "airtest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 22FACEEA18880983000FDBC1 /* Debug */, + 22FACEEB18880983000FDBC1 /* Release */, + 22C2ACA118BE3FEC006B3677 /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 2D07B9741636FC9900DB751B /* Build configuration list for PBXNativeTarget "mpseventsql" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -5467,7 +6106,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "fbmtest" */ = { + 3114A653156E9596001E0AA3 /* Build configuration list for PBXNativeTarget "landtest" */ = { isa = XCConfigurationList; buildConfigurations = ( 3114A654156E9596001E0AA3 /* Debug */, diff --git a/code/mpsacl.h b/code/mpsacl.h index b9fc2913f6..85536ef4ed 100644 --- a/code/mpsacl.h +++ b/code/mpsacl.h @@ -1,7 +1,7 @@ /* mpsacl.h: MEMORY POOL SYSTEM ARENA CLASS "CL" * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef mpsacl_h @@ -10,8 +10,8 @@ #include "mps.h" /* Client arena base address argument */ -extern const struct mps_key_s _mps_key_arena_cl_addr; -#define MPS_KEY_ARENA_CL_BASE (&_mps_key_arena_cl_addr) +extern const struct mps_key_s _mps_key_ARENA_CL_BASE; +#define MPS_KEY_ARENA_CL_BASE (&_mps_key_ARENA_CL_BASE) #define MPS_KEY_ARENA_CL_BASE_FIELD addr extern mps_arena_class_t mps_arena_class_cl(void); @@ -22,7 +22,7 @@ extern mps_arena_class_t mps_arena_class_cl(void); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpsavm.h b/code/mpsavm.h index 5eae2ef201..05beebf05c 100644 --- a/code/mpsavm.h +++ b/code/mpsavm.h @@ -11,7 +11,6 @@ extern mps_arena_class_t mps_arena_class_vm(void); -extern mps_arena_class_t mps_arena_class_vmnz(void); /* The vm arena class supports extensions to the arena protocol: */ diff --git a/code/mpscamc.h b/code/mpscamc.h index 710eea2f26..c02261dffe 100644 --- a/code/mpscamc.h +++ b/code/mpscamc.h @@ -1,7 +1,7 @@ /* mpscamc.h: MEMORY POOL SYSTEM CLASS "AMC" * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef mpscamc_h @@ -9,8 +9,8 @@ #include "mps.h" -extern mps_class_t mps_class_amc(void); -extern mps_class_t mps_class_amcz(void); +extern mps_pool_class_t mps_class_amc(void); +extern mps_pool_class_t mps_class_amcz(void); typedef void (*mps_amc_apply_stepper_t)(mps_addr_t, void *, size_t); extern void mps_amc_apply(mps_pool_t, mps_amc_apply_stepper_t, @@ -21,7 +21,7 @@ extern void mps_amc_apply(mps_pool_t, mps_amc_apply_stepper_t, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpscams.h b/code/mpscams.h index 5c1cc1b6f8..49a3de484c 100644 --- a/code/mpscams.h +++ b/code/mpscams.h @@ -1,7 +1,7 @@ /* mpscams.h: MEMORY POOL SYSTEM CLASS "AMS" * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. */ @@ -10,19 +10,19 @@ #include "mps.h" -extern const struct mps_key_s _mps_key_ams_support_ambiguous; -#define MPS_KEY_AMS_SUPPORT_AMBIGUOUS (&_mps_key_ams_support_ambiguous) +extern const struct mps_key_s _mps_key_AMS_SUPPORT_AMBIGUOUS; +#define MPS_KEY_AMS_SUPPORT_AMBIGUOUS (&_mps_key_AMS_SUPPORT_AMBIGUOUS) #define MPS_KEY_AMS_SUPPORT_AMBIGUOUS_FIELD b -extern mps_class_t mps_class_ams(void); -extern mps_class_t mps_class_ams_debug(void); +extern mps_pool_class_t mps_class_ams(void); +extern mps_pool_class_t mps_class_ams_debug(void); #endif /* mpscams_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpscawl.h b/code/mpscawl.h index 4c0af1fb17..087e927bf7 100644 --- a/code/mpscawl.h +++ b/code/mpscawl.h @@ -1,7 +1,7 @@ /* mpscaawl.h: MEMORY POOL SYSTEM CLASS "AWL" * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef mpscawl_h @@ -9,11 +9,11 @@ #include "mps.h" -extern const struct mps_key_s _mps_key_awl_find_dependent; -#define MPS_KEY_AWL_FIND_DEPENDENT (&_mps_key_awl_find_dependent) +extern const struct mps_key_s _mps_key_AWL_FIND_DEPENDENT; +#define MPS_KEY_AWL_FIND_DEPENDENT (&_mps_key_AWL_FIND_DEPENDENT) #define MPS_KEY_AWL_FIND_DEPENDENT_FIELD addr_method -extern mps_class_t mps_class_awl(void); +extern mps_pool_class_t mps_class_awl(void); typedef mps_addr_t (*mps_awl_find_dependent_t)(mps_addr_t addr); @@ -22,7 +22,7 @@ typedef mps_addr_t (*mps_awl_find_dependent_t)(mps_addr_t addr); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpsclo.h b/code/mpsclo.h index 15813bbfde..1e1681009d 100644 --- a/code/mpsclo.h +++ b/code/mpsclo.h @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef mpsclo_h @@ -10,14 +10,14 @@ #include "mps.h" -extern mps_class_t mps_class_lo(void); +extern mps_pool_class_t mps_class_lo(void); #endif /* mpsclo_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpscmfs.h b/code/mpscmfs.h index cc0a411260..bb3b1013cf 100644 --- a/code/mpscmfs.h +++ b/code/mpscmfs.h @@ -1,7 +1,7 @@ /* mpscamfs.h: MEMORY POOL SYSTEM CLASS "MFS" * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef mpscmfs_h @@ -9,18 +9,18 @@ #include "mps.h" -extern const struct mps_key_s _mps_key_mfs_unit_size; -#define MPS_KEY_MFS_UNIT_SIZE (&_mps_key_mfs_unit_size) +extern const struct mps_key_s _mps_key_MFS_UNIT_SIZE; +#define MPS_KEY_MFS_UNIT_SIZE (&_mps_key_MFS_UNIT_SIZE) #define MPS_KEY_MFS_UNIT_SIZE_FIELD size -extern mps_class_t mps_class_mfs(void); +extern mps_pool_class_t mps_class_mfs(void); #endif /* mpscmfs_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpscmv.h b/code/mpscmv.h index 805db19b8a..871ab7d4f6 100644 --- a/code/mpscmv.h +++ b/code/mpscmv.h @@ -1,7 +1,7 @@ /* mpscmv.h: MEMORY POOL SYSTEM CLASS "MV" * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef mpscmv_h @@ -9,17 +9,18 @@ #include "mps.h" -extern size_t mps_mv_free_size(mps_pool_t mps_pool); -extern size_t mps_mv_size(mps_pool_t mps_pool); -extern mps_class_t mps_class_mv(void); -extern mps_class_t mps_class_mv_debug(void); +#define mps_mv_free_size mps_pool_free_size +#define mps_mv_size mps_pool_total_size + +extern mps_pool_class_t mps_class_mv(void); +extern mps_pool_class_t mps_class_mv_debug(void); #endif /* mpscmv_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpscmv2.h b/code/mpscmv2.h index 8490b8f311..8925d38d3b 100644 --- a/code/mpscmv2.h +++ b/code/mpscmv2.h @@ -1,43 +1,24 @@ /* mpscmv2.h: MEMORY POOL SYSTEM CLASS "MVT" * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * + * The MVT pool class used to be known as "MV2" in some places: this + * header provides backwards compatibility for prograns that included + * it under its old name. */ #ifndef mpscmv2_h #define mpscmv2_h -#include "mps.h" - -/* The mvt pool class has five extra parameters to mps_pool_create: - * mps_res_t mps_pool_create(mps_pool_t * pool, mps_arena_t arena, - * mps_class_t mvt_class, - * size_t minimum_size, - * size_t mean_size, - * size_t maximum_size, - * mps_count_t reserve_depth - * mps_count_t fragmentation_limit); - * minimum_, mean_, and maximum_size are the mimimum, mean, and - * maximum (typical) size of objects expected to be allocated in the - * pool. reserve_depth is a measure of the expected hysteresis of the - * object population. fragmentation_limit is a percentage (between 0 - * and 100): if the free space managed by the pool exceeds the - * specified percentage, the pool will resort to a "first fit" - * allocation policy. - */ -extern mps_class_t mps_class_mvt(void); - -/* The mvt pool class supports two extensions to the pool protocol: - size and free_size. */ -extern size_t mps_mvt_free_size(mps_pool_t mps_pool); -extern size_t mps_mvt_size(mps_pool_t mps_pool); +#include "mpscmvt.h" #endif /* mpscmv2_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpscmvff.h b/code/mpscmvff.h index f8bc97b7f3..2f468c5e48 100644 --- a/code/mpscmvff.h +++ b/code/mpscmvff.h @@ -1,7 +1,7 @@ /* mpscmvff.h: MEMORY POOL SYSTEM CLASS "MVFF" * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef mpscmvff_h @@ -9,27 +9,28 @@ #include "mps.h" -extern const struct mps_key_s _mps_key_mvff_slot_high; -#define MPS_KEY_MVFF_SLOT_HIGH (&_mps_key_mvff_slot_high) +extern const struct mps_key_s _mps_key_MVFF_SLOT_HIGH; +#define MPS_KEY_MVFF_SLOT_HIGH (&_mps_key_MVFF_SLOT_HIGH) #define MPS_KEY_MVFF_SLOT_HIGH_FIELD b -extern const struct mps_key_s _mps_key_mvff_arena_high; -#define MPS_KEY_MVFF_ARENA_HIGH (&_mps_key_mvff_arena_high) +extern const struct mps_key_s _mps_key_MVFF_ARENA_HIGH; +#define MPS_KEY_MVFF_ARENA_HIGH (&_mps_key_MVFF_ARENA_HIGH) #define MPS_KEY_MVFF_ARENA_HIGH_FIELD b -extern const struct mps_key_s _mps_key_mvff_first_fit; -#define MPS_KEY_MVFF_FIRST_FIT (&_mps_key_mvff_first_fit) +extern const struct mps_key_s _mps_key_MVFF_FIRST_FIT; +#define MPS_KEY_MVFF_FIRST_FIT (&_mps_key_MVFF_FIRST_FIT) #define MPS_KEY_MVFF_FIRST_FIT_FIELD b -extern size_t mps_mvff_free_size(mps_pool_t mps_pool); -extern size_t mps_mvff_size(mps_pool_t mps_pool); -extern mps_class_t mps_class_mvff(void); -extern mps_class_t mps_class_mvff_debug(void); +#define mps_mvff_free_size mps_pool_free_size +#define mps_mvff_size mps_pool_total_size + +extern mps_pool_class_t mps_class_mvff(void); +extern mps_pool_class_t mps_class_mvff_debug(void); #endif /* mpscmvff_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpscmvt.h b/code/mpscmvt.h index f46e43267d..b3a6273468 100644 --- a/code/mpscmvt.h +++ b/code/mpscmvt.h @@ -1,7 +1,7 @@ /* mpscmvt.h: MEMORY POOL SYSTEM CLASS "MVT" * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef mpscmvt_h @@ -9,42 +9,24 @@ #include "mps.h" -extern const struct mps_key_s _mps_key_mvt_reserve_depth; -#define MPS_KEY_MVT_RESERVE_DEPTH (&_mps_key_mvt_reserve_depth) +extern const struct mps_key_s _mps_key_MVT_RESERVE_DEPTH; +#define MPS_KEY_MVT_RESERVE_DEPTH (&_mps_key_MVT_RESERVE_DEPTH) #define MPS_KEY_MVT_RESERVE_DEPTH_FIELD count -extern const struct mps_key_s _mps_key_mvt_frag_limit; -#define MPS_KEY_MVT_FRAG_LIMIT (&_mps_key_mvt_frag_limit) +extern const struct mps_key_s _mps_key_MVT_FRAG_LIMIT; +#define MPS_KEY_MVT_FRAG_LIMIT (&_mps_key_MVT_FRAG_LIMIT) #define MPS_KEY_MVT_FRAG_LIMIT_FIELD d -/* The mvt pool class has five extra parameters to mps_pool_create: - * mps_res_t mps_pool_create(mps_pool_t * pool, mps_arena_t arena, - * mps_class_t mvt_class, - * size_t minimum_size, - * size_t mean_size, - * size_t maximum_size, - * mps_count_t reserve_depth - * mps_count_t fragmentation_limit); - * minimum_, mean_, and maximum_size are the mimimum, mean, and - * maximum (typical) size of objects expected to be allocated in the - * pool. reserve_depth is a measure of the expected hysteresis of the - * object population. fragmentation_limit is a percentage (between 0 - * and 100): if the free space managed by the pool exceeds the - * specified percentage, the pool will resort to a "first fit" - * allocation policy. - */ -extern mps_class_t mps_class_mvt(void); +extern mps_pool_class_t mps_class_mvt(void); -/* The mvt pool class supports two extensions to the pool protocol: - size and free_size. */ -extern size_t mps_mvt_free_size(mps_pool_t mps_pool); -extern size_t mps_mvt_size(mps_pool_t mps_pool); +#define mps_mvt_free_size mps_pool_free_size +#define mps_mvt_size mps_pool_total_size #endif /* mpscmvt_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpscsnc.h b/code/mpscsnc.h index 8a87d7b5d6..9f95faedb5 100644 --- a/code/mpscsnc.h +++ b/code/mpscsnc.h @@ -1,7 +1,7 @@ /* mpscsnc.h: MEMORY POOL SYSTEM CLASS "SNC" * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #ifndef mpscsnc_h @@ -9,14 +9,14 @@ #include "mps.h" -extern mps_class_t mps_class_snc(void); +extern mps_pool_class_t mps_class_snc(void); #endif /* mpscsnc_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpsi.c b/code/mpsi.c index 1d52208be9..283fbc6123 100644 --- a/code/mpsi.c +++ b/code/mpsi.c @@ -1,14 +1,14 @@ /* mpsi.c: MEMORY POOL SYSTEM C INTERFACE LAYER * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * .purpose: This code bridges between the MPS interface to C, * , and the internal MPM interfaces, as defined by * . .purpose.check: It performs checking of the C client's * usage of the MPS Interface. .purpose.thread: It excludes multiple - * threads from the MPM by locking the Arena (see .thread-safety). + * threads from the MPM by locking the Arena (see ). * * .design: * @@ -19,6 +19,11 @@ * (between ArenaEnter and ArenaLeave) as this will leave the Arena in * an unsuitable state for re-entry. * + * .note.avert: Use AVERT only when "inside" the Arena (between + * ArenaEnter and ArenaLeave), as it's not thread-safe in all + * varieties. Use AVER(TESTT) otherwise. See + * . + * * * TRANSGRESSIONS (rule.impl.trans) * @@ -46,11 +51,6 @@ #include "mpm.h" #include "mps.h" #include "sac.h" -#include "chain.h" - -/* TODO: Remove these includes when varargs support is removed. */ -#include "mpsacl.h" -#include "mpsavm.h" #include @@ -70,6 +70,7 @@ SRCID(mpsi, "$Id$"); * .check.enum.cast: enum comparisons have to be cast to avoid a warning * from the SunPro C compiler. See builder.sc.warn.enum. */ +ATTRIBUTE_UNUSED static Bool mpsi_check(void) { CHECKL(COMPATTYPE(mps_res_t, Res)); @@ -197,7 +198,7 @@ mps_res_t mps_arena_commit_limit_set(mps_arena_t arena, size_t limit) res = ArenaSetCommitLimit(arena, limit); ArenaLeave(arena); - return res; + return (mps_res_t)res; } void mps_arena_spare_commit_limit_set(mps_arena_t arena, size_t limit) @@ -247,7 +248,7 @@ void mps_arena_park(mps_arena_t arena) void mps_arena_expose(mps_arena_t arena) { ArenaEnter(arena); - ArenaExposeRemember(ArenaGlobals(arena), 0); + ArenaExposeRemember(ArenaGlobals(arena), FALSE); ArenaLeave(arena); } @@ -255,7 +256,7 @@ void mps_arena_expose(mps_arena_t arena) void mps_arena_unsafe_expose_remember_protection(mps_arena_t arena) { ArenaEnter(arena); - ArenaExposeRemember(ArenaGlobals(arena), 1); + ArenaExposeRemember(ArenaGlobals(arena), TRUE); ArenaLeave(arena); } @@ -273,7 +274,7 @@ mps_res_t mps_arena_start_collect(mps_arena_t arena) ArenaEnter(arena); res = ArenaStartCollect(ArenaGlobals(arena), TraceStartWhyCLIENTFULL_INCREMENTAL); ArenaLeave(arena); - return res; + return (mps_res_t)res; } mps_res_t mps_arena_collect(mps_arena_t arena) @@ -282,7 +283,7 @@ mps_res_t mps_arena_collect(mps_arena_t arena) ArenaEnter(arena); res = ArenaCollect(ArenaGlobals(arena), TraceStartWhyCLIENTFULL_BLOCK); ArenaLeave(arena); - return res; + return (mps_res_t)res; } mps_bool_t mps_arena_step(mps_arena_t arena, @@ -307,7 +308,7 @@ mps_res_t mps_arena_create(mps_arena_t *mps_arena_o, va_start(varargs, mps_arena_class); res = mps_arena_create_v(mps_arena_o, mps_arena_class, varargs); va_end(varargs); - return res; + return (mps_res_t)res; } @@ -318,7 +319,7 @@ mps_res_t mps_arena_create_v(mps_arena_t *mps_arena_o, va_list varargs) { mps_arg_s args[MPS_ARGS_MAX]; - AVERT(ArenaClass, arena_class); + AVER(TESTT(ArenaClass, arena_class)); arena_class->varargs(args, varargs); return mps_arena_create_k(mps_arena_o, arena_class, args); } @@ -341,7 +342,7 @@ mps_res_t mps_arena_create_k(mps_arena_t *mps_arena_o, res = ArenaCreate(&arena, arena_class, mps_args); if (res != ResOK) - return res; + return (mps_res_t)res; ArenaLeave(arena); *mps_arena_o = (mps_arena_t)arena; @@ -459,13 +460,14 @@ mps_res_t mps_fmt_create_k(mps_fmt_t *mps_fmt_o, AVER(mps_fmt_o != NULL); AVERT(Arena, arena); - AVER(ArgListCheck(args)); + AVERT(ArgList, args); res = FormatCreate(&format, arena, args); ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_fmt_o = (mps_fmt_t)format; return MPS_RES_OK; } @@ -503,7 +505,8 @@ mps_res_t mps_fmt_create_A(mps_fmt_t *mps_fmt_o, ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_fmt_o = (mps_fmt_t)format; return MPS_RES_OK; } @@ -537,7 +540,8 @@ mps_res_t mps_fmt_create_B(mps_fmt_t *mps_fmt_o, ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_fmt_o = (mps_fmt_t)format; return MPS_RES_OK; } @@ -571,7 +575,8 @@ mps_res_t mps_fmt_create_auto_header(mps_fmt_t *mps_fmt_o, ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_fmt_o = (mps_fmt_t)format; return MPS_RES_OK; } @@ -603,7 +608,8 @@ mps_res_t mps_fmt_create_fixed(mps_fmt_t *mps_fmt_o, ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_fmt_o = (mps_fmt_t)format; return MPS_RES_OK; } @@ -627,7 +633,7 @@ void mps_fmt_destroy(mps_fmt_t format) mps_res_t mps_pool_create(mps_pool_t *mps_pool_o, mps_arena_t arena, - mps_class_t mps_class, ...) + mps_pool_class_t mps_class, ...) { mps_res_t res; va_list varargs; @@ -638,16 +644,16 @@ mps_res_t mps_pool_create(mps_pool_t *mps_pool_o, mps_arena_t arena, } mps_res_t mps_pool_create_v(mps_pool_t *mps_pool_o, mps_arena_t arena, - mps_class_t class, va_list varargs) + mps_pool_class_t pool_class, va_list varargs) { mps_arg_s args[MPS_ARGS_MAX]; - AVERT(PoolClass, class); - class->varargs(args, varargs); - return mps_pool_create_k(mps_pool_o, arena, class, args); + AVER(TESTT(PoolClass, pool_class)); + pool_class->varargs(args, varargs); + return mps_pool_create_k(mps_pool_o, arena, pool_class, args); } mps_res_t mps_pool_create_k(mps_pool_t *mps_pool_o, mps_arena_t arena, - mps_class_t class, mps_arg_s args[]) + mps_pool_class_t pool_class, mps_arg_s args[]) { Pool pool; Res res; @@ -656,16 +662,17 @@ mps_res_t mps_pool_create_k(mps_pool_t *mps_pool_o, mps_arena_t arena, AVER(mps_pool_o != NULL); AVERT(Arena, arena); - AVERT(PoolClass, class); - AVER(ArgListCheck(args)); + AVERT(PoolClass, pool_class); + AVERT(ArgList, args); - res = PoolCreate(&pool, arena, class, args); + res = PoolCreate(&pool, arena, pool_class, args); ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_pool_o = (mps_pool_t)pool; - return res; + return MPS_RES_OK; } void mps_pool_destroy(mps_pool_t pool) @@ -682,6 +689,40 @@ void mps_pool_destroy(mps_pool_t pool) ArenaLeave(arena); } +size_t mps_pool_total_size(mps_pool_t pool) +{ + Arena arena; + Size size; + + AVER(TESTT(Pool, pool)); + arena = PoolArena(pool); + + ArenaEnter(arena); + + size = PoolTotalSize(pool); + + ArenaLeave(arena); + + return (size_t)size; +} + +size_t mps_pool_free_size(mps_pool_t pool) +{ + Arena arena; + Size size; + + AVER(TESTT(Pool, pool)); + arena = PoolArena(pool); + + ArenaEnter(arena); + + size = PoolFreeSize(pool); + + ArenaLeave(arena); + + return (size_t)size; +} + mps_res_t mps_alloc(mps_addr_t *p_o, mps_pool_t pool, size_t size) { @@ -709,7 +750,8 @@ mps_res_t mps_alloc(mps_addr_t *p_o, mps_pool_t pool, size_t size) ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *p_o = (mps_addr_t)p; return MPS_RES_OK; } @@ -806,7 +848,7 @@ mps_res_t mps_ap_create_k(mps_ap_t *mps_ap_o, ArenaLeave(arena); if (res != ResOK) - return res; + return (mps_res_t)res; *mps_ap_o = BufferAP(buf); return MPS_RES_OK; @@ -886,7 +928,7 @@ mps_bool_t (mps_commit)(mps_ap_t mps_ap, mps_addr_t p, size_t size) AVER(p != NULL); AVER(size > 0); AVER(p == mps_ap->init); - AVER((void *)((char *)mps_ap->init + size) == mps_ap->alloc); + AVER(PointerAdd(mps_ap->init, size) == mps_ap->alloc); return mps_commit(mps_ap, p, size); } @@ -1011,7 +1053,8 @@ mps_res_t mps_ap_fill(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size) ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *p_o = (mps_addr_t)p; return MPS_RES_OK; } @@ -1042,7 +1085,8 @@ mps_res_t mps_ap_fill_with_reservoir_permit(mps_addr_t *p_o, mps_ap_t mps_ap, ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *p_o = (mps_addr_t)p; return MPS_RES_OK; } @@ -1097,7 +1141,8 @@ mps_res_t mps_sac_create(mps_sac_t *mps_sac_o, mps_pool_t pool, ArenaLeave(arena); - if (res != ResOK) return (mps_res_t)res; + if (res != ResOK) + return (mps_res_t)res; *mps_sac_o = ExternalSACOfSAC(sac); return (mps_res_t)res; } @@ -1159,7 +1204,8 @@ mps_res_t mps_sac_fill(mps_addr_t *p_o, mps_sac_t mps_sac, size_t size, ArenaLeave(arena); - if (res != ResOK) return (mps_res_t)res; + if (res != ResOK) + return (mps_res_t)res; *p_o = (mps_addr_t)p; return (mps_res_t)res; } @@ -1195,7 +1241,7 @@ mps_res_t mps_sac_alloc(mps_addr_t *p_o, mps_sac_t mps_sac, size_t size, AVER(size > 0); MPS_SAC_ALLOC_FAST(res, *p_o, mps_sac, size, (has_reservoir_permit != 0)); - return res; + return (mps_res_t)res; } @@ -1232,7 +1278,8 @@ mps_res_t mps_root_create(mps_root_t *mps_root_o, mps_arena_t arena, ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_root_o = (mps_root_t)root; return MPS_RES_OK; } @@ -1261,7 +1308,8 @@ mps_res_t mps_root_create_table(mps_root_t *mps_root_o, mps_arena_t arena, ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_root_o = (mps_root_t)root; return MPS_RES_OK; } @@ -1292,7 +1340,8 @@ mps_res_t mps_root_create_table_masked(mps_root_t *mps_root_o, ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_root_o = (mps_root_t)root; return MPS_RES_OK; } @@ -1314,7 +1363,8 @@ mps_res_t mps_root_create_fmt(mps_root_t *mps_root_o, mps_arena_t arena, res = RootCreateFmt(&root, arena, rank, mode, scan, (Addr)base, (Addr)limit); ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_root_o = (mps_root_t)root; return MPS_RES_OK; } @@ -1334,6 +1384,7 @@ mps_res_t mps_root_create_reg(mps_root_t *mps_root_o, mps_arena_t arena, AVER(mps_reg_scan != NULL); AVER(mps_reg_scan == mps_stack_scan_ambig); /* .reg.scan */ AVER(reg_scan_p != NULL); /* stackBot */ + AVER(AddrIsAligned(reg_scan_p, sizeof(Word))); AVER(rank == mps_rank_ambig()); AVER(mps_rm == (mps_rm_t)0); @@ -1343,7 +1394,8 @@ mps_res_t mps_root_create_reg(mps_root_t *mps_root_o, mps_arena_t arena, ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_root_o = (mps_root_t)root; return MPS_RES_OK; } @@ -1385,7 +1437,7 @@ void (mps_tramp)(void **r_o, AVER(FUNCHECK(f)); /* Can't check p and s as they are interpreted by the client */ - ProtTramp(r_o, f, p, s); + *r_o = (*f)(p, s); } @@ -1403,7 +1455,8 @@ mps_res_t mps_thread_reg(mps_thr_t *mps_thr_o, mps_arena_t arena) ArenaLeave(arena); - if (res != ResOK) return res; + if (res != ResOK) + return (mps_res_t)res; *mps_thr_o = (mps_thr_t)thread; return MPS_RES_OK; } @@ -1504,7 +1557,7 @@ mps_res_t mps_finalize(mps_arena_t arena, mps_addr_t *refref) res = ArenaFinalize(arena, object); ArenaLeave(arena); - return res; + return (mps_res_t)res; } @@ -1521,7 +1574,7 @@ mps_res_t mps_definalize(mps_arena_t arena, mps_addr_t *refref) res = ArenaDefinalize(arena, object); ArenaLeave(arena); - return res; + return (mps_res_t)res; } @@ -1737,12 +1790,12 @@ mps_word_t mps_telemetry_control(mps_word_t resetMask, mps_word_t flipMask) void mps_telemetry_set(mps_word_t setMask) { - EventControl((Word)setMask, (Word)setMask); + (void)EventControl((Word)setMask, (Word)setMask); } void mps_telemetry_reset(mps_word_t resetMask) { - EventControl((Word)resetMask, 0); + (void)EventControl((Word)resetMask, 0); } mps_word_t mps_telemetry_get(void) @@ -1827,7 +1880,7 @@ mps_res_t mps_ap_alloc_pattern_end(mps_ap_t mps_ap, ArenaPoll(ArenaGlobals(arena)); /* .poll */ ArenaLeave(arena); - return res; + return (mps_res_t)res; } @@ -1912,7 +1965,7 @@ mps_res_t mps_chain_create(mps_chain_t *chain_o, mps_arena_t arena, ArenaLeave(arena); if (res != ResOK) - return res; + return (mps_res_t)res; *chain_o = (mps_chain_t)chain; return MPS_RES_OK; } @@ -1933,9 +1986,27 @@ void mps_chain_destroy(mps_chain_t chain) } +/* _mps_args_set_key -- set the key for a keyword argument + * + * This sets the key for the i'th keyword argument in the array args, + * with bounds checking on i. It is used by the MPS_ARGS_BEGIN, + * MPS_ARGS_ADD, and MPS_ARGS_DONE macros in mps.h. + * + * We implement this in a function here, rather than in a macro in + * mps.h, so that we can use AVER to do the bounds checking. + */ + +void _mps_args_set_key(mps_arg_s args[MPS_ARGS_MAX], unsigned i, + mps_key_t key) +{ + AVER(i < MPS_ARGS_MAX); + args[i].key = key; +} + + /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpsicv.c b/code/mpsicv.c index 142498a450..44036efead 100644 --- a/code/mpsicv.c +++ b/code/mpsicv.c @@ -1,7 +1,7 @@ /* mpsicv.c: MPSI COVERAGE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. */ @@ -15,18 +15,13 @@ #include "fmtdytst.h" #include "mps.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -# include "mpsw3.h" -#endif -#include -#include -#include -#include + +#include /* printf */ #define exactRootsCOUNT 49 #define ambigRootsCOUNT 49 -#define OBJECTS 200000 +#define OBJECTS 100000 #define patternFREQ 100 /* objNULL needs to be odd so that it's ignored in exactRoots. */ @@ -94,8 +89,6 @@ struct tlonglong { /* alignmentTest -- test default alignment is acceptable */ -#define max(a, b) (((a) > (b)) ? (a) : (b)) - static void alignmentTest(mps_arena_t arena) { mps_pool_t pool; @@ -137,11 +130,13 @@ static mps_addr_t make(void) do { MPS_RESERVE_BLOCK(res, pMps, ap, sizeMps); - if (res != MPS_RES_OK) die(res, "MPS_RESERVE_BLOCK"); + if (res != MPS_RES_OK) + die(res, "MPS_RESERVE_BLOCK"); HeaderInit(pMps); pCli = PtrMps2Cli(pMps); res = dylan_init(pCli, sizeCli, exactRoots, exactRootsCOUNT); - if (res != MPS_RES_OK) die(res, "dylan_init"); + if (res != MPS_RES_OK) + die(res, "dylan_init"); } while(!mps_commit(ap, pMps, sizeMps)); return pCli; @@ -160,11 +155,13 @@ static mps_addr_t make_with_permit(void) do { MPS_RESERVE_WITH_RESERVOIR_PERMIT_BLOCK(res, pMps, ap, sizeMps); - if (res != MPS_RES_OK) die(res, "MPS_RESERVE_WITH_RESERVOIR_PERMIT_BLOCK"); + if (res != MPS_RES_OK) + die(res, "MPS_RESERVE_WITH_RESERVOIR_PERMIT_BLOCK"); HeaderInit(pMps); pCli = PtrMps2Cli(pMps); res = dylan_init(pCli, sizeCli, exactRoots, exactRootsCOUNT); - if (res != MPS_RES_OK) die(res, "dylan_init"); + if (res != MPS_RES_OK) + die(res, "dylan_init"); } while(!mps_commit(ap, pMps, sizeMps)); return pCli; @@ -183,11 +180,13 @@ static mps_addr_t make_no_inline(void) do { res = (mps_reserve)(&pMps, ap, sizeMps); - if (res != MPS_RES_OK) die(res, "(mps_reserve)"); + if (res != MPS_RES_OK) + die(res, "(mps_reserve)"); HeaderInit(pMps); pCli = PtrMps2Cli(pMps); res = dylan_init(pCli, sizeCli, exactRoots, exactRootsCOUNT); - if (res != MPS_RES_OK) die(res, "dylan_init"); + if (res != MPS_RES_OK) + die(res, "dylan_init"); } while(!(mps_commit)(ap, pMps, sizeMps)); return pCli; @@ -234,6 +233,7 @@ static void ap_create_v_test(mps_pool_t pool, ...) /* addr_pool_test * * intended to test: + * mps_arena_has_addr * mps_addr_pool * mps_addr_fmt */ @@ -271,6 +271,7 @@ static void addr_pool_test(mps_arena_t arena, addr = obj1; pool = poolDistinguished; fmt = fmtDistinguished; + cdie(mps_arena_has_addr(arena, addr), "mps_arena_has_addr 0a"); b = mps_addr_pool(&pool, arena, addr); /* printf("b %d; pool %p; sig %lx\n", b, (void *)pool, b ? ((mps_word_t*)pool)[0] : (mps_word_t)0); */ @@ -284,6 +285,7 @@ static void addr_pool_test(mps_arena_t arena, addr = obj2; pool = poolDistinguished; fmt = fmtDistinguished; + cdie(mps_arena_has_addr(arena, addr), "mps_arena_has_addr 0b"); b = mps_addr_pool(&pool, arena, addr); /* printf("b %d; pool %p; sig %lx\n", b, (void *)pool, b ? ((mps_word_t*)pool)[0] : (mps_word_t)0); */ @@ -297,6 +299,7 @@ static void addr_pool_test(mps_arena_t arena, addr = &pool; /* point at stack, not in any chunk */ pool = poolDistinguished; fmt = fmtDistinguished; + cdie(mps_arena_has_addr(arena, addr) == FALSE, "mps_arena_has_addr 5"); b = mps_addr_pool(&pool, arena, addr); cdie(b == FALSE && pool == poolDistinguished, "mps_addr_pool 5"); b = mps_addr_fmt(&fmt, arena, addr); @@ -321,6 +324,7 @@ static mps_res_t root_single(mps_ss_t ss, void *p, size_t s) * mps_arena_reserved * incidentally tests: * mps_alloc + * mps_arena_commit_limit_set * mps_class_mv * mps_pool_create * mps_pool_destroy @@ -404,7 +408,7 @@ static void *test(void *arg, size_t s) if (rnd() & 1) { printf("Using auto_header format.\n"); - EnsureHeaderFormat(&format, arena); + die(EnsureHeaderFormat(&format, arena), "EnsureHeaderFormat"); ap_headerSIZE = headerSIZE; /* from fmthe.h */ } else { printf("Using normal format (no implicit object header: client pointers point at start of storage).\n"); @@ -518,7 +522,8 @@ static void *test(void *arg, size_t s) if (rnd() % patternFREQ == 0) { switch(rnd() % 4) { - case 0: case 1: + case 0: /* fall through */ + case 1: die(mps_ap_alloc_pattern_begin(ap, ramp), "alloc_pattern_begin"); ++rampCount; break; @@ -530,7 +535,7 @@ static void *test(void *arg, size_t s) --rampCount; } break; - case 3: + default: die(mps_ap_alloc_pattern_reset(ap), "alloc_pattern_reset"); rampCount = 0; break; @@ -558,6 +563,8 @@ static void *test(void *arg, size_t s) mps_free(mv, alloced_obj, 32); alloc_v_test(mv); + + mps_arena_park(arena); mps_pool_destroy(mv); mps_ap_destroy(ap); mps_root_destroy(fmtRoot); @@ -583,8 +590,7 @@ int main(int argc, char *argv[]) void *r; void *marker = ▮ - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), TEST_ARENA_SIZE), "arena_create"); @@ -596,7 +602,6 @@ int main(int argc, char *argv[]) marker, (size_t)0), "root_create_reg"); - (mps_tramp)(&r, test, arena, 0); /* non-inlined trampoline */ mps_tramp(&r, test, arena, 0); mps_root_destroy(reg_root); mps_thread_dereg(thread); @@ -609,7 +614,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mpsliban.c b/code/mpsliban.c index e84b2aed00..2e391596e0 100644 --- a/code/mpsliban.c +++ b/code/mpsliban.c @@ -61,13 +61,20 @@ int mps_lib_fputs(const char *s, mps_lib_FILE *stream) } -static void mps_lib_assert_fail_default(const char *file, - unsigned line, +static void mps_lib_assert_fail_default(const char *file, unsigned line, const char *condition) { - fflush(stdout); /* synchronize */ - fprintf(stderr, "%s:%u: MPS ASSERTION FAILED: %s\n", file, line, condition); - fflush(stderr); /* make sure the message is output */ + /* Synchronize with stdout. */ + (void)fflush(stdout); + (void)fprintf(stderr, + "The MPS detected a problem!\n" + "%s:%u: MPS ASSERTION FAILED: %s\n" + "See the \"Assertions\" section in the reference manual:\n" + "http://www.ravenbrook.com/project/mps/master/manual/html/topic/error.html#assertions\n", + file, line, condition); + /* Ensure the message is output even if stderr is buffered. */ + (void)fflush(stderr); + mps_telemetry_flush(); ASSERT_ABORT(); /* see config.h */ } @@ -104,9 +111,6 @@ int (mps_lib_memcmp)(const void *s1, const void *s2, size_t n) } -/* @@@@ Platform specific conversion? */ -/* See http://devworld.apple.com/dev/techsupport/insidemac/OSUtilities/OSUtilities-94.html#MARKER-9-32 */ - /* If your platform has a low-resolution clock(), and there are * higher-resolution clocks readily available, then using one of those * will improve MPS scheduling decisions and the quality of telemetry diff --git a/code/mpstd.h b/code/mpstd.h index 88b4575155..1306281224 100644 --- a/code/mpstd.h +++ b/code/mpstd.h @@ -31,9 +31,12 @@ * Alignment of 4 would work, but the MS library uses 8 bytes for * doubles and __int64, so we choose that. The actual granularity of * VC malloc is 16! + * + * PellesC /Ze (Microsoft compatibility mode) defines _MSC_VER but + * isn't compatible enough for MPS purposes. */ -#if defined(_MSC_VER) && defined(_WIN32) && defined(_M_IX86) +#if defined(_MSC_VER) && defined(_WIN32) && defined(_M_IX86) && !defined(__POCC__) #if defined(CONFIG_PF_STRING) && ! defined(CONFIG_PF_W3I3MV) #error "specified CONFIG_PF_... inconsistent with detected w3i3mv" #endif @@ -58,7 +61,7 @@ * */ -#elif defined(_MSC_VER) && defined(_WIN32) && defined(_WIN64) && defined(_M_X64) +#elif defined(_MSC_VER) && defined(_WIN32) && defined(_WIN64) && defined(_M_X64) && !defined(__POCC__) #if defined(CONFIG_PF_STRING) && ! defined(CONFIG_PF_W3I6MV) #error "specified CONFIG_PF_... inconsistent with detected w3i6mv" #endif @@ -74,6 +77,47 @@ #define MPS_PF_ALIGN 16 +/* PellesC version 7.00.25 with /Ze option (Microsoft compatibility mode) + * Help node "Predefined preprocessor symbols (POCC)" + */ + +#elif defined(__POCC__) && defined(_WIN32) && defined(_M_IX86) +#if defined(CONFIG_PF_STRING) && ! defined(CONFIG_PF_W3I3PC) +#error "specified CONFIG_PF_... inconsistent with detected w3i3pc" +#endif +#define MPS_PF_W3I3PC +#define MPS_PF_STRING "w3i3pc" +#define MPS_OS_W3 +#define MPS_ARCH_I3 +#define MPS_BUILD_PC +#define MPS_T_WORD unsigned long +#define MPS_T_ULONGEST unsigned long +#define MPS_WORD_WIDTH 32 +#define MPS_WORD_SHIFT 5 +#define MPS_PF_ALIGN 8 + + +/* PellesC version 7.00.25 with /Ze option (Microsoft compatibility mode) + * and /Tarm64-coff (Create a COFF object file for a X64 processor). + * Help node "Predefined preprocessor symbols (POCC)" + */ + +#elif defined(__POCC__) && defined(_WIN32) && defined(_WIN64) && defined(_M_X64) +#if defined(CONFIG_PF_STRING) && ! defined(CONFIG_PF_W3I6PC) +#error "specified CONFIG_PF_... inconsistent with detected w3i6pc" +#endif +#define MPS_PF_W3I6PC +#define MPS_PF_STRING "w3i6pc" +#define MPS_OS_W3 +#define MPS_ARCH_I6 +#define MPS_BUILD_PC +#define MPS_T_WORD unsigned __int64 +#define MPS_T_ULONGEST unsigned __int64 +#define MPS_WORD_WIDTH 64 +#define MPS_WORD_SHIFT 6 +#define MPS_PF_ALIGN 16 + + /* GCC 4.0.1 (As supplied by Apple on Mac OS X 10.4.8 on an Intel Mac), * gcc -E -dM * And above for xcppgc. diff --git a/code/mpswin.h b/code/mpswin.h index 99317fb790..2f88fc25c1 100644 --- a/code/mpswin.h +++ b/code/mpswin.h @@ -1,7 +1,7 @@ /* mpswin.h: RAVENBROOK MEMORY POOL SYSTEM WINDOWS.H INTERFACE * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .readership: For MPS client application developers, MPS developers. * @@ -11,21 +11,14 @@ #ifndef mpswin_h #define mpswin_h -/* Suppress Visual C warnings from windows.h at warning level 4. */ -#ifdef MPS_BUILD_MV -#pragma warning(disable: 4115 4201 4209 4214) -#endif - +#ifndef WIN32_LEAN_AND_MEAN +/* Speed up the build process by excluding parts of windows.h that we + * don't use. See */ +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN +#else #include - -#ifdef MPS_BUILD_MV -#pragma warning(default: 4115 4201 4209 4214) -/* windows.h might also cause warnings about "unreferenced inline - * function has been removed". In Visual C, these can be turned off: - * #pragma warning(disable: 4514) - * But they are generated at the end of the compilation, so you have - * to turn them off permanently. - */ #endif #endif /* mpswin_h */ @@ -33,7 +26,7 @@ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/mv.nmk b/code/mv.nmk new file mode 100644 index 0000000000..d0759bad53 --- /dev/null +++ b/code/mv.nmk @@ -0,0 +1,78 @@ +# -*- makefile -*- +# +# mv.nmk: NMAKE FRAGMENT FOR MICROSOFT VISUAL C/C++ +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. +# +# This file is included by platform nmake files that use the Microsoft +# Visual C/C+ compiler. It defines the compiler-specific variables +# that the common nmake file fragment () requires. + +CC = cl +LIBMAN = lib +LINKER = link + +# /D_CRT_SECURE_NO_WARNINGS suppresses "This function or variable may +# be unsafe" warnings for standard C library functions fopen, getenv, +# snprintf, sscanf, strcpy, and so on. +# +# /Gs appears to be necessary to suppress stack checks. Stack checks +# (if not suppressed) generate a dependency on the C library, __chkesp, +# which causes the linker step to fail when building the DLL, mpsdy.dll. +CFLAGSCOMMONPRE = $(CFLAGSCOMMONPRE) /D_CRT_SECURE_NO_WARNINGS /W4 /WX /Gs /Fd$(PFM)\$(VARIETY) +LIBFLAGSCOMMON = $(LIBFLAGSCOMMON) /nologo + +# /MD means compile for multi-threaded environment with separate C library DLL. +# /MT means compile for multi-threaded environment. +# /ML means compile for single-threaded environment. +# A 'd' at the end means compile for debugging. +CRTFLAGSHOT = $(CRTFLAGSHOT) /MT +CRTFLAGSCOOL = $(CRTFLAGSCOOL) /MTd + +CFLAGSCOOL = $(CFLAGSCOOL) /Od + +LINKFLAGSCOMMON = $(LINKFLAGSCOMMON) /PDB:$*.pdb +LINKFLAGSHOT = $(LINKFLAGSHOT) libcmt.lib +LINKFLAGSCOOL = $(LINKFLAGSCOOL) libcmtd.lib + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2014 Ravenbrook Limited . +# All rights reserved. This is an open source license. Contact +# Ravenbrook for commercial licensing options. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Redistributions in any form must be accompanied by information on how +# to obtain complete source code for this software and any accompanying +# software that uses this software. The source code must either be +# included in the distribution or be available for no more than the cost +# of distribution plus a nominal fee, and must be freely redistributable +# under reasonable conditions. For an executable file, complete source +# code means the source code for all modules it contains. It does not +# include source code for modules or files that typically accompany the +# major components of the operating system on which the executable file +# runs. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/mv2test.c b/code/mv2test.c index 10f7f3353c..4670abbf07 100644 --- a/code/mv2test.c +++ b/code/mv2test.c @@ -1,25 +1,22 @@ /* mv2test.c: POOLMVT STRESS TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ -#include +#include #include -#include "mpstd.h" +#include #include -#include "mpscmvt.h" +#include "mpm.h" #include "mps.h" - -typedef mps_word_t mps_count_t; /* machine word (target dep.) */ - -#include "mpslib.h" #include "mpsavm.h" +#include "mpscmvt.h" +#include "mpslib.h" +#include "mpstd.h" #include "testlib.h" -#include - /* expdev() -- exponentially distributed random deviates * * From @@ -38,11 +35,9 @@ static double expdev(void) } -#define max(a, b) (((a) > (b)) ? (a) : (b)) - -static size_t min; -static size_t mean; -static size_t max; +static size_t size_min; +static size_t size_mean; +static size_t size_max; static int verbose = 0; static mps_pool_t pool; @@ -50,8 +45,8 @@ static size_t randomSize(unsigned long i) { /* Distribution centered on mean. Verify that allocations below min and above max are handled correctly */ - size_t s = (max - mean)/4; - size_t m = mean; + size_t s = (size_max - size_mean)/4; + size_t m = size_mean; double r; double x; @@ -73,13 +68,11 @@ static size_t randomSize(unsigned long i) #define TEST_SET_SIZE 1234 #define TEST_LOOPS 27 -#define alignUp(w, a) (((w) + (a) - 1) & ~((size_t)(a) - 1)) - -static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size) +static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size, mps_align_t align) { mps_res_t res; - size = alignUp(size, MPS_PF_ALIGN); + size = alignUp(size, align); do { MPS_RESERVE_BLOCK(res, *p, ap, size); @@ -91,8 +84,9 @@ static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size) } -static mps_res_t stress(mps_class_t class, mps_arena_t arena, - size_t (*size)(unsigned long i), mps_arg_s args[]) +static mps_res_t stress(mps_arena_t arena, mps_align_t align, + size_t (*size)(unsigned long i), + mps_pool_class_t pool_class, mps_arg_s args[]) { mps_res_t res; mps_ap_t ap; @@ -100,8 +94,9 @@ static mps_res_t stress(mps_class_t class, mps_arena_t arena, int *ps[TEST_SET_SIZE]; size_t ss[TEST_SET_SIZE]; - res = mps_pool_create_k(&pool, arena, class, args); - if(res != MPS_RES_OK) return res; + res = mps_pool_create_k(&pool, arena, pool_class, args); + if (res != MPS_RES_OK) + return res; die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate"); @@ -109,17 +104,21 @@ static mps_res_t stress(mps_class_t class, mps_arena_t arena, for(i=0; i. + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/nailboard.c b/code/nailboard.c new file mode 100644 index 0000000000..0b91ab35e1 --- /dev/null +++ b/code/nailboard.c @@ -0,0 +1,495 @@ +/* nailboard.c: NAILBOARD IMPLEMENTATION + * + * $Id$ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * .sources: . + */ + +#include "bt.h" +#include "check.h" +#include "mpm.h" +#include "nailboard.h" + +SRCID(nailboard, "$Id$"); + + +/* Log2 of scale factor between levels. See . */ +#define LEVEL_SHIFT MPS_WORD_SHIFT + + +/* nailboardLevels -- return the number of levels in a nailboard with + * the given number of nails. + * + * See + */ + +static Count nailboardLevels(Count nails) +{ + return (SizeFloorLog2((Size)nails) + LEVEL_SHIFT) / LEVEL_SHIFT; +} + + +/* nailboardNails -- return the total number of nails in the board */ + +static Count nailboardNails(Nailboard board) +{ + return RangeSize(&board->range) >> board->alignShift; +} + + +/* nailboardLevelBits -- return the number of bits in the bit table + * for the given level. + */ + +static Count nailboardLevelBits(Count nails, Index level) +{ + Shift shift = (Shift)(level * LEVEL_SHIFT); + return (nails + ((Count)1 << shift) - 1) >> shift; +} + +Bool NailboardCheck(Nailboard board) +{ + Index i; + Count nails; + CHECKS(Nailboard, board); + CHECKL(RangeCheck(&board->range)); + CHECKL(0 < board->levels); + nails = nailboardNails(board); + CHECKL(board->levels == nailboardLevels(nails)); + CHECKL(nails == nailboardLevelBits(nails, 0)); + CHECKL(nailboardLevelBits(nails, board->levels - 1) != 0); + CHECKL(nailboardLevelBits(nails, board->levels) == 1); + CHECKL(BoolCheck(board->newNails)); + for (i = 0; i < board->levels; ++i) { + CHECKL(board->level[i] != NULL); + } + return TRUE; +} + + +/* nailboardStructSize -- return the size of the nailboard structure, + * plus the array of pointers to levels. + */ + +static Size nailboardStructSize(Count levels) +{ + return offsetof(NailboardStruct, level) + sizeof(BT *) * levels; +} + + +/* nailboardSize -- return the total size of the nailboard + * + * This is the size of the nailboard structure plus the combined sizes + * of the bit tables. + */ + +static Size nailboardSize(Count nails, Count levels) +{ + Index i; + Size size; + size = nailboardStructSize(levels); + for (i = 0; i < levels; ++i) { + size += BTSize(nailboardLevelBits(nails, i)); + } + return size; +} + + +/* NailboardCreate -- allocate a nailboard + * + * Allocate a nailboard in the control pool for arena, to cover the + * range of addresses from base to limit (which must be non-empty). If + * successful, set *boardReturn to point to the nailboard and return + * ResOK. Otherwise, return a result code to indicate failure. + * + * alignment specifies the granularity of the nails: that is, the + * number of bytes covered by each nail. + */ + +Res NailboardCreate(Nailboard *boardReturn, Arena arena, Align alignment, + Addr base, Addr limit) +{ + void *p; + Nailboard board; + Shift alignShift; + Count nails, levels; + Index i; + Res res; + + AVER(boardReturn != NULL); + AVERT(Arena, arena); + AVERT(Align, alignment); + AVER(base < limit); + AVER(AddrIsAligned(base, alignment)); + AVER(AddrIsAligned(limit, alignment)); + + alignShift = SizeLog2((Size)alignment); + nails = AddrOffset(base, limit) >> alignShift; + levels = nailboardLevels(nails); + res = ControlAlloc(&p, arena, nailboardSize(nails, levels), FALSE); + if (res != ResOK) + return res; + + board = p; + RangeInit(&board->range, base, limit); + board->levels = levels; + board->alignShift = alignShift; + board->newNails = FALSE; + + p = PointerAdd(p, nailboardStructSize(levels)); + for (i = 0; i < levels; ++i) { + Count levelBits = nailboardLevelBits(nails, i); + AVER(levelBits > 0); + board->level[i] = p; + BTResRange(board->level[i], 0, levelBits); + p = PointerAdd(p, BTSize(levelBits)); + } + + board->sig = NailboardSig; + AVERT(Nailboard, board); + *boardReturn = board; + return ResOK; +} + + +/* NailboardDestroy -- destroy a nailboard */ + +void NailboardDestroy(Nailboard board, Arena arena) +{ + Count nails; + Size size; + + AVERT(Nailboard, board); + AVERT(Arena, arena); + + nails = nailboardNails(board); + size = nailboardSize(nails, board->levels); + + board->sig = SigInvalid; + ControlFree(arena, board, size); +} + + +/* NailboardClearNewNails -- clear the "new nails" flag */ + +void (NailboardClearNewNails)(Nailboard board) +{ + AVERT(Nailboard, board); + NailboardClearNewNails(board); +} + + +/* NailboardNewNails -- return the "new nails" flag + * + * Return TRUE if any new nails have been set in the nailboard since + * the last call to NailboardClearNewNails (or since the nailboard was + * created, if there have never been any such calls), FALSE otherwise. + */ + +Bool (NailboardNewNails)(Nailboard board) +{ + AVERT(Nailboard, board); + return NailboardNewNails(board); +} + + +/* nailboardIndex -- return the index of the nail corresponding to + * addr in the given level. + */ + +static Index nailboardIndex(Nailboard board, Index level, Addr addr) +{ + Index i = AddrOffset(RangeBase(&board->range), addr) + >> (board->alignShift + level * LEVEL_SHIFT); + AVER_CRITICAL(i < nailboardLevelBits(nailboardNails(board), level)); + return i; +} + + +/* nailboardAddr -- return the address corresponding to the index in + * the given level. + */ + +static Addr nailboardAddr(Nailboard board, Index level, Index index) +{ + return AddrAdd(RangeBase(&board->range), + index << (board->alignShift + level * LEVEL_SHIFT)); +} + + +/* nailboardIndexRange -- update *ibaseReturn and *ilimitReturn to be + * the interval of indexes of nails in the given level, corresponding + * to the interval of addresses base and limit. See + * . + */ + +static void nailboardIndexRange(Index *ibaseReturn, Index *ilimitReturn, + Nailboard board, Index level, + Addr base, Addr limit) +{ + *ibaseReturn = nailboardIndex(board, level, base); + *ilimitReturn = nailboardIndex(board, level, AddrSub(limit, 1)) + 1; +} + + +/* NailboardGet -- return nail corresponding to address + * + * Return the nail in the nailboard corresponding to the address addr. + * It is an error if addr does not lie in the range of addresses + * covered by the nailboard. + */ + +Bool NailboardGet(Nailboard board, Addr addr) +{ + AVERT(Nailboard, board); + AVER(RangeContains(&board->range, addr)); + return BTGet(board->level[0], nailboardIndex(board, 0, addr)); +} + + +/* NailboardSet -- set nail corresponding to address + * + * Set the nail in the nailboard corresponding to the address addr. + * Return the old nail at that position. It is an error if addr does + * not lie in the range of addresses covered by the nailboard. + * + * This function is on the critical path because it is called for + * every fix of an ambiguous reference to an address in an AMC pool. + */ + +Bool NailboardSet(Nailboard board, Addr addr) +{ + Index i, j; + + AVERT_CRITICAL(Nailboard, board); + AVER_CRITICAL(RangeContains(&board->range, addr)); + + j = nailboardIndex(board, 0, addr); + if (BTGet(board->level[0], j)) + return TRUE; + board->newNails = TRUE; + BTSet(board->level[0], j); + + for (i = 1; i < board->levels; ++i) { + j = nailboardIndex(board, i, addr); + if (BTGet(board->level[i], j)) + break; + BTSet(board->level[i], j); + } + return FALSE; +} + + +/* NailboardSetRange -- set all nails in range + * + * Set all nails in the nailboard corresponding to the range between + * base and limit. It is an error if any part of the range is not + * covered by the nailboard, or if any nail in the range is set. + */ + +void NailboardSetRange(Nailboard board, Addr base, Addr limit) +{ + Index i, ibase, ilimit; + nailboardIndexRange(&ibase, &ilimit, board, 0, base, limit); + AVER(BTIsResRange(board->level[0], ibase, ilimit)); + BTSetRange(board->level[0], ibase, ilimit); + for (i = 1; i < board->levels; ++i) { + nailboardIndexRange(&ibase, &ilimit, board, i, base, limit); + BTSetRange(board->level[i], ibase, ilimit); + } +} + + +/* NailboardIsSetRange -- test if all nails are set in a range + * + * Return TRUE if all nails are set in the range between base and + * limit, or FALSE if any nail is unset. It is an error if any part of + * the range is not covered by the nailboard. + * + * This function is not expected to be efficient because it is only + * used in an AVER in AMCWhiten to check that the unused part of the + * buffer for a nailboarded segment has in fact been nailed. + */ + +Bool NailboardIsSetRange(Nailboard board, Addr base, Addr limit) +{ + Index ibase, ilimit; + AVERT(Nailboard, board); + nailboardIndexRange(&ibase, &ilimit, board, 0, base, limit); + return BTIsSetRange(board->level[0], ibase, ilimit); +} + + +/* NailboardIsResRange -- test if all nails are reset in a range + * + * Return TRUE if no nails are set in the range between base and + * limit, or FALSE if any nail is set. It is an error if any part of + * the range is not covered by the nailboard. + * + * This function is on the critical path as it is called for every + * object in every nailed segment. It must take time that is no more + * than logarithmic in the size of the range. + * + * See . + */ + +Bool NailboardIsResRange(Nailboard board, Addr base, Addr limit) +{ + Index i, ibase, ilimit; + Index j, jbase, jlimit; + Addr leftLimit, rightBase; + + AVERT_CRITICAL(Nailboard, board); + + /* Descend levels until ibase and ilimit are two or more bits apart: + * that is, until there is an "inner" part to the range. */ + i = board->levels; + do { + -- i; + nailboardIndexRange(&ibase, &ilimit, board, i, base, limit); + if (BTIsResRange(board->level[i], ibase, ilimit)) + /* The entire range was clear. This is expected to be the common + * case. */ + return TRUE; + if (i == 0) + /* At level 0 there is only one nail per bit so the set bit is known + * to be within the range. */ + return FALSE; + } while (ibase + 1 >= ilimit - 1); + + /* At this point we know there is an "inner" part. Are there any + * bits set in it? */ + if (!BTIsResRange(board->level[i], ibase + 1, ilimit - 1)) + return FALSE; + + /* At this point we know that in level i, there is is a bit set at + * ibase or at ilimit - 1 (or both), and everything between them is + * reset. */ + AVER_CRITICAL(BTGet(board->level[i], ibase) + || BTGet(board->level[i], ilimit - 1)); + + /* Left splinter */ + for (j = i, jbase = ibase;;) { + leftLimit = nailboardAddr(board, j, jbase + 1); + AVER_CRITICAL(base < leftLimit); + AVER_CRITICAL(leftLimit < limit); + -- j; + nailboardIndexRange(&jbase, &jlimit, board, j, base, leftLimit); + if (jbase + 1 < jlimit && !BTIsResRange(board->level[j], jbase + 1, jlimit)) + return FALSE; /* */ + if (!BTGet(board->level[j], jbase)) + break; + if (j == 0) + return FALSE; + } + + /* Right splinter */ + for (j = i, jlimit = ilimit;;) { + rightBase = nailboardAddr(board, j, jlimit - 1); + AVER_CRITICAL(base < rightBase); + AVER_CRITICAL(rightBase < limit); + -- j; + nailboardIndexRange(&jbase, &jlimit, board, j, rightBase, limit); + if (jbase < jlimit - 1 && !BTIsResRange(board->level[j], jbase, jlimit - 1)) + return FALSE; /* */ + if (!BTGet(board->level[j], jlimit - 1)) + break; + if (j == 0) + return FALSE; + } + + return TRUE; +} + + +Res NailboardDescribe(Nailboard board, mps_lib_FILE *stream, Count depth) +{ + Index i, j; + Res res; + + if (!TESTT(Nailboard, board)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "Nailboard $P {\n", (WriteFP)board, + " levels: $U\n", (WriteFU)board->levels, + " newNails: $S\n", WriteFYesNo(board->newNails), + " alignShift: $U\n", (WriteFU)board->alignShift, + NULL); + if (res != ResOK) + return res; + + res = RangeDescribe(&board->range, stream, depth + 2); + if (res != ResOK) + return res; + + for(i = 0; i < board->levels; ++i) { + Count levelNails = nailboardLevelBits(nailboardNails(board), i); + Count resetNails = BTCountResRange(board->level[i], 0, levelNails); + res = WriteF(stream, depth + 2, "Level $U ($U bits, $U set): ", + (WriteFU)i, (WriteFU)levelNails, + (WriteFU)(levelNails - resetNails), + NULL); + if (res != ResOK) + return res; + for (j = 0; j < levelNails; ++j) { + char c = BTGet(board->level[i], j) ? '*' : '.'; + res = WriteF(stream, 0, "$C", (WriteFC)c, NULL); + if (res != ResOK) + return res; + } + res = WriteF(stream, 0, "\n", NULL); + if (res != ResOK) + return res; + } + res = WriteF(stream, depth, "} Nailboard $P\n", (WriteFP)board, NULL); + if (res != ResOK) + return res; + + return ResOK; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/nailboard.h b/code/nailboard.h new file mode 100644 index 0000000000..538a46e924 --- /dev/null +++ b/code/nailboard.h @@ -0,0 +1,92 @@ +/* nailboard.h: NAILBOARD INTERFACE + * + * $Id$ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * .source: . + */ + +#ifndef nailboard_h +#define nailboard_h + +#include "mpmtypes.h" +#include "range.h" + +typedef struct NailboardStruct *Nailboard; + +/* NOTE: we could reduce the size of this structure using bitfields. + * levels can be at most MPS_WORD_WIDTH / LEVEL_SHIFT + 1, which is 11 + * on 64-bit, so it would fit in 4 bits. (Or it could be recalculated + * from range each time it's needed.) alignShift is at most + * MPS_WORD_SHIFT so would fit in 3 bits. (Or it could be supplied in + * each function call by the owner.) newNails would fit in 1 bit. + */ +typedef struct NailboardStruct { + Sig sig; + RangeStruct range; /* range of addresses covered by nailboard */ + Count levels; /* number of levels */ + Shift alignShift; /* shift due to address alignment */ + Bool newNails; /* set to TRUE if a new nail is set */ + BT level[1]; /* bit tables for each level */ +} NailboardStruct; + +#define NailboardSig ((Sig)0x5194A17B) /* SIGnature NAILBoard */ + +#define NailboardClearNewNails(board) ((board)->newNails = FALSE) +#define NailboardNewNails(board) RVALUE((board)->newNails) + +extern Bool NailboardCheck(Nailboard board); +extern Res NailboardCreate(Nailboard *boardReturn, Arena arena, Align alignment, Addr base, Addr limit); +extern void NailboardDestroy(Nailboard board, Arena arena); +extern void (NailboardClearNewNails)(Nailboard board); +extern Bool (NailboardNewNails)(Nailboard board); +extern Bool NailboardGet(Nailboard board, Addr addr); +extern Bool NailboardSet(Nailboard board, Addr addr); +extern void NailboardSetRange(Nailboard board, Addr base, Addr limit); +extern Bool NailboardIsSetRange(Nailboard board, Addr base, Addr limit); +extern Bool NailboardIsResRange(Nailboard board, Addr base, Addr limit); +extern Res NailboardDescribe(Nailboard board, mps_lib_FILE *stream, Count depth); + +#endif /* nailboard.h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/nailboardtest.c b/code/nailboardtest.c new file mode 100644 index 0000000000..e1c071f3ac --- /dev/null +++ b/code/nailboardtest.c @@ -0,0 +1,114 @@ +/* nailboardtest.c: NAILBOARD TEST + * + * $Id: //info.ravenbrook.com/project/mps/branch/2014-01-15/nailboard/code/fotest.c#1 $ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + */ + +#include "mpm.h" +#include "mps.h" +#include "mpsavm.h" +#include "testlib.h" +#include "bt.h" +#include "nailboard.h" + +#include /* printf */ + + +static void test(mps_arena_t arena) +{ + BT bt; + Nailboard board; + Align align; + Count nails; + Addr base, limit; + Index i, j, k; + + align = (Align)1 << (rnd() % 10); + nails = (Count)1 << (rnd() % 16); + nails += rnd() % nails; + base = AddrAlignUp(0, align); + limit = AddrAdd(base, nails * align); + + die(BTCreate(&bt, arena, nails), "BTCreate"); + BTResRange(bt, 0, nails); + die(NailboardCreate(&board, arena, align, base, limit), "NailboardCreate"); + + for (i = 0; i <= nails / 8; ++i) { + Bool old; + j = rnd() % nails; + old = BTGet(bt, j); + BTSet(bt, j); + cdie(NailboardSet(board, AddrAdd(base, j * align)) == old, "NailboardSet"); + for (k = 0; k < nails / 8; ++k) { + Index b, l; + b = rnd() % nails; + l = b + rnd() % (nails - b) + 1; + cdie(BTIsResRange(bt, b, l) + == NailboardIsResRange(board, AddrAdd(base, b * align), + AddrAdd(base, l * align)), + "NailboardIsResRange"); + } + } + + die(NailboardDescribe(board, mps_lib_get_stdout(), 0), "NailboardDescribe"); +} + +int main(int argc, char **argv) +{ + mps_arena_t arena; + + testlib_init(argc, argv); + + die(mps_arena_create(&arena, mps_arena_class_vm(), 1024 * 1024), + "mps_arena_create"); + + test(arena); + + mps_arena_destroy(arena); + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); + return 0; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (c) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + diff --git a/code/pc.nmk b/code/pc.nmk new file mode 100644 index 0000000000..e52addfba4 --- /dev/null +++ b/code/pc.nmk @@ -0,0 +1,59 @@ +# -*- makefile -*- +# +# pc.nmk: NMAKE FRAGMENT FOR PELLES C +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. +# +# This file is included by platform nmake files that use the Pelles C +# compiler. It defines the compiler-specific variables that the common +# nmake file fragment () requires. + +CC = pocc +LIBMAN = polib +LINKER = polink + +CFLAGSCOMMONPRE = $(CFLAGSCOMMONPRE) /Ze /W2 +CRTFLAGSHOT = /MT +CRTFLAGSCOOL = /MT + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2014 Ravenbrook Limited . +# All rights reserved. This is an open source license. Contact +# Ravenbrook for commercial licensing options. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Redistributions in any form must be accompanied by information on how +# to obtain complete source code for this software and any accompanying +# software that uses this software. The source code must either be +# included in the distribution or be available for no more than the cost +# of distribution plus a nominal fee, and must be freely redistributable +# under reasonable conditions. For an executable file, complete source +# code means the source code for all modules it contains. It does not +# include source code for modules or files that typically accompany the +# major components of the operating system on which the executable file +# runs. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/policy.c b/code/policy.c new file mode 100644 index 0000000000..dbcb33e278 --- /dev/null +++ b/code/policy.c @@ -0,0 +1,424 @@ +/* policy.c: POLICY DECISIONS + * + * $Id$ + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * + * This module collects the decision-making code for the MPS, so that + * policy can be maintained and adjusted. + * + * .sources: . + */ + +#include "chain.h" +#include "mpm.h" + +SRCID(policy, "$Id$"); + + +/* PolicyAlloc -- allocation policy + * + * This is the code responsible for making decisions about where to allocate + * memory. + * + * pref describes the address space preferences for the allocation. + * size is the amount of memory requested to be allocated, in bytes. + * pool is the pool that is requresting the memory. + * + * If successful, update *tractReturn to point to the initial tract of + * the allocated memory and return ResOK. Otherwise return a result + * code describing the problem. + */ + +Res PolicyAlloc(Tract *tractReturn, Arena arena, LocusPref pref, + Size size, Pool pool) +{ + Res res; + Tract tract; + ZoneSet zones, moreZones, evenMoreZones; + + AVER(tractReturn != NULL); + AVERT(Arena, arena); + AVERT(LocusPref, pref); + AVER(size > (Size)0); + AVER(SizeIsArenaGrains(size, arena)); + AVERT(Pool, pool); + AVER(arena == PoolArena(pool)); + + /* Don't attempt to allocate if doing so would definitely exceed the + * commit limit. */ + if (arena->spareCommitted < size) { + Size necessaryCommitIncrease = size - arena->spareCommitted; + if (arena->committed + necessaryCommitIncrease > arena->commitLimit + || arena->committed + necessaryCommitIncrease < arena->committed) { + return ResCOMMIT_LIMIT; + } + } + + /* Plan A: allocate from the free land in the requested zones */ + zones = ZoneSetDiff(pref->zones, pref->avoid); + if (zones != ZoneSetEMPTY) { + res = ArenaFreeLandAlloc(&tract, arena, zones, pref->high, size, pool); + if (res == ResOK) + goto found; + } + + /* Plan B: add free zones that aren't blacklisted */ + /* TODO: Pools without ambiguous roots might not care about the blacklist. */ + /* TODO: zones are precious and (currently) never deallocated, so we + * should consider extending the arena first if address space is plentiful. + * See also job003384. */ + moreZones = ZoneSetUnion(pref->zones, ZoneSetDiff(arena->freeZones, pref->avoid)); + if (moreZones != zones) { + res = ArenaFreeLandAlloc(&tract, arena, moreZones, pref->high, size, pool); + if (res == ResOK) + goto found; + } + + /* Plan C: Extend the arena, then try A and B again. */ + if (moreZones != ZoneSetEMPTY) { + res = arena->class->grow(arena, pref, size); + if (res != ResOK) + return res; + if (zones != ZoneSetEMPTY) { + res = ArenaFreeLandAlloc(&tract, arena, zones, pref->high, size, pool); + if (res == ResOK) + goto found; + } + if (moreZones != zones) { + res = ArenaFreeLandAlloc(&tract, arena, moreZones, pref->high, + size, pool); + if (res == ResOK) + goto found; + } + } + + /* Plan D: add every zone that isn't blacklisted. This might mix GC'd + * objects with those from other generations, causing the zone check + * to give false positives and slowing down the collector. */ + /* TODO: log an event for this */ + evenMoreZones = ZoneSetDiff(ZoneSetUNIV, pref->avoid); + if (evenMoreZones != moreZones) { + res = ArenaFreeLandAlloc(&tract, arena, evenMoreZones, pref->high, + size, pool); + if (res == ResOK) + goto found; + } + + /* Last resort: try anywhere. This might put GC'd objects in zones where + * common ambiguous bit patterns pin them down, causing the zone check + * to give even more false positives permanently, and possibly retaining + * garbage indefinitely. */ + res = ArenaFreeLandAlloc(&tract, arena, ZoneSetUNIV, pref->high, size, pool); + if (res == ResOK) + goto found; + + /* Uh oh. */ + return res; + +found: + *tractReturn = tract; + return ResOK; +} + + +/* policyCollectionTime -- estimate time to collect the world, in seconds */ + +static double policyCollectionTime(Arena arena) +{ + Size collectableSize; + double collectionRate; + double collectionTime; + + AVERT(Arena, arena); + + collectableSize = ArenaCollectable(arena); + /* The condition arena->tracedTime >= 1.0 ensures that the division + * can't overflow. */ + if (arena->tracedSize >= ARENA_MINIMUM_COLLECTABLE_SIZE + && arena->tracedTime >= 1.0) + collectionRate = arena->tracedSize / arena->tracedTime; + else + collectionRate = ARENA_DEFAULT_COLLECTION_RATE; + collectionTime = collectableSize / collectionRate; + collectionTime += ARENA_DEFAULT_COLLECTION_OVERHEAD; + + return collectionTime; +} + + +/* PolicyShouldCollectWorld -- should we collect the world now? + * + * Return TRUE if we should try collecting the world now, FALSE if + * not. + * + * This is the policy behind mps_arena_step, and so there the client + * must have provided us with be enough time to collect the world, and + * enough time must have passed since the last time we did that + * opportunistically. + */ + +Bool PolicyShouldCollectWorld(Arena arena, double interval, double multiplier, + Clock now, Clock clocks_per_sec) +{ + /* don't collect the world if we're not given any time */ + if ((interval > 0.0) && (multiplier > 0.0)) { + /* don't collect the world if we're already collecting. */ + if (arena->busyTraces == TraceSetEMPTY) { + /* don't collect the world if it's very small */ + Size collectableSize = ArenaCollectable(arena); + if (collectableSize > ARENA_MINIMUM_COLLECTABLE_SIZE) { + /* how long would it take to collect the world? */ + double collectionTime = policyCollectionTime(arena); + + /* how long since we last collected the world? */ + double sinceLastWorldCollect = ((now - arena->lastWorldCollect) / + (double) clocks_per_sec); + /* have to be offered enough time, and it has to be a long time + * since we last did it. */ + if ((interval * multiplier > collectionTime) && + sinceLastWorldCollect > collectionTime / ARENA_MAX_COLLECT_FRACTION) + return TRUE; + } + } + } + return FALSE; +} + + +/* policyCondemnChain -- condemn approriate parts of this chain + * + * If successful, set *mortalityReturn to an estimate of the mortality + * of the condemned parts of this chain and return ResOK. + * + * This is only called if ChainDeferral returned a value sufficiently + * low that we decided to start the collection. (Usually such values + * are less than zero; see .) + */ + +static Res policyCondemnChain(double *mortalityReturn, Chain chain, Trace trace) +{ + Res res; + size_t topCondemnedGen, i; + GenDesc gen; + ZoneSet condemnedSet = ZoneSetEMPTY; + Size condemnedSize = 0, survivorSize = 0, genNewSize, genTotalSize; + + AVERT(Chain, chain); + AVERT(Trace, trace); + + /* Find the highest generation that's over capacity. We will condemn + * this and all lower generations in the chain. */ + topCondemnedGen = chain->genCount; + for (;;) { + /* It's an error to call this function unless some generation is + * over capacity as reported by ChainDeferral. */ + AVER(topCondemnedGen > 0); + if (topCondemnedGen == 0) + return ResFAIL; + -- topCondemnedGen; + gen = &chain->gens[topCondemnedGen]; + AVERT(GenDesc, gen); + genNewSize = GenDescNewSize(gen); + if (genNewSize >= gen->capacity * (Size)1024) + break; + } + + /* At this point, we've decided to condemn topCondemnedGen and all + * lower generations. */ + for (i = 0; i <= topCondemnedGen; ++i) { + gen = &chain->gens[i]; + AVERT(GenDesc, gen); + condemnedSet = ZoneSetUnion(condemnedSet, gen->zones); + genTotalSize = GenDescTotalSize(gen); + genNewSize = GenDescNewSize(gen); + condemnedSize += genTotalSize; + survivorSize += (Size)(genNewSize * (1.0 - gen->mortality)) + /* predict survivors will survive again */ + + (genTotalSize - genNewSize); + } + + AVER(condemnedSet != ZoneSetEMPTY || condemnedSize == 0); + EVENT3(ChainCondemnAuto, chain, topCondemnedGen, chain->genCount); + + /* Condemn everything in these zones. */ + if (condemnedSet != ZoneSetEMPTY) { + res = TraceCondemnZones(trace, condemnedSet); + if (res != ResOK) + return res; + } + + *mortalityReturn = 1.0 - (double)survivorSize / condemnedSize; + return ResOK; +} + + +/* PolicyStartTrace -- consider starting a trace + * + * If a trace was started, update *traceReturn and return TRUE. + * Otherwise, leave *traceReturn unchanged and return FALSE. + */ + +Bool PolicyStartTrace(Trace *traceReturn, Arena arena) +{ + Res res; + Trace trace; + Size sFoundation, sCondemned, sSurvivors, sConsTrace; + double tTracePerScan; /* tTrace/cScan */ + double dynamicDeferral; + + /* Compute dynamic criterion. See strategy.lisp-machine. */ + AVER(arena->topGen.mortality >= 0.0); + AVER(arena->topGen.mortality <= 1.0); + sFoundation = (Size)0; /* condemning everything, only roots @@@@ */ + /* @@@@ sCondemned should be scannable only */ + sCondemned = ArenaCommitted(arena) - ArenaSpareCommitted(arena); + sSurvivors = (Size)(sCondemned * (1 - arena->topGen.mortality)); + tTracePerScan = sFoundation + (sSurvivors * (1 + TraceCopyScanRATIO)); + AVER(TraceWorkFactor >= 0); + AVER(sSurvivors + tTracePerScan * TraceWorkFactor <= (double)SizeMAX); + sConsTrace = (Size)(sSurvivors + tTracePerScan * TraceWorkFactor); + dynamicDeferral = (double)ArenaAvail(arena) - (double)sConsTrace; + + if (dynamicDeferral < 0.0) { + /* Start full collection. */ + res = TraceStartCollectAll(&trace, arena, TraceStartWhyDYNAMICCRITERION); + if (res != ResOK) + goto failStart; + *traceReturn = trace; + return TRUE; + } else { + /* Find the chain most over its capacity. */ + Ring node, nextNode; + double firstTime = 0.0; + Chain firstChain = NULL; + + RING_FOR(node, &arena->chainRing, nextNode) { + Chain chain = RING_ELT(Chain, chainRing, node); + double time; + + AVERT(Chain, chain); + time = ChainDeferral(chain); + if (time < firstTime) { + firstTime = time; firstChain = chain; + } + } + + /* If one was found, start collection on that chain. */ + if(firstTime < 0) { + double mortality; + + res = TraceCreate(&trace, arena, TraceStartWhyCHAIN_GEN0CAP); + AVER(res == ResOK); + res = policyCondemnChain(&mortality, firstChain, trace); + if (res != ResOK) /* should try some other trace, really @@@@ */ + goto failCondemn; + trace->chain = firstChain; + ChainStartGC(firstChain, trace); + res = TraceStart(trace, mortality, trace->condemned * TraceWorkFactor); + /* We don't expect normal GC traces to fail to start. */ + AVER(res == ResOK); + *traceReturn = trace; + return TRUE; + } + } /* (dynamicDeferral > 0.0) */ + return FALSE; + +failCondemn: + TraceDestroy(trace); + /* This is an unlikely case, but clear the emergency flag so the next attempt + starts normally. */ + ArenaSetEmergency(arena, FALSE); +failStart: + return FALSE; +} + + +/* PolicyPoll -- do some tracing work? + * + * Return TRUE if the MPS should do some tracing work; FALSE if it + * should return to the mutator. + */ + +Bool PolicyPoll(Arena arena) +{ + Globals globals; + AVERT(Arena, arena); + globals = ArenaGlobals(arena); + return globals->pollThreshold <= globals->fillMutatorSize; +} + + +/* PolicyPollAgain -- do another unit of work? + * + * Return TRUE if the MPS should do another unit of work; FALSE if it + * should return to the mutator. + * + * start is the clock time when the MPS was entered. + * tracedSize is the amount of work done by the last call to TracePoll. + */ + +Bool PolicyPollAgain(Arena arena, Clock start, Size tracedSize) +{ + Globals globals; + double nextPollThreshold; + + AVERT(Arena, arena); + globals = ArenaGlobals(arena); + UNUSED(start); + + if (tracedSize == 0) { + /* No work was done. Sleep until NOW + a bit. */ + nextPollThreshold = globals->fillMutatorSize + ArenaPollALLOCTIME; + } else { + /* We did one quantum of work; consume one unit of 'time'. */ + nextPollThreshold = globals->pollThreshold + ArenaPollALLOCTIME; + } + + /* Advance pollThreshold; check: enough precision? */ + AVER(nextPollThreshold > globals->pollThreshold); + globals->pollThreshold = nextPollThreshold; + + return PolicyPoll(arena); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2001-2015 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/pool.c b/code/pool.c index df6b0fa903..db43e1e72a 100644 --- a/code/pool.c +++ b/code/pool.c @@ -1,7 +1,7 @@ /* pool.c: POOL IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2001 Global Graphics Software. * * DESIGN @@ -18,9 +18,6 @@ * .purpose.dispatch: Dispatch functions that implement the generic * function dispatch mechanism for Pool Classes (PoolAlloc, PoolFix, * etc.). - * .purpose.core: A selection of default, trivial, or useful methods - * that Pool Classes can use as the implementations for some of their - * methods (such as PoolTrivWhiten, PoolNoFix, etc.). * * SOURCES * @@ -40,13 +37,14 @@ SRCID(pool, "$Id$"); Bool PoolClassCheck(PoolClass class) { - CHECKL(ProtocolClassCheck(&class->protocol)); + CHECKD(ProtocolClass, &class->protocol); CHECKL(class->name != NULL); /* Should be <=6 char C identifier */ CHECKL(class->size >= sizeof(PoolStruct)); /* Offset of generic Pool within class-specific instance cannot be */ /* greater than the size of the class-specific portion of the instance */ CHECKL(class->offset <= (size_t)(class->size - sizeof(PoolStruct))); CHECKL(AttrCheck(class->attr)); + CHECKL(!(class->attr & AttrMOVINGGC) || (class->attr & AttrGC)); CHECKL(FUNCHECK(class->varargs)); CHECKL(FUNCHECK(class->init)); CHECKL(FUNCHECK(class->finish)); @@ -74,6 +72,8 @@ Bool PoolClassCheck(PoolClass class) CHECKL(FUNCHECK(class->bufferClass)); CHECKL(FUNCHECK(class->describe)); CHECKL(FUNCHECK(class->debugMixin)); + CHECKL(FUNCHECK(class->totalSize)); + CHECKL(FUNCHECK(class->freeSize)); CHECKS(PoolClass, class); return TRUE; } @@ -89,35 +89,32 @@ Bool PoolCheck(Pool pool) CHECKL(pool->serial < ArenaGlobals(pool->arena)->poolSerial); CHECKD(PoolClass, pool->class); CHECKU(Arena, pool->arena); - CHECKL(RingCheck(&pool->arenaRing)); - CHECKL(RingCheck(&pool->bufferRing)); + CHECKD_NOSIG(Ring, &pool->arenaRing); + CHECKD_NOSIG(Ring, &pool->bufferRing); /* Cannot check pool->bufferSerial */ - CHECKL(RingCheck(&pool->segRing)); + CHECKD_NOSIG(Ring, &pool->segRing); CHECKL(AlignCheck(pool->alignment)); - /* normally pool->format iff pool->class->attr&AttrFMT, but not */ - /* during pool initialization */ - if (pool->format != NULL) { - CHECKL((pool->class->attr & AttrFMT) != 0); - } - CHECKL(pool->fillMutatorSize >= 0.0); - CHECKL(pool->emptyMutatorSize >= 0.0); - CHECKL(pool->fillInternalSize >= 0.0); - CHECKL(pool->emptyInternalSize >= 0.0); + /* normally pool->format iff PoolHasAttr(pool, AttrFMT), but during + * pool initialization pool->format may not yet be set. */ + CHECKL(pool->format == NULL || PoolHasAttr(pool, AttrFMT)); return TRUE; } /* Common keywords to PoolInit */ -ARG_DEFINE_KEY(format, Format); -ARG_DEFINE_KEY(chain, Chain); -ARG_DEFINE_KEY(gen, Cant); -ARG_DEFINE_KEY(rank, Rank); -ARG_DEFINE_KEY(extend_by, Size); -ARG_DEFINE_KEY(min_size, Size); -ARG_DEFINE_KEY(mean_size, Size); -ARG_DEFINE_KEY(max_size, Size); -ARG_DEFINE_KEY(align, Align); +ARG_DEFINE_KEY(FORMAT, Format); +ARG_DEFINE_KEY(CHAIN, Chain); +ARG_DEFINE_KEY(GEN, Cant); +ARG_DEFINE_KEY(RANK, Rank); +ARG_DEFINE_KEY(EXTEND_BY, Size); +ARG_DEFINE_KEY(LARGE_SIZE, Size); +ARG_DEFINE_KEY(MIN_SIZE, Size); +ARG_DEFINE_KEY(MEAN_SIZE, Size); +ARG_DEFINE_KEY(MAX_SIZE, Size); +ARG_DEFINE_KEY(ALIGN, Align); +ARG_DEFINE_KEY(SPARE, double); +ARG_DEFINE_KEY(INTERIOR, Bool); /* PoolInit -- initialize a pool @@ -156,10 +153,6 @@ Res PoolInit(Pool pool, Arena arena, PoolClass class, ArgList args) pool->alignment = MPS_PF_ALIGN; pool->format = NULL; pool->fix = class->fix; - pool->fillMutatorSize = 0.0; - pool->emptyMutatorSize = 0.0; - pool->fillInternalSize = 0.0; - pool->emptyInternalSize = 0.0; /* Initialise signature last; see */ pool->sig = PoolSig; @@ -288,9 +281,8 @@ Res PoolAlloc(Addr *pReturn, Pool pool, Size size, AVER(pReturn != NULL); AVERT(Pool, pool); - AVER((pool->class->attr & AttrALLOC) != 0); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); res = (*pool->class->alloc)(pReturn, pool, size, withReservoirPermit); if (res != ResOK) @@ -304,7 +296,6 @@ Res PoolAlloc(Addr *pReturn, Pool pool, Size size, /* All PoolAllocs should advance the allocation clock, so we count */ /* it all in the fillMutatorSize field. */ - pool->fillMutatorSize += size; ArenaGlobals(PoolArena(pool))->fillMutatorSize += size; EVENT3(PoolAlloc, pool, *pReturn, size); @@ -318,10 +309,10 @@ Res PoolAlloc(Addr *pReturn, Pool pool, Size size, void PoolFree(Pool pool, Addr old, Size size) { AVERT(Pool, pool); - AVER((pool->class->attr & AttrFREE) != 0); AVER(old != NULL); /* The pool methods should check that old is in pool. */ AVER(size > 0); + AVER(AddrIsAligned(old, pool->alignment)); AVER(PoolHasRange(pool, old, AddrAdd(old, size))); (*pool->class->free)(pool, old, size); @@ -337,7 +328,7 @@ Res PoolAccess(Pool pool, Seg seg, Addr addr, AVERT(Seg, seg); AVER(SegBase(seg) <= addr); AVER(addr < SegLimit(seg)); - /* Can't check mode as there is no check method */ + AVERT(AccessSet, mode); /* Can't check MutatorFaultContext as there is no check method */ return (*pool->class->access)(pool, seg, addr, mode, context); @@ -409,14 +400,14 @@ Res PoolScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) Res (PoolFix)(Pool pool, ScanState ss, Seg seg, Addr *refIO) { - AVERT(Pool, pool); - AVERT(ScanState, ss); - AVERT(Seg, seg); - AVER(pool == SegPool(seg)); - AVER(refIO != NULL); + AVERT_CRITICAL(Pool, pool); + AVERT_CRITICAL(ScanState, ss); + AVERT_CRITICAL(Seg, seg); + AVER_CRITICAL(pool == SegPool(seg)); + AVER_CRITICAL(refIO != NULL); /* Should only be fixing references to white segments. */ - AVER(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); + AVER_CRITICAL(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); return PoolFix(pool, ss, seg, refIO); } @@ -425,24 +416,24 @@ Res PoolFixEmergency(Pool pool, ScanState ss, Seg seg, Addr *refIO) { Res res; - AVERT(Pool, pool); - AVERT(ScanState, ss); - AVERT(Seg, seg); - AVER(pool == SegPool(seg)); - AVER(refIO != NULL); + AVERT_CRITICAL(Pool, pool); + AVERT_CRITICAL(ScanState, ss); + AVERT_CRITICAL(Seg, seg); + AVER_CRITICAL(pool == SegPool(seg)); + AVER_CRITICAL(refIO != NULL); /* Should only be fixing references to white segments. */ - AVER(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); + AVER_CRITICAL(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); res = (pool->class->fixEmergency)(pool, ss, seg, refIO); - AVER(res == ResOK); + AVER_CRITICAL(res == ResOK); return res; } /* PoolReclaim -- reclaim a segment in the pool */ -void PoolReclaim(Pool pool, Trace trace, Seg seg) +Bool PoolReclaim(Pool pool, Trace trace, Seg seg) { AVERT_CRITICAL(Pool, pool); AVERT_CRITICAL(Trace, trace); @@ -455,7 +446,7 @@ void PoolReclaim(Pool pool, Trace trace, Seg seg) /* Should only be reclaiming segments which are still white. */ AVER_CRITICAL(TraceSetIsMember(SegWhite(seg), trace)); - (*pool->class->reclaim)(pool, trace, seg); + return (*pool->class->reclaim)(pool, trace, seg); } @@ -488,15 +479,15 @@ Res PoolAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr) AVERT(Pool, pool); AVERT(Seg, seg); AVER(pool == SegPool(seg)); - AVER(SegBase(seg) <= addr && addr < SegLimit(seg)); + AVER(SegBase(seg) <= addr); + AVER(addr < SegLimit(seg)); return (*pool->class->addrObject)(pReturn, pool, seg, addr); } /* PoolWalk -- walk objects in this segment */ -void PoolWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, - void *p, size_t s) +void PoolWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, void *p, size_t s) { AVERT(Pool, pool); AVERT(Seg, seg); @@ -512,7 +503,7 @@ void PoolWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, * PoolFreeWalk is not required to find all free blocks. */ -void PoolFreeWalk(Pool pool, FreeBlockStepMethod f, void *p) +void PoolFreeWalk(Pool pool, FreeBlockVisitor f, void *p) { AVERT(Pool, pool); AVER(FUNCHECK(f)); @@ -522,54 +513,70 @@ void PoolFreeWalk(Pool pool, FreeBlockStepMethod f, void *p) } +/* PoolTotalSize -- return total memory allocated from arena */ + +Size PoolTotalSize(Pool pool) +{ + AVERT(Pool, pool); + + return (*pool->class->totalSize)(pool); +} + + +/* PoolFreeSize -- return free memory (unused by client program) */ + +Size PoolFreeSize(Pool pool) +{ + AVERT(Pool, pool); + + return (*pool->class->freeSize)(pool); +} + + /* PoolDescribe -- describe a pool */ -Res PoolDescribe(Pool pool, mps_lib_FILE *stream) +Res PoolDescribe(Pool pool, mps_lib_FILE *stream, Count depth) { Res res; Ring node, nextNode; - if (!TESTT(Pool, pool)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Pool, pool)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = WriteF(stream, + res = WriteF(stream, depth, "Pool $P ($U) {\n", (WriteFP)pool, (WriteFU)pool->serial, " class $P (\"$S\")\n", - (WriteFP)pool->class, pool->class->name, + (WriteFP)pool->class, (WriteFS)pool->class->name, " arena $P ($U)\n", (WriteFP)pool->arena, (WriteFU)pool->arena->serial, " alignment $W\n", (WriteFW)pool->alignment, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; if (NULL != pool->format) { - res = FormatDescribe(pool->format, stream); - if (res != ResOK) return res; + res = FormatDescribe(pool->format, stream, depth + 2); + if (res != ResOK) + return res; } - res = WriteF(stream, - " fillMutatorSize $UKb\n", - (WriteFU)(pool->fillMutatorSize / 1024), - " emptyMutatorSize $UKb\n", - (WriteFU)(pool->emptyMutatorSize / 1024), - " fillInternalSize $UKb\n", - (WriteFU)(pool->fillInternalSize / 1024), - " emptyInternalSize $UKb\n", - (WriteFU)(pool->emptyInternalSize / 1024), - NULL); - if (res != ResOK) return res; - res = (*pool->class->describe)(pool, stream); - if (res != ResOK) return res; + res = (*pool->class->describe)(pool, stream, depth + 2); + if (res != ResOK) + return res; RING_FOR(node, &pool->bufferRing, nextNode) { Buffer buffer = RING_ELT(Buffer, poolRing, node); - res = BufferDescribe(buffer, stream); - if (res != ResOK) return res; + res = BufferDescribe(buffer, stream, depth + 2); + if (res != ResOK) + return res; } - res = WriteF(stream, + res = WriteF(stream, depth, "} Pool $P ($U)\n", (WriteFP)pool, (WriteFU)pool->serial, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; return ResOK; } @@ -598,7 +605,7 @@ Bool PoolFormat(Format *formatReturn, Pool pool) /* PoolOfAddr -- return the pool containing the given address * - * If the address points to a page assigned to a pool, this returns TRUE + * If the address points to a tract assigned to a pool, this returns TRUE * and sets *poolReturn to that pool. Otherwise, it returns FALSE, and * *poolReturn is unchanged. */ @@ -627,29 +634,32 @@ Bool PoolOfAddr(Pool *poolReturn, Arena arena, Addr addr) */ Bool PoolOfRange(Pool *poolReturn, Arena arena, Addr base, Addr limit) { - Pool pool; + Bool havePool = FALSE; + Pool pool = NULL; Tract tract; + Addr addr, alignedBase, alignedLimit; AVER(poolReturn != NULL); AVERT(Arena, arena); AVER(base < limit); - if (!TractOfAddr(&tract, arena, base)) - return FALSE; - - pool = TractPool(tract); - if (!pool) - return FALSE; + alignedBase = AddrArenaGrainDown(base, arena); + alignedLimit = AddrArenaGrainUp(limit, arena); - while (TractLimit(tract) < limit) { - if (!TractNext(&tract, arena, TractBase(tract))) - return FALSE; - if (TractPool(tract) != pool) + TRACT_FOR(tract, addr, arena, alignedBase, alignedLimit) { + Pool p = TractPool(tract); + if (havePool && pool != p) return FALSE; + pool = p; + havePool = TRUE; } - *poolReturn = pool; - return TRUE; + if (havePool) { + *poolReturn = pool; + return TRUE; + } else { + return FALSE; + } } @@ -684,7 +694,7 @@ Bool PoolHasRange(Pool pool, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolabs.c b/code/poolabs.c index af355577cc..ed8a172425 100644 --- a/code/poolabs.c +++ b/code/poolabs.c @@ -1,7 +1,7 @@ /* poolabs.c: ABSTRACT POOL CLASSES * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * PURPOSE @@ -18,7 +18,6 @@ * * .hierarchy: define the following hierarchy of abstract pool classes: * AbstractPoolClass - implements init, finish, describe - * AbstractAllocFreePoolClass - implements alloc & free * AbstractBufferPoolClass - implements the buffer protocol * AbstractSegBufPoolClass - uses SegBuf buffer class * AbstractScanPoolClass - implements basic scanning @@ -31,7 +30,6 @@ SRCID(poolabs, "$Id$"); typedef PoolClassStruct AbstractPoolClassStruct; -typedef PoolClassStruct AbstractAllocFreePoolClassStruct; typedef PoolClassStruct AbstractBufferPoolClassStruct; typedef PoolClassStruct AbstractSegBufPoolClassStruct; typedef PoolClassStruct AbstractScanPoolClassStruct; @@ -49,28 +47,17 @@ typedef PoolClassStruct AbstractCollectPoolClassStruct; */ -/* PoolClassMixInAllocFree -- mix in the protocol for Alloc / Free */ - -void PoolClassMixInAllocFree(PoolClass class) -{ - /* Can't check class because it's not initialized yet */ - class->attr |= (AttrALLOC | AttrFREE); - class->alloc = PoolTrivAlloc; - class->free = PoolTrivFree; -} - - /* PoolClassMixInBuffer -- mix in the protocol for buffer reserve / commit */ void PoolClassMixInBuffer(PoolClass class) { /* Can't check class because it's not initialized yet */ - class->attr |= (AttrBUF | AttrBUF_RESERVE); class->bufferFill = PoolTrivBufferFill; class->bufferEmpty = PoolTrivBufferEmpty; /* By default, buffered pools treat frame operations as NOOPs */ class->framePush = PoolTrivFramePush; class->framePop = PoolTrivFramePop; + class->framePopPending = PoolTrivFramePopPending; class->bufferClass = BufferClassGet; } @@ -80,12 +67,13 @@ void PoolClassMixInBuffer(PoolClass class) void PoolClassMixInScan(PoolClass class) { /* Can't check class because it's not initialized yet */ - class->attr |= AttrSCAN; class->access = PoolSegAccess; class->blacken = PoolTrivBlacken; class->grey = PoolTrivGrey; - /* Scan is part of the scanning protocol - but there is */ - /* no useful default method */ + /* scan is part of the scanning protocol, but there is no useful + * default method. + */ + class->scan = NULL; } @@ -95,6 +83,10 @@ void PoolClassMixInFormat(PoolClass class) { /* Can't check class because it's not initialized yet */ class->attr |= AttrFMT; + /* walk is part of the format protocol, but there is no useful + * default method. + */ + class->walk = NULL; } @@ -103,10 +95,14 @@ void PoolClassMixInFormat(PoolClass class) void PoolClassMixInCollect(PoolClass class) { /* Can't check class because it's not initialized yet */ - class->attr |= (AttrGC | AttrINCR_RB); + class->attr |= AttrGC; class->whiten = PoolTrivWhiten; - /* Fix & reclaim are part of the collection protocol - but there */ - /* are no useful default methods for them. */ + /* fix, fixEmergency and reclaim are part of the collection + * protocol, but there are no useful default methods for them. + */ + class->fix = NULL; + class->fixEmergency = NULL; + class->reclaim = NULL; class->rampBegin = PoolTrivRampBegin; class->rampEnd = PoolTrivRampEnd; } @@ -145,20 +141,16 @@ DEFINE_CLASS(AbstractPoolClass, class) class->framePopPending = PoolNoFramePopPending; class->addrObject = PoolNoAddrObject; class->walk = PoolNoWalk; - class->freewalk = PoolNoFreeWalk; + class->freewalk = PoolTrivFreeWalk; class->bufferClass = PoolNoBufferClass; class->describe = PoolTrivDescribe; class->debugMixin = PoolNoDebugMixin; + class->totalSize = PoolNoSize; + class->freeSize = PoolNoSize; class->labelled = FALSE; class->sig = PoolClassSig; } -DEFINE_CLASS(AbstractAllocFreePoolClass, class) -{ - INHERIT_CLASS(class, AbstractPoolClass); - PoolClassMixInAllocFree(class); -} - DEFINE_CLASS(AbstractBufferPoolClass, class) { INHERIT_CLASS(class, AbstractPoolClass); @@ -199,7 +191,7 @@ void PoolTrivFinish(Pool pool) Res PoolTrivInit(Pool pool, ArgList args) { AVERT(Pool, pool); - AVER(ArgListCheck(args)); + AVERT(ArgList, args); UNUSED(args); return ResOK; } @@ -210,7 +202,7 @@ Res PoolNoAlloc(Addr *pReturn, Pool pool, Size size, AVER(pReturn != NULL); AVERT(Pool, pool); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); NOTREACHED; return ResUNIMPL; } @@ -221,7 +213,7 @@ Res PoolTrivAlloc(Addr *pReturn, Pool pool, Size size, AVER(pReturn != NULL); AVERT(Pool, pool); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); return ResLIMIT; } @@ -251,7 +243,7 @@ Res PoolNoBufferFill(Addr *baseReturn, Addr *limitReturn, AVERT(Pool, pool); AVERT(Buffer, buffer); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); NOTREACHED; return ResUNIMPL; } @@ -268,10 +260,11 @@ Res PoolTrivBufferFill(Addr *baseReturn, Addr *limitReturn, AVERT(Pool, pool); AVERT(Buffer, buffer); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); res = PoolAlloc(&p, pool, size, withReservoirPermit); - if(res != ResOK) return res; + if (res != ResOK) + return res; *baseReturn = p; *limitReturn = AddrAdd(p, size); @@ -300,11 +293,13 @@ void PoolTrivBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) } -Res PoolTrivDescribe(Pool pool, mps_lib_FILE *stream) +Res PoolTrivDescribe(Pool pool, mps_lib_FILE *stream, Count depth) { AVERT(Pool, pool); AVER(stream != NULL); - return WriteF(stream, " No class-specific description available.\n", NULL); + return WriteF(stream, depth, + "No class-specific description available.\n", + NULL); } @@ -339,7 +334,7 @@ Res PoolNoAccess(Pool pool, Seg seg, Addr addr, AVERT(Seg, seg); AVER(SegBase(seg) <= addr); AVER(addr < SegLimit(seg)); - /* can't check AccessSet as there is no Check method */ + AVERT(AccessSet, mode); /* can't check context as there is no Check method */ UNUSED(mode); UNUSED(context); @@ -365,7 +360,7 @@ Res PoolSegAccess(Pool pool, Seg seg, Addr addr, AVER(SegBase(seg) <= addr); AVER(addr < SegLimit(seg)); AVER(SegPool(seg) == pool); - /* can't check AccessSet as there is no Check method */ + AVERT(AccessSet, mode); /* can't check context as there is no Check method */ UNUSED(addr); @@ -401,7 +396,7 @@ Res PoolSingleAccess(Pool pool, Seg seg, Addr addr, AVER(SegBase(seg) <= addr); AVER(addr < SegLimit(seg)); AVER(SegPool(seg) == pool); - /* can't check AccessSet as there is no Check method */ + AVERT(AccessSet, mode); /* can't check context as there is no Check method */ arena = PoolArena(pool); @@ -534,12 +529,13 @@ Res PoolNoFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) return ResUNIMPL; } -void PoolNoReclaim(Pool pool, Trace trace, Seg seg) +Bool PoolNoReclaim(Pool pool, Trace trace, Seg seg) { AVERT(Pool, pool); AVERT(Trace, trace); AVERT(Seg, seg); NOTREACHED; + return FALSE; } void PoolTrivTraceEnd(Pool pool, Trace trace) @@ -596,7 +592,7 @@ Res PoolNoFramePop(Pool pool, Buffer buf, AllocFrame frame) { AVERT(Pool, pool); AVERT(Buffer, buf); - /* frame is of a abstract type & can't be checked */ + /* frame is of an abstract type & can't be checked */ UNUSED(frame); NOTREACHED; return ResUNIMPL; @@ -607,7 +603,7 @@ void PoolNoFramePopPending(Pool pool, Buffer buf, AllocFrame frame) { AVERT(Pool, pool); AVERT(Buffer, buf); - /* frame is of a abstract type & can't be checked */ + /* frame is of an abstract type & can't be checked */ UNUSED(frame); NOTREACHED; } @@ -626,24 +622,35 @@ Res PoolTrivFramePop(Pool pool, Buffer buf, AllocFrame frame) { AVERT(Pool, pool); AVERT(Buffer, buf); - /* frame is of a abstract type & can't be checked */ + /* frame is of an abstract type & can't be checked */ UNUSED(frame); return ResOK; } +void PoolTrivFramePopPending(Pool pool, Buffer buf, AllocFrame frame) +{ + AVERT(Pool, pool); + AVERT(Buffer, buf); + /* frame is of an abstract type & can't be checked */ + UNUSED(frame); + NOOP; +} + + Res PoolNoAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr) { AVER(pReturn != NULL); AVERT(Pool, pool); AVERT(Seg, seg); AVER(SegPool(seg) == pool); - AVER(SegBase(seg) <= addr && addr < SegLimit(seg)); + AVER(SegBase(seg) <= addr); + AVER(addr < SegLimit(seg)); return ResUNIMPL; } -void PoolNoWalk(Pool pool, Seg seg, - FormattedObjectsStepMethod f, void *p, size_t s) +void PoolNoWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, + void *p, size_t s) { AVERT(Pool, pool); AVERT(Seg, seg); @@ -656,7 +663,7 @@ void PoolNoWalk(Pool pool, Seg seg, } -void PoolNoFreeWalk(Pool pool, FreeBlockStepMethod f, void *p) +void PoolTrivFreeWalk(Pool pool, FreeBlockVisitor f, void *p) { AVERT(Pool, pool); AVER(FUNCHECK(f)); @@ -675,9 +682,17 @@ BufferClass PoolNoBufferClass(void) } +Size PoolNoSize(Pool pool) +{ + AVERT(Pool, pool); + NOTREACHED; + return UNUSED_SIZE; +} + + /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolamc.c b/code/poolamc.c index d7d8e0cbfd..0ba811b622 100644 --- a/code/poolamc.c +++ b/code/poolamc.c @@ -11,25 +11,27 @@ #include "chain.h" #include "bt.h" #include "mpm.h" +#include "nailboard.h" SRCID(poolamc, "$Id$"); -/* PType enumeration -- distinguishes AMCGen and AMCNailboard */ -enum {AMCPTypeGen = 1, AMCPTypeNailboard}; - /* AMC typedef */ typedef struct AMCStruct *AMC; /* amcGen typedef */ typedef struct amcGenStruct *amcGen; +/* Function returning TRUE if block in nailboarded segment is pinned. */ +typedef Bool (*amcPinnedFunction)(AMC amc, Nailboard board, Addr base, Addr limit); + + /* forward declarations */ static Bool amcSegHasNailboard(Seg seg); +static Nailboard amcSegNailboard(Seg seg); static Bool AMCCheck(AMC amc); static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO); -static Res AMCHeaderFix(Pool pool, ScanState ss, Seg seg, Ref *refIO); -extern PoolClass AMCPoolClassGet(void); +extern PoolClass AMCZPoolClassGet(void); extern BufferClass amcBufClassGet(void); extern SegClass amcSegClassGet(void); @@ -40,14 +42,12 @@ extern SegClass amcSegClassGet(void); typedef struct amcGenStruct { PoolGenStruct pgen; - int type; /* AMCPTypeGen for a gen */ RingStruct amcRing; /* link in list of gens in pool */ Buffer forward; /* forwarding buffer */ - Count segs; /* number of segs in gen */ Sig sig; /* */ } amcGenStruct; -#define amcGenAMC(amcgen) Pool2AMC((amcgen)->pgen.pool) +#define amcGenAMC(amcgen) PoolAMC((amcgen)->pgen.pool) #define amcGenPool(amcgen) ((amcgen)->pgen.pool) #define amcGenNr(amcgen) ((amcgen)->pgen.nr) @@ -68,44 +68,21 @@ enum { #undef RAMP_ENUM -/* amcNailboard -- the nailboard */ - -typedef struct amcNailboardStruct *amcNailboard; -typedef struct amcNailboardStruct { - Sig sig; - int type; /* AMCPTypeNailboard for a nailboard */ - amcGen gen; /* generation of this segment */ - Count nails; /* no. of ambigFixes, not necessarily distinct */ - Count distinctNails; /* number of distinct ambigFixes */ - Bool newMarks; /* set to TRUE if a new mark bit is added */ - Shift markShift; /* to convert offset into bit index for mark */ - BT mark; /* mark table used to record ambiguous fixes */ -} amcNailboardStruct; - -#define amcNailboardSig ((Sig)0x519A3C4B) /* SIGnature AMC Nailboard */ - - /* amcSegStruct -- AMC-specific fields appended to GCSegStruct * - * .segtype: logically, AMC segs should have pointers to: - * - the generation (amcGenStruct); - * - the nailboard (or NULL if not present). - * But in fact (apparently to save space in the amcSegStruct?) these - * pointers are encoded, so as to use only a single-word "segTypeP" - * field in amcSegStruct, as follows: - * The "segTypeP" field is a pointer to (the type field of) either - * a nailboard or a generation. The value stored in the type field - * indicates whether its enclosing struct is a generation or a - * nailboard. The segTypeP field is initialised by passing an - * additional parameter (the address of the segment's generation) to - * SegAlloc. See . + * .seg.old: The "old" flag is FALSE if the segment has never been + * collected, and so its size is accounted against the pool + * generation's newSize; it is TRUE if the segment has been collected + * at least once, and so its size is accounted against the pool + * generation's oldSize. * - * .seg-ramp-new: The "new" flag is usually true, and indicates that the - * segment has been counted towards the pool generation's newSize. It is - * set to FALSE otherwise. This is used by both ramping and hash array - * allocations. TODO: The code for this is scrappy and needs refactoring, - * and the *reasons* for setting these flags need properly documenting. - * RB 2013-07-17 + * .seg.deferred: The "deferred" flag is TRUE if its size accounting + * in the pool generation has been deferred. This is set if the + * segment was created in ramping mode (and so we don't want it to + * contribute to the pool generation's newSize and so provoke a + * collection via TracePoll), and by hash array allocations (where we + * don't want the allocation to provoke a collection that makes the + * location dependency stale immediately). */ typedef struct amcSegStruct *amcSeg; @@ -114,48 +91,49 @@ typedef struct amcSegStruct *amcSeg; typedef struct amcSegStruct { GCSegStruct gcSegStruct; /* superclass fields must come first */ - int *segTypeP; /* .segtype */ - Bool new; /* .seg-ramp-new */ + amcGen gen; /* generation this segment belongs to */ + Nailboard board; /* nailboard for this segment or NULL if none */ + BOOLFIELD(old); /* .seg.old */ + BOOLFIELD(deferred); /* .seg.deferred */ Sig sig; /* */ } amcSegStruct; #define Seg2amcSeg(seg) ((amcSeg)(seg)) #define amcSeg2Seg(amcseg) ((Seg)(amcseg)) -#define amcSegTypeP(seg) (Seg2amcSeg(seg)->segTypeP) -#define amcSegSetTypeP(seg, type) (Seg2amcSeg(seg)->segTypeP = (type)) - +ATTRIBUTE_UNUSED static Bool amcSegCheck(amcSeg amcseg) { CHECKS(amcSeg, amcseg); CHECKD(GCSeg, &amcseg->gcSegStruct); - CHECKL(*amcseg->segTypeP == AMCPTypeNailboard - || *amcseg->segTypeP == AMCPTypeGen); - if(*amcseg->segTypeP == AMCPTypeNailboard) { + CHECKU(amcGen, amcseg->gen); + if (amcseg->board) { + CHECKD(Nailboard, amcseg->board); CHECKL(SegNailed(amcSeg2Seg(amcseg)) != TraceSetEMPTY); } - CHECKL(BoolCheck(amcseg->new)); + /* CHECKL(BoolCheck(amcseg->old)); */ + /* CHECKL(BoolCheck(amcseg->deferred)); */ return TRUE; } /* AMCSegInit -- initialise an AMC segment */ -ARG_DEFINE_KEY(amc_seg_type, Pointer); -#define amcKeySegType (&_mps_key_amc_seg_type) +ARG_DEFINE_KEY(amc_seg_gen, Pointer); +#define amcKeySegGen (&_mps_key_amc_seg_gen) static Res AMCSegInit(Seg seg, Pool pool, Addr base, Size size, Bool reservoirPermit, ArgList args) { - int *segtype; + amcGen amcgen; SegClass super; amcSeg amcseg; Res res; ArgStruct arg; - ArgRequire(&arg, args, amcKeySegType); /* .segtype */ - segtype = arg.val.p; + ArgRequire(&arg, args, amcKeySegGen); + amcgen = arg.val.p; AVERT(Seg, seg); amcseg = Seg2amcSeg(seg); @@ -168,8 +146,10 @@ static Res AMCSegInit(Seg seg, Pool pool, Addr base, Size size, if(res != ResOK) return res; - amcseg->segTypeP = segtype; /* .segtype */ - amcseg->new = TRUE; + amcseg->gen = amcgen; + amcseg->board = NULL; + amcseg->old = FALSE; + amcseg->deferred = FALSE; amcseg->sig = amcSegSig; AVERT(amcSeg, amcseg); @@ -254,7 +234,7 @@ static void AMCSegSketch(Seg seg, char *pbSketch, size_t cbSketch) * * See . */ -static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream) +static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) { Res res; Pool pool; @@ -275,7 +255,7 @@ static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream) /* Describe the superclass fields first via next-method call */ super = SEG_SUPERCLASS(amcSegClass); - res = super->describe(seg, stream); + res = super->describe(seg, stream, depth); if(res != ResOK) return res; @@ -287,7 +267,7 @@ static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream) p = AddrAdd(base, pool->format->headerSize); limit = SegLimit(seg); - res = WriteF(stream, + res = WriteF(stream, depth, "AMC seg $P [$A,$A){\n", (WriteFP)seg, (WriteFA)base, (WriteFA)limit, NULL); @@ -295,19 +275,17 @@ static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream) return res; if(amcSegHasNailboard(seg)) { - res = WriteF(stream, " Boarded\n", NULL); - /* @@@@ should have AMCNailboardDescribe() */ + res = WriteF(stream, depth + 2, "Boarded\n", NULL); + } else if(SegNailed(seg) == TraceSetEMPTY) { + res = WriteF(stream, depth + 2, "Mobile\n", NULL); } else { - if(SegNailed(seg) == TraceSetEMPTY) { - res = WriteF(stream, " Mobile\n", NULL); - } else { - res = WriteF(stream, " Stuck\n", NULL); - } + res = WriteF(stream, depth + 2, "Stuck\n", NULL); } if(res != ResOK) return res; - res = WriteF(stream, " Map: *===:object bbbb:buffer\n", NULL); + res = WriteF(stream, depth + 2, + "Map: *===:object @+++:nails bbbb:buffer\n", NULL); if(res != ResOK) return res; @@ -320,39 +298,42 @@ static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream) Addr j; char c; - res = WriteF(stream, " $A ", i, NULL); + res = WriteF(stream, depth + 2, "$A ", (WriteFA)i, NULL); if(res != ResOK) return res; - /* @@@@ This needs to describe nailboards as well */ /* @@@@ This misses a header-sized pad at the end. */ for(j = i; j < AddrAdd(i, row); j = AddrAdd(j, step)) { if(j >= limit) c = ' '; /* if seg is not a whole number of print rows */ else if(j >= init) c = 'b'; - else if(j == p) { - c = '*'; - p = (pool->format->skip)(p); - } else { - c = '='; + else { + Bool nailed = amcSegHasNailboard(seg) + && NailboardGet(amcSegNailboard(seg), j); + if(j == p) { + c = (nailed ? '@' : '*'); + p = (pool->format->skip)(p); + } else { + c = (nailed ? '+' : '='); + } } - res = WriteF(stream, "$C", c, NULL); + res = WriteF(stream, 0, "$C", (WriteFC)c, NULL); if(res != ResOK) return res; } - res = WriteF(stream, "\n", NULL); + res = WriteF(stream, 0, "\n", NULL); if(res != ResOK) return res; } AMCSegSketch(seg, abzSketch, NELEMS(abzSketch)); - res = WriteF(stream, " Sketch: $S\n", (WriteFS)abzSketch, NULL); + res = WriteF(stream, depth + 2, "Sketch: $S\n", (WriteFS)abzSketch, NULL); if(res != ResOK) return res; - res = WriteF(stream, "} AMC Seg $P\n", (WriteFP)seg, NULL); + res = WriteF(stream, depth, "} AMC Seg $P\n", (WriteFP)seg, NULL); if(res != ResOK) return res; @@ -370,6 +351,7 @@ DEFINE_SEG_CLASS(amcSegClass, class) class->size = sizeof(amcSegStruct); class->init = AMCSegInit; class->describe = AMCSegDescribe; + AVERT(SegClass, class); } @@ -380,23 +362,18 @@ DEFINE_SEG_CLASS(amcSegClass, class) */ static Bool amcSegHasNailboard(Seg seg) { - int type; - - type = *amcSegTypeP(seg); - AVER(type == AMCPTypeNailboard || type == AMCPTypeGen); - return type == AMCPTypeNailboard; + amcSeg amcseg = Seg2amcSeg(seg); + return amcseg->board != NULL; } /* amcSegNailboard -- get the nailboard for this segment */ -static amcNailboard amcSegNailboard(Seg seg) +static Nailboard amcSegNailboard(Seg seg) { - int *p; - - p = amcSegTypeP(seg); + amcSeg amcseg = Seg2amcSeg(seg); AVER(amcSegHasNailboard(seg)); - return PARENT(amcNailboardStruct, type, p); + return amcseg->board; } @@ -404,14 +381,8 @@ static amcNailboard amcSegNailboard(Seg seg) static amcGen amcSegGen(Seg seg) { - if(amcSegHasNailboard(seg)) { - amcNailboard Nailboard = amcSegNailboard(seg); - return Nailboard->gen; - } else { - int *p; - p = amcSegTypeP(seg); - return PARENT(amcGenStruct, type, p); - } + amcSeg amcseg = Seg2amcSeg(seg); + return amcseg->gen; } @@ -422,77 +393,11 @@ static amcGen amcSegGen(Seg seg) #define AMCSig ((Sig)0x519A3C99) /* SIGnature AMC */ -typedef struct PageRetStruct { - Count pCond; /* pages Condemned */ - Count pRet; /* pages Retained (in place) */ - /* Small */ - Count pCS; /* pages Condemned in Small segments */ - Count pRS; /* pages Retained in Small segments */ - /* Medium */ - Count sCM; /* segments Condemned: Medium */ - /* ...= upper bound of how many extra pages it */ - /* would have cost, had we chosen to LSP-pad */ - /* all these segments. */ - Count pCM; /* pages Condemned in Medium segments */ - Count sRM; /* segments Retained: Medium */ - Count pRM; /* pages Retained in Medium segments: */ - Count pRM1; /* ...because obj 1 was preserved in place */ - /* ...because a rest obj was pip, causing: */ - Count pRMrr; /* ...retained rest pages (page where rest obj is) */ - Count pRMr1; /* ...retained obj 1 pages (purely NMR pad) */ - /* Large */ - Count sCL; /* segments Condemned: Large */ - /* ...= upper bound of how many extra pages it */ - /* has cost to LSP-pad all these segments. */ - Count pCL; /* pages Condemned in Large segments */ - Count sRL; /* segments Retained: Large */ - Count pRL; /* pages Retained in Large segments */ - Count pRLr; /* ...because a rest obj (actually LSP) was pip */ - - /* The interesting things about this report are: - * - How many pages are actually being retained? (pRet) - * - Percentage? (pRet/pCond) - * - Is the major contribution from Small, Medium, or Large segs? - * - * Generally, pages retained because obj 1 needed to be preserved in - * place are ok (because no alternative placement could have retained - * fewer pages), but pages retained by a rest obj are unfortunate - * (better placement, putting the small rest objs in their own seg, - * would have retained fewer pages). In particular: - * - * The LSP threshold is a payoff between the wasted space from - * LSP-padding, versus the risk of increased page-retention (due to - * rest objs) from not LSP-padding. - * - * For Medium segs, where we do not do LSP-padding: - * - LSP would have required at most sCM extra pages; - * - the extra retention incurred by not LSP-padding is pRMr1. - * A high pRMr1 => lots of Medium segs getting retained by the rest - * objs tacked on after obj 1. Consider lowering LSP-threshold. - * - * For Large segs we do LSP padding. This has a cost; upper bound is - * sCL extra pages. But the benefit should be greatly reduced ambig - * refs to rest objs. With LSP, the only rest obj is the LSP pad - * itself. We expect that ambig refs to this are rare, so currently - * we do not implement .large.lsp-no-retain. But we do record the - * occurrence of pages retained by a ref to an LSP pad: pPLr. A high - * pRLr => perhaps .large.lsp-no-retain should be implemented? - * - * If the mutator is causing a lot of page retention, then sRM/pRM - * and sRL/pRL should give some picture of the number of retained - * objects and their average size. - */ -} PageRetStruct; - -/* static => init'd to zero */ -static struct PageRetStruct pageretstruct_Zero; - typedef struct AMCStruct { /* */ PoolStruct poolStruct; /* generic pool structure */ RankSet rankSet; /* rankSet for entire pool */ RingStruct genRing; /* ring of generations */ Bool gensBooted; /* used during boot (init) */ - Chain chain; /* chain used by this pool */ size_t gens; /* number of generations */ amcGen *gen; /* (pointer to) array of generations */ amcGen nursery; /* the default mutator generation */ @@ -500,53 +405,30 @@ typedef struct AMCStruct { /* */ amcGen afterRampGen; /* the generation after rampGen */ unsigned rampCount; /* */ int rampMode; /* */ - - /* page retention in an in-progress trace */ - STATISTIC_DECL(PageRetStruct pageretstruct[TraceLIMIT]); - + amcPinnedFunction pinned; /* function determining if block is pinned */ + Size extendBy; /* segment size to extend pool by */ + Size largeSize; /* min size of "large" segments */ Sig sig; /* */ } AMCStruct; -#define Pool2AMC(pool) PARENT(AMCStruct, poolStruct, (pool)) -#define AMC2Pool(amc) (&(amc)->poolStruct) +#define PoolAMC(pool) PARENT(AMCStruct, poolStruct, (pool)) +#define AMCPool(amc) (&(amc)->poolStruct) /* amcGenCheck -- check consistency of a generation structure */ +ATTRIBUTE_UNUSED static Bool amcGenCheck(amcGen gen) { - Arena arena; AMC amc; CHECKS(amcGen, gen); CHECKD(PoolGen, &gen->pgen); amc = amcGenAMC(gen); CHECKU(AMC, amc); - CHECKL(gen->type == AMCPTypeGen); CHECKD(Buffer, gen->forward); - CHECKL(RingCheck(&gen->amcRing)); - CHECKL((gen->pgen.totalSize == 0) == (gen->segs == 0)); - arena = amc->poolStruct.arena; - CHECKL(gen->pgen.totalSize >= gen->segs * ArenaAlign(arena)); - return TRUE; -} - + CHECKD_NOSIG(Ring, &gen->amcRing); -/* amcNailboardCheck -- check the nailboard */ - -static Bool amcNailboardCheck(amcNailboard board) -{ - CHECKS(amcNailboard, board); - CHECKL(board->type == AMCPTypeNailboard); - CHECKD(amcGen, board->gen); - /* nails is >= number of set bits in mark, but we can't check this */ - /* We know that shift corresponds to pool->align. */ - CHECKL(BoolCheck(board->newMarks)); - CHECKL(board->distinctNails <= board->nails); - CHECKL((Align)1 << board->markShift - == PoolAlignment(amcGenPool(board->gen))); - /* weak check for BTs @@@@ */ - CHECKL(board->mark != NULL); return TRUE; } @@ -582,10 +464,11 @@ typedef struct amcBufStruct { /* amcBufCheck -- check consistency of an amcBuf */ +ATTRIBUTE_UNUSED static Bool amcBufCheck(amcBuf amcbuf) { CHECKS(amcBuf, amcbuf); - CHECKL(SegBufCheck(&amcbuf->segbufStruct)); + CHECKD(SegBuf, &amcbuf->segbufStruct); if(amcbuf->gen != NULL) CHECKD(amcGen, amcbuf->gen); CHECKL(BoolCheck(amcbuf->forHashArrays)); @@ -633,7 +516,7 @@ static Res AMCBufInit(Buffer buffer, Pool pool, ArgList args) AVERT(Buffer, buffer); AVERT(Pool, pool); - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); if (ArgPick(&arg, args, amcKeyAPHashArrays)) @@ -691,46 +574,45 @@ DEFINE_BUFFER_CLASS(amcBufClass, class) class->size = sizeof(amcBufStruct); class->init = AMCBufInit; class->finish = AMCBufFinish; + AVERT(BufferClass, class); } /* amcGenCreate -- create a generation */ -static Res amcGenCreate(amcGen *genReturn, AMC amc, Serial genNr) +static Res amcGenCreate(amcGen *genReturn, AMC amc, GenDesc gen) { Arena arena; Buffer buffer; Pool pool; - amcGen gen; + amcGen amcgen; Res res; void *p; - pool = AMC2Pool(amc); + pool = AMCPool(amc); arena = pool->arena; res = ControlAlloc(&p, arena, sizeof(amcGenStruct), FALSE); if(res != ResOK) goto failControlAlloc; - gen = (amcGen)p; + amcgen = (amcGen)p; res = BufferCreate(&buffer, EnsureamcBufClass(), pool, FALSE, argsNone); if(res != ResOK) goto failBufferCreate; - res = PoolGenInit(&gen->pgen, amc->chain, genNr, pool); + res = PoolGenInit(&amcgen->pgen, gen, pool); if(res != ResOK) goto failGenInit; - gen->type = AMCPTypeGen; - RingInit(&gen->amcRing); - gen->segs = 0; - gen->forward = buffer; - gen->sig = amcGenSig; + RingInit(&amcgen->amcRing); + amcgen->forward = buffer; + amcgen->sig = amcGenSig; - AVERT(amcGen, gen); + AVERT(amcGen, amcgen); - RingAppend(&amc->genRing, &gen->amcRing); - EVENT2(AMCGenCreate, amc, gen); - *genReturn = gen; + RingAppend(&amc->genRing, &amcgen->amcRing); + EVENT2(AMCGenCreate, amc, amcgen); + *genReturn = amcgen; return ResOK; failGenInit: @@ -749,8 +631,6 @@ static void amcGenDestroy(amcGen gen) Arena arena; AVERT(amcGen, gen); - AVER(gen->segs == 0); - AVER(gen->pgen.totalSize == 0); EVENT1(AMCGenDestroy, gen); arena = PoolArena(amcGenPool(gen)); @@ -765,22 +645,26 @@ static void amcGenDestroy(amcGen gen) /* amcGenDescribe -- describe an AMC generation */ -static Res amcGenDescribe(amcGen gen, mps_lib_FILE *stream) +static Res amcGenDescribe(amcGen gen, mps_lib_FILE *stream, Count depth) { Res res; if(!TESTT(amcGen, gen)) return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "amcGen $P {\n", (WriteFP)gen, + " buffer $P\n", (WriteFP)gen->forward, NULL); + if (res != ResOK) + return res; + + res = PoolGenDescribe(&gen->pgen, stream, depth + 2); + if (res != ResOK) + return res; - res = WriteF(stream, - " amcGen $P ($U) {\n", - (WriteFP)gen, (WriteFU)amcGenNr(gen), - " buffer $P\n", gen->forward, - " segs $U, totalSize $U, newSize $U\n", - (WriteFU)gen->segs, - (WriteFU)gen->pgen.totalSize, - (WriteFU)gen->pgen.newSize, - " } amcGen\n", NULL); + res = WriteF(stream, depth, "} amcGen $P\n", (WriteFP)gen, NULL); return res; } @@ -789,166 +673,41 @@ static Res amcGenDescribe(amcGen gen, mps_lib_FILE *stream) static Res amcSegCreateNailboard(Seg seg, Pool pool) { - amcNailboard board; + amcSeg amcseg; + Nailboard board; Arena arena; - Count bits; Res res; - void *p; + amcseg = Seg2amcSeg(seg); AVER(!amcSegHasNailboard(seg)); - arena = PoolArena(pool); - res = ControlAlloc(&p, arena, sizeof(amcNailboardStruct), FALSE); - if(res != ResOK) - goto failAllocNailboard; - board = p; - board->type = AMCPTypeNailboard; - board->gen = amcSegGen(seg); - board->nails = (Count)0; - board->distinctNails = (Count)0; - board->newMarks = FALSE; - board->markShift = SizeLog2((Size)pool->alignment); - /* [I wonder what this comment is referring to? 2007-07-11 DRJ] */ - /* See d.m.p.Nailboard.size. */ - bits = (SegSize(seg) + pool->format->headerSize) >> board->markShift; - res = ControlAlloc(&p, arena, BTSize(bits), FALSE); - if(res != ResOK) - goto failMarkTable; - board->mark = p; - BTResRange(board->mark, 0, bits); - board->sig = amcNailboardSig; - AVERT(amcNailboard, board); - amcSegSetTypeP(seg, &board->type); /* .segtype */ + res = NailboardCreate(&board, arena, pool->alignment, + SegBase(seg), SegLimit(seg)); + if (res != ResOK) + return res; + amcseg->board = board; return ResOK; - -failMarkTable: - ControlFree(arena, board, sizeof(amcNailboardStruct)); -failAllocNailboard: - return res; } -/* amcSegDestroyNailboard -- destroy the nailboard of a segment */ +/* amcPinnedInterior -- block is pinned by any nail */ -static void amcSegDestroyNailboard(Seg seg, Pool pool) +static Bool amcPinnedInterior(AMC amc, Nailboard board, Addr base, Addr limit) { - amcNailboard board; - amcGen gen; - Arena arena; - Count bits; - - gen = amcSegGen(seg); - board = amcSegNailboard(seg); - AVERT(amcNailboard, board); - - arena = PoolArena(pool); - /* See d.m.p.Nailboard.size. */ - bits = (SegSize(seg) + pool->format->headerSize) >> board->markShift; - ControlFree(arena, board->mark, BTSize(bits)); - board->sig = SigInvalid; - ControlFree(arena, board, sizeof(amcNailboardStruct)); - amcSegSetTypeP(seg, &gen->type); /* .segtype */ + Size headerSize = AMCPool(amc)->format->headerSize; + return !NailboardIsResRange(board, AddrSub(base, headerSize), + AddrSub(limit, headerSize)); } -/* amcNailGetMark -- get the mark bit for ref from the nailboard */ - -static Bool amcNailGetMark(Seg seg, Ref ref) -{ - amcNailboard board; - Index i; - - board = amcSegNailboard(seg); - AVERT(amcNailboard, board); - - i = AddrOffset(SegBase(seg), ref) >> board->markShift; - return BTGet(board->mark, i); -} - +/* amcPinnedBase -- block is pinned only if base is nailed */ -/* amcNailGetAndSetMark -- set the mark bit for ref in the nailboard - * - * Returns the old value. - */ -static Bool amcNailGetAndSetMark(Seg seg, Ref ref) +static Bool amcPinnedBase(AMC amc, Nailboard board, Addr base, Addr limit) { - amcNailboard board; - Index i; - - board = amcSegNailboard(seg); - AVERT(amcNailboard, board); - - ++board->nails; - i = AddrOffset(SegBase(seg), ref) >> board->markShift; - if(!BTGet(board->mark, i)) { - BTSet(board->mark, i); - board->newMarks = TRUE; - ++board->distinctNails; - return FALSE; - } - return TRUE; -} - - -/* amcNailMarkRange -- nail a range in the board - * - * We nail the objects laying between base and limit, i.e., mark the - * bits that correspond to client pointers for them. We may assume - * that the range is unmarked. - */ -static void amcNailMarkRange(Seg seg, Addr base, Addr limit) -{ - amcNailboard board; - Index ibase, ilimit; - Size headerSize; - - AVER(SegBase(seg) <= base); - AVER(base < SegLimit(seg)); - AVER(SegBase(seg) <= limit); - AVER(limit <= SegLimit(seg)); - AVER(base < limit); - - board = amcSegNailboard(seg); - AVERT(amcNailboard, board); - headerSize = SegPool(seg)->format->headerSize; - ibase = (AddrOffset(SegBase(seg), base) + headerSize) - >> board->markShift; - ilimit = (AddrOffset(SegBase(seg), limit) + headerSize) - >> board->markShift; - AVER(BTIsResRange(board->mark, ibase, ilimit)); - - BTSetRange(board->mark, ibase, ilimit); - board->nails += ilimit - ibase; - board->distinctNails += ilimit - ibase; -} - - -/* amcNailRangeIsMarked -- check that a range in the board is marked - * - * Like amcNailMarkRange, we take the arguments as referring to base - * pointers and look at the bits of the corresponding client pointers. - */ -static Bool amcNailRangeIsMarked(Seg seg, Addr base, Addr limit) -{ - amcNailboard board; - Index ibase, ilimit; - Size headerSize; - - AVER(SegBase(seg) <= base); - AVER(base < SegLimit(seg)); - AVER(SegBase(seg) <= limit); - AVER(limit <= SegLimit(seg)); - AVER(base < limit); - - board = amcSegNailboard(seg); - AVERT(amcNailboard, board); - headerSize = SegPool(seg)->format->headerSize; - ibase = (AddrOffset(SegBase(seg), base) + headerSize) - >> board->markShift; - ilimit = (AddrOffset(SegBase(seg), limit) + headerSize) - >> board->markShift; - return BTIsSetRange(board->mark, ibase, ilimit); + UNUSED(amc); + UNUSED(limit); + return NailboardGet(board, base); } @@ -961,7 +720,7 @@ static void AMCVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[1].key = MPS_KEY_CHAIN; args[1].val.chain = va_arg(varargs, Chain); args[2].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } @@ -975,38 +734,46 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) AMC amc; Res res; Arena arena; - TraceId ti; - Trace trace; Index i; size_t genArraySize; size_t genCount; + Bool interior = AMC_INTERIOR_DEFAULT; + Chain chain; + Size extendBy = AMC_EXTEND_BY_DEFAULT; + Size largeSize = AMC_LARGE_SIZE_DEFAULT; ArgStruct arg; - /* Suppress a warning about this structure not being used when there - are no statistics. Note that simply making the declaration conditional - does not work, because we carefully reference expressions inside - STATISTICS to prevent such warnings on parameters and local variables. - It's just that clang 4.0 on Mac OS X does some sort of extra check - that produces a special warnings about static variables. */ -#if !defined(STATISTICS) - UNUSED(pageretstruct_Zero); -#endif - AVER(pool != NULL); - amc = Pool2AMC(pool); + amc = PoolAMC(pool); arena = PoolArena(pool); ArgRequire(&arg, args, MPS_KEY_FORMAT); pool->format = arg.val.format; if (ArgPick(&arg, args, MPS_KEY_CHAIN)) - amc->chain = arg.val.chain; + chain = arg.val.chain; else - amc->chain = ArenaGlobals(arena)->defaultChain; + chain = ArenaGlobals(arena)->defaultChain; + if (ArgPick(&arg, args, MPS_KEY_INTERIOR)) + interior = arg.val.b; + if (ArgPick(&arg, args, MPS_KEY_EXTEND_BY)) + extendBy = arg.val.size; + if (ArgPick(&arg, args, MPS_KEY_LARGE_SIZE)) + largeSize = arg.val.size; AVERT(Format, pool->format); - AVERT(Chain, amc->chain); + AVER(FormatArena(pool->format) == arena); + AVERT(Chain, chain); + AVER(chain->arena == arena); + AVER(extendBy > 0); + AVER(largeSize > 0); + /* TODO: it would be nice to be able to manage large objects that + * are smaller than the extendBy, but currently this results in + * unacceptable fragmentation due to the padding objects. This + * assertion catches this bad case. */ + AVER(largeSize >= extendBy); pool->alignment = pool->format->alignment; + pool->fix = AMCFix; amc->rankSet = rankSet; RingInit(&amc->genRing); @@ -1021,21 +788,20 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) amc->rampCount = 0; amc->rampMode = RampOUTSIDE; - TRACE_SET_ITER(ti, trace, TraceSetUNIV, arena) - STATISTIC(amc->pageretstruct[ti] = pageretstruct_Zero); - TRACE_SET_ITER_END(ti, trace, TraceSetUNIV, arena); - - if(pool->format->headerSize == 0) { - pool->fix = AMCFix; + if (interior) { + amc->pinned = amcPinnedInterior; } else { - pool->fix = AMCHeaderFix; + amc->pinned = amcPinnedBase; } + /* .extend-by.aligned: extendBy is aligned to the arena alignment. */ + amc->extendBy = SizeArenaGrains(extendBy, arena); + amc->largeSize = largeSize; amc->sig = AMCSig; AVERT(AMC, amc); /* Init generations. */ - genCount = ChainGens(amc->chain); + genCount = ChainGens(chain); { void *p; @@ -1045,11 +811,10 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) if(res != ResOK) goto failGensAlloc; amc->gen = p; - for(i = 0; i < genCount + 1; ++i) { - res = amcGenCreate(&amc->gen[i], amc, (Serial)i); - if(res != ResOK) { + for (i = 0; i <= genCount; ++i) { + res = amcGenCreate(&amc->gen[i], amc, ChainGen(chain, i)); + if (res != ResOK) goto failGenAlloc; - } } /* Set up forwarding buffers. */ for(i = 0; i < genCount; ++i) { @@ -1103,7 +868,7 @@ static void AMCFinish(Pool pool) Ring node, nextNode; AVERT(Pool, pool); - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); EVENT1(AMCFinish, amc); @@ -1115,21 +880,19 @@ static void AMCFinish(Pool pool) RING_FOR(node, &amc->genRing, nextNode) { amcGen gen = RING_ELT(amcGen, amcRing, node); BufferDetach(gen->forward, pool); - /* Maintain invariant < totalSize. */ - gen->pgen.newSize = (Size)0; } ring = PoolSegRing(pool); RING_FOR(node, ring, nextNode) { Seg seg = SegOfPoolRing(node); - Size size; amcGen gen = amcSegGen(seg); - - --gen->segs; - size = SegSize(seg); - gen->pgen.totalSize -= size; - - SegFree(seg); + amcSeg amcseg = Seg2amcSeg(seg); + AVERT(amcSeg, amcseg); + PoolGenFree(&gen->pgen, seg, + 0, + amcseg->old ? SegSize(seg) : 0, + amcseg->old ? 0 : SegSize(seg), + amcseg->deferred); } /* Disassociate forwarding buffers from gens before they are */ @@ -1161,14 +924,13 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, Res res; Addr base, limit; Arena arena; - Size alignedSize; + Size grainsSize; amcGen gen; PoolGen pgen; amcBuf amcbuf; - Bool isRamping; AVERT(Pool, pool); - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); AVER(baseReturn != NULL); AVER(limitReturn != NULL); @@ -1186,17 +948,21 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, pgen = &gen->pgen; /* Create and attach segment. The location of this segment is */ - /* expressed as a generation number. We rely on the arena to */ + /* expressed via the pool generation. We rely on the arena to */ /* organize locations appropriately. */ - alignedSize = SizeAlignUp(size, ArenaAlign(arena)); + if (size < amc->extendBy) { + grainsSize = amc->extendBy; /* .extend-by.aligned */ + } else { + grainsSize = SizeArenaGrains(size, arena); + } MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD_FIELD(args, amcKeySegType, p, &gen->type); /* .segtype */ - res = ChainAlloc(&seg, amc->chain, PoolGenNr(pgen), amcSegClassGet(), - alignedSize, pool, withReservoirPermit, args); + MPS_ARGS_ADD_FIELD(args, amcKeySegGen, p, gen); + res = PoolGenAlloc(&seg, pgen, amcSegClassGet(), grainsSize, + withReservoirPermit, args); } MPS_ARGS_END(args); if(res != ResOK) return res; - AVER(alignedSize == SegSize(seg)); + AVER(grainsSize == SegSize(seg)); /* */ if(BufferRankSet(buffer) == RankSetEMPTY) @@ -1204,26 +970,20 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, else SegSetRankAndSummary(seg, BufferRankSet(buffer), RefSetUNIV); - /* Put the segment in the generation indicated by the buffer. */ - ++gen->segs; - pgen->totalSize += alignedSize; - - /* If ramping, or if the buffer is intended for allocating - hash table arrays, don't count it towards newSize. */ - isRamping = (amc->rampMode == RampRAMPING && - buffer == amc->rampGen->forward && - gen == amc->rampGen); - if (isRamping || amcbuf->forHashArrays) { - Seg2amcSeg(seg)->new = FALSE; - } else { - pgen->newSize += alignedSize; + /* If ramping, or if the buffer is intended for allocating hash + * table arrays, defer the size accounting. */ + if ((amc->rampMode == RampRAMPING + && buffer == amc->rampGen->forward + && gen == amc->rampGen) + || amcbuf->forHashArrays) + { + Seg2amcSeg(seg)->deferred = TRUE; } base = SegBase(seg); - *baseReturn = base; - if(alignedSize < AMCLargeSegPAGES * ArenaAlign(arena)) { + if (size < amc->largeSize) { /* Small or Medium segment: give the buffer the entire seg. */ - limit = AddrAdd(base, alignedSize); + limit = AddrAdd(base, grainsSize); AVER(limit == SegLimit(seg)); } else { /* Large segment: ONLY give the buffer the size requested, and */ @@ -1233,7 +993,7 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, limit = AddrAdd(base, size); AVER(limit <= SegLimit(seg)); - padSize = alignedSize - size; + padSize = grainsSize - size; AVER(SizeIsAligned(padSize, PoolAlignment(pool))); AVER(AddrAdd(limit, padSize) == SegLimit(seg)); if(padSize > 0) { @@ -1242,6 +1002,9 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, ShieldCover(arena, seg); } } + + PoolGenAccountForFill(pgen, SegSize(seg), Seg2amcSeg(seg)->deferred); + *baseReturn = base; *limitReturn = limit; return ResOK; } @@ -1260,7 +1023,7 @@ static void AMCBufferEmpty(Pool pool, Buffer buffer, Seg seg; AVERT(Pool, pool); - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); AVERT(Buffer, buffer); AVER(BufferIsReady(buffer)); @@ -1269,7 +1032,7 @@ static void AMCBufferEmpty(Pool pool, Buffer buffer, AVER(init <= limit); arena = BufferArena(buffer); - if(SegSize(seg) < AMCLargeSegPAGES * ArenaAlign(arena)) { + if(SegSize(seg) < amc->largeSize) { /* Small or Medium segment: buffer had the entire seg. */ AVER(limit == SegLimit(seg)); } else { @@ -1284,6 +1047,11 @@ static void AMCBufferEmpty(Pool pool, Buffer buffer, (*pool->format->pad)(init, size); ShieldCover(arena, seg); } + + /* The unused part of the buffer is not reused by AMC, so we pass 0 + * for the unused argument. This call therefore has no effect on the + * accounting, but we call it anyway for consistency. */ + PoolGenAccountForEmpty(&amcSegGen(seg)->pgen, 0, Seg2amcSeg(seg)->deferred); } @@ -1294,7 +1062,7 @@ static void AMCRampBegin(Pool pool, Buffer buf, Bool collectAll) AMC amc; AVERT(Pool, pool); - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); AVERT(Buffer, buf); AVERT(Bool, collectAll); @@ -1316,7 +1084,7 @@ static void AMCRampEnd(Pool pool, Buffer buf) AMC amc; AVERT(Pool, pool); - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); AVERT(Buffer, buf); @@ -1347,16 +1115,19 @@ static void AMCRampEnd(Pool pool, Buffer buf) NOTREACHED; } - /* Adjust amc->rampGen->pgen.newSize: Now count all the segments */ - /* in the ramp generation as new (except if they're white). */ + /* Now all the segments in the ramp generation contribute to the + * pool generation's sizes. */ RING_FOR(node, PoolSegRing(pool), nextNode) { Seg seg = SegOfPoolRing(node); - - if(amcSegGen(seg) == amc->rampGen && !Seg2amcSeg(seg)->new + amcSeg amcseg = Seg2amcSeg(seg); + if(amcSegGen(seg) == amc->rampGen + && amcseg->deferred && SegWhite(seg) == TraceSetEMPTY) { - pgen->newSize += SegSize(seg); - Seg2amcSeg(seg)->new = TRUE; + PoolGenUndefer(pgen, + amcseg->old ? SegSize(seg) : 0, + amcseg->old ? 0 : SegSize(seg)); + amcseg->deferred = FALSE; } } } @@ -1370,14 +1141,17 @@ static void AMCRampEnd(Pool pool, Buffer buf) */ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) { + Size condemned = 0; amcGen gen; AMC amc; Buffer buffer; + amcSeg amcseg; Res res; AVERT(Pool, pool); AVERT(Trace, trace); AVERT(Seg, seg); + amcseg = Seg2amcSeg(seg); buffer = SegBuffer(seg); if(buffer != NULL) { @@ -1409,8 +1183,9 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) return ResOK; } if(BufferScanLimit(buffer) != BufferLimit(buffer)) { - amcNailMarkRange(seg, BufferScanLimit(buffer), - BufferLimit(buffer)); + NailboardSetRange(amcSegNailboard(seg), + BufferScanLimit(buffer), + BufferLimit(buffer)); } ++trace->nailCount; SegSetNailed(seg, TraceSetSingle(trace)); @@ -1421,9 +1196,10 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) } } else { /* We have a nailboard, the buffer must be nailed already. */ - AVER((BufferScanLimit(buffer) == BufferLimit(buffer)) - || amcNailRangeIsMarked(seg, BufferScanLimit(buffer), - BufferLimit(buffer))); + AVER(BufferScanLimit(buffer) == BufferLimit(buffer) + || NailboardIsSetRange(amcSegNailboard(seg), + BufferScanLimit(buffer), + BufferLimit(buffer))); /* Nail it for this trace as well. */ SegSetNailed(seg, TraceSetAdd(SegNailed(seg), trace)); } @@ -1431,40 +1207,23 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) /* @@@@ We could subtract all the nailed grains. */ /* Relies on unsigned arithmetic wrapping round */ /* on under- and overflow (which it does). */ - trace->condemned -= AddrOffset(BufferScanLimit(buffer), - BufferLimit(buffer)); + condemned -= AddrOffset(BufferScanLimit(buffer), BufferLimit(buffer)); } } } SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); - trace->condemned += SegSize(seg); + condemned += SegSize(seg); + trace->condemned += condemned; - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); - STATISTIC_STAT( { - Count pages; - AVER(SizeIsAligned(SegSize(seg), ArenaAlign(pool->arena))); - pages = SegSize(seg) / ArenaAlign(pool->arena); - AVER(pages != 0); - amc->pageretstruct[trace->ti].pCond += pages; - if(pages == 1) { - amc->pageretstruct[trace->ti].pCS += pages; - } else if(pages < AMCLargeSegPAGES) { - amc->pageretstruct[trace->ti].sCM += 1; - amc->pageretstruct[trace->ti].pCM += pages; - } else { - amc->pageretstruct[trace->ti].sCL += 1; - amc->pageretstruct[trace->ti].pCL += pages; - } - } ); - gen = amcSegGen(seg); AVERT(amcGen, gen); - if(Seg2amcSeg(seg)->new) { - gen->pgen.newSize -= SegSize(seg); - Seg2amcSeg(seg)->new = FALSE; + if (!amcseg->old) { + PoolGenAccountForAge(&gen->pgen, SegSize(seg), amcseg->deferred); + amcseg->old = TRUE; } /* Ensure we are forwarding into the right generation. */ @@ -1486,85 +1245,98 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) } +/* amcScanNailedRange -- make one scanning pass over a range of + * addresses in a nailed segment. + * + * *totalReturn is set to FALSE if not all the objects between base and + * limit have been scanned. It is not touched otherwise. + */ +static Res amcScanNailedRange(Bool *totalReturn, Bool *moreReturn, + Size *bytesScanned, ScanState ss, + AMC amc, Nailboard board, + Addr base, Addr limit) +{ + Format format; + Size headerSize; + Addr p, clientLimit; + Pool pool = AMCPool(amc); + format = pool->format; + headerSize = format->headerSize; + p = AddrAdd(base, headerSize); + clientLimit = AddrAdd(limit, headerSize); + while (p < clientLimit) { + Addr q; + q = (*format->skip)(p); + if ((*amc->pinned)(amc, board, p, q)) { + Res res; + res = (*format->scan)(&ss->ss_s, p, q); + if(res != ResOK) { + *totalReturn = FALSE; + *moreReturn = TRUE; + return res; + } + *bytesScanned += AddrOffset(p, q); + } else { + *totalReturn = FALSE; + } + AVER(p < q); + p = q; + } + AVER(p == clientLimit); + return ResOK; +} + + /* amcScanNailedOnce -- make one scanning pass over a nailed segment * - * *totalReturn set to TRUE iff all objects in segment scanned. - * *moreReturn set to FALSE only if there are no more objects + * *totalReturn is set to TRUE iff all objects in segment scanned. + * *moreReturn is set to FALSE only if there are no more objects * on the segment that need scanning (which is normally the case). * It is set to TRUE if scanning had to be abandoned early on, and * also if during emergency fixing any new marks got added to the * nailboard. */ static Res amcScanNailedOnce(Bool *totalReturn, Bool *moreReturn, - ScanState ss, Pool pool, Seg seg, AMC amc) + ScanState ss, Seg seg, AMC amc) { Addr p, limit; - Format format; - Res res; - Bool total = TRUE; Size bytesScanned = 0; + Nailboard board; + Res res; EVENT3(AMCScanBegin, amc, seg, ss); /* TODO: consider using own event */ - format = pool->format; - amcSegNailboard(seg)->newMarks = FALSE; + *totalReturn = TRUE; + board = amcSegNailboard(seg); + NailboardClearNewNails(board); - p = AddrAdd(SegBase(seg), format->headerSize); + p = SegBase(seg); while(SegBuffer(seg) != NULL) { - limit = AddrAdd(BufferScanLimit(SegBuffer(seg)), - format->headerSize); + limit = BufferScanLimit(SegBuffer(seg)); if(p >= limit) { AVER(p == limit); goto returnGood; } - while(p < limit) { - Addr q; - q = (*format->skip)(p); - if(amcNailGetMark(seg, p)) { - res = (*format->scan)(&ss->ss_s, p, q); - if(res != ResOK) { - *totalReturn = FALSE; - *moreReturn = TRUE; - return res; - } - bytesScanned += AddrOffset(p, q); - } else { - total = FALSE; - } - p = q; - } - AVER(p == limit); + res = amcScanNailedRange(totalReturn, moreReturn, &bytesScanned, + ss, amc, board, p, limit); + if (res != ResOK) + return res; + p = limit; } - /* Should have a ScanMarkedRange or something like that @@@@ */ - /* to abstract common code. */ - limit = AddrAdd(SegLimit(seg), format->headerSize); + limit = SegLimit(seg); /* @@@@ Shouldn't p be set to BufferLimit here?! */ - while(p < limit) { - Addr q; - q = (*format->skip)(p); - if(amcNailGetMark(seg, p)) { - res = (*format->scan)(&ss->ss_s, p, q); - if(res != ResOK) { - *totalReturn = FALSE; - *moreReturn = TRUE; - return res; - } - bytesScanned += AddrOffset(p, q); - } else { - total = FALSE; - } - p = q; - } - AVER(p == limit); + res = amcScanNailedRange(totalReturn, moreReturn, &bytesScanned, + ss, amc, board, p, limit); + if (res != ResOK) + return res; returnGood: EVENT3(AMCScanEnd, amc, seg, ss); /* TODO: consider using own event */ AVER(bytesScanned <= SegSize(seg)); ss->scannedSize += bytesScanned; - *totalReturn = total; - *moreReturn = amcSegNailboard(seg)->newMarks; + *moreReturn = NailboardNewNails(board); return ResOK; } @@ -1579,7 +1351,7 @@ static Res amcScanNailed(Bool *totalReturn, ScanState ss, Pool pool, do { Res res; - res = amcScanNailedOnce(&total, &moreScanning, ss, pool, seg, amc); + res = amcScanNailedOnce(&total, &moreScanning, ss, seg, amc); if(res != ResOK) { *totalReturn = FALSE; return res; @@ -1630,7 +1402,7 @@ static Res AMCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) AVERT(ScanState, ss); AVERT(Seg, seg); AVERT(Pool, pool); - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); @@ -1707,7 +1479,7 @@ static void amcFixInPlace(Pool pool, Seg seg, ScanState ss, Ref *refIO) EVENT0(AMCFixInPlace); if(amcSegHasNailboard(seg)) { - Bool wasMarked = amcNailGetAndSetMark(seg, ref); + Bool wasMarked = NailboardSet(amcSegNailboard(seg), ref); /* If there are no new marks (i.e., no new traces for which we */ /* are marking, and no new mark bits set) then we can return */ /* immediately, without changing colour. */ @@ -1742,7 +1514,7 @@ static Res AMCFixEmergency(Pool pool, ScanState ss, Seg seg, arena = PoolArena(pool); AVERT(Arena, arena); - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); ss->wasMarked = TRUE; @@ -1778,8 +1550,11 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) AMC amc; Res res; Format format; /* cache of pool->format */ + Size headerSize; /* cache of pool->format->headerSize */ Ref ref; /* reference to be fixed */ + Addr base; /* base address of reference */ Ref newRef; /* new location, if moved */ + Addr newBase; /* base address of new copy */ Size length; /* length of object to be relocated */ Buffer buffer; /* buffer to allocate new copy into */ amcGen gen; /* generation of old copy of object */ @@ -1820,12 +1595,15 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) return ResOK; } - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT_CRITICAL(AMC, amc); format = pool->format; + headerSize = format->headerSize; ref = *refIO; - AVER_CRITICAL(SegBase(seg) <= ref); - AVER_CRITICAL(ref < SegLimit(seg)); + AVER_CRITICAL(AddrAdd(SegBase(seg), headerSize) <= ref); + base = AddrSub(ref, headerSize); + AVER_CRITICAL(AddrIsAligned(base, PoolAlignment(pool))); + AVER_CRITICAL(ref < SegLimit(seg)); /* see .ref-limit */ arena = pool->arena; /* .exposed.seg: Statements tagged ".exposed.seg" below require */ @@ -1834,9 +1612,14 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) newRef = (*format->isMoved)(ref); /* .exposed.seg */ if(newRef == (Addr)0) { + Addr clientQ; + clientQ = (*format->skip)(ref); + /* If object is nailed already then we mustn't copy it: */ - if(SegNailed(seg) != TraceSetEMPTY - && (!amcSegHasNailboard(seg) || amcNailGetMark(seg, ref))) { + if (SegNailed(seg) != TraceSetEMPTY + && !(amcSegHasNailboard(seg) + && !(*amc->pinned)(amc, amcSegNailboard(seg), ref, clientQ))) + { /* Segment only needs greying if there are new traces for */ /* which we are nailing. */ if(!TraceSetSub(ss->traces, SegNailed(seg))) { @@ -1853,148 +1636,7 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) } /* Object is not preserved yet (neither moved, nor nailed) */ /* so should be preserved by forwarding. */ - EVENT1(AMCFixForward, newRef); - /* */ - ss->wasMarked = FALSE; - - /* Get the forwarding buffer from the object's generation. */ - gen = amcSegGen(seg); - buffer = gen->forward; - AVER_CRITICAL(buffer != NULL); - - length = AddrOffset(ref, (*format->skip)(ref)); /* .exposed.seg */ - STATISTIC_STAT(++ss->forwardedCount); - ss->forwardedSize += length; - do { - res = BUFFER_RESERVE(&newRef, buffer, length, FALSE); - if(res != ResOK) - goto returnRes; - - toSeg = BufferSeg(buffer); - ShieldExpose(arena, toSeg); - - /* Since we're moving an object from one segment to another, */ - /* union the greyness and the summaries together. */ - grey = SegGrey(seg); - if(SegRankSet(seg) != RankSetEMPTY) /* not for AMCZ */ - grey = TraceSetUnion(grey, ss->traces); - SegSetGrey(toSeg, TraceSetUnion(SegGrey(toSeg), grey)); - SegSetSummary(toSeg, RefSetUnion(SegSummary(toSeg), SegSummary(seg))); - - /* */ - (void)AddrCopy(newRef, ref, length); /* .exposed.seg */ - - ShieldCover(arena, toSeg); - } while(!BUFFER_COMMIT(buffer, newRef, length)); - ss->copiedSize += length; - - (*format->move)(ref, newRef); /* .exposed.seg */ - } else { - /* reference to broken heart (which should be snapped out -- */ - /* consider adding to (non-existant) snap-out cache here) */ - STATISTIC_STAT(++ss->snapCount); - } - - /* .fix.update: update the reference to whatever the above code */ - /* decided it should be */ -updateReference: - *refIO = newRef; - res = ResOK; - -returnRes: - ShieldCover(arena, seg); /* .exposed.seg */ - return res; -} - - -/* AMCHeaderFix -- fix a reference to the pool, with headers - * - * See . - */ -static Res AMCHeaderFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) -{ - Arena arena; - AMC amc; - Res res; - Format format; /* cache of pool->format */ - Ref ref; /* reference to be fixed */ - Ref newRef; /* new location, if moved */ - Addr newBase; /* base address of new copy */ - Size length; /* length of object to be relocated */ - Buffer buffer; /* buffer to allocate new copy into */ - amcGen gen; /* generation of old copy of object */ - TraceSet grey; /* greyness of object being relocated */ - Seg toSeg; /* segment to which object is being relocated */ - /* */ - AVERT_CRITICAL(Pool, pool); - AVERT_CRITICAL(ScanState, ss); - AVERT_CRITICAL(Seg, seg); - AVER_CRITICAL(refIO != NULL); - EVENT0(AMCFix); - - /* For the moment, assume that the object was already marked. */ - /* (See .) */ - ss->wasMarked = TRUE; - - /* If the reference is ambiguous, set up the datastructures for */ - /* managing a nailed segment. This involves marking the segment */ - /* as nailed, and setting up a per-word mark table */ - if(ss->rank == RankAMBIG) { - /* .nail.new: Check to see whether we need a Nailboard for */ - /* this seg. We use "SegNailed(seg) == TraceSetEMPTY" */ - /* rather than "!amcSegHasNailboard(seg)" because this avoids */ - /* setting up a new nailboard when the segment was nailed, but */ - /* had no nailboard. This must be avoided because otherwise */ - /* assumptions in AMCFixEmergency will be wrong (essentially */ - /* we will lose some pointer fixes because we introduced a */ - /* nailboard). */ - if(SegNailed(seg) == TraceSetEMPTY) { - res = amcSegCreateNailboard(seg, pool); - if(res != ResOK) - return res; - ++ss->nailCount; - SegSetNailed(seg, TraceSetUnion(SegNailed(seg), ss->traces)); - } - amcFixInPlace(pool, seg, ss, refIO); - return ResOK; - } - - amc = Pool2AMC(pool); - AVERT_CRITICAL(AMC, amc); - format = pool->format; - ref = *refIO; - AVER_CRITICAL(AddrAdd(SegBase(seg), pool->format->headerSize) - <= ref); - AVER_CRITICAL(ref < SegLimit(seg)); /* see .ref-limit */ - arena = pool->arena; - - /* .exposed.seg: Statements tagged ".exposed.seg" below require */ - /* that "seg" (that is: the 'from' seg) has been ShieldExposed. */ - ShieldExpose(arena, seg); - newRef = (*format->isMoved)(ref); /* .exposed.seg */ - - if(newRef == (Addr)0) { - /* If object is nailed already then we mustn't copy it: */ - if(SegNailed(seg) != TraceSetEMPTY - && (!amcSegHasNailboard(seg) || amcNailGetMark(seg, ref))) { - /* Segment only needs greying if there are new traces for */ - /* which we are nailing. */ - if(!TraceSetSub(ss->traces, SegNailed(seg))) { - if(SegRankSet(seg) != RankSetEMPTY) /* not for AMCZ */ - SegSetGrey(seg, TraceSetUnion(SegGrey(seg), ss->traces)); - SegSetNailed(seg, TraceSetUnion(SegNailed(seg), ss->traces)); - } - res = ResOK; - goto returnRes; - } else if(ss->rank == RankWEAK) { - /* object is not preserved (neither moved, nor nailed) */ - /* hence, reference should be splatted */ - goto updateReference; - } - /* object is not preserved yet (neither moved, nor nailed) */ - /* so should be preserved by forwarding */ - EVENT1(AMCFixForward, newRef); /* */ ss->wasMarked = FALSE; @@ -2003,12 +1645,10 @@ static Res AMCHeaderFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) buffer = gen->forward; AVER_CRITICAL(buffer != NULL); - length = AddrOffset(ref, (*format->skip)(ref)); /* .exposed.seg */ + length = AddrOffset(ref, clientQ); /* .exposed.seg */ STATISTIC_STAT(++ss->forwardedCount); ss->forwardedSize += length; do { - Size headerSize = format->headerSize; - res = BUFFER_RESERVE(&newBase, buffer, length, FALSE); if (res != ResOK) goto returnRes; @@ -2020,19 +1660,24 @@ static Res AMCHeaderFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) /* Since we're moving an object from one segment to another, */ /* union the greyness and the summaries together. */ grey = SegGrey(seg); - if(SegRankSet(seg) != RankSetEMPTY) /* not for AMCZ */ + if(SegRankSet(seg) != RankSetEMPTY) { /* not for AMCZ */ grey = TraceSetUnion(grey, ss->traces); + SegSetSummary(toSeg, RefSetUnion(SegSummary(toSeg), SegSummary(seg))); + } else { + AVER(SegRankSet(toSeg) == RankSetEMPTY); + } SegSetGrey(toSeg, TraceSetUnion(SegGrey(toSeg), grey)); - SegSetSummary(toSeg, RefSetUnion(SegSummary(toSeg), SegSummary(seg))); /* */ - (void)AddrCopy(newBase, AddrSub(ref, headerSize), length); /* .exposed.seg */ + (void)AddrCopy(newBase, base, length); /* .exposed.seg */ ShieldCover(arena, toSeg); } while (!BUFFER_COMMIT(buffer, newBase, length)); ss->copiedSize += length; (*format->move)(ref, newRef); /* .exposed.seg */ + + EVENT1(AMCFixForward, newRef); } else { /* reference to broken heart (which should be snapped out -- */ /* consider adding to (non-existent) snap-out cache here) */ @@ -2053,7 +1698,7 @@ static Res AMCHeaderFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) /* amcReclaimNailed -- reclaim what you can from a nailed segment */ -static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) +static Bool amcReclaimNailed(Pool pool, Trace trace, Seg seg) { Addr p, limit; Arena arena; @@ -2063,12 +1708,12 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) Size preservedInPlaceSize = (Size)0; AMC amc; Size headerSize; - Addr p1; /* first obj in seg */ - Bool obj1pip = FALSE; /* first obj was preserved in place */ + Addr padBase; /* base of next padding object */ + Size padLength; /* length of next padding object */ /* All arguments AVERed by AMCReclaim */ - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); format = pool->format; @@ -2078,49 +1723,63 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) /* see for improvements */ headerSize = format->headerSize; ShieldExpose(arena, seg); - p = AddrAdd(SegBase(seg), headerSize); + p = SegBase(seg); if(SegBuffer(seg) != NULL) { limit = BufferScanLimit(SegBuffer(seg)); } else { limit = SegLimit(seg); } - limit = AddrAdd(limit, headerSize); - p1 = p; + padBase = p; + padLength = 0; while(p < limit) { - Addr q; + Addr clientP, q, clientQ; Size length; Bool preserve; - q = (*format->skip)(p); + clientP = AddrAdd(p, headerSize); + clientQ = (*format->skip)(clientP); + q = AddrSub(clientQ, headerSize); length = AddrOffset(p, q); if(amcSegHasNailboard(seg)) { - preserve = amcNailGetMark(seg, p); + preserve = (*amc->pinned)(amc, amcSegNailboard(seg), clientP, clientQ); } else { /* There's no nailboard, so preserve everything that hasn't been * forwarded. In this case, preservedInPlace* become somewhat * overstated. */ - preserve = !(*format->isMoved)(p); + preserve = !(*format->isMoved)(clientP); } if(preserve) { ++preservedInPlaceCount; preservedInPlaceSize += length; - if(p == p1) - obj1pip = TRUE; + if (padLength > 0) { + /* Replace run of forwarding pointers and unreachable objects + * with a padding object. */ + (*format->pad)(padBase, padLength); + bytesReclaimed += padLength; + padLength = 0; + } + padBase = q; } else { - /* Replace forwarding pointer / unreachable object with pad. */ - (*format->pad)(AddrSub(p, headerSize), length); - bytesReclaimed += length; + padLength += length; } AVER(p < q); p = q; } AVER(p == limit); + AVER(AddrAdd(padBase, padLength) == limit); + if (padLength > 0) { + /* Replace final run of forwarding pointers and unreachable + * objects with a padding object. */ + (*format->pad)(padBase, padLength); + bytesReclaimed += padLength; + } ShieldCover(arena, seg); SegSetNailed(seg, TraceSetDel(SegNailed(seg), trace)); SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); if(SegNailed(seg) == TraceSetEMPTY && amcSegHasNailboard(seg)) { - amcSegDestroyNailboard(seg, pool); + NailboardDestroy(amcSegNailboard(seg), arena); + Seg2amcSeg(seg)->board = NULL; } AVER(bytesReclaimed <= SegSize(seg)); @@ -2138,41 +1797,11 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) /* We may not free a buffered seg. */ AVER(SegBuffer(seg) == NULL); - --gen->segs; - gen->pgen.totalSize -= SegSize(seg); - SegFree(seg); - } else { - /* Seg retained */ - STATISTIC_STAT( { - Count pages; - AVER(SizeIsAligned(SegSize(seg), ArenaAlign(pool->arena))); - pages = SegSize(seg) / ArenaAlign(pool->arena); - AVER(pages != 0); - amc->pageretstruct[trace->ti].pRet += pages; - if(pages == 1) { - amc->pageretstruct[trace->ti].pRS += pages; - } else if(pages < AMCLargeSegPAGES) { - amc->pageretstruct[trace->ti].sRM += 1; - amc->pageretstruct[trace->ti].pRM += pages; - if(obj1pip) { - amc->pageretstruct[trace->ti].pRM1 += pages; - } else { - /* Seg retained by a rest obj. Cost: one rest page, */ - /* plus pages-1 pages of pure padding. */ - amc->pageretstruct[trace->ti].pRMrr += 1; - amc->pageretstruct[trace->ti].pRMr1 += pages - 1; - } - } else { - amc->pageretstruct[trace->ti].sRL += 1; - amc->pageretstruct[trace->ti].pRL += pages; - if(!obj1pip) { - /* Seg retained by a rest obj */ - amc->pageretstruct[trace->ti].pRLr += pages; - } - } - } ); - + PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, Seg2amcSeg(seg)->deferred); + return TRUE; } + + return FALSE; } @@ -2180,14 +1809,13 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) * * See . */ -static void AMCReclaim(Pool pool, Trace trace, Seg seg) +static Bool AMCReclaim(Pool pool, Trace trace, Seg seg) { AMC amc; amcGen gen; - Size size; AVERT_CRITICAL(Pool, pool); - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT_CRITICAL(AMC, amc); AVERT_CRITICAL(Trace, trace); AVERT_CRITICAL(Seg, seg); @@ -2209,57 +1837,24 @@ static void AMCReclaim(Pool pool, Trace trace, Seg seg) } if(SegNailed(seg) != TraceSetEMPTY) { - amcReclaimNailed(pool, trace, seg); - return; + return amcReclaimNailed(pool, trace, seg); } /* We may not free a buffered seg. (But all buffered + condemned */ /* segs should have been nailed anyway). */ AVER(SegBuffer(seg) == NULL); - --gen->segs; - size = SegSize(seg); - gen->pgen.totalSize -= size; - - trace->reclaimSize += size; - - SegFree(seg); -} - - -/* AMCTraceEnd -- emit end-of-trace event */ + trace->reclaimSize += SegSize(seg); -static void AMCTraceEnd(Pool pool, Trace trace) -{ - AMC amc; - TraceId ti; - - AVERT(Pool, pool); - AVERT(Trace, trace); + PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, Seg2amcSeg(seg)->deferred); - amc = Pool2AMC(pool); - AVERT(AMC, amc); - ti = trace->ti; - AVER(TraceIdCheck(ti)); - - STATISTIC_BEGIN { - Count pRetMin = 100; - PageRetStruct *pr = &amc->pageretstruct[ti]; - if(pr->pRet >= pRetMin) { - EVENT21(AMCTraceEnd, ArenaEpoch(pool->arena), (EventFU)trace->why, - ArenaAlign(pool->arena), AMCLargeSegPAGES, pRetMin, pr->pCond, - pr->pRet, pr->pCS, pr->pRS, pr->sCM, pr->pCM, pr->sRM, pr->pRM, - pr->pRM1, pr->pRMrr, pr->pRMr1, pr->sCL, pr->pCL, pr->sRL, - pr->pRL, pr->pRLr); - } - *pr = pageretstruct_Zero; - } STATISTIC_END; + return TRUE; } /* AMCWalk -- Apply function to (black) objects in segment */ -static void AMCWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, +static void AMCWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, void *p, size_t s) { Addr object, nextObject, limit; @@ -2281,7 +1876,7 @@ static void AMCWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, if(SegWhite(seg) == TraceSetEMPTY && SegGrey(seg) == TraceSetEMPTY && SegNailed(seg) == TraceSetEMPTY) { - amc = Pool2AMC(pool); + amc = PoolAMC(pool); AVERT(AMC, amc); format = pool->format; @@ -2309,13 +1904,12 @@ static void AMCWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, /* amcWalkAll -- Apply a function to all (black) objects in a pool */ -static void amcWalkAll(Pool pool, FormattedObjectsStepMethod f, - void *p, size_t s) +static void amcWalkAll(Pool pool, FormattedObjectsVisitor f, void *p, size_t s) { Arena arena; Ring ring, next, node; - AVER(IsSubclassPoly(pool->class, AMCPoolClassGet())); + AVER(IsSubclassPoly(pool->class, AMCZPoolClassGet())); arena = PoolArena(pool); ring = PoolSegRing(pool); @@ -2354,7 +1948,8 @@ static Res amcAddrObjectSearch(Addr *pReturn, Pool pool, Addr objBase, Addr objLimit = AddrSub((*format->skip)(objRef), hdrSize); AVER(objBase < objLimit); if (addr < objLimit) { - AVER(objBase <= addr && addr < objLimit); /* the point */ + AVER(objBase <= addr); + AVER(addr < objLimit); /* the point */ if (!(*format->isMoved)(objRef)) { *pReturn = objRef; return ResOK; @@ -2381,7 +1976,8 @@ static Res AMCAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr) AVERT(Pool, pool); AVERT(Seg, seg); AVER(SegPool(seg) == pool); - AVER(SegBase(seg) <= addr && addr < SegLimit(seg)); + AVER(SegBase(seg) <= addr); + AVER(addr < SegLimit(seg)); arena = PoolArena(pool); base = SegBase(seg); @@ -2409,11 +2005,55 @@ static Res AMCAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr) } +/* AMCTotalSize -- total memory allocated from the arena */ + +static Size AMCTotalSize(Pool pool) +{ + AMC amc; + Size size = 0; + Ring node, nextNode; + + AVERT(Pool, pool); + amc = PoolAMC(pool); + AVERT(AMC, amc); + + RING_FOR(node, &amc->genRing, nextNode) { + amcGen gen = RING_ELT(amcGen, amcRing, node); + AVERT(amcGen, gen); + size += gen->pgen.totalSize; + } + + return size; +} + + +/* AMCFreeSize -- free memory (unused by client program) */ + +static Size AMCFreeSize(Pool pool) +{ + AMC amc; + Size size = 0; + Ring node, nextNode; + + AVERT(Pool, pool); + amc = PoolAMC(pool); + AVERT(AMC, amc); + + RING_FOR(node, &amc->genRing, nextNode) { + amcGen gen = RING_ELT(amcGen, amcRing, node); + AVERT(amcGen, gen); + size += gen->pgen.freeSize; + } + + return size; +} + + /* AMCDescribe -- describe the contents of the AMC pool * * See . */ -static Res AMCDescribe(Pool pool, mps_lib_FILE *stream) +static Res AMCDescribe(Pool pool, mps_lib_FILE *stream, Count depth) { Res res; AMC amc; @@ -2422,59 +2062,55 @@ static Res AMCDescribe(Pool pool, mps_lib_FILE *stream) if(!TESTT(Pool, pool)) return ResFAIL; - amc = Pool2AMC(pool); + amc = PoolAMC(pool); if(!TESTT(AMC, amc)) return ResFAIL; if(stream == NULL) return ResFAIL; - res = WriteF(stream, + res = WriteF(stream, depth, (amc->rankSet == RankSetEMPTY) ? "AMCZ" : "AMC", " $P {\n", (WriteFP)amc, " pool $P ($U)\n", - (WriteFP)AMC2Pool(amc), (WriteFU)AMC2Pool(amc)->serial, + (WriteFP)AMCPool(amc), (WriteFU)AMCPool(amc)->serial, NULL); if(res != ResOK) return res; switch(amc->rampMode) { - #define RAMP_DESCRIBE(e, s) \ case e: \ rampmode = s; \ break; - RAMP_RELATION(RAMP_DESCRIBE) #undef RAMP_DESCRIBE - default: rampmode = "unknown ramp mode"; break; - } - res = WriteF(stream, - " ", rampmode, " ($U)\n", (WriteFU)amc->rampCount, + res = WriteF(stream, depth + 2, + rampmode, " ($U)\n", (WriteFU)amc->rampCount, NULL); if(res != ResOK) return res; RING_FOR(node, &amc->genRing, nextNode) { amcGen gen = RING_ELT(amcGen, amcRing, node); - res = amcGenDescribe(gen, stream); + res = amcGenDescribe(gen, stream, depth + 2); if(res != ResOK) return res; } if(0) { /* SegDescribes */ - RING_FOR(node, &AMC2Pool(amc)->segRing, nextNode) { + RING_FOR(node, &AMCPool(amc)->segRing, nextNode) { Seg seg = RING_ELT(Seg, poolRing, node); - res = AMCSegDescribe(seg, stream); + res = AMCSegDescribe(seg, stream, depth + 2); if(res != ResOK) return res; } } - res = WriteF(stream, "} AMC $P\n", (WriteFP)amc, NULL); + res = WriteF(stream, depth, "} AMC $P\n", (WriteFP)amc, NULL); if(res != ResOK) return res; @@ -2482,61 +2118,63 @@ static Res AMCDescribe(Pool pool, mps_lib_FILE *stream) } -/* AMCPoolClass -- the class definition */ +/* AMCZPoolClass -- the class definition */ -DEFINE_POOL_CLASS(AMCPoolClass, this) +DEFINE_POOL_CLASS(AMCZPoolClass, this) { - INHERIT_CLASS(this, AbstractCollectPoolClass); + INHERIT_CLASS(this, AbstractSegBufPoolClass); PoolClassMixInFormat(this); - this->name = "AMC"; + PoolClassMixInCollect(this); + this->name = "AMCZ"; this->size = sizeof(AMCStruct); this->offset = offsetof(AMCStruct, poolStruct); this->attr |= AttrMOVINGGC; this->varargs = AMCVarargs; - this->init = AMCInit; + this->init = AMCZInit; this->finish = AMCFinish; this->bufferFill = AMCBufferFill; this->bufferEmpty = AMCBufferEmpty; this->whiten = AMCWhiten; - this->scan = AMCScan; this->fix = AMCFix; this->fixEmergency = AMCFixEmergency; this->reclaim = AMCReclaim; - this->traceEnd = AMCTraceEnd; this->rampBegin = AMCRampBegin; this->rampEnd = AMCRampEnd; this->addrObject = AMCAddrObject; this->walk = AMCWalk; this->bufferClass = amcBufClassGet; + this->totalSize = AMCTotalSize; + this->freeSize = AMCFreeSize; this->describe = AMCDescribe; + AVERT(PoolClass, this); } -/* AMCZPoolClass -- the class definition */ +/* AMCPoolClass -- the class definition */ -DEFINE_POOL_CLASS(AMCZPoolClass, this) +DEFINE_POOL_CLASS(AMCPoolClass, this) { - INHERIT_CLASS(this, AMCPoolClass); - this->name = "AMCZ"; - this->attr &= ~(AttrSCAN | AttrINCR_RB); - this->init = AMCZInit; - this->grey = PoolNoGrey; - this->scan = PoolNoScan; + INHERIT_CLASS(this, AMCZPoolClass); + PoolClassMixInScan(this); + this->name = "AMC"; + this->init = AMCInit; + this->scan = AMCScan; + AVERT(PoolClass, this); } /* mps_class_amc -- return the pool class descriptor to the client */ -mps_class_t mps_class_amc(void) +mps_pool_class_t mps_class_amc(void) { - return (mps_class_t)AMCPoolClassGet(); + return (mps_pool_class_t)AMCPoolClassGet(); } /* mps_class_amcz -- return the pool class descriptor to the client */ -mps_class_t mps_class_amcz(void) +mps_pool_class_t mps_class_amcz(void) { - return (mps_class_t)AMCZPoolClassGet(); + return (mps_pool_class_t)AMCZPoolClassGet(); } @@ -2595,13 +2233,15 @@ void mps_amc_apply(mps_pool_t mps_pool, * * See . */ + +ATTRIBUTE_UNUSED static Bool AMCCheck(AMC amc) { CHECKS(AMC, amc); - CHECKD(Pool, &amc->poolStruct); - CHECKL(IsSubclassPoly(amc->poolStruct.class, EnsureAMCPoolClass())); + CHECKD(Pool, AMCPool(amc)); + CHECKL(IsSubclassPoly(AMCPool(amc)->class, AMCZPoolClassGet())); CHECKL(RankSetCheck(amc->rankSet)); - CHECKL(RingCheck(&amc->genRing)); + CHECKD_NOSIG(Ring, &amc->genRing); CHECKL(BoolCheck(amc->gensBooted)); if(amc->gensBooted) { CHECKD(amcGen, amc->nursery); @@ -2618,7 +2258,6 @@ static Bool AMCCheck(AMC amc) /* if BEGIN or RAMPING, count must not be zero. */ CHECKL((amc->rampCount != 0) || ((amc->rampMode != RampBEGIN) && (amc->rampMode != RampRAMPING))); - /* pageretstruct[ti] is statistics only, currently unchecked */ return TRUE; } diff --git a/code/poolams.c b/code/poolams.c index f34f0ac129..f0ba744022 100644 --- a/code/poolams.c +++ b/code/poolams.c @@ -1,7 +1,7 @@ /* poolams.c: AUTOMATIC MARK & SWEEP POOL CLASS * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * @@ -48,19 +48,19 @@ Bool AMSSegCheck(AMSSeg amsseg) { Seg seg = AMSSeg2Seg(amsseg); CHECKS(AMSSeg, amsseg); - CHECKL(GCSegCheck(&amsseg->gcSegStruct)); + CHECKD(GCSeg, &amsseg->gcSegStruct); CHECKU(AMS, amsseg->ams); - CHECKL(AMS2Pool(amsseg->ams) == SegPool(seg)); + CHECKL(AMSPool(amsseg->ams) == SegPool(seg)); CHECKD_NOSIG(Ring, &amsseg->segRing); CHECKL(amsseg->grains == AMSGrains(amsseg->ams, SegSize(seg))); CHECKL(amsseg->grains > 0); - CHECKL(amsseg->grains >= amsseg->free + amsseg->newAlloc); + CHECKL(amsseg->grains == amsseg->freeGrains + amsseg->oldGrains + amsseg->newGrains); CHECKL(BoolCheck(amsseg->allocTableInUse)); if (!amsseg->allocTableInUse) CHECKL(amsseg->firstFree <= amsseg->grains); - CHECKL(amsseg->allocTable != NULL); + CHECKD_NOSIG(BT, amsseg->allocTable); if (SegWhite(seg) != TraceSetEMPTY) { /* */ @@ -71,8 +71,13 @@ Bool AMSSegCheck(AMSSeg amsseg) CHECKL(BoolCheck(amsseg->marksChanged)); CHECKL(BoolCheck(amsseg->ambiguousFixes)); CHECKL(BoolCheck(amsseg->colourTablesInUse)); - CHECKL(amsseg->nongreyTable != NULL); - CHECKL(amsseg->nonwhiteTable != NULL); + CHECKD_NOSIG(BT, amsseg->nongreyTable); + CHECKD_NOSIG(BT, amsseg->nonwhiteTable); + + /* If tables are shared, they mustn't both be in use. */ + CHECKL(!(amsseg->ams->shareAllocTable + && amsseg->allocTableInUse + && amsseg->colourTablesInUse)); return TRUE; } @@ -80,7 +85,7 @@ Bool AMSSegCheck(AMSSeg amsseg) /* AMSSegFreeWalk -- walk the free space in a segment */ -void AMSSegFreeWalk(AMSSeg amsseg, FreeBlockStepMethod f, void *p) +void AMSSegFreeWalk(AMSSeg amsseg, FreeBlockVisitor f, void *p) { Pool pool; Seg seg; @@ -89,7 +94,7 @@ void AMSSegFreeWalk(AMSSeg amsseg, FreeBlockStepMethod f, void *p) pool = SegPool(AMSSeg2Seg(amsseg)); seg = AMSSeg2Seg(amsseg); - if (amsseg->free == 0) + if (amsseg->freeGrains == 0) return; if (amsseg->allocTableInUse) { Index base, limit, next; @@ -98,14 +103,13 @@ void AMSSegFreeWalk(AMSSeg amsseg, FreeBlockStepMethod f, void *p) while (next < amsseg->grains) { Bool found = BTFindLongResRange(&base, &limit, amsseg->allocTable, next, amsseg->grains, 1); - if (!found) break; + if (!found) + break; (*f)(AMS_INDEX_ADDR(seg, base), AMS_INDEX_ADDR(seg, limit), pool, p); next = limit + 1; } - } else { - if ( amsseg->firstFree < amsseg->grains ) - (*f)(AMS_INDEX_ADDR(seg, amsseg->firstFree), SegLimit(seg), pool, p); - } + } else if (amsseg->firstFree < amsseg->grains) + (*f)(AMS_INDEX_ADDR(seg, amsseg->firstFree), SegLimit(seg), pool, p); } @@ -124,7 +128,7 @@ void AMSSegFreeCheck(AMSSeg amsseg) AVERT(AMSSeg, amsseg); - if (amsseg->free == 0) + if (amsseg->freeGrains == 0) return; /* If it's not a debug class, don't bother walking. */ @@ -167,6 +171,15 @@ static Res amsCreateTables(AMS ams, BT *allocReturn, goto failWhite; } +#if defined(AVER_AND_CHECK_ALL) + /* Invalidate the colour tables in checking varieties. The algorithm + * is designed not to depend on the initial values of these tables, + * so by invalidating them we get some checking of this. + */ + BTResRange(nongreyTable, 0, length); + BTSetRange(nonwhiteTable, 0, length); +#endif + *allocReturn = allocTable; *nongreyReturn = nongreyTable; *nonwhiteReturn = nonwhiteTable; @@ -214,11 +227,11 @@ static Res AMSSegInit(Seg seg, Pool pool, Addr base, Size size, AVERT(Seg, seg); amsseg = Seg2AMSSeg(seg); AVERT(Pool, pool); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS, ams); arena = PoolArena(pool); /* no useful checks for base and size */ - AVER(BoolCheck(reservoirPermit)); + AVERT(Bool, reservoirPermit); /* Initialize the superclass fields first via next-method call */ super = SEG_SUPERCLASS(AMSSegClass); @@ -227,8 +240,9 @@ static Res AMSSegInit(Seg seg, Pool pool, Addr base, Size size, goto failNextMethod; amsseg->grains = size >> ams->grainShift; - amsseg->free = amsseg->grains; - amsseg->newAlloc = (Count)0; + amsseg->freeGrains = amsseg->grains; + amsseg->oldGrains = (Count)0; + amsseg->newGrains = (Count)0; amsseg->marksChanged = FALSE; /* */ amsseg->ambiguousFixes = FALSE; @@ -249,7 +263,6 @@ static Res AMSSegInit(Seg seg, Pool pool, Addr base, Size size, &amsseg->segRing); amsseg->sig = AMSSegSig; - ams->size += size; AVERT(AMSSeg, amsseg); return ResOK; @@ -275,7 +288,7 @@ static void AMSSegFinish(Seg seg) AVERT(AMSSeg, amsseg); ams = amsseg->ams; AVERT(AMS, ams); - arena = PoolArena(AMS2Pool(ams)); + arena = PoolArena(AMSPool(ams)); AVER(SegBuffer(seg) == NULL); /* keep the destructions in step with AMSSegInit failure cases */ @@ -285,8 +298,6 @@ static void AMSSegFinish(Seg seg) RingRemove(&amsseg->segRing); RingFinish(&amsseg->segRing); - AVER(ams->size >= SegSize(seg)); - ams->size -= SegSize(seg); amsseg->sig = SigInvalid; /* finish the superclass fields last */ @@ -336,7 +347,7 @@ static Res AMSSegMerge(Seg seg, Seg segHi, AVERT(AMSSeg, amssegHi); /* other parameters are checked by next-method */ arena = PoolArena(SegPool(seg)); - ams = Pool2AMS(SegPool(seg)); + ams = PoolAMS(SegPool(seg)); loGrains = amsseg->grains; hiGrains = amssegHi->grains; @@ -345,7 +356,7 @@ static Res AMSSegMerge(Seg seg, Seg segHi, /* checks for .grain-align */ AVER(allGrains == AddrOffset(base, limit) >> ams->grainShift); /* checks for .empty */ - AVER(amssegHi->free == hiGrains); + AVER(amssegHi->freeGrains == hiGrains); AVER(!amssegHi->marksChanged); /* .alloc-early */ @@ -379,8 +390,9 @@ static Res AMSSegMerge(Seg seg, Seg segHi, MERGE_TABLES(nonwhiteTable, BTSetRange); amsseg->grains = allGrains; - amsseg->free = amsseg->free + amssegHi->free; - amsseg->newAlloc = amsseg->newAlloc + amssegHi->newAlloc; + amsseg->freeGrains = amsseg->freeGrains + amssegHi->freeGrains; + amsseg->oldGrains = amsseg->oldGrains + amssegHi->oldGrains; + amsseg->newGrains = amsseg->newGrains + amssegHi->newGrains; /* other fields in amsseg are unaffected */ RingRemove(&amssegHi->segRing); @@ -388,6 +400,7 @@ static Res AMSSegMerge(Seg seg, Seg segHi, amssegHi->sig = SigInvalid; AVERT(AMSSeg, amsseg); + PoolGenAccountForSegMerge(&ams->pgen); return ResOK; failSuper: @@ -420,7 +433,7 @@ static Res AMSSegSplit(Seg seg, Seg segHi, AVERT(AMSSeg, amsseg); /* other parameters are checked by next-method */ arena = PoolArena(SegPool(seg)); - ams = Pool2AMS(SegPool(seg)); + ams = PoolAMS(SegPool(seg)); loGrains = AMSGrains(ams, AddrOffset(base, mid)); hiGrains = AMSGrains(ams, AddrOffset(mid, limit)); @@ -429,7 +442,7 @@ static Res AMSSegSplit(Seg seg, Seg segHi, /* checks for .grain-align */ AVER(allGrains == amsseg->grains); /* checks for .empty */ - AVER(amsseg->free >= hiGrains); + AVER(amsseg->freeGrains >= hiGrains); if (amsseg->allocTableInUse) { AVER(BTIsResRange(amsseg->allocTable, loGrains, allGrains)); } else { @@ -471,9 +484,11 @@ static Res AMSSegSplit(Seg seg, Seg segHi, amsseg->grains = loGrains; amssegHi->grains = hiGrains; - amsseg->free -= hiGrains; - amssegHi->free = hiGrains; - amssegHi->newAlloc = (Count)0; + AVER(amsseg->freeGrains >= hiGrains); + amsseg->freeGrains -= hiGrains; + amssegHi->freeGrains = hiGrains; + amssegHi->oldGrains = (Count)0; + amssegHi->newGrains = (Count)0; amssegHi->marksChanged = FALSE; /* */ amssegHi->ambiguousFixes = FALSE; @@ -491,6 +506,7 @@ static Res AMSSegSplit(Seg seg, Seg segHi, amssegHi->sig = AMSSegSig; AVERT(AMSSeg, amsseg); AVERT(AMSSeg, amssegHi); + PoolGenAccountForSegSplit(&ams->pgen); return ResOK; failSuper: @@ -507,16 +523,16 @@ static Res AMSSegSplit(Seg seg, Seg segHi, /* AMSSegDescribe -- describe an AMS segment */ -#define WRITE_BUFFER_LIMIT(stream, seg, i, buffer, accessor, char) \ +#define WRITE_BUFFER_LIMIT(stream, seg, i, buffer, accessor, code) \ BEGIN \ if ((buffer) != NULL \ && (i) == AMS_ADDR_INDEX(seg, accessor(buffer))) { \ - Res _res = WriteF(stream, char, NULL); \ + Res _res = WriteF(stream, 0, code, NULL); \ if (_res != ResOK) return _res; \ } \ END -static Res AMSSegDescribe(Seg seg, mps_lib_FILE *stream) +static Res AMSSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) { Res res; AMSSeg amsseg; @@ -524,46 +540,60 @@ static Res AMSSegDescribe(Seg seg, mps_lib_FILE *stream) Buffer buffer; /* the segment's buffer, if it has one */ Index i; - if (!TESTT(Seg, seg)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Seg, seg)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; amsseg = Seg2AMSSeg(seg); - if (!TESTT(AMSSeg, amsseg)) return ResFAIL; + if (!TESTT(AMSSeg, amsseg)) + return ResFAIL; /* Describe the superclass fields first via next-method call */ super = SEG_SUPERCLASS(AMSSegClass); - res = super->describe(seg, stream); - if (res != ResOK) return res; + res = super->describe(seg, stream, depth); + if (res != ResOK) + return res; buffer = SegBuffer(seg); - res = WriteF(stream, + res = WriteF(stream, depth, " AMS $P\n", (WriteFP)amsseg->ams, " grains $W\n", (WriteFW)amsseg->grains, + " freeGrains $W\n", (WriteFW)amsseg->freeGrains, + " oldGrains $W\n", (WriteFW)amsseg->oldGrains, + " newGrains $W\n", (WriteFW)amsseg->newGrains, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; if (amsseg->allocTableInUse) - res = WriteF(stream, - " alloctable $P\n", (WriteFP)amsseg->allocTable, + res = WriteF(stream, depth, + "alloctable $P\n", (WriteFP)amsseg->allocTable, NULL); else - res = WriteF(stream, - " firstFree $W\n", (WriteFW)amsseg->firstFree, + res = WriteF(stream, depth, + "firstFree $W\n", (WriteFW)amsseg->firstFree, NULL); - if (res != ResOK) return res; - res = WriteF(stream, - " tables: nongrey $P, nonwhite $P\n", + if (res != ResOK) + return res; + res = WriteF(stream, depth, + "tables: nongrey $P, nonwhite $P\n", (WriteFP)amsseg->nongreyTable, (WriteFP)amsseg->nonwhiteTable, - " map: \n", + "map:", NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; for (i=0; i < amsseg->grains; ++i) { char c = 0; if (i % 64 == 0) { - res = WriteF(stream, "\n ", NULL); - if (res != ResOK) return res; + res = WriteF(stream, 0, "\n", NULL); + if (res != ResOK) + return res; + res = WriteF(stream, depth, " ", NULL); + if (res != ResOK) + return res; } WRITE_BUFFER_LIMIT(stream, seg, i, buffer, BufferBase, "["); @@ -584,7 +614,7 @@ static Res AMSSegDescribe(Seg seg, mps_lib_FILE *stream) c = '.'; } else c = ' '; - res = WriteF(stream, "$C", c, NULL); + res = WriteF(stream, 0, "$C", (WriteFC)c, NULL); if (res != ResOK) return res; @@ -592,8 +622,7 @@ static Res AMSSegDescribe(Seg seg, mps_lib_FILE *stream) WRITE_BUFFER_LIMIT(stream, seg, i+1, buffer, BufferLimit, "]"); } - res = WriteF(stream, "\n", NULL); - return res; + return ResOK; } @@ -609,11 +638,10 @@ DEFINE_CLASS(AMSSegClass, class) class->merge = AMSSegMerge; class->split = AMSSegSplit; class->describe = AMSSegDescribe; + AVERT(SegClass, class); } - - /* AMSPoolRing -- the ring of segments in the pool */ static Ring AMSPoolRing(AMS ams, RankSet rankSet, Size size) @@ -627,7 +655,7 @@ static Ring AMSPoolRing(AMS ams, RankSet rankSet, Size size) /* AMSSegSizePolicy * * Picks a segment size. This policy simply rounds the size - * up to the arena alignment. + * up to the arena grain size. */ static Res AMSSegSizePolicy(Size *sizeReturn, Pool pool, Size size, RankSet rankSet) @@ -637,11 +665,11 @@ static Res AMSSegSizePolicy(Size *sizeReturn, AVER(sizeReturn != NULL); AVERT(Pool, pool); AVER(size > 0); - AVER(RankSetCheck(rankSet)); + AVERT(RankSet, rankSet); arena = PoolArena(pool); - size = SizeAlignUp(size, ArenaAlign(arena)); + size = SizeArenaGrains(size, arena); if (size == 0) { /* overflow */ return ResMEMORY; @@ -654,8 +682,7 @@ static Res AMSSegSizePolicy(Size *sizeReturn, /* AMSSegCreate -- create a single AMSSeg */ static Res AMSSegCreate(Seg *segReturn, Pool pool, Size size, - SegPref segPref, RankSet rankSet, - Bool withReservoirPermit) + RankSet rankSet, Bool withReservoirPermit) { Seg seg; AMS ams; @@ -667,10 +694,9 @@ static Res AMSSegCreate(Seg *segReturn, Pool pool, Size size, AVERT(Pool, pool); AVER(size > 0); AVERT(RankSet, rankSet); - AVERT(SegPref, segPref); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS,ams); arena = PoolArena(pool); @@ -678,14 +704,14 @@ static Res AMSSegCreate(Seg *segReturn, Pool pool, Size size, if (res != ResOK) goto failSize; - res = ChainAlloc(&seg, ams->chain, ams->pgen.nr, (*ams->segClass)(), - prefSize, pool, withReservoirPermit, argsNone); + res = PoolGenAlloc(&seg, &ams->pgen, (*ams->segClass)(), prefSize, + withReservoirPermit, argsNone); if (res != ResOK) { /* try to allocate one that's just large enough */ - Size minSize = SizeAlignUp(size, ArenaAlign(arena)); + Size minSize = SizeArenaGrains(size, arena); if (minSize == prefSize) goto failSeg; - res = ChainAlloc(&seg, ams->chain, ams->pgen.nr, (*ams->segClass)(), - prefSize, pool, withReservoirPermit, argsNone); + res = PoolGenAlloc(&seg, &ams->pgen, (*ams->segClass)(), prefSize, + withReservoirPermit, argsNone); if (res != ResOK) goto failSeg; } @@ -715,12 +741,18 @@ static void AMSSegsDestroy(AMS ams) { Ring ring, node, next; /* for iterating over the segments */ - ring = PoolSegRing(AMS2Pool(ams)); + ring = PoolSegRing(AMSPool(ams)); RING_FOR(node, ring, next) { Seg seg = SegOfPoolRing(node); - AVER(Seg2AMSSeg(seg)->ams == ams); - AMSSegFreeCheck(Seg2AMSSeg(seg)); - SegFree(seg); + AMSSeg amsseg = Seg2AMSSeg(seg); + AVERT(AMSSeg, amsseg); + AVER(amsseg->ams == ams); + AMSSegFreeCheck(amsseg); + PoolGenFree(&ams->pgen, seg, + AMSGrainsSize(ams, amsseg->freeGrains), + AMSGrainsSize(ams, amsseg->oldGrains), + AMSGrainsSize(ams, amsseg->newGrains), + FALSE); } } @@ -736,7 +768,7 @@ static void AMSVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[2].key = MPS_KEY_AMS_SUPPORT_AMBIGUOUS; args[2].val.b = va_arg(varargs, Bool); args[3].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } static void AMSDebugVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) @@ -753,7 +785,7 @@ static void AMSDebugVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) * allocated in the pool. See . */ -ARG_DEFINE_KEY(ams_support_ambiguous, Bool); +ARG_DEFINE_KEY(AMS_SUPPORT_AMBIGUOUS, Bool); static Res AMSInit(Pool pool, ArgList args) { @@ -765,7 +797,7 @@ static Res AMSInit(Pool pool, ArgList args) ArgStruct arg; AVERT(Pool, pool); - AVER(ArgListCheck(args)); + AVERT(ArgList, args); if (ArgPick(&arg, args, MPS_KEY_CHAIN)) chain = arg.val.chain; @@ -782,7 +814,7 @@ static Res AMSInit(Pool pool, ArgList args) /* .ambiguous.noshare: If the pool is required to support ambiguous */ /* references, the alloc and white tables cannot be shared. */ - res = AMSInitInternal(Pool2AMS(pool), format, chain, gen, !supportAmbiguous); + res = AMSInitInternal(PoolAMS(pool), format, chain, gen, !supportAmbiguous); if (res == ResOK) { EVENT3(PoolInitAMS, pool, PoolArena(pool), format); } @@ -799,18 +831,19 @@ Res AMSInitInternal(AMS ams, Format format, Chain chain, unsigned gen, Res res; /* Can't check ams, it's not initialized. */ + pool = AMSPool(ams); + AVERT(Pool, pool); AVERT(Format, format); + AVER(FormatArena(format) == PoolArena(pool)); + pool->format = format; AVERT(Chain, chain); AVER(gen <= ChainGens(chain)); + AVER(chain->arena == PoolArena(pool)); - pool = AMS2Pool(ams); - AVERT(Pool, pool); - pool->format = format; pool->alignment = pool->format->alignment; ams->grainShift = SizeLog2(PoolAlignment(pool)); - ams->chain = chain; - res = PoolGenInit(&ams->pgen, ams->chain, gen, pool); + res = PoolGenInit(&ams->pgen, ChainGen(chain, gen), pool); if (res != ResOK) return res; @@ -824,8 +857,6 @@ Res AMSInitInternal(AMS ams, Format format, Chain chain, unsigned gen, ams->segsDestroy = AMSSegsDestroy; ams->segClass = AMSSegClassGet; - ams->size = 0; - ams->sig = AMSSig; AVERT(AMS, ams); return ResOK; @@ -842,7 +873,7 @@ void AMSFinish(Pool pool) AMS ams; AVERT(Pool, pool); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS, ams); (ams->segsDestroy)(ams); @@ -876,7 +907,7 @@ static Bool amsSegAlloc(Index *baseReturn, Index *limitReturn, AVERT(AMS, ams); AVER(size > 0); - AVER(SizeIsAligned(size, PoolAlignment(AMS2Pool(ams)))); + AVER(SizeIsAligned(size, PoolAlignment(AMSPool(ams)))); grains = AMSGrains(ams, size); AVER(grains > 0); @@ -892,15 +923,17 @@ static Bool amsSegAlloc(Index *baseReturn, Index *limitReturn, } else { if (amsseg->firstFree > amsseg->grains - grains) return FALSE; - base = amsseg->firstFree; limit = amsseg->grains; + base = amsseg->firstFree; + limit = amsseg->grains; amsseg->firstFree = limit; } /* We don't place buffers on white segments, so no need to adjust colour. */ AVER(!amsseg->colourTablesInUse); - amsseg->free -= limit - base; - amsseg->newAlloc += limit - base; + AVER(amsseg->freeGrains >= limit - base); + amsseg->freeGrains -= limit - base; + amsseg->newGrains += limit - base; *baseReturn = base; *limitReturn = limit; return TRUE; @@ -924,18 +957,17 @@ static Res AMSBufferFill(Addr *baseReturn, Addr *limitReturn, Addr baseAddr, limitAddr; RankSet rankSet; Bool b; /* the return value of amsSegAlloc */ - SegPrefStruct segPrefStruct; Size allocatedSize; AVER(baseReturn != NULL); AVER(limitReturn != NULL); AVERT(Pool, pool); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS, ams); AVERT(Buffer, buffer); AVER(size > 0); AVER(SizeIsAligned(size, PoolAlignment(pool))); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); /* Check that we're not in the grey mutator phase (see */ /* ). */ @@ -947,12 +979,15 @@ static Res AMSBufferFill(Addr *baseReturn, Addr *limitReturn, RING_FOR(node, ring, nextNode) { AMSSeg amsseg = RING_ELT(AMSSeg, segRing, node); AVERT_CRITICAL(AMSSeg, amsseg); - if (amsseg->free >= AMSGrains(ams, size)) { + if (amsseg->freeGrains >= AMSGrains(ams, size)) { seg = AMSSeg2Seg(amsseg); - if (SegRankSet(seg) == rankSet && SegBuffer(seg) == NULL + if (SegRankSet(seg) == rankSet + && SegBuffer(seg) == NULL /* Can't use a white or grey segment, see d.m.p.fill.colour. */ - && SegWhite(seg) == TraceSetEMPTY && SegGrey(seg) == TraceSetEMPTY) { + && SegWhite(seg) == TraceSetEMPTY + && SegGrey(seg) == TraceSetEMPTY) + { b = amsSegAlloc(&base, &limit, seg, size); if (b) goto found; @@ -961,9 +996,7 @@ static Res AMSBufferFill(Addr *baseReturn, Addr *limitReturn, } /* No suitable segment found; make a new one. */ - SegPrefInit(&segPrefStruct); - SegPrefExpress(&segPrefStruct, SegPrefCollected, NULL); - res = AMSSegCreate(&seg, pool, size, &segPrefStruct, rankSet, + res = AMSSegCreate(&seg, pool, size, rankSet, withReservoirPermit); if (res != ResOK) return res; @@ -974,10 +1007,10 @@ static Res AMSBufferFill(Addr *baseReturn, Addr *limitReturn, baseAddr = AMS_INDEX_ADDR(seg, base); limitAddr = AMS_INDEX_ADDR(seg, limit); DebugPoolFreeCheck(pool, baseAddr, limitAddr); allocatedSize = AddrOffset(baseAddr, limitAddr); - ams->pgen.totalSize += allocatedSize; - ams->pgen.newSize += allocatedSize; - *baseReturn = baseAddr; *limitReturn = limitAddr; + PoolGenAccountForFill(&ams->pgen, allocatedSize, FALSE); + *baseReturn = baseAddr; + *limitReturn = limitAddr; return ResOK; } @@ -996,12 +1029,12 @@ static void AMSBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) Size size; AVERT(Pool, pool); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS, ams); AVERT(Buffer,buffer); AVER(BufferIsReady(buffer)); seg = BufferSeg(buffer); - AVER(SegCheck(seg)); + AVERT(Seg, seg); AVER(init <= limit); AVER(AddrIsAligned(init, PoolAlignment(pool))); AVER(AddrIsAligned(limit, PoolAlignment(pool))); @@ -1027,7 +1060,20 @@ static void AMSBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) AVER(limitIndex <= amsseg->firstFree); if (limitIndex == amsseg->firstFree) /* is it at the end? */ { amsseg->firstFree = initIndex; - } else { /* start using allocTable */ + } else if (ams->shareAllocTable && amsseg->colourTablesInUse) { + /* The nonwhiteTable is shared with allocTable and in use, so we + * mustn't start using allocTable. In this case we know: 1. the + * segment has been condemned (because colour tables are turned + * on in AMSWhiten); 2. the segment has not yet been reclaimed + * (because colour tables are turned off in AMSReclaim); 3. the + * unused portion of the buffer is black (see AMSWhiten). So we + * need to whiten the unused portion of the buffer. The + * allocTable will be turned back on (if necessary) in + * AMSReclaim, when we know that the nonwhite grains are exactly + * the allocated grains. + */ + } else { + /* start using allocTable */ amsseg->allocTableInUse = TRUE; BTSetRange(amsseg->allocTable, 0, amsseg->firstFree); if (amsseg->firstFree < amsseg->grains) @@ -1039,20 +1085,19 @@ static void AMSBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) if (amsseg->colourTablesInUse) AMS_RANGE_WHITEN(seg, initIndex, limitIndex); - amsseg->free += limitIndex - initIndex; - /* The unused portion of the buffer must be new, since it's not condemned. */ - AVER(amsseg->newAlloc >= limitIndex - initIndex); - amsseg->newAlloc -= limitIndex - initIndex; + amsseg->freeGrains += limitIndex - initIndex; + /* Unused portion of the buffer must be new, since it's not condemned. */ + AVER(amsseg->newGrains >= limitIndex - initIndex); + amsseg->newGrains -= limitIndex - initIndex; size = AddrOffset(init, limit); - ams->pgen.totalSize -= size; - ams->pgen.newSize -= size; + PoolGenAccountForEmpty(&ams->pgen, size, FALSE); } -/* amsRangeCondemn -- Condemn a part of an AMS segment +/* amsRangeWhiten -- Condemn a part of an AMS segment * Allow calling it with base = limit, to simplify the callers. */ -static void amsRangeCondemn(Seg seg, Index base, Index limit) +static void amsRangeWhiten(Seg seg, Index base, Index limit) { if (base != limit) { AMSSeg amsseg = Seg2AMSSeg(seg); @@ -1065,9 +1110,9 @@ static void amsRangeCondemn(Seg seg, Index base, Index limit) } -/* AMSCondemn -- the pool class segment condemning method */ +/* AMSWhiten -- the pool class segment condemning method */ -static Res AMSCondemn(Pool pool, Trace trace, Seg seg) +static Res AMSWhiten(Pool pool, Trace trace, Seg seg) { AMS ams; AMSSeg amsseg; @@ -1075,11 +1120,11 @@ static Res AMSCondemn(Pool pool, Trace trace, Seg seg) Count uncondemned; AVERT(Pool, pool); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS, ams); AVERT(Trace, trace); - AVER(SegCheck(seg)); + AVERT(Seg, seg); amsseg = Seg2AMSSeg(seg); AVERT(AMSSeg, amsseg); @@ -1117,23 +1162,24 @@ static Res AMSCondemn(Pool pool, Trace trace, Seg seg) scanLimitIndex = AMS_ADDR_INDEX(seg, BufferScanLimit(buffer)); limitIndex = AMS_ADDR_INDEX(seg, BufferLimit(buffer)); - amsRangeCondemn(seg, 0, scanLimitIndex); + amsRangeWhiten(seg, 0, scanLimitIndex); if (scanLimitIndex < limitIndex) AMS_RANGE_BLACKEN(seg, scanLimitIndex, limitIndex); - amsRangeCondemn(seg, limitIndex, amsseg->grains); + amsRangeWhiten(seg, limitIndex, amsseg->grains); /* We didn't condemn the buffer, subtract it from the count. */ uncondemned = limitIndex - scanLimitIndex; } else { /* condemn whole seg */ - amsRangeCondemn(seg, 0, amsseg->grains); + amsRangeWhiten(seg, 0, amsseg->grains); uncondemned = (Count)0; } - trace->condemned += SegSize(seg) - AMSGrainsSize(ams, uncondemned); - /* The unused part of the buffer is new allocation by definition. */ - ams->pgen.newSize -= AMSGrainsSize(ams, amsseg->newAlloc - uncondemned); - amsseg->newAlloc = uncondemned; + /* The unused part of the buffer remains new: the rest becomes old. */ + PoolGenAccountForAge(&ams->pgen, AMSGrainsSize(ams, amsseg->newGrains - uncondemned), FALSE); + amsseg->oldGrains += amsseg->newGrains - uncondemned; + amsseg->newGrains = uncondemned; amsseg->marksChanged = FALSE; /* */ amsseg->ambiguousFixes = FALSE; + trace->condemned += AMSGrainsSize(ams, amsseg->oldGrains); SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); @@ -1179,9 +1225,9 @@ static Res amsIterate(Seg seg, AMSObjectFunction f, void *closure) AVERT(AMSSeg, amsseg); ams = amsseg->ams; AVERT(AMS, ams); - format = AMS2Pool(ams)->format; + format = AMSPool(ams)->format; AVERT(Format, format); - alignment = PoolAlignment(AMS2Pool(ams)); + alignment = PoolAlignment(AMSPool(ams)); /* If we're using the alloc table as a white table, we can't use it to */ /* determine where there are objects. */ @@ -1265,9 +1311,9 @@ static Res amsScanObject(Seg seg, Index i, Addr p, Addr next, void *clos) AVER(clos != NULL); closure = (amsScanClosure)clos; AVERT(ScanState, closure->ss); - AVER(BoolCheck(closure->scanAllObjects)); + AVERT(Bool, closure->scanAllObjects); - format = AMS2Pool(amsseg->ams)->format; + format = AMSPool(amsseg->ams)->format; AVERT(Format, format); /* @@@@ This isn't quite right for multiple traces. */ @@ -1308,10 +1354,10 @@ Res AMSScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) AVER(totalReturn != NULL); AVERT(ScanState, ss); AVERT(Pool, pool); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS, ams); arena = PoolArena(pool); - AVER(SegCheck(seg)); + AVERT(Seg, seg); amsseg = Seg2AMSSeg(seg); AVERT(AMSSeg, amsseg); @@ -1336,7 +1382,7 @@ Res AMSScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) AVER(amsseg->colourTablesInUse); format = pool->format; AVERT(Format, format); - alignment = PoolAlignment(AMS2Pool(ams)); + alignment = PoolAlignment(AMSPool(ams)); do { /* */ amsseg->marksChanged = FALSE; /* */ /* */ @@ -1401,7 +1447,7 @@ static Res AMSFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) Format format; AVERT_CRITICAL(Pool, pool); - AVER_CRITICAL(TESTT(AMS, Pool2AMS(pool))); + AVER_CRITICAL(TESTT(AMS, PoolAMS(pool))); AVERT_CRITICAL(ScanState, ss); AVERT_CRITICAL(Seg, seg); AVER_CRITICAL(refIO != NULL); @@ -1419,23 +1465,27 @@ static Res AMSFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) /* doing that here (this can be called from RootScan, during flip). */ clientRef = *refIO; + AVER_CRITICAL(SegBase(seg) <= clientRef); + AVER_CRITICAL(clientRef < SegLimit(seg)); /* see .ref-limit */ base = AddrSub((Addr)clientRef, format->headerSize); /* can get an ambiguous reference too close to the base of the * segment, so when we subtract the header we are not in the * segment any longer. This isn't a real reference, * so we can just skip it. */ if (base < SegBase(seg)) { - return ResOK; + AVER_CRITICAL(ss->rank == RankAMBIG); + return ResOK; } i = AMS_ADDR_INDEX(seg, base); + AVER_CRITICAL(i < amsseg->grains); AVER_CRITICAL(!AMS_IS_INVALID_COLOUR(seg, i)); ss->wasMarked = TRUE; switch (ss->rank) { case RankAMBIG: - if (Pool2AMS(pool)->shareAllocTable) + if (PoolAMS(pool)->shareAllocTable) /* In this state, the pool doesn't support ambiguous references (see */ /* .ambiguous.noshare), so this is not a reference. */ break; @@ -1512,7 +1562,7 @@ static void AMSBlacken(Pool pool, TraceSet traceSet, Seg seg) Res res; AVERT(Pool, pool); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS, ams); AVERT(TraceSet, traceSet); AVERT(Seg, seg); @@ -1531,16 +1581,15 @@ static void AMSBlacken(Pool pool, TraceSet traceSet, Seg seg) /* AMSReclaim -- the pool class reclamation method */ -static void AMSReclaim(Pool pool, Trace trace, Seg seg) +static Bool AMSReclaim(Pool pool, Trace trace, Seg seg) { AMS ams; AMSSeg amsseg; - Count nowFree, grains; - Size reclaimedSize; + Count nowFree, grains, reclaimedGrains; PoolDebugMixin debug; AVERT(Pool, pool); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS, ams); AVERT(Trace, trace); AVERT(Seg, seg); @@ -1581,32 +1630,42 @@ static void AMSReclaim(Pool pool, Trace trace, Seg seg) } } - reclaimedSize = (nowFree - amsseg->free) << ams->grainShift; - amsseg->free = nowFree; - trace->reclaimSize += reclaimedSize; - ams->pgen.totalSize -= reclaimedSize; + reclaimedGrains = nowFree - amsseg->freeGrains; + AVER(amsseg->oldGrains >= reclaimedGrains); + amsseg->oldGrains -= reclaimedGrains; + amsseg->freeGrains += reclaimedGrains; + PoolGenAccountForReclaim(&ams->pgen, AMSGrainsSize(ams, reclaimedGrains), FALSE); + trace->reclaimSize += AMSGrainsSize(ams, reclaimedGrains); /* preservedInPlaceCount is updated on fix */ - trace->preservedInPlaceSize += (grains - amsseg->free) << ams->grainShift; + trace->preservedInPlaceSize += AMSGrainsSize(ams, amsseg->oldGrains); + + /* Ensure consistency of segment even if are just about to free it */ + amsseg->colourTablesInUse = FALSE; + SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); - if (amsseg->free == grains && SegBuffer(seg) == NULL) { + if (amsseg->freeGrains == grains && SegBuffer(seg) == NULL) { /* No survivors */ - SegFree(seg); - } else { - amsseg->colourTablesInUse = FALSE; - SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); + PoolGenFree(&ams->pgen, seg, + AMSGrainsSize(ams, amsseg->freeGrains), + AMSGrainsSize(ams, amsseg->oldGrains), + AMSGrainsSize(ams, amsseg->newGrains), + FALSE); + return TRUE; } + + return FALSE; } /* AMSFreeWalk -- free block walking method of the pool class */ -static void AMSFreeWalk(Pool pool, FreeBlockStepMethod f, void *p) +static void AMSFreeWalk(Pool pool, FreeBlockVisitor f, void *p) { AMS ams; Ring node, ring, nextNode; /* for iterating over the segments */ AVERT(Pool, pool); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS, ams); ring = &ams->segRing; @@ -1616,48 +1675,76 @@ static void AMSFreeWalk(Pool pool, FreeBlockStepMethod f, void *p) } +/* AMSTotalSize -- total memory allocated from the arena */ + +static Size AMSTotalSize(Pool pool) +{ + AMS ams; + + AVERT(Pool, pool); + ams = PoolAMS(pool); + AVERT(AMS, ams); + + return ams->pgen.totalSize; +} + + +/* AMSFreeSize -- free memory (unused by client program) */ + +static Size AMSFreeSize(Pool pool) +{ + AMS ams; + + AVERT(Pool, pool); + ams = PoolAMS(pool); + AVERT(AMS, ams); + + return ams->pgen.freeSize; +} + + /* AMSDescribe -- the pool class description method * * Iterates over the segments, describing all of them. */ -static Res AMSDescribe(Pool pool, mps_lib_FILE *stream) +static Res AMSDescribe(Pool pool, mps_lib_FILE *stream, Count depth) { AMS ams; Ring node, nextNode; Res res; - if (!TESTT(Pool, pool)) return ResFAIL; - ams = Pool2AMS(pool); - if (!TESTT(AMS, ams)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Pool, pool)) + return ResFAIL; + ams = PoolAMS(pool); + if (!TESTT(AMS, ams)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = WriteF(stream, + res = WriteF(stream, depth, "AMS $P {\n", (WriteFP)ams, " pool $P ($U)\n", (WriteFP)pool, (WriteFU)pool->serial, - " size $W\n", - (WriteFW)ams->size, " grain shift $U\n", (WriteFU)ams->grainShift, - " chain $P\n", - (WriteFP)ams->chain, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; - res = WriteF(stream, - " segments\n" - " * = black, + = grey, - = white, . = alloc, ! = bad\n" - " buffers: [ = base, < = scan limit, | = init,\n" - " > = alloc, ] = limit\n", + res = WriteF(stream, depth + 2, + "segments: * black + grey - white . alloc ! bad\n" + "buffers: [ base < scan limit | init > alloc ] limit\n", NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; RING_FOR(node, &ams->segRing, nextNode) { AMSSeg amsseg = RING_ELT(AMSSeg, segRing, node); - res = SegDescribe(AMSSeg2Seg(amsseg), stream); - if (res != ResOK) return res; + res = SegDescribe(AMSSeg2Seg(amsseg), stream, depth + 2); + if (res != ResOK) + return res; } - res = WriteF(stream, "} AMS $P\n",(WriteFP)ams, NULL); + res = WriteF(stream, depth, "} AMS $P\n",(WriteFP)ams, NULL); if (res != ResOK) return res; @@ -1683,14 +1770,18 @@ DEFINE_CLASS(AMSPoolClass, this) this->bufferClass = RankBufClassGet; this->bufferFill = AMSBufferFill; this->bufferEmpty = AMSBufferEmpty; - this->whiten = AMSCondemn; + this->whiten = AMSWhiten; this->blacken = AMSBlacken; this->scan = AMSScan; this->fix = AMSFix; this->fixEmergency = AMSFix; this->reclaim = AMSReclaim; + this->walk = PoolNoWalk; /* TODO: job003738 */ this->freewalk = AMSFreeWalk; + this->totalSize = AMSTotalSize; + this->freeSize = AMSFreeSize; this->describe = AMSDescribe; + AVERT(PoolClass, this); } @@ -1701,7 +1792,7 @@ static PoolDebugMixin AMSDebugMixin(Pool pool) AMS ams; AVERT(Pool, pool); - ams = Pool2AMS(pool); + ams = PoolAMS(pool); AVERT(AMS, ams); /* Can't check AMSDebug, because this is called during init */ return &(AMS2AMSDebug(ams)->debug); @@ -1718,6 +1809,7 @@ DEFINE_POOL_CLASS(AMSDebugPoolClass, this) this->size = sizeof(AMSDebugStruct); this->varargs = AMSDebugVarargs; this->debugMixin = AMSDebugMixin; + AVERT(PoolClass, this); } @@ -1726,15 +1818,13 @@ DEFINE_POOL_CLASS(AMSDebugPoolClass, this) Bool AMSCheck(AMS ams) { CHECKS(AMS, ams); - CHECKL(PoolCheck(AMS2Pool(ams))); - CHECKL(IsSubclassPoly(AMS2Pool(ams)->class, AMSPoolClassGet())); - CHECKL(PoolAlignment(AMS2Pool(ams)) == ((Size)1 << ams->grainShift)); - CHECKL(PoolAlignment(AMS2Pool(ams)) == AMS2Pool(ams)->format->alignment); - CHECKD(Chain, ams->chain); + CHECKD(Pool, AMSPool(ams)); + CHECKL(IsSubclassPoly(AMSPool(ams)->class, AMSPoolClassGet())); + CHECKL(PoolAlignment(AMSPool(ams)) == AMSGrainsSize(ams, (Size)1)); + CHECKL(PoolAlignment(AMSPool(ams)) == AMSPool(ams)->format->alignment); CHECKD(PoolGen, &ams->pgen); - CHECKL(SizeIsAligned(ams->size, ArenaAlign(PoolArena(AMS2Pool(ams))))); CHECKL(FUNCHECK(ams->segSize)); - CHECKL(RingCheck(&ams->segRing)); + CHECKD_NOSIG(Ring, &ams->segRing); CHECKL(FUNCHECK(ams->allocRing)); CHECKL(FUNCHECK(ams->segsDestroy)); CHECKL(FUNCHECK(ams->segClass)); @@ -1745,7 +1835,7 @@ Bool AMSCheck(AMS ams) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolams.h b/code/poolams.h index 96cec6c6c7..8617063fc8 100644 --- a/code/poolams.h +++ b/code/poolams.h @@ -1,7 +1,7 @@ /* poolams.h: AUTOMATIC MARK & SWEEP POOL CLASS INTERFACE * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .purpose: Internal interface to AMS functionality. */ @@ -41,7 +41,6 @@ typedef Res (*AMSSegSizePolicyFunction)(Size *sizeReturn, typedef struct AMSStruct { PoolStruct poolStruct; /* generic pool structure */ Shift grainShift; /* log2 of grain size */ - Chain chain; /* chain used by this pool */ PoolGenStruct pgen; /* generation representing the pool */ Size size; /* total segment size of the pool */ AMSSegSizePolicyFunction segSize; /* SegSize policy */ @@ -58,9 +57,10 @@ typedef struct AMSSegStruct { GCSegStruct gcSegStruct; /* superclass fields must come first */ AMS ams; /* owning ams */ RingStruct segRing; /* ring that this seg belongs to */ - Count grains; /* number of grains */ - Count free; /* number of free grains */ - Count newAlloc; /* number of grains allocated since last GC */ + Count grains; /* total grains */ + Count freeGrains; /* free grains */ + Count oldGrains; /* grains allocated prior to last collection */ + Count newGrains; /* grains allocated since last collection */ Bool allocTableInUse; /* allocTable is used */ Index firstFree; /* 1st free grain, if allocTable is not used */ BT allocTable; /* set if grain is allocated */ @@ -79,8 +79,8 @@ typedef struct AMSSegStruct { #define Seg2AMSSeg(seg) ((AMSSeg)(seg)) #define AMSSeg2Seg(amsseg) ((Seg)(amsseg)) -#define Pool2AMS(pool) PARENT(AMSStruct, poolStruct, pool) -#define AMS2Pool(ams) (&(ams)->poolStruct) +#define PoolAMS(pool) PARENT(AMSStruct, poolStruct, pool) +#define AMSPool(ams) (&(ams)->poolStruct) /* macros for abstracting index/address computations */ @@ -175,7 +175,7 @@ extern Res AMSScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg); #define AMSChain(ams) ((ams)->chain) -extern void AMSSegFreeWalk(AMSSeg amsseg, FreeBlockStepMethod f, void *p); +extern void AMSSegFreeWalk(AMSSeg amsseg, FreeBlockVisitor f, void *p); extern void AMSSegFreeCheck(AMSSeg amsseg); @@ -198,7 +198,7 @@ extern AMSPoolClass AMSDebugPoolClassGet(void); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolamsi.c b/code/poolamsi.c index 4e78c445bb..20ed01d6b3 100644 --- a/code/poolamsi.c +++ b/code/poolamsi.c @@ -1,7 +1,7 @@ /* poolamsi.c: AUTOMATIC MARK & SWEEP POOL CLASS C INTERFACE * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "mpscams.h" @@ -13,23 +13,23 @@ SRCID(poolamsi, "$Id$"); /* mps_class_ams -- return the AMS pool class descriptor */ -mps_class_t mps_class_ams(void) +mps_pool_class_t mps_class_ams(void) { - return (mps_class_t)AMSPoolClassGet(); + return (mps_pool_class_t)AMSPoolClassGet(); } /* mps_class_ams_debug -- return the AMS (debug) pool class descriptor */ -mps_class_t mps_class_ams_debug(void) +mps_pool_class_t mps_class_ams_debug(void) { - return (mps_class_t)AMSDebugPoolClassGet(); + return (mps_pool_class_t)AMSDebugPoolClassGet(); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolawl.c b/code/poolawl.c index 83890b34b6..55e7ccda53 100644 --- a/code/poolawl.c +++ b/code/poolawl.c @@ -1,7 +1,7 @@ /* poolawl.c: AUTOMATIC WEAK LINKED POOL CLASS * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * * * DESIGN @@ -74,7 +74,7 @@ typedef struct awlStatTotalStruct { /* the type of a function to find an object's dependent object */ -typedef Addr (*FindDependentMethod)(Addr object); +typedef Addr (*FindDependentFunction)(Addr object); /* AWLStruct -- AWL pool structure * @@ -84,16 +84,16 @@ typedef Addr (*FindDependentMethod)(Addr object); typedef struct AWLStruct { PoolStruct poolStruct; Shift alignShift; - Chain chain; /* dummy chain */ PoolGenStruct pgen; /* generation representing the pool */ - Size size; /* allocated size in bytes */ Count succAccesses; /* number of successive single accesses */ - FindDependentMethod findDependent; /* to find a dependent object */ + FindDependentFunction findDependent; /* to find a dependent object */ awlStatTotalStruct stats; Sig sig; } AWLStruct, *AWL; -#define Pool2AWL(pool) PARENT(AWLStruct, poolStruct, pool) +#define PoolAWL(pool) PARENT(AWLStruct, poolStruct, pool) +#define AWLPool(awl) (&(awl)->poolStruct) +#define AWLGrainsSize(awl, grains) ((grains) << (awl)->alignShift) static Bool AWLCheck(AWL awl); @@ -102,6 +102,8 @@ static Bool AWLCheck(AWL awl); /* Conversion between indexes and Addrs */ #define awlIndexOfAddr(base, awl, p) \ (AddrOffset((base), (p)) >> (awl)->alignShift) +#define awlAddrOfIndex(base, awl, i) \ + AddrAdd(base, AWLGrainsSize(awl, i)) /* AWLSegStruct -- AWL segment subclass @@ -118,8 +120,10 @@ typedef struct AWLSegStruct { BT scanned; BT alloc; Count grains; - Count free; /* number of free grains */ - Count singleAccesses; /* number of accesses processed singly */ + Count freeGrains; /* free grains */ + Count oldGrains; /* grains allocated prior to last collection */ + Count newGrains; /* grains allocated since last collection */ + Count singleAccesses; /* number of accesses processed singly */ awlStatSegStruct stats; Sig sig; } AWLSegStruct, *AWLSeg; @@ -131,6 +135,7 @@ typedef struct AWLSegStruct { extern SegClass AWLSegClassGet(void); +ATTRIBUTE_UNUSED static Bool AWLSegCheck(AWLSeg awlseg) { CHECKS(AWLSeg, awlseg); @@ -138,9 +143,8 @@ static Bool AWLSegCheck(AWLSeg awlseg) CHECKL(awlseg->mark != NULL); CHECKL(awlseg->scanned != NULL); CHECKL(awlseg->alloc != NULL); - /* Can't do any real check on ->grains */ CHECKL(awlseg->grains > 0); - CHECKL(awlseg->free <= awlseg->grains); + CHECKL(awlseg->grains == awlseg->freeGrains + awlseg->oldGrains + awlseg->newGrains); return TRUE; } @@ -187,7 +191,7 @@ static Res AWLSegInit(Seg seg, Pool pool, Addr base, Size size, AVERT(Pool, pool); arena = PoolArena(pool); /* no useful checks for base and size */ - AVER(BoolCheck(reservoirPermit)); + AVERT(Bool, reservoirPermit); ArgRequire(&arg, args, awlKeySegRankSet); rankSet = arg.val.u; AVERT(RankSet, rankSet); @@ -195,7 +199,7 @@ static Res AWLSegInit(Seg seg, Pool pool, Addr base, Size size, /* AWL only accepts two ranks */ AVER(RankSetSingle(RankEXACT) == rankSet || RankSetSingle(RankWEAK) == rankSet); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); /* Initialize the superclass fields first via next-method call */ @@ -223,10 +227,12 @@ static Res AWLSegInit(Seg seg, Pool pool, Addr base, Size size, BTResRange(awlseg->scanned, 0, bits); BTResRange(awlseg->alloc, 0, bits); SegSetRankAndSummary(seg, rankSet, RefSetUNIV); - awlseg->free = bits; - awlseg->sig = AWLSegSig; + awlseg->freeGrains = bits; + awlseg->oldGrains = (Count)0; + awlseg->newGrains = (Count)0; awlseg->singleAccesses = 0; awlStatSegInit(awlseg); + awlseg->sig = AWLSegSig; AVERT(AWLSeg, awlseg); return ResOK; @@ -257,7 +263,7 @@ static void AWLSegFinish(Seg seg) AVERT(AWLSeg, awlseg); pool = SegPool(seg); AVERT(Pool, pool); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); arena = PoolArena(pool); AVERT(Arena, arena); @@ -288,6 +294,7 @@ DEFINE_SEG_CLASS(AWLSegClass, class) class->size = sizeof(AWLSegStruct); class->init = AWLSegInit; class->finish = AWLSegFinish; + AVERT(SegClass, class); } @@ -304,10 +311,14 @@ DEFINE_SEG_CLASS(AWLSegClass, class) * it's possible to tweak them in a debugger. */ +extern Count AWLSegSALimit; Count AWLSegSALimit = AWL_SEG_SA_LIMIT; +extern Bool AWLHaveSegSALimit; Bool AWLHaveSegSALimit = AWL_HAVE_SEG_SA_LIMIT; +extern Count AWLTotalSALimit; Count AWLTotalSALimit = AWL_TOTAL_SA_LIMIT; +extern Bool AWLHaveTotalSALimit; Bool AWLHaveTotalSALimit = AWL_HAVE_TOTAL_SA_LIMIT; @@ -450,25 +461,25 @@ static Res AWLSegCreate(AWLSeg *awlsegReturn, Arena arena; AVER(awlsegReturn != NULL); - AVER(RankSetCheck(rankSet)); + AVERT(RankSet, rankSet); AVERT(Pool, pool); AVER(size > 0); - AVER(BoolCheck(reservoirPermit)); + AVERT(Bool, reservoirPermit); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); arena = PoolArena(pool); AVERT(Arena, arena); - size = SizeAlignUp(size, ArenaAlign(arena)); + size = SizeArenaGrains(size, arena); /* beware of large sizes overflowing upon rounding */ if (size == 0) return ResMEMORY; MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD_FIELD(args, awlKeySegRankSet, u, rankSet); - res = ChainAlloc(&seg, awl->chain, awl->pgen.nr, AWLSegClassGet(), - size, pool, reservoirPermit, args); + res = PoolGenAlloc(&seg, &awl->pgen, AWLSegClassGet(), size, + reservoirPermit, args); } MPS_ARGS_END(args); if (res != ResOK) return res; @@ -495,7 +506,7 @@ static Bool AWLSegAlloc(Addr *baseReturn, Addr *limitReturn, AVERT(AWLSeg, awlseg); AVERT(AWL, awl); AVER(size > 0); - AVER(size << awl->alignShift >= size); + AVER(AWLGrainsSize(awl, size) >= size); seg = AWLSeg2Seg(awlseg); if (size > SegSize(seg)) @@ -503,9 +514,8 @@ static Bool AWLSegAlloc(Addr *baseReturn, Addr *limitReturn, n = size >> awl->alignShift; if (!BTFindLongResRange(&i, &j, awlseg->alloc, 0, awlseg->grains, n)) return FALSE; - awl->size += size; - *baseReturn = AddrAdd(SegBase(seg), i << awl->alignShift); - *limitReturn = AddrAdd(SegBase(seg), j << awl->alignShift); + *baseReturn = awlAddrOfIndex(SegBase(seg), awl, i); + *limitReturn = awlAddrOfIndex(SegBase(seg),awl, j); return TRUE; } @@ -519,19 +529,28 @@ static void AWLVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[1].key = MPS_KEY_AWL_FIND_DEPENDENT; args[1].val.addr_method = va_arg(varargs, mps_awl_find_dependent_t); args[2].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); +} + + +/* awlNoDependent -- no dependent object */ + +static Addr awlNoDependent(Addr addr) +{ + UNUSED(addr); + return NULL; } /* AWLInit -- initialize an AWL pool */ -ARG_DEFINE_KEY(awl_find_dependent, Fun); +ARG_DEFINE_KEY(AWL_FIND_DEPENDENT, Fun); static Res AWLInit(Pool pool, ArgList args) { AWL awl; Format format; - FindDependentMethod findDependent; + FindDependentFunction findDependent = awlNoDependent; Chain chain; Res res; ArgStruct arg; @@ -540,12 +559,12 @@ static Res AWLInit(Pool pool, ArgList args) /* Weak check, as half-way through initialization. */ AVER(pool != NULL); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); ArgRequire(&arg, args, MPS_KEY_FORMAT); format = arg.val.format; - ArgRequire(&arg, args, MPS_KEY_AWL_FIND_DEPENDENT); - findDependent = (FindDependentMethod)arg.val.addr_method; + if (ArgPick(&arg, args, MPS_KEY_AWL_FIND_DEPENDENT)) + findDependent = (FindDependentFunction)arg.val.addr_method; if (ArgPick(&arg, args, MPS_KEY_CHAIN)) chain = arg.val.chain; else { @@ -556,22 +575,22 @@ static Res AWLInit(Pool pool, ArgList args) gen = arg.val.u; AVERT(Format, format); + AVER(FormatArena(format) == PoolArena(pool)); pool->format = format; + pool->alignment = format->alignment; AVER(FUNCHECK(findDependent)); awl->findDependent = findDependent; AVERT(Chain, chain); AVER(gen <= ChainGens(chain)); - awl->chain = chain; + AVER(chain->arena == PoolArena(pool)); - res = PoolGenInit(&awl->pgen, chain, gen, pool); + res = PoolGenInit(&awl->pgen, ChainGen(chain, gen), pool); if (res != ResOK) goto failGenInit; - awl->alignShift = SizeLog2(pool->alignment); - awl->size = (Size)0; - + awl->alignShift = SizeLog2(PoolAlignment(pool)); awl->succAccesses = 0; awlStatTotalInit(awl); awl->sig = AWLSig; @@ -595,14 +614,19 @@ static void AWLFinish(Pool pool) AVERT(Pool, pool); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); ring = &pool->segRing; RING_FOR(node, ring, nextNode) { Seg seg = SegOfPoolRing(node); - AVERT(Seg, seg); - SegFree(seg); + AWLSeg awlseg = Seg2AWLSeg(seg); + AVERT(AWLSeg, awlseg); + PoolGenFree(&awl->pgen, seg, + AWLGrainsSize(awl, awlseg->freeGrains), + AWLGrainsSize(awl, awlseg->oldGrains), + AWLGrainsSize(awl, awlseg->newGrains), + FALSE); } awl->sig = SigInvalid; PoolGenFinish(&awl->pgen); @@ -626,9 +650,9 @@ static Res AWLBufferFill(Addr *baseReturn, Addr *limitReturn, AVERT(Pool, pool); AVERT(Buffer, buffer); AVER(size > 0); - AVER(BoolCheck(reservoirPermit)); + AVERT(Bool, reservoirPermit); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); RING_FOR(node, &pool->segRing, nextNode) { @@ -641,10 +665,11 @@ static Res AWLBufferFill(Addr *baseReturn, Addr *limitReturn, /* Only try to allocate in the segment if it is not already */ /* buffered, and has the same ranks as the buffer. */ - if (SegBuffer(seg) == NULL && SegRankSet(seg) == BufferRankSet(buffer)) - if (awlseg->free << awl->alignShift >= size - && AWLSegAlloc(&base, &limit, awlseg, awl, size)) - goto found; + if (SegBuffer(seg) == NULL + && SegRankSet(seg) == BufferRankSet(buffer) + && AWLGrainsSize(awl, awlseg->freeGrains) >= size + && AWLSegAlloc(&base, &limit, awlseg, awl, size)) + goto found; } /* No free space in existing awlsegs, so create new awlseg */ @@ -668,7 +693,10 @@ static Res AWLBufferFill(Addr *baseReturn, Addr *limitReturn, /* Shouldn't this depend on trace phase? @@@@ */ BTSetRange(awlseg->mark, i, j); BTSetRange(awlseg->scanned, i, j); - awlseg->free -= j - i; + AVER(awlseg->freeGrains >= j - i); + awlseg->freeGrains -= j - i; + awlseg->newGrains += j - i; + PoolGenAccountForFill(&awl->pgen, AddrOffset(base, limit), FALSE); } *baseReturn = base; *limitReturn = limit; @@ -692,7 +720,7 @@ static void AWLBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) AVERT(Seg, seg); AVER(init <= limit); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); awlseg = Seg2AWLSeg(seg); AVERT(AWLSeg, awlseg); @@ -704,7 +732,10 @@ static void AWLBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) AVER(i <= j); if (i < j) { BTResRange(awlseg->alloc, i, j); - awlseg->free += j - i; + AVER(awlseg->newGrains >= j - i); + awlseg->newGrains -= j - i; + awlseg->freeGrains += j - i; + PoolGenAccountForEmpty(&awl->pgen, AddrOffset(init, limit), FALSE); } } @@ -730,10 +761,11 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg) AWL awl; AWLSeg awlseg; Buffer buffer; + Count uncondemned; /* All parameters checked by generic PoolWhiten. */ - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); awlseg = Seg2AWLSeg(seg); AVERT(AWLSeg, awlseg); @@ -745,15 +777,13 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg) if(buffer == NULL) { awlRangeWhiten(awlseg, 0, awlseg->grains); - trace->condemned += SegSize(seg); + uncondemned = (Count)0; } else { /* Whiten everything except the buffer. */ Addr base = SegBase(seg); - Index scanLimitIndex = awlIndexOfAddr(base, awl, - BufferScanLimit(buffer)); - Index limitIndex = awlIndexOfAddr(base, awl, - BufferLimit(buffer)); - + Index scanLimitIndex = awlIndexOfAddr(base, awl, BufferScanLimit(buffer)); + Index limitIndex = awlIndexOfAddr(base, awl, BufferLimit(buffer)); + uncondemned = limitIndex - scanLimitIndex; awlRangeWhiten(awlseg, 0, scanLimitIndex); awlRangeWhiten(awlseg, limitIndex, awlseg->grains); @@ -764,14 +794,12 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg) AVER(BTIsSetRange(awlseg->mark, scanLimitIndex, limitIndex)); AVER(BTIsSetRange(awlseg->scanned, scanLimitIndex, limitIndex)); } - - /* We didn't condemn the buffer, subtract it from the count. */ - /* @@@@ We could subtract all the free grains. */ - trace->condemned += SegSize(seg) - - AddrOffset(BufferScanLimit(buffer), - BufferLimit(buffer)); } + PoolGenAccountForAge(&awl->pgen, AWLGrainsSize(awl, awlseg->newGrains - uncondemned), FALSE); + awlseg->oldGrains += awlseg->newGrains - uncondemned; + awlseg->newGrains = uncondemned; + trace->condemned += AWLGrainsSize(awl, awlseg->oldGrains); SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); return ResOK; } @@ -803,7 +831,7 @@ static void AWLGrey(Pool pool, Trace trace, Seg seg) AWL awl; AWLSeg awlseg; - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); awlseg = Seg2AWLSeg(seg); AVERT(AWLSeg, awlseg); @@ -834,10 +862,10 @@ static void AWLBlacken(Pool pool, TraceSet traceSet, Seg seg) AWLSeg awlseg; AVERT(Pool, pool); - AVER(TraceSetCheck(traceSet)); + AVERT(TraceSet, traceSet); AVERT(Seg, seg); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); awlseg = Seg2AWLSeg(seg); AVERT(AWLSeg, awlseg); @@ -904,7 +932,7 @@ static Res awlScanSinglePass(Bool *anyScannedReturn, AVERT(Seg, seg); AVERT(Bool, scanAllObjects); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); arena = PoolArena(pool); AVERT(Arena, arena); @@ -936,7 +964,7 @@ static Res awlScanSinglePass(Bool *anyScannedReturn, i = awlIndexOfAddr(base, awl, p); if (!BTGet(awlseg->alloc, i)) { - p = AddrAdd(p, pool->alignment); + p = AddrAdd(p, PoolAlignment(pool)); continue; } hp = AddrAdd(p, format->headerSize); @@ -953,7 +981,8 @@ static Res awlScanSinglePass(Bool *anyScannedReturn, } objectLimit = AddrSub(objectLimit, format->headerSize); AVER(p < objectLimit); - p = AddrAlignUp(objectLimit, pool->alignment); + AVER(AddrIsAligned(objectLimit, PoolAlignment(pool))); + p = objectLimit; } AVER(p == limit); @@ -979,7 +1008,7 @@ static Res AWLScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) awlseg = Seg2AWLSeg(seg); AVERT(AWLSeg, awlseg); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); /* If the scanner isn't going to scan all the objects then the */ @@ -1029,7 +1058,7 @@ static Res AWLFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) AVER(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); AVER(refIO != NULL); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); awlseg = Seg2AWLSeg(seg); AVERT(AWLSeg, awlseg); @@ -1050,7 +1079,7 @@ static Res AWLFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) switch(ss->rank) { case RankAMBIG: /* not a real pointer if not aligned or not allocated */ - if (!AddrIsAligned(base, pool->alignment) || !BTGet(awlseg->alloc, i)) + if (!AddrIsAligned(base, sizeof(void *)) || !BTGet(awlseg->alloc, i)) return ResOK; /* falls through */ case RankEXACT: @@ -1077,23 +1106,23 @@ static Res AWLFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) /* AWLReclaim -- reclaim dead objects in an AWL segment */ -static void AWLReclaim(Pool pool, Trace trace, Seg seg) +static Bool AWLReclaim(Pool pool, Trace trace, Seg seg) { Addr base; AWL awl; AWLSeg awlseg; + Buffer buffer; Index i; - Count oldFree; Format format; + Count reclaimedGrains = (Count)0; Count preservedInPlaceCount = (Count)0; Size preservedInPlaceSize = (Size)0; - Size freed; /* amount reclaimed, in bytes */ AVERT(Pool, pool); AVERT(Trace, trace); AVERT(Seg, seg); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); awlseg = Seg2AWLSeg(seg); AVERT(AWLSeg, awlseg); @@ -1101,8 +1130,9 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) format = pool->format; base = SegBase(seg); + buffer = SegBuffer(seg); - i = 0; oldFree = awlseg->free; + i = 0; while(i < awlseg->grains) { Addr p, q; Index j; @@ -1111,20 +1141,17 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) ++i; continue; } - p = AddrAdd(base, i << awl->alignShift); - if(SegBuffer(seg) != NULL) { - Buffer buffer = SegBuffer(seg); - - if(p == BufferScanLimit(buffer) - && BufferScanLimit(buffer) != BufferLimit(buffer)) - { - i = awlIndexOfAddr(base, awl, BufferLimit(buffer)); - continue; - } + p = awlAddrOfIndex(base, awl, i); + if (buffer != NULL + && p == BufferScanLimit(buffer) + && BufferScanLimit(buffer) != BufferLimit(buffer)) + { + i = awlIndexOfAddr(base, awl, BufferLimit(buffer)); + continue; } q = format->skip(AddrAdd(p, format->headerSize)); q = AddrSub(q, format->headerSize); - q = AddrAlignUp(q, pool->alignment); + AVER(AddrIsAligned(q, PoolAlignment(pool))); j = awlIndexOfAddr(base, awl, q); AVER(j <= awlseg->grains); if(BTGet(awlseg->mark, i)) { @@ -1137,20 +1164,34 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) BTResRange(awlseg->mark, i, j); BTSetRange(awlseg->scanned, i, j); BTResRange(awlseg->alloc, i, j); - awlseg->free += j - i; + reclaimedGrains += j - i; } i = j; } AVER(i == awlseg->grains); - freed = (awlseg->free - oldFree) << awl->alignShift; - awl->size -= freed; - trace->reclaimSize += freed; + AVER(reclaimedGrains <= awlseg->grains); + AVER(awlseg->oldGrains >= reclaimedGrains); + awlseg->oldGrains -= reclaimedGrains; + awlseg->freeGrains += reclaimedGrains; + PoolGenAccountForReclaim(&awl->pgen, AWLGrainsSize(awl, reclaimedGrains), FALSE); + + trace->reclaimSize += AWLGrainsSize(awl, reclaimedGrains); trace->preservedInPlaceCount += preservedInPlaceCount; trace->preservedInPlaceSize += preservedInPlaceSize; SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); - /* @@@@ never frees a segment, see job001687. */ - return; + + if (awlseg->freeGrains == awlseg->grains && buffer == NULL) { + /* No survivors */ + PoolGenFree(&awl->pgen, seg, + AWLGrainsSize(awl, awlseg->freeGrains), + AWLGrainsSize(awl, awlseg->oldGrains), + AWLGrainsSize(awl, awlseg->newGrains), + FALSE); + return TRUE; + } + + return FALSE; } @@ -1163,12 +1204,13 @@ static Res AWLAccess(Pool pool, Seg seg, Addr addr, Res res; AVERT(Pool, pool); - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); AVERT(Seg, seg); AVER(SegBase(seg) <= addr); AVER(addr < SegLimit(seg)); AVER(SegPool(seg) == pool); + AVERT(AccessSet, mode); /* Attempt scanning a single reference if permitted */ if(AWLCanTrySingleAccess(PoolArena(pool), awl, seg, addr)) { @@ -1197,7 +1239,7 @@ static Res AWLAccess(Pool pool, Seg seg, Addr addr, /* AWLWalk -- walk all objects */ -static void AWLWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, +static void AWLWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, void *p, size_t s) { AWL awl; @@ -1210,7 +1252,7 @@ static void AWLWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, AVER(FUNCHECK(f)); /* p and s are arbitrary closures and can't be checked */ - awl = Pool2AWL(pool); + awl = PoolAWL(pool); AVERT(AWL, awl); awlseg = Seg2AWLSeg(seg); AVERT(AWLSeg, awlseg); @@ -1242,13 +1284,13 @@ static void AWLWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, i = awlIndexOfAddr(base, awl, object); if (!BTGet(awlseg->alloc, i)) { /* This grain is free */ - object = AddrAdd(object, pool->alignment); + object = AddrAdd(object, PoolAlignment(pool)); continue; } object = AddrAdd(object, format->headerSize); next = format->skip(object); next = AddrSub(next, format->headerSize); - next = AddrAlignUp(next, pool->alignment); + AVER(AddrIsAligned(next, PoolAlignment(pool))); if (BTGet(awlseg->mark, i) && BTGet(awlseg->scanned, i)) (*f)(object, pool->format, pool, p, s); object = next; @@ -1256,6 +1298,34 @@ static void AWLWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, } +/* AWLTotalSize -- total memory allocated from the arena */ + +static Size AWLTotalSize(Pool pool) +{ + AWL awl; + + AVERT(Pool, pool); + awl = PoolAWL(pool); + AVERT(AWL, awl); + + return awl->pgen.totalSize; +} + + +/* AWLFreeSize -- free memory (unused by client program) */ + +static Size AWLFreeSize(Pool pool) +{ + AWL awl; + + AVERT(Pool, pool); + awl = PoolAWL(pool); + AVERT(AWL, awl); + + return awl->pgen.freeSize; +} + + /* AWLPoolClass -- the class definition */ DEFINE_POOL_CLASS(AWLPoolClass, this) @@ -1280,24 +1350,27 @@ DEFINE_POOL_CLASS(AWLPoolClass, this) this->fixEmergency = AWLFix; this->reclaim = AWLReclaim; this->walk = AWLWalk; + this->totalSize = AWLTotalSize; + this->freeSize = AWLFreeSize; + AVERT(PoolClass, this); } -mps_class_t mps_class_awl(void) +mps_pool_class_t mps_class_awl(void) { - return (mps_class_t)AWLPoolClassGet(); + return (mps_pool_class_t)AWLPoolClassGet(); } /* AWLCheck -- check an AWL pool */ +ATTRIBUTE_UNUSED static Bool AWLCheck(AWL awl) { CHECKS(AWL, awl); - CHECKD(Pool, &awl->poolStruct); - CHECKL(awl->poolStruct.class == AWLPoolClassGet()); - CHECKL((Align)1 << awl->alignShift == awl->poolStruct.alignment); - CHECKD(Chain, awl->chain); + CHECKD(Pool, AWLPool(awl)); + CHECKL(AWLPool(awl)->class == AWLPoolClassGet()); + CHECKL(AWLGrainsSize(awl, (Count)1) == PoolAlignment(AWLPool(awl))); /* Nothing to check about succAccesses. */ CHECKL(FUNCHECK(awl->findDependent)); /* Don't bother to check stats. */ @@ -1307,7 +1380,7 @@ static Bool AWLCheck(AWL awl) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poollo.c b/code/poollo.c index f81ab6a181..17603b72ce 100644 --- a/code/poollo.c +++ b/code/poollo.c @@ -1,7 +1,7 @@ /* poollo.c: LEAF POOL CLASS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -24,13 +24,13 @@ typedef struct LOStruct *LO; typedef struct LOStruct { PoolStruct poolStruct; /* generic pool structure */ Shift alignShift; /* log_2 of pool alignment */ - Chain chain; /* chain used by this pool */ PoolGenStruct pgen; /* generation representing the pool */ Sig sig; } LOStruct; #define PoolPoolLO(pool) PARENT(LOStruct, poolStruct, pool) #define LOPool(lo) (&(lo)->poolStruct) +#define LOGrainsSize(lo, grains) ((grains) << (lo)->alignShift) /* forward declaration */ @@ -48,8 +48,9 @@ typedef struct LOSegStruct { LO lo; /* owning LO */ BT mark; /* mark bit table */ BT alloc; /* alloc bit table */ - Count free; /* number of free grains */ - Count newAlloc; /* number of grains allocated since last GC */ + Count freeGrains; /* free grains */ + Count oldGrains; /* grains allocated prior to last collection */ + Count newGrains; /* grains allocated since last collection */ Sig sig; /* */ } LOSegStruct; @@ -61,6 +62,7 @@ typedef struct LOSegStruct { static Res loSegInit(Seg seg, Pool pool, Addr base, Size size, Bool reservoirPermit, ArgList args); static void loSegFinish(Seg seg); +static Count loSegGrains(LOSeg loseg); /* LOSegClass -- Class definition for LO segments */ @@ -73,21 +75,23 @@ DEFINE_SEG_CLASS(LOSegClass, class) class->size = sizeof(LOSegStruct); class->init = loSegInit; class->finish = loSegFinish; + AVERT(SegClass, class); } /* LOSegCheck -- check an LO segment */ +ATTRIBUTE_UNUSED static Bool LOSegCheck(LOSeg loseg) { CHECKS(LOSeg, loseg); - CHECKL(GCSegCheck(&loseg->gcSegStruct)); + CHECKD(GCSeg, &loseg->gcSegStruct); CHECKU(LO, loseg->lo); CHECKL(loseg->mark != NULL); CHECKL(loseg->alloc != NULL); /* Could check exactly how many bits are set in the alloc table. */ - CHECKL(loseg->free + loseg->newAlloc - <= SegSize(LOSegSeg(loseg)) >> loseg->lo->alignShift); + CHECKL(loseg->freeGrains + loseg->oldGrains + loseg->newGrains + == SegSize(LOSegSeg(loseg)) >> loseg->lo->alignShift); return TRUE; } @@ -104,7 +108,7 @@ static Res loSegInit(Seg seg, Pool pool, Addr base, Size size, Size tablebytes; /* # bytes in each control array */ Arena arena; /* number of bits needed in each control array */ - Count bits; + Count grains; void *p; AVERT(Seg, seg); @@ -112,7 +116,7 @@ static Res loSegInit(Seg seg, Pool pool, Addr base, Size size, AVERT(Pool, pool); arena = PoolArena(pool); /* no useful checks for base and size */ - AVER(BoolCheck(reservoirPermit)); + AVERT(Bool, reservoirPermit); lo = PoolPoolLO(pool); AVERT(LO, lo); @@ -124,8 +128,8 @@ static Res loSegInit(Seg seg, Pool pool, Addr base, Size size, AVER(SegWhite(seg) == TraceSetEMPTY); - bits = size >> lo->alignShift; - tablebytes = BTSize(bits); + grains = size >> lo->alignShift; + tablebytes = BTSize(grains); res = ControlAlloc(&p, arena, tablebytes, reservoirPermit); if(res != ResOK) goto failMarkTable; @@ -134,11 +138,12 @@ static Res loSegInit(Seg seg, Pool pool, Addr base, Size size, if(res != ResOK) goto failAllocTable; loseg->alloc = p; - BTResRange(loseg->alloc, 0, bits); - BTSetRange(loseg->mark, 0, bits); + BTResRange(loseg->alloc, 0, grains); + BTSetRange(loseg->mark, 0, grains); loseg->lo = lo; - loseg->free = bits; - loseg->newAlloc = (Count)0; + loseg->freeGrains = grains; + loseg->oldGrains = (Count)0; + loseg->newGrains = (Count)0; loseg->sig = LOSegSig; AVERT(LOSeg, loseg); return ResOK; @@ -161,7 +166,7 @@ static void loSegFinish(Seg seg) Pool pool; Arena arena; Size tablesize; - Count bits; + Count grains; AVERT(Seg, seg); loseg = SegLOSeg(seg); @@ -171,8 +176,8 @@ static void loSegFinish(Seg seg) AVERT(LO, lo); arena = PoolArena(pool); - bits = SegSize(seg) >> lo->alignShift; - tablesize = BTSize(bits); + grains = loSegGrains(loseg); + tablesize = BTSize(grains); ControlFree(arena, (Addr)loseg->alloc, tablesize); ControlFree(arena, (Addr)loseg->mark, tablesize); loseg->sig = SigInvalid; @@ -183,7 +188,8 @@ static void loSegFinish(Seg seg) } -static Count loSegBits(LOSeg loseg) +ATTRIBUTE_UNUSED +static Count loSegGrains(LOSeg loseg) { LO lo; Size size; @@ -202,7 +208,7 @@ static Count loSegBits(LOSeg loseg) (AddrOffset((base), (p)) >> (lo)->alignShift) #define loAddrOfIndex(base, lo, i) \ - (AddrAdd((base), (i) << (lo)->alignShift)) + (AddrAdd((base), LOGrainsSize((lo), (i)))) /* loSegFree -- mark block from baseIndex to limitIndex free */ @@ -211,12 +217,11 @@ static void loSegFree(LOSeg loseg, Index baseIndex, Index limitIndex) { AVERT(LOSeg, loseg); AVER(baseIndex < limitIndex); - AVER(limitIndex <= loSegBits(loseg)); + AVER(limitIndex <= loSegGrains(loseg)); AVER(BTIsSetRange(loseg->alloc, baseIndex, limitIndex)); BTResRange(loseg->alloc, baseIndex, limitIndex); BTSetRange(loseg->mark, baseIndex, limitIndex); - loseg->free += limitIndex - baseIndex; } @@ -231,7 +236,7 @@ static Bool loSegFindFree(Addr *bReturn, Addr *lReturn, LO lo; Seg seg; Count agrains; - Count bits; + Count grains; Addr segBase; AVER(bReturn != NULL); @@ -246,23 +251,22 @@ static Bool loSegFindFree(Addr *bReturn, Addr *lReturn, /* of the allocation request */ agrains = size >> lo->alignShift; AVER(agrains >= 1); - AVER(agrains <= loseg->free); + AVER(agrains <= loseg->freeGrains); AVER(size <= SegSize(seg)); - if(SegBuffer(seg) != NULL) { + if(SegBuffer(seg) != NULL) /* Don't bother trying to allocate from a buffered segment */ return FALSE; - } - bits = SegSize(seg) >> lo->alignShift; + grains = loSegGrains(loseg); if(!BTFindLongResRange(&baseIndex, &limitIndex, loseg->alloc, - 0, bits, agrains)) { + 0, grains, agrains)) { return FALSE; } /* check that BTFindLongResRange really did find enough space */ AVER(baseIndex < limitIndex); - AVER((limitIndex-baseIndex) << lo->alignShift >= size); + AVER(LOGrainsSize(lo, limitIndex - baseIndex) >= size); segBase = SegBase(seg); *bReturn = loAddrOfIndex(segBase, lo, baseIndex); *lReturn = loAddrOfIndex(segBase, lo, limitIndex); @@ -273,7 +277,7 @@ static Bool loSegFindFree(Addr *bReturn, Addr *lReturn, /* loSegCreate -- Creates a segment of size at least size. * - * Segments will be ArenaAlign aligned . + * Segments will be multiples of ArenaGrainSize. */ static Res loSegCreate(LOSeg *loSegReturn, Pool pool, Size size, @@ -286,13 +290,13 @@ static Res loSegCreate(LOSeg *loSegReturn, Pool pool, Size size, AVER(loSegReturn != NULL); AVERT(Pool, pool); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); lo = PoolPoolLO(pool); AVERT(LO, lo); - res = ChainAlloc(&seg, lo->chain, lo->pgen.nr, EnsureLOSegClass(), - SizeAlignUp(size, ArenaAlign(PoolArena(pool))), - pool, withReservoirPermit, argsNone); + res = PoolGenAlloc(&seg, &lo->pgen, EnsureLOSegClass(), + SizeArenaGrains(size, PoolArena(pool)), + withReservoirPermit, argsNone); if (res != ResOK) return res; @@ -306,11 +310,11 @@ static Res loSegCreate(LOSeg *loSegReturn, Pool pool, Size size, * Could consider implementing this using Walk. */ -static void loSegReclaim(LOSeg loseg, Trace trace) +static Bool loSegReclaim(LOSeg loseg, Trace trace) { Addr p, base, limit; Bool marked; - Count bytesReclaimed = (Count)0; + Count reclaimedGrains = (Count)0; Seg seg; LO lo; Format format; @@ -368,36 +372,46 @@ static void loSegReclaim(LOSeg loseg, Trace trace) Index j = loIndexOfAddr(base, lo, q); /* This object is not marked, so free it */ loSegFree(loseg, i, j); - bytesReclaimed += AddrOffset(p, q); + reclaimedGrains += j - i; } p = q; } AVER(p == limit); - AVER(bytesReclaimed <= SegSize(seg)); - trace->reclaimSize += bytesReclaimed; - lo->pgen.totalSize -= bytesReclaimed; + AVER(reclaimedGrains <= loSegGrains(loseg)); + AVER(loseg->oldGrains >= reclaimedGrains); + loseg->oldGrains -= reclaimedGrains; + loseg->freeGrains += reclaimedGrains; + PoolGenAccountForReclaim(&lo->pgen, LOGrainsSize(lo, reclaimedGrains), FALSE); + + trace->reclaimSize += LOGrainsSize(lo, reclaimedGrains); trace->preservedInPlaceCount += preservedInPlaceCount; trace->preservedInPlaceSize += preservedInPlaceSize; SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); - if(!marked) { - SegFree(seg); + if (!marked) { + PoolGenFree(&lo->pgen, seg, + LOGrainsSize(lo, loseg->freeGrains), + LOGrainsSize(lo, loseg->oldGrains), + LOGrainsSize(lo, loseg->newGrains), + FALSE); + return TRUE; } + + return FALSE; } /* This walks over _all_ objects in the heap, whether they are */ /* black or white, they are still validly formatted as this is */ /* a leaf pool, so there can't be any dangling references */ -static void LOWalk(Pool pool, Seg seg, - FormattedObjectsStepMethod f, +static void LOWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, void *p, size_t s) { Addr base; LO lo; LOSeg loseg; - Index i, limit; + Index i, grains; Format format; AVERT(Pool, pool); @@ -414,10 +428,10 @@ static void LOWalk(Pool pool, Seg seg, AVERT(Format, format); base = SegBase(seg); - limit = SegSize(seg) >> lo->alignShift; + grains = loSegGrains(loseg); i = 0; - while(i < limit) { + while(i < grains) { /* object is a slight misnomer because it might point to a */ /* free grain */ Addr object = loAddrOfIndex(base, lo, i); @@ -460,7 +474,7 @@ static void LOVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[0].key = MPS_KEY_FORMAT; args[0].val.format = va_arg(varargs, Format); args[1].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } @@ -472,9 +486,11 @@ static Res LOInit(Pool pool, ArgList args) Arena arena; Res res; ArgStruct arg; + Chain chain; unsigned gen = LO_GEN_DEFAULT; AVERT(Pool, pool); + AVERT(ArgList, args); arena = PoolArena(pool); @@ -483,22 +499,24 @@ static Res LOInit(Pool pool, ArgList args) ArgRequire(&arg, args, MPS_KEY_FORMAT); pool->format = arg.val.format; if (ArgPick(&arg, args, MPS_KEY_CHAIN)) - lo->chain = arg.val.chain; + chain = arg.val.chain; else { - lo->chain = ArenaGlobals(arena)->defaultChain; + chain = ArenaGlobals(arena)->defaultChain; gen = 1; /* avoid the nursery of the default chain by default */ } if (ArgPick(&arg, args, MPS_KEY_GEN)) gen = arg.val.u; AVERT(Format, pool->format); - AVERT(Chain, lo->chain); - AVER(gen <= ChainGens(lo->chain)); + AVER(FormatArena(pool->format) == arena); + AVERT(Chain, chain); + AVER(gen <= ChainGens(chain)); + AVER(chain->arena == arena); pool->alignment = pool->format->alignment; lo->alignShift = SizeLog2((Size)PoolAlignment(pool)); - res = PoolGenInit(&lo->pgen, lo->chain, gen, pool); + res = PoolGenInit(&lo->pgen, ChainGen(chain, gen), pool); if (res != ResOK) goto failGenInit; @@ -527,10 +545,12 @@ static void LOFinish(Pool pool) RING_FOR(node, &pool->segRing, nextNode) { Seg seg = SegOfPoolRing(node); LOSeg loseg = SegLOSeg(seg); - AVERT(LOSeg, loseg); - UNUSED(loseg); /* */ - SegFree(seg); + PoolGenFree(&lo->pgen, seg, + LOGrainsSize(lo, loseg->freeGrains), + LOGrainsSize(lo, loseg->oldGrains), + LOGrainsSize(lo, loseg->newGrains), + FALSE); } PoolGenFinish(&lo->pgen); @@ -558,14 +578,14 @@ static Res LOBufferFill(Addr *baseReturn, Addr *limitReturn, AVER(BufferRankSet(buffer) == RankSetEMPTY); AVER(size > 0); AVER(SizeIsAligned(size, PoolAlignment(pool))); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); /* Try to find a segment with enough space already. */ RING_FOR(node, &pool->segRing, nextNode) { Seg seg = SegOfPoolRing(node); loseg = SegLOSeg(seg); AVERT(LOSeg, loseg); - if((loseg->free << lo->alignShift) >= size + if(LOGrainsSize(lo, loseg->freeGrains) >= size && loSegFindFree(&base, &limit, loseg, size)) goto found; } @@ -590,12 +610,12 @@ static Res LOBufferFill(Addr *baseReturn, Addr *limitReturn, AVER(BTIsResRange(loseg->alloc, baseIndex, limitIndex)); AVER(BTIsSetRange(loseg->mark, baseIndex, limitIndex)); BTSetRange(loseg->alloc, baseIndex, limitIndex); - loseg->free -= limitIndex - baseIndex; - loseg->newAlloc += limitIndex - baseIndex; + AVER(loseg->freeGrains >= limitIndex - baseIndex); + loseg->freeGrains -= limitIndex - baseIndex; + loseg->newGrains += limitIndex - baseIndex; } - lo->pgen.totalSize += AddrOffset(base, limit); - lo->pgen.newSize += AddrOffset(base, limit); + PoolGenAccountForFill(&lo->pgen, AddrOffset(base, limit), FALSE); *baseReturn = base; *limitReturn = limit; @@ -614,7 +634,7 @@ static void LOBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) Addr base, segBase; Seg seg; LOSeg loseg; - Index baseIndex, initIndex, limitIndex; + Index initIndex, limitIndex; AVERT(Pool, pool); lo = PARENT(LOStruct, poolStruct, pool); @@ -639,21 +659,17 @@ static void LOBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) AVER(init <= SegLimit(seg)); /* convert base, init, and limit, to quantum positions */ - baseIndex = loIndexOfAddr(segBase, lo, base); initIndex = loIndexOfAddr(segBase, lo, init); limitIndex = loIndexOfAddr(segBase, lo, limit); - /* Record the unused portion at the end of the buffer */ - /* as being free. */ - AVER(baseIndex == limitIndex - || BTIsSetRange(loseg->alloc, baseIndex, limitIndex)); if(initIndex != limitIndex) { + /* Free the unused portion of the buffer (this must be "new", since + * it's not condemned). */ loSegFree(loseg, initIndex, limitIndex); - lo->pgen.totalSize -= AddrOffset(init, limit); - /* All of the buffer must be new, since buffered segs are not condemned. */ - AVER(loseg->newAlloc >= limitIndex - baseIndex); - loseg->newAlloc -= limitIndex - initIndex; - lo->pgen.newSize -= AddrOffset(init, limit); + AVER(loseg->newGrains >= limitIndex - initIndex); + loseg->newGrains -= limitIndex - initIndex; + loseg->freeGrains += limitIndex - initIndex; + PoolGenAccountForEmpty(&lo->pgen, AddrOffset(init, limit), FALSE); } } @@ -663,7 +679,9 @@ static void LOBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) static Res LOWhiten(Pool pool, Trace trace, Seg seg) { LO lo; - Count bits; + LOSeg loseg; + Buffer buffer; + Count grains, uncondemned; AVERT(Pool, pool); lo = PoolPoolLO(pool); @@ -673,21 +691,32 @@ static Res LOWhiten(Pool pool, Trace trace, Seg seg) AVERT(Seg, seg); AVER(SegWhite(seg) == TraceSetEMPTY); - if(SegBuffer(seg) == NULL) { - LOSeg loseg = SegLOSeg(seg); - AVERT(LOSeg, loseg); - - bits = SegSize(seg) >> lo->alignShift; - /* Allocated objects should be whitened, free areas should */ - /* be left "black". */ - BTCopyInvertRange(loseg->alloc, loseg->mark, 0, bits); - /* @@@@ We could subtract all the free grains. */ - trace->condemned += SegSize(seg); - lo->pgen.newSize -= loseg->newAlloc << lo->alignShift; - loseg->newAlloc = (Count)0; - SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); + loseg = SegLOSeg(seg); + AVERT(LOSeg, loseg); + grains = loSegGrains(loseg); + + /* Whiten allocated objects; leave free areas black. */ + buffer = SegBuffer(seg); + if (buffer != NULL) { + Addr base = SegBase(seg); + Index scanLimitIndex = loIndexOfAddr(base, lo, BufferScanLimit(buffer)); + Index limitIndex = loIndexOfAddr(base, lo, BufferLimit(buffer)); + uncondemned = limitIndex - scanLimitIndex; + if (0 < scanLimitIndex) + BTCopyInvertRange(loseg->alloc, loseg->mark, 0, scanLimitIndex); + if (limitIndex < grains) + BTCopyInvertRange(loseg->alloc, loseg->mark, limitIndex, grains); + } else { + uncondemned = (Count)0; + BTCopyInvertRange(loseg->alloc, loseg->mark, 0, grains); } + PoolGenAccountForAge(&lo->pgen, LOGrainsSize(lo, loseg->newGrains - uncondemned), FALSE); + loseg->oldGrains += loseg->newGrains - uncondemned; + loseg->newGrains = uncondemned; + trace->condemned += LOGrainsSize(lo, loseg->oldGrains); + SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); + return ResOK; } @@ -752,7 +781,7 @@ static Res LOFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) } -static void LOReclaim(Pool pool, Trace trace, Seg seg) +static Bool LOReclaim(Pool pool, Trace trace, Seg seg) { LO lo; LOSeg loseg; @@ -766,7 +795,35 @@ static void LOReclaim(Pool pool, Trace trace, Seg seg) AVER(TraceSetIsMember(SegWhite(seg), trace)); loseg = SegLOSeg(seg); - loSegReclaim(loseg, trace); + return loSegReclaim(loseg, trace); +} + + +/* LOTotalSize -- total memory allocated from the arena */ + +static Size LOTotalSize(Pool pool) +{ + LO lo; + + AVERT(Pool, pool); + lo = PoolPoolLO(pool); + AVERT(LO, lo); + + return lo->pgen.totalSize; +} + + +/* LOFreeSize -- free memory (unused by client program) */ + +static Size LOFreeSize(Pool pool) +{ + LO lo; + + AVERT(Pool, pool); + lo = PoolPoolLO(pool); + AVERT(LO, lo); + + return lo->pgen.freeSize; } @@ -774,46 +831,46 @@ static void LOReclaim(Pool pool, Trace trace, Seg seg) DEFINE_POOL_CLASS(LOPoolClass, this) { - INHERIT_CLASS(this, AbstractCollectPoolClass); + INHERIT_CLASS(this, AbstractSegBufPoolClass); PoolClassMixInFormat(this); + PoolClassMixInCollect(this); this->name = "LO"; this->size = sizeof(LOStruct); this->offset = offsetof(LOStruct, poolStruct); - this->attr &= ~(AttrSCAN | AttrINCR_RB); this->varargs = LOVarargs; this->init = LOInit; this->finish = LOFinish; this->bufferFill = LOBufferFill; this->bufferEmpty = LOBufferEmpty; this->whiten = LOWhiten; - this->grey = PoolNoGrey; - this->blacken = PoolNoBlacken; - this->scan = PoolNoScan; this->fix = LOFix; this->fixEmergency = LOFix; this->reclaim = LOReclaim; this->walk = LOWalk; + this->totalSize = LOTotalSize; + this->freeSize = LOFreeSize; + AVERT(PoolClass, this); } /* mps_class_lo -- the external interface to get the LO pool class */ -mps_class_t mps_class_lo(void) +mps_pool_class_t mps_class_lo(void) { - return (mps_class_t)EnsureLOPoolClass(); + return (mps_pool_class_t)EnsureLOPoolClass(); } /* LOCheck -- check an LO pool */ +ATTRIBUTE_UNUSED static Bool LOCheck(LO lo) { CHECKS(LO, lo); - CHECKD(Pool, &lo->poolStruct); - CHECKL(lo->poolStruct.class == EnsureLOPoolClass()); + CHECKD(Pool, LOPool(lo)); + CHECKL(LOPool(lo)->class == EnsureLOPoolClass()); CHECKL(ShiftCheck(lo->alignShift)); - CHECKL((Align)1 << lo->alignShift == PoolAlignment(&lo->poolStruct)); - CHECKD(Chain, lo->chain); + CHECKL(LOGrainsSize(lo, (Count)1) == PoolAlignment(LOPool(lo))); CHECKD(PoolGen, &lo->pgen); return TRUE; } @@ -821,7 +878,7 @@ static Bool LOCheck(LO lo) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolmfs.c b/code/poolmfs.c index 3f596c5d25..40832e0f00 100644 --- a/code/poolmfs.c +++ b/code/poolmfs.c @@ -1,7 +1,7 @@ /* poolmfs.c: MANUAL FIXED SMALL UNIT POOL * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * This is the implementation of the MFS pool class. * @@ -57,25 +57,8 @@ typedef struct MFSHeaderStruct { } HeaderStruct, *Header; - #define UNIT_MIN sizeof(HeaderStruct) -MFSInfo MFSGetInfo(void) -{ - static const struct MFSInfoStruct info = - { - /* unitSizeMin */ UNIT_MIN - }; - return &info; -} - - -Pool (MFSPool)(MFS mfs) -{ - AVERT(MFS, mfs); - return &mfs->poolStruct; -} - /* MFSVarargs -- decode obsolete varargs */ @@ -86,32 +69,34 @@ static void MFSVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[1].key = MPS_KEY_MFS_UNIT_SIZE; args[1].val.size = va_arg(varargs, Size); args[2].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } -ARG_DEFINE_KEY(mfs_unit_size, Size); +ARG_DEFINE_KEY(MFS_UNIT_SIZE, Size); +ARG_DEFINE_KEY(MFSExtendSelf, Bool); static Res MFSInit(Pool pool, ArgList args) { Size extendBy = MFS_EXTEND_BY_DEFAULT; + Bool extendSelf = TRUE; Size unitSize; MFS mfs; Arena arena; ArgStruct arg; AVER(pool != NULL); - AVER(ArgListCheck(args)); + AVERT(ArgList, args); ArgRequire(&arg, args, MPS_KEY_MFS_UNIT_SIZE); unitSize = arg.val.size; if (ArgPick(&arg, args, MPS_KEY_EXTEND_BY)) extendBy = arg.val.size; - else { - if (extendBy < unitSize) - extendBy = unitSize; - } + if (ArgPick(&arg, args, MFSExtendSelf)) + extendSelf = arg.val.b; - AVER(extendBy >= unitSize); + AVER(unitSize > 0); + AVER(extendBy > 0); + AVERT(Bool, extendSelf); mfs = PoolPoolMFS(pool); arena = PoolArena(pool); @@ -121,41 +106,117 @@ static Res MFSInit(Pool pool, ArgList args) if (unitSize < UNIT_MIN) unitSize = UNIT_MIN; unitSize = SizeAlignUp(unitSize, MPS_PF_ALIGN); - extendBy = SizeAlignUp(extendBy, ArenaAlign(arena)); + /* Ensure extents have space for at least one unit and the linked + list of extents. */ + if (extendBy < unitSize + sizeof(Addr)) + extendBy = unitSize + sizeof(Addr); + extendBy = SizeArenaGrains(extendBy, arena); mfs->extendBy = extendBy; + mfs->extendSelf = extendSelf; mfs->unitSize = unitSize; - mfs->unitsPerExtent = extendBy/unitSize; mfs->freeList = NULL; - mfs->tractList = NULL; + mfs->extentList = NULL; + mfs->total = 0; + mfs->free = 0; mfs->sig = MFSSig; AVERT(MFS, mfs); - EVENT4(PoolInitMFS, pool, arena, extendBy, unitSize); + EVENT5(PoolInitMFS, pool, arena, extendBy, BOOLOF(extendSelf), unitSize); return ResOK; } +void MFSFinishExtents(Pool pool, MFSExtentVisitor visitor, + void *closureP, Size closureS) +{ + MFS mfs; + + AVERT(Pool, pool); + mfs = PoolPoolMFS(pool); + AVERT(MFS, mfs); + + while (mfs->extentList != NULL) { + Addr nextExtent = *(Addr *)AddrSub(AddrAdd(mfs->extentList, mfs->extendBy), sizeof(Addr)); /* .extent.chain */ + visitor(pool, mfs->extentList, mfs->extendBy, closureP, closureS); + mfs->extentList = nextExtent; + } +} + + +static void MFSExtentFreeVisitor(Pool pool, Addr base, Size size, + void *closureP, Size closureS) +{ + AVER(closureP == UNUSED_POINTER); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureP); + UNUSED(closureS); + ArenaFree(base, size, pool); +} + + static void MFSFinish(Pool pool) { - Tract tract; MFS mfs; AVERT(Pool, pool); mfs = PoolPoolMFS(pool); AVERT(MFS, mfs); - tract = mfs->tractList; - while(tract != NULL) { - Tract nextTract = (Tract)TractP(tract); /* .tract.chain */ - ArenaFree(TractBase(tract), mfs->extendBy, pool); - tract = nextTract; - } + MFSFinishExtents(pool, MFSExtentFreeVisitor, UNUSED_POINTER, UNUSED_SIZE); mfs->sig = SigInvalid; } +void MFSExtend(Pool pool, Addr base, Size size) +{ + MFS mfs; + Word i, unitsPerExtent; + Size unitSize; + Header header = NULL; + + AVERT(Pool, pool); + mfs = PoolPoolMFS(pool); + AVERT(MFS, mfs); + AVER(size == mfs->extendBy); + + /* Ensure that the memory we're adding belongs to this pool. This is + automatic if it was allocated using ArenaAlloc, but if the memory is + being inserted from elsewhere then it must have been set up correctly. */ + AVER(PoolHasAddr(pool, base)); + + /* .extent.chain: chain extents through pointer just below limit */ + *(Addr *)AddrSub(AddrAdd(base, size), sizeof(Addr)) = mfs->extentList; + mfs->extentList = base; + + /* Update accounting */ + mfs->total += size; + mfs->free += size; + + /* Sew together all the new empty units in the region, working down */ + /* from the top so that they are in ascending order of address on the */ + /* free list. */ + + unitSize = mfs->unitSize; + unitsPerExtent = (size - sizeof(Addr))/unitSize; + AVER(unitsPerExtent > 0); + +#define SUB(b, s, i) ((Header)AddrAdd(b, (s)*(i))) + + for(i = 0; i < unitsPerExtent; ++i) + { + header = SUB(base, unitSize, unitsPerExtent-i - 1); + AVER(AddrIsAligned(header, pool->alignment)); + AVER(AddrAdd((Addr)header, unitSize) <= AddrAdd(base, size)); + header->next = mfs->freeList; + mfs->freeList = header; + } + +#undef SUB +} + + /* == Allocate == * * Allocation simply involves taking a unit from the front of the freelist @@ -176,7 +237,7 @@ static Res MFSAlloc(Addr *pReturn, Pool pool, Size size, AVER(pReturn != NULL); AVER(size == mfs->unroundedUnitSize); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); f = mfs->freeList; @@ -184,46 +245,22 @@ static Res MFSAlloc(Addr *pReturn, Pool pool, Size size, if(f == NULL) { - Tract tract; - Word i, unitsPerExtent; - Size unitSize; Addr base; - Header header = NULL, next; + + /* See design.mps.bootstrap.land.sol.pool. */ + if (!mfs->extendSelf) + return ResLIMIT; /* Create a new region and attach it to the pool. */ - res = ArenaAlloc(&base, SegPrefDefault(), mfs->extendBy, pool, + res = ArenaAlloc(&base, LocusPrefDefault(), mfs->extendBy, pool, withReservoirPermit); if(res != ResOK) return res; - /* .tract.chain: chain first tracts through TractP(tract) */ - tract = TractOfBaseAddr(PoolArena(pool), base); - TractSetP(tract, (void *)mfs->tractList); - mfs->tractList = tract; - - /* Sew together all the new empty units in the region, working down */ - /* from the top so that they are in ascending order of address on the */ - /* free list. */ - - unitsPerExtent = mfs->unitsPerExtent; - unitSize = mfs->unitSize; - next = NULL; - -#define SUB(b, s, i) ((Header)AddrAdd(b, (s)*(i))) - - for(i=0; ialignment)); - AVER(AddrAdd((Addr)header, unitSize) <= AddrAdd(base, mfs->extendBy)); - header->next = next; - next = header; - } - -#undef SUB + MFSExtend(pool, base, mfs->extendBy); /* The first unit in the region is now the head of the new free list. */ - f = header; + f = mfs->freeList; } AVER(f != NULL); @@ -231,6 +268,8 @@ static Res MFSAlloc(Addr *pReturn, Pool pool, Size size, /* Detach the first free unit from the free list and return its address. */ mfs->freeList = f->next; + AVER(mfs->free >= mfs->unitSize); + mfs->free -= mfs->unitSize; *pReturn = (Addr)f; return ResOK; @@ -259,10 +298,39 @@ static void MFSFree(Pool pool, Addr old, Size size) h = (Header)old; h->next = mfs->freeList; mfs->freeList = h; + mfs->free += mfs->unitSize; +} + + +/* MFSTotalSize -- total memory allocated from the arena */ + +static Size MFSTotalSize(Pool pool) +{ + MFS mfs; + + AVERT(Pool, pool); + mfs = PoolPoolMFS(pool); + AVERT(MFS, mfs); + + return mfs->total; +} + + +/* MFSFreeSize -- free memory (unused by client program) */ + +static Size MFSFreeSize(Pool pool) +{ + MFS mfs; + + AVERT(Pool, pool); + mfs = PoolPoolMFS(pool); + AVERT(MFS, mfs); + + return mfs->free; } -static Res MFSDescribe(Pool pool, mps_lib_FILE *stream) +static Res MFSDescribe(Pool pool, mps_lib_FILE *stream, Count depth) { MFS mfs; Res res; @@ -273,15 +341,18 @@ static Res MFSDescribe(Pool pool, mps_lib_FILE *stream) AVER(stream != NULL); - res = WriteF(stream, - " unrounded unit size $W\n", (WriteFW)mfs->unroundedUnitSize, - " unit size $W\n", (WriteFW)mfs->unitSize, - " extent size $W\n", (WriteFW)mfs->extendBy, - " units per extent $U\n", (WriteFU)mfs->unitsPerExtent, - " free list begins at $P\n", (WriteFP)mfs->freeList, - " tract list begin at $P\n", (WriteFP)mfs->tractList, + res = WriteF(stream, depth, + "unroundedUnitSize $W\n", (WriteFW)mfs->unroundedUnitSize, + "extendBy $W\n", (WriteFW)mfs->extendBy, + "extendSelf $S\n", WriteFYesNo(mfs->extendSelf), + "unitSize $W\n", (WriteFW)mfs->unitSize, + "freeList $P\n", (WriteFP)mfs->freeList, + "total $W\n", (WriteFW)mfs->total, + "free $W\n", (WriteFW)mfs->free, + "extentList $P\n", (WriteFP)mfs->extentList, NULL); - if(res != ResOK) return res; + if (res != ResOK) + return res; return ResOK; } @@ -289,7 +360,7 @@ static Res MFSDescribe(Pool pool, mps_lib_FILE *stream) DEFINE_POOL_CLASS(MFSPoolClass, this) { - INHERIT_CLASS(this, AbstractAllocFreePoolClass); + INHERIT_CLASS(this, AbstractPoolClass); this->name = "MFS"; this->size = sizeof(MFSStruct); this->offset = offsetof(MFSStruct, poolStruct); @@ -298,7 +369,10 @@ DEFINE_POOL_CLASS(MFSPoolClass, this) this->finish = MFSFinish; this->alloc = MFSAlloc; this->free = MFSFree; + this->totalSize = MFSTotalSize; + this->freeSize = MFSFreeSize; this->describe = MFSDescribe; + AVERT(PoolClass, this); } @@ -308,9 +382,9 @@ PoolClass PoolClassMFS(void) } -mps_class_t mps_class_mfs(void) +mps_pool_class_t mps_class_mfs(void) { - return (mps_class_t)PoolClassMFS(); + return (mps_pool_class_t)PoolClassMFS(); } @@ -319,25 +393,25 @@ Bool MFSCheck(MFS mfs) Arena arena; CHECKS(MFS, mfs); - CHECKD(Pool, &mfs->poolStruct); - CHECKL(mfs->poolStruct.class == EnsureMFSPoolClass()); - CHECKL(mfs->unroundedUnitSize >= UNIT_MIN); + CHECKD(Pool, MFSPool(mfs)); + CHECKL(MFSPool(mfs)->class == EnsureMFSPoolClass()); + CHECKL(mfs->unitSize >= UNIT_MIN); CHECKL(mfs->extendBy >= UNIT_MIN); - arena = PoolArena(&mfs->poolStruct); - CHECKL(SizeIsAligned(mfs->extendBy, ArenaAlign(arena))); - CHECKL(SizeAlignUp(mfs->unroundedUnitSize, mfs->poolStruct.alignment) == + CHECKL(BoolCheck(mfs->extendSelf)); + arena = PoolArena(MFSPool(mfs)); + CHECKL(SizeIsArenaGrains(mfs->extendBy, arena)); + CHECKL(SizeAlignUp(mfs->unroundedUnitSize, PoolAlignment(MFSPool(mfs))) == mfs->unitSize); - CHECKL(mfs->unitsPerExtent == mfs->extendBy/mfs->unitSize); - if(mfs->tractList != NULL) { - CHECKL(TractCheck(mfs->tractList)); - } + /* Can't check extentList. */ + CHECKL(mfs->free <= mfs->total); + CHECKL((mfs->total - mfs->free) % mfs->unitSize == 0); return TRUE; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolmfs.h b/code/poolmfs.h index 467743ce8e..7b700893d6 100644 --- a/code/poolmfs.h +++ b/code/poolmfs.h @@ -2,13 +2,13 @@ * * $Id$ * - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * The MFS pool is used to manage small fixed-size chunks of memory. It * stores control structures in the memory it manages, rather than to one * side. It therefore achieves better locality for small objects, but * wastes memory for large objects. It should not be used unless you are - * packing a reasonable number of objects on to a page. + * packing a reasonable number of objects into an arena grain. * * Create and Init take the following arguments: * @@ -33,26 +33,29 @@ typedef struct MFSStruct *MFS; +#define MFSPool(mfs) (&(mfs)->poolStruct) + extern PoolClass PoolClassMFS(void); extern Bool MFSCheck(MFS mfs); -extern Pool (MFSPool)(MFS mfs); - -typedef const struct MFSInfoStruct *MFSInfo; +extern const struct mps_key_s _mps_key_MFSExtendSelf; +#define MFSExtendSelf (&_mps_key_MFSExtendSelf) +#define MFSExtendSelf_FIELD b -struct MFSInfoStruct { - Size unitSizeMin; /* minimum unit size */ -}; +extern void MFSExtend(Pool pool, Addr base, Size size); -extern MFSInfo MFSGetInfo(void); +typedef void MFSExtentVisitor(Pool pool, Addr base, Size size, + void *closureP, Size closureS); +extern void MFSFinishExtents(Pool pool, MFSExtentVisitor visitor, + void *closureP, Size closureS); #endif /* poolmfs_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolmrg.c b/code/poolmrg.c index c945029a9d..2cb5e7a2f2 100644 --- a/code/poolmrg.c +++ b/code/poolmrg.c @@ -1,7 +1,7 @@ /* poolmrg.c: MANUAL RANK GUARDIAN POOL * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * @@ -119,21 +119,22 @@ typedef struct MRGStruct { Sig sig; /* */ } MRGStruct; -#define Pool2MRG(pool) PARENT(MRGStruct, poolStruct, pool) -#define MRG2Pool(mrg) (&(mrg)->poolStruct) +#define PoolMRG(pool) PARENT(MRGStruct, poolStruct, pool) +#define MRGPool(mrg) (&(mrg)->poolStruct) /* MRGCheck -- check an MRG pool */ +ATTRIBUTE_UNUSED static Bool MRGCheck(MRG mrg) { CHECKS(MRG, mrg); - CHECKD(Pool, &mrg->poolStruct); - CHECKL(MRG2Pool(mrg)->class == PoolClassMRG()); - CHECKL(RingCheck(&mrg->entryRing)); - CHECKL(RingCheck(&mrg->freeRing)); - CHECKL(RingCheck(&mrg->refRing)); - CHECKL(mrg->extendBy == ArenaAlign(PoolArena(MRG2Pool(mrg)))); + CHECKD(Pool, MRGPool(mrg)); + CHECKL(MRGPool(mrg)->class == PoolClassMRG()); + CHECKD_NOSIG(Ring, &mrg->entryRing); + CHECKD_NOSIG(Ring, &mrg->freeRing); + CHECKD_NOSIG(Ring, &mrg->refRing); + CHECKL(mrg->extendBy == ArenaGrainSize(PoolArena(MRGPool(mrg)))); return TRUE; } @@ -178,12 +179,14 @@ extern SegClass MRGRefSegClassGet(void); * field will be NULL. This will be initialized when the reference * segment is initialized. See . */ + +ATTRIBUTE_UNUSED static Bool MRGLinkSegCheck(MRGLinkSeg linkseg) { Seg seg; CHECKS(MRGLinkSeg, linkseg); - CHECKL(SegCheck(&linkseg->segStruct)); + CHECKD(Seg, &linkseg->segStruct); seg = LinkSeg2Seg(linkseg); if (NULL != linkseg->refSeg) { /* see .link.nullref */ CHECKL(SegPool(seg) == SegPool(RefSeg2Seg(linkseg->refSeg))); @@ -193,15 +196,16 @@ static Bool MRGLinkSegCheck(MRGLinkSeg linkseg) return TRUE; } +ATTRIBUTE_UNUSED static Bool MRGRefSegCheck(MRGRefSeg refseg) { Seg seg; CHECKS(MRGRefSeg, refseg); - CHECKL(GCSegCheck(&refseg->gcSegStruct)); + CHECKD(GCSeg, &refseg->gcSegStruct); seg = RefSeg2Seg(refseg); CHECKL(SegPool(seg) == SegPool(LinkSeg2Seg(refseg->linkSeg))); - CHECKL(RingCheck(&refseg->mrgRing)); + CHECKD_NOSIG(Ring, &refseg->mrgRing); CHECKD(MRGLinkSeg, refseg->linkSeg); CHECKL(refseg->linkSeg->refSeg == refseg); return TRUE; @@ -221,10 +225,10 @@ static Res MRGLinkSegInit(Seg seg, Pool pool, Addr base, Size size, AVERT(Seg, seg); linkseg = Seg2LinkSeg(seg); AVERT(Pool, pool); - mrg = Pool2MRG(pool); + mrg = PoolMRG(pool); AVERT(MRG, mrg); /* no useful checks for base and size */ - AVER(BoolCheck(reservoirPermit)); + AVERT(Bool, reservoirPermit); /* Initialize the superclass fields first via next-method call */ super = SEG_SUPERCLASS(MRGLinkSegClass); @@ -264,10 +268,10 @@ static Res MRGRefSegInit(Seg seg, Pool pool, Addr base, Size size, AVERT(Seg, seg); refseg = Seg2RefSeg(seg); AVERT(Pool, pool); - mrg = Pool2MRG(pool); + mrg = PoolMRG(pool); AVERT(MRG, mrg); /* no useful checks for base and size */ - AVER(BoolCheck(reservoirPermit)); + AVERT(Bool, reservoirPermit); AVERT(MRGLinkSeg, linkseg); /* Initialize the superclass fields first via next-method call */ @@ -302,6 +306,7 @@ DEFINE_SEG_CLASS(MRGLinkSegClass, class) class->name = "MRGLSEG"; class->size = sizeof(MRGLinkSegStruct); class->init = MRGLinkSegInit; + AVERT(SegClass, class); } @@ -314,6 +319,7 @@ DEFINE_SEG_CLASS(MRGRefSegClass, class) class->name = "MRGRSEG"; class->size = sizeof(MRGRefSegStruct); class->init = MRGRefSegInit; + AVERT(SegClass, class); } @@ -354,7 +360,7 @@ static RefPart MRGRefPartOfLink(Link link, Arena arena) linkBase = (Link)SegBase(seg); AVER(link >= linkBase); indx = (Index)(link - linkBase); - AVER(indx < MRGGuardiansPerSeg(Pool2MRG(SegPool(seg)))); + AVER(indx < MRGGuardiansPerSeg(PoolMRG(SegPool(seg)))); return refPartOfIndex(linkseg->refSeg, indx); } @@ -383,7 +389,7 @@ static Link MRGLinkOfRefPart(RefPart refPart, Arena arena) refPartBase = (RefPart)SegBase(seg); AVER(refPart >= refPartBase); indx = refPart - refPartBase; - AVER(indx < MRGGuardiansPerSeg(Pool2MRG(SegPool(seg)))); + AVER(indx < MRGGuardiansPerSeg(PoolMRG(SegPool(seg)))); return linkOfIndex(refseg->linkSeg, indx); } @@ -402,7 +408,7 @@ static void MRGGuardianInit(MRG mrg, Link link, RefPart refPart) link->state = MRGGuardianFREE; RingAppend(&mrg->freeRing, &link->the.linkRing); /* */ - MRGRefPartSetRef(PoolArena(&mrg->poolStruct), refPart, 0); + MRGRefPartSetRef(PoolArena(MRGPool(mrg)), refPart, 0); } @@ -428,7 +434,7 @@ static void MRGMessageDelete(Message message) link = linkOfMessage(message); AVER(link->state == MRGGuardianFINAL); MessageFinish(message); - MRGGuardianInit(Pool2MRG(pool), link, MRGRefPartOfLink(link, arena)); + MRGGuardianInit(PoolMRG(pool), link, MRGRefPartOfLink(link, arena)); } @@ -510,15 +516,15 @@ static Res MRGSegPairCreate(MRGRefSeg *refSegReturn, MRG mrg, AVER(refSegReturn != NULL); - pool = MRG2Pool(mrg); + pool = MRGPool(mrg); arena = PoolArena(pool); nGuardians = MRGGuardiansPerSeg(mrg); linkSegSize = nGuardians * sizeof(LinkStruct); - linkSegSize = SizeAlignUp(linkSegSize, ArenaAlign(arena)); + linkSegSize = SizeArenaGrains(linkSegSize, arena); res = SegAlloc(&segLink, EnsureMRGLinkSegClass(), - SegPrefDefault(), linkSegSize, pool, + LocusPrefDefault(), linkSegSize, pool, withReservoirPermit, argsNone); if (res != ResOK) goto failLinkSegAlloc; @@ -527,7 +533,7 @@ static Res MRGSegPairCreate(MRGRefSeg *refSegReturn, MRG mrg, MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD_FIELD(args, mrgKeyLinkSeg, p, linkseg); /* .ref.initarg */ res = SegAlloc(&segRefPart, EnsureMRGRefSegClass(), - SegPrefDefault(), mrg->extendBy, pool, + LocusPrefDefault(), mrg->extendBy, pool, withReservoirPermit, args); } MPS_ARGS_END(args); if (res != ResOK) @@ -560,7 +566,7 @@ static void MRGFinalize(Arena arena, MRGLinkSeg linkseg, Index indx) Link link; Message message; - AVER(indx < MRGGuardiansPerSeg(Pool2MRG(SegPool(LinkSeg2Seg(linkseg))))); + AVER(indx < MRGGuardiansPerSeg(PoolMRG(SegPool(LinkSeg2Seg(linkseg))))); link = linkOfIndex(linkseg, indx); @@ -591,7 +597,7 @@ static Res MRGRefSegScan(ScanState ss, MRGRefSeg refseg, MRG mrg) AVERT(MRGRefSeg, refseg); AVERT(MRG, mrg); - arena = PoolArena(MRG2Pool(mrg)); + arena = PoolArena(MRGPool(mrg)); linkseg = refseg->linkSeg; nGuardians = MRGGuardiansPerSeg(mrg); @@ -629,15 +635,15 @@ static Res MRGInit(Pool pool, ArgList args) MRG mrg; AVER(pool != NULL); /* Can't check more; see pool contract @@@@ */ - AVER(ArgListCheck(args)); + AVERT(ArgList, args); UNUSED(args); - mrg = Pool2MRG(pool); + mrg = PoolMRG(pool); RingInit(&mrg->entryRing); RingInit(&mrg->freeRing); RingInit(&mrg->refRing); - mrg->extendBy = ArenaAlign(PoolArena(pool)); + mrg->extendBy = ArenaGrainSize(PoolArena(pool)); mrg->sig = MRGSig; AVERT(MRG, mrg); @@ -654,7 +660,7 @@ static void MRGFinish(Pool pool) Ring node, nextNode; AVERT(Pool, pool); - mrg = Pool2MRG(pool); + mrg = PoolMRG(pool); AVERT(MRG, mrg); /* .finish.ring: Before destroying the segments, we isolate the */ @@ -708,7 +714,7 @@ Res MRGRegister(Pool pool, Ref ref) AVERT(Pool, pool); AVER(ref != 0); - mrg = Pool2MRG(pool); + mrg = PoolMRG(pool); AVERT(MRG, mrg); arena = PoolArena(pool); @@ -739,7 +745,12 @@ Res MRGRegister(Pool pool, Ref ref) } -/* MRGDeregister -- deregister (once) an object for finalization */ +/* MRGDeregister -- deregister (once) an object for finalization + * + * TODO: Definalization loops over all finalizable objects in the heap, + * and so using it could accidentally be disastrous for performance. + * See job003953 and back out changelist 187123 if this is fixed. + */ Res MRGDeregister(Pool pool, Ref obj) { @@ -751,7 +762,7 @@ Res MRGDeregister(Pool pool, Ref obj) AVERT(Pool, pool); /* Can't check obj */ - mrg = Pool2MRG(pool); + mrg = PoolMRG(pool); AVERT(MRG, mrg); nGuardians = MRGGuardiansPerSeg(mrg); arena = PoolArena(pool); @@ -790,7 +801,7 @@ Res MRGDeregister(Pool pool, Ref obj) * This could be improved by implementing MRGSegDescribe * and having MRGDescribe iterate over all the pool's segments. */ -static Res MRGDescribe(Pool pool, mps_lib_FILE *stream) +static Res MRGDescribe(Pool pool, mps_lib_FILE *stream, Count depth) { MRG mrg; Arena arena; @@ -798,22 +809,35 @@ static Res MRGDescribe(Pool pool, mps_lib_FILE *stream) RefPart refPart; Res res; - if (!TESTT(Pool, pool)) return ResFAIL; - mrg = Pool2MRG(pool); - if (!TESTT(MRG, mrg)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Pool, pool)) + return ResFAIL; + mrg = PoolMRG(pool); + if (!TESTT(MRG, mrg)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; arena = PoolArena(pool); - res = WriteF(stream, " extendBy $W\n", mrg->extendBy, NULL); - if (res != ResOK) return res; - res = WriteF(stream, " Entry queue:\n", NULL); - if (res != ResOK) return res; + res = WriteF(stream, depth, "extendBy $W\n", (WriteFW)mrg->extendBy, NULL); + if (res != ResOK) + return res; + res = WriteF(stream, depth, "Entry queue:\n", NULL); + if (res != ResOK) + return res; RING_FOR(node, &mrg->entryRing, nextNode) { + Bool outsideShield = !arena->insideShield; refPart = MRGRefPartOfLink(linkOfRing(node), arena); - res = WriteF(stream, " at $A Ref $A\n", + if (outsideShield) { + ShieldEnter(arena); + } + res = WriteF(stream, depth, "at $A Ref $A\n", (WriteFA)refPart, (WriteFA)MRGRefPartRef(arena, refPart), NULL); - if (res != ResOK) return res; + if (outsideShield) { + ShieldLeave(arena); + } + if (res != ResOK) + return res; } return ResOK; @@ -830,7 +854,7 @@ static Res MRGScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) AVERT(Pool, pool); AVERT(Seg, seg); - mrg = Pool2MRG(pool); + mrg = PoolMRG(pool); AVERT(MRG, mrg); AVER(SegRankSet(seg) == RankSetSingle(RankFINAL)); /* .improve.rank */ @@ -855,13 +879,13 @@ DEFINE_POOL_CLASS(MRGPoolClass, this) this->name = "MRG"; this->size = sizeof(MRGStruct); this->offset = offsetof(MRGStruct, poolStruct); - this->attr |= (AttrSCAN | AttrFREE | AttrINCR_RB); this->init = MRGInit; this->finish = MRGFinish; this->grey = PoolTrivGrey; this->blacken = PoolTrivBlacken; this->scan = MRGScan; this->describe = MRGDescribe; + AVERT(PoolClass, this); } @@ -873,7 +897,7 @@ PoolClass PoolClassMRG(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolmv.c b/code/poolmv.c index 9acd64380f..19b3c2bb44 100644 --- a/code/poolmv.c +++ b/code/poolmv.c @@ -1,7 +1,7 @@ /* poolmv.c: MANUAL VARIABLE POOL * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * **** RESTRICTION: This pool may not allocate from the arena control @@ -38,7 +38,7 @@ SRCID(poolmv, "$Id$"); #define mvSpanPool(mv) MFSPool(&(mv)->spanPoolStruct) -#define Pool2MV(pool) PARENT(MVStruct, poolStruct, pool) +#define PoolMV(pool) PARENT(MVStruct, poolStruct, pool) /* MVDebug -- MV Debug pool class */ @@ -72,6 +72,7 @@ typedef struct MVBlockStruct { /* MVBlockCheck -- check the consistency of a block structure */ +ATTRIBUTE_UNUSED static Bool MVBlockCheck(MVBlock block) { AVER(block != NULL); @@ -115,7 +116,7 @@ typedef struct MVSpanStruct { MVBlockStruct base; /* sentinel at base of span */ MVBlockStruct limit; /* sentinel at limit of span */ MVBlock blocks; /* allocated blocks */ - Size space; /* total free space in span */ + Size free; /* free space in span */ Size largest; /* .design.largest */ Bool largestKnown; /* .design.largest */ unsigned blockCount; /* number of blocks on chain */ @@ -130,19 +131,18 @@ typedef struct MVSpanStruct { /* MVSpanCheck -- check the consistency of a span structure */ +ATTRIBUTE_UNUSED static Bool MVSpanCheck(MVSpan span) { - Addr addr, base, limit; - Arena arena; - Tract tract; + Addr base, limit; CHECKS(MVSpan, span); - CHECKL(RingCheck(&span->spans)); + CHECKD_NOSIG(Ring, &span->spans); CHECKU(MV, span->mv); CHECKD_NOSIG(Tract, span->tract); - CHECKL(MVBlockCheck(&span->base)); - CHECKL(MVBlockCheck(&span->limit)); + CHECKD_NOSIG(MVBlock, &span->base); + CHECKD_NOSIG(MVBlock, &span->limit); /* The block chain starts with the base sentinel. */ CHECKL(span->blocks == &span->base); /* Since there is a limit sentinel, the chain can't end just after the */ @@ -160,23 +160,32 @@ static Bool MVSpanCheck(MVSpan span) /* The sentinels mustn't overlap. */ CHECKL(span->base.limit <= span->limit.base); /* The free space can't be more than the gap between the sentinels. */ - CHECKL(span->space <= SpanInsideSentinels(span)); + CHECKL(span->free <= SpanInsideSentinels(span)); CHECKL(BoolCheck(span->largestKnown)); if (span->largestKnown) { /* .design.largest */ - CHECKL(span->largest <= span->space); + CHECKL(span->largest <= span->free); /* at least this much is free */ } else { CHECKL(span->largest == SpanSize(span)+1); } - /* Each tract of the span must refer to the span */ - arena = PoolArena(TractPool(span->tract)); - TRACT_FOR(tract, addr, arena, base, limit) { - CHECKD_NOSIG(Tract, tract); - CHECKL(TractP(tract) == (void *)span); + /* Note that even if the CHECKs are compiled away there is still a + * significant cost in looping over the tracts, hence this guard. */ +#if defined(AVER_AND_CHECK_ALL) + { + Addr addr; + Arena arena; + Tract tract; + /* Each tract of the span must refer to the span */ + arena = PoolArena(TractPool(span->tract)); + TRACT_FOR(tract, addr, arena, base, limit) { + CHECKD_NOSIG(Tract, tract); + CHECKL(TractP(tract) == (void *)span); + } + CHECKL(addr == limit); } - CHECKL(addr == limit); +#endif return TRUE; } @@ -193,7 +202,7 @@ static void MVVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[2].key = MPS_KEY_MAX_SIZE; args[2].val.size = va_arg(varargs, Size); args[3].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } static void MVDebugVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) @@ -208,6 +217,7 @@ static void MVDebugVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) static Res MVInit(Pool pool, ArgList args) { + Align align = MV_ALIGN_DEFAULT; Size extendBy = MV_EXTEND_BY_DEFAULT; Size avgSize = MV_AVG_SIZE_DEFAULT; Size maxSize = MV_MAX_SIZE_DEFAULT; @@ -217,6 +227,8 @@ static Res MVInit(Pool pool, ArgList args) Res res; ArgStruct arg; + if (ArgPick(&arg, args, MPS_KEY_ALIGN)) + align = arg.val.align; if (ArgPick(&arg, args, MPS_KEY_EXTEND_BY)) extendBy = arg.val.size; if (ArgPick(&arg, args, MPS_KEY_MEAN_SIZE)) @@ -224,13 +236,15 @@ static Res MVInit(Pool pool, ArgList args) if (ArgPick(&arg, args, MPS_KEY_MAX_SIZE)) maxSize = arg.val.size; + AVERT(Align, align); AVER(extendBy > 0); AVER(avgSize > 0); AVER(avgSize <= extendBy); AVER(maxSize > 0); AVER(extendBy <= maxSize); - mv = Pool2MV(pool); + pool->alignment = align; + mv = PoolMV(pool); arena = PoolArena(pool); /* At 100% fragmentation we will need one block descriptor for every other */ @@ -243,33 +257,38 @@ static Res MVInit(Pool pool, ArgList args) MPS_ARGS_BEGIN(piArgs) { MPS_ARGS_ADD(piArgs, MPS_KEY_EXTEND_BY, blockExtendBy); MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(MVBlockStruct)); - res = PoolInit(&mv->blockPoolStruct.poolStruct, arena, PoolClassMFS(), piArgs); + res = PoolInit(mvBlockPool(mv), arena, PoolClassMFS(), piArgs); } MPS_ARGS_END(piArgs); if(res != ResOK) - return res; + goto failBlockPoolInit; spanExtendBy = sizeof(MVSpanStruct) * (maxSize/extendBy); MPS_ARGS_BEGIN(piArgs) { MPS_ARGS_ADD(piArgs, MPS_KEY_EXTEND_BY, spanExtendBy); MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(MVSpanStruct)); - res = PoolInit(&mv->spanPoolStruct.poolStruct, arena, PoolClassMFS(), piArgs); + res = PoolInit(mvSpanPool(mv), arena, PoolClassMFS(), piArgs); } MPS_ARGS_END(piArgs); if(res != ResOK) - return res; + goto failSpanPoolInit; mv->extendBy = extendBy; mv->avgSize = avgSize; mv->maxSize = maxSize; RingInit(&mv->spans); - mv->space = 0; + mv->free = 0; mv->lost = 0; mv->sig = MVSig; AVERT(MV, mv); EVENT5(PoolInitMV, pool, arena, extendBy, avgSize, maxSize); return ResOK; + +failSpanPoolInit: + PoolFinish(mvBlockPool(mv)); +failBlockPoolInit: + return res; } @@ -282,7 +301,7 @@ static void MVFinish(Pool pool) MVSpan span; AVERT(Pool, pool); - mv = Pool2MV(pool); + mv = PoolMV(pool); AVERT(MV, mv); /* Destroy all the spans attached to the pool. */ @@ -295,8 +314,8 @@ static void MVFinish(Pool pool) mv->sig = SigInvalid; - PoolFinish(&mv->blockPoolStruct.poolStruct); - PoolFinish(&mv->spanPoolStruct.poolStruct); + PoolFinish(mvBlockPool(mv)); + PoolFinish(mvSpanPool(mv)); } @@ -354,7 +373,7 @@ static Bool MVSpanAlloc(Addr *addrReturn, MVSpan span, Size size, span->largest = SpanSize(span) + 1; /* .design.largest */ } - span->space -= size; + span->free -= size; *addrReturn = new; return TRUE; } @@ -441,7 +460,8 @@ static Res MVSpanFree(MVSpan span, Addr base, Addr limit, Pool blockPool) /* block must be split into two parts. */ res = PoolAlloc(&addr, blockPool, sizeof(MVBlockStruct), /* withReservoirPermit */ FALSE); - if(res != ResOK) return res; + if (res != ResOK) + return res; new = (MVBlock)addr; freeAreaSize = AddrOffset(base, limit); @@ -470,7 +490,7 @@ static Res MVSpanFree(MVSpan span, Addr base, Addr limit, Pool blockPool) AVERT(MVBlock, block); - span->space += AddrOffset(base, limit); + span->free += AddrOffset(base, limit); if (freeAreaSize > span->largest) { /* .design.largest */ AVER(span->largestKnown); @@ -507,23 +527,23 @@ static Res MVAlloc(Addr *pReturn, Pool pool, Size size, AVER(pReturn != NULL); AVERT(Pool, pool); - mv = Pool2MV(pool); + mv = PoolMV(pool); AVERT(MV, mv); AVER(size > 0); AVERT(Bool, withReservoirPermit); size = SizeAlignUp(size, pool->alignment); - if(size <= mv->space) { + if(size <= mv->free) { spans = &mv->spans; RING_FOR(node, spans, nextNode) { span = RING_ELT(MVSpan, spans, node); if((size <= span->largest) && /* .design.largest.alloc */ - (size <= span->space)) { + (size <= span->free)) { Addr new; if(MVSpanAlloc(&new, span, size, mvBlockPool(mv))) { - mv->space -= size; + mv->free -= size; AVER(AddrIsAligned(new, pool->alignment)); *pReturn = new; return ResOK; @@ -548,13 +568,13 @@ static Res MVAlloc(Addr *pReturn, Pool pool, Size size, regionSize = size; arena = PoolArena(pool); - regionSize = SizeAlignUp(regionSize, ArenaAlign(arena)); + regionSize = SizeArenaGrains(regionSize, arena); - res = ArenaAlloc(&base, SegPrefDefault(), regionSize, pool, + res = ArenaAlloc(&base, LocusPrefDefault(), regionSize, pool, withReservoirPermit); if(res != ResOK) { /* try again with a region big enough for this object */ - regionSize = SizeAlignUp(size, ArenaAlign(arena)); - res = ArenaAlloc(&base, SegPrefDefault(), regionSize, pool, + regionSize = SizeArenaGrains(size, arena); + res = ArenaAlloc(&base, LocusPrefDefault(), regionSize, pool, withReservoirPermit); if (res != ResOK) { PoolFree(mvSpanPool(mv), (Addr)span, sizeof(MVSpanStruct)); @@ -570,7 +590,7 @@ static Res MVAlloc(Addr *pReturn, Pool pool, Size size, span->mv = mv; /* Set the p field for each tract of the span */ TRACT_FOR(tract, addr, arena, base, limit) { - AVER(TractCheck(tract)); + AVERT(Tract, tract); AVER(TractP(tract) == NULL); AVER(TractPool(tract) == pool); TractSetP(tract, (void *)span); @@ -579,20 +599,20 @@ static Res MVAlloc(Addr *pReturn, Pool pool, Size size, RingInit(&span->spans); span->base.base = span->base.limit = base; span->limit.base = span->limit.limit = limit; - span->space = AddrOffset(span->base.limit, span->limit.base); + span->free = AddrOffset(span->base.limit, span->limit.base); span->limit.next = NULL; span->base.next = &span->limit; span->blocks = &span->base; span->blockCount = 2; span->base.limit = AddrAdd(span->base.limit, size); - span->space -= size; - span->largest = span->space; + span->free -= size; + span->largest = span->free; span->largestKnown = TRUE; span->sig = MVSpanSig; AVERT(MVSpan, span); - mv->space += span->space; + mv->free += span->free; RingInsert(&mv->spans, &span->spans); /* use RingInsert so that we examine this new span first when allocating */ @@ -613,10 +633,11 @@ static void MVFree(Pool pool, Addr old, Size size) Tract tract = NULL; /* suppress "may be used uninitialized" */ AVERT(Pool, pool); - mv = Pool2MV(pool); + mv = PoolMV(pool); AVERT(MV, mv); AVER(old != (Addr)0); + AVER(AddrIsAligned(old, pool->alignment)); AVER(size > 0); size = SizeAlignUp(size, pool->alignment); @@ -640,16 +661,16 @@ static void MVFree(Pool pool, Addr old, Size size) if(res != ResOK) mv->lost += size; else - mv->space += size; + mv->free += size; /* free space should be less than total space */ - AVER(span->space <= SpanInsideSentinels(span)); - if(span->space == SpanSize(span)) { /* the whole span is free */ + AVER(span->free <= SpanInsideSentinels(span)); + if(span->free == SpanSize(span)) { /* the whole span is free */ AVER(span->blockCount == 2); /* both blocks are the trivial sentinel blocks */ AVER(span->base.limit == span->base.base); AVER(span->limit.limit == span->limit.base); - mv->space -= span->space; + mv->free -= span->free; ArenaFree(TractBase(span->tract), span->size, pool); RingRemove(&span->spans); RingFinish(&span->spans); @@ -665,14 +686,59 @@ static PoolDebugMixin MVDebugMixin(Pool pool) MV mv; AVERT(Pool, pool); - mv = Pool2MV(pool); + mv = PoolMV(pool); AVERT(MV, mv); /* Can't check MVDebug, because this is called during MVDebug init */ return &(MV2MVDebug(mv)->debug); } -static Res MVDescribe(Pool pool, mps_lib_FILE *stream) +/* MVTotalSize -- total memory allocated from the arena */ + +static Size MVTotalSize(Pool pool) +{ + MV mv; + Size size = 0; + Ring node, next; + + AVERT(Pool, pool); + mv = PoolMV(pool); + AVERT(MV, mv); + + RING_FOR(node, &mv->spans, next) { + MVSpan span = RING_ELT(MVSpan, spans, node); + AVERT(MVSpan, span); + size += span->size; + } + + return size; +} + + +/* MVFreeSize -- free memory (unused by client program) */ + +static Size MVFreeSize(Pool pool) +{ + MV mv; + Size size = 0; + Ring node, next; + + AVERT(Pool, pool); + mv = PoolMV(pool); + AVERT(MV, mv); + + RING_FOR(node, &mv->spans, next) { + MVSpan span = RING_ELT(MVSpan, spans, node); + AVERT(MVSpan, span); + size += span->free; + } + + AVER(size == mv->free + mv->lost); + return size; +} + + +static Res MVDescribe(Pool pool, mps_lib_FILE *stream, Count depth) { Res res; MV mv; @@ -682,67 +748,62 @@ static Res MVDescribe(Pool pool, mps_lib_FILE *stream) char c; Ring spans, node = NULL, nextNode; /* gcc whinge stop */ - if(!TESTT(Pool, pool)) return ResFAIL; - mv = Pool2MV(pool); - if(!TESTT(MV, mv)) return ResFAIL; - if(stream == NULL) return ResFAIL; + if (!TESTT(Pool, pool)) + return ResFAIL; + mv = PoolMV(pool); + if (!TESTT(MV, mv)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = WriteF(stream, - " blockPool $P ($U)\n", + res = WriteF(stream, depth, + "blockPool $P ($U)\n", (WriteFP)mvBlockPool(mv), (WriteFU)mvBlockPool(mv)->serial, - " spanPool $P ($U)\n", + "spanPool $P ($U)\n", (WriteFP)mvSpanPool(mv), (WriteFU)mvSpanPool(mv)->serial, - " extendBy $W\n", (WriteFW)mv->extendBy, - " avgSize $W\n", (WriteFW)mv->avgSize, - " maxSize $W\n", (WriteFW)mv->maxSize, - " space $P\n", (WriteFP)mv->space, + "extendBy $W\n", (WriteFW)mv->extendBy, + "avgSize $W\n", (WriteFW)mv->avgSize, + "maxSize $W\n", (WriteFW)mv->maxSize, + "free $W\n", (WriteFP)mv->free, + "lost $W\n", (WriteFP)mv->lost, NULL); if(res != ResOK) return res; - res = WriteF(stream, " Spans\n", NULL); - if(res != ResOK) return res; + step = pool->alignment; + length = 0x40 * step; spans = &mv->spans; RING_FOR(node, spans, nextNode) { + Addr i, j; + MVBlock block; span = RING_ELT(MVSpan, spans, node); - AVERT(MVSpan, span); + res = WriteF(stream, depth, "MVSpan $P {\n", (WriteFP)span, NULL); + if (res != ResOK) + return res; - res = WriteF(stream, - " span $P", (WriteFP)span, - " tract $P", (WriteFP)span->tract, - " space $W", (WriteFW)span->space, - " blocks $U", (WriteFU)span->blockCount, - " largest ", + res = WriteF(stream, depth + 2, + "span $P\n", (WriteFP)span, + "tract $P\n", (WriteFP)span->tract, + "free $W\n", (WriteFW)span->free, + "blocks $U\n", (WriteFU)span->blockCount, + "largest ", NULL); - if(res != ResOK) return res; + if (res != ResOK) + return res; if (span->largestKnown) /* .design.largest */ - res = WriteF(stream, "$W\n", (WriteFW)span->largest, NULL); + res = WriteF(stream, 0, "$W\n", (WriteFW)span->largest, NULL); else - res = WriteF(stream, "unknown\n", NULL); - - if(res != ResOK) return res; - } - - res = WriteF(stream, " Span allocation maps\n", NULL); - if(res != ResOK) return res; - - step = pool->alignment; - length = 0x40 * step; - - spans = &mv->spans; - RING_FOR(node, spans, nextNode) { - Addr i, j; - MVBlock block; - span = RING_ELT(MVSpan, spans, node); - res = WriteF(stream, " MVSpan $P\n", (WriteFP)span, NULL); - if(res != ResOK) return res; + res = WriteF(stream, 0, "unknown\n", NULL); + if (res != ResOK) + return res; block = span->blocks; for(i = span->base.base; i < span->limit.limit; i = AddrAdd(i, length)) { - res = WriteF(stream, " $A ", i, NULL); - if(res != ResOK) return res; + res = WriteF(stream, depth + 2, "$A ", (WriteFA)i, NULL); + if (res != ResOK) + return res; for(j = i; j < AddrAdd(i, length) && j < span->limit.limit; @@ -764,12 +825,17 @@ static Res MVDescribe(Pool pool, mps_lib_FILE *stream) c = ']'; else /* j > block->base && j < block->limit */ c = '='; - res = WriteF(stream, "$C", c, NULL); - if(res != ResOK) return res; + res = WriteF(stream, 0, "$C", (WriteFC)c, NULL); + if (res != ResOK) + return res; } - res = WriteF(stream, "\n", NULL); - if(res != ResOK) return res; + res = WriteF(stream, 0, "\n", NULL); + if (res != ResOK) + return res; } + res = WriteF(stream, depth, "} MVSpan $P\n", (WriteFP)span, NULL); + if (res != ResOK) + return res; } return ResOK; @@ -782,7 +848,6 @@ static Res MVDescribe(Pool pool, mps_lib_FILE *stream) DEFINE_POOL_CLASS(MVPoolClass, this) { INHERIT_CLASS(this, AbstractBufferPoolClass); - PoolClassMixInAllocFree(this); this->name = "MV"; this->size = sizeof(MVStruct); this->offset = offsetof(MVStruct, poolStruct); @@ -791,7 +856,10 @@ DEFINE_POOL_CLASS(MVPoolClass, this) this->finish = MVFinish; this->alloc = MVAlloc; this->free = MVFree; + this->totalSize = MVTotalSize; + this->freeSize = MVFreeSize; this->describe = MVDescribe; + AVERT(PoolClass, this); } @@ -811,6 +879,7 @@ DEFINE_POOL_CLASS(MVDebugPoolClass, this) this->size = sizeof(MVDebugStruct); this->varargs = MVDebugVarargs; this->debugMixin = MVDebugMixin; + AVERT(PoolClass, this); } @@ -819,66 +888,14 @@ DEFINE_POOL_CLASS(MVDebugPoolClass, this) * Note this is an MPS interface extension */ -mps_class_t mps_class_mv(void) +mps_pool_class_t mps_class_mv(void) { - return (mps_class_t)(EnsureMVPoolClass()); + return (mps_pool_class_t)(EnsureMVPoolClass()); } -mps_class_t mps_class_mv_debug(void) +mps_pool_class_t mps_class_mv_debug(void) { - return (mps_class_t)(EnsureMVDebugPoolClass()); -} - - -/* mps_mv_free_size -- free bytes in pool */ - -size_t mps_mv_free_size(mps_pool_t mps_pool) -{ - Pool pool; - MV mv; - MVSpan span; - Size f = 0; - Ring spans, node = NULL, nextNode; /* gcc whinge stop */ - - pool = (Pool)mps_pool; - - AVERT(Pool, pool); - mv = Pool2MV(pool); - AVERT(MV, mv); - - spans = &mv->spans; - RING_FOR(node, spans, nextNode) { - span = RING_ELT(MVSpan, spans, node); - AVERT(MVSpan, span); - f += span->space; - } - - return (size_t)f; -} - - -size_t mps_mv_size(mps_pool_t mps_pool) -{ - Pool pool; - MV mv; - MVSpan span; - Size f = 0; - Ring spans, node = NULL, nextNode; /* gcc whinge stop */ - - pool = (Pool)mps_pool; - - AVERT(Pool, pool); - mv = Pool2MV(pool); - AVERT(MV, mv); - - spans = &mv->spans; - RING_FOR(node, spans, nextNode) { - span = RING_ELT(MVSpan, spans, node); - AVERT(MVSpan, span); - f += span->size; - } - - return (size_t)f; + return (mps_pool_class_t)(EnsureMVDebugPoolClass()); } @@ -887,8 +904,8 @@ size_t mps_mv_size(mps_pool_t mps_pool) Bool MVCheck(MV mv) { CHECKS(MV, mv); - CHECKD(Pool, &mv->poolStruct); - CHECKL(IsSubclassPoly(mv->poolStruct.class, EnsureMVPoolClass())); + CHECKD(Pool, MVPool(mv)); + CHECKL(IsSubclassPoly(MVPool(mv)->class, EnsureMVPoolClass())); CHECKD(MFS, &mv->blockPoolStruct); CHECKD(MFS, &mv->spanPoolStruct); CHECKL(mv->extendBy > 0); @@ -901,7 +918,7 @@ Bool MVCheck(MV mv) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolmv.h b/code/poolmv.h index 098cd3eaa2..01c5b9ebd7 100644 --- a/code/poolmv.h +++ b/code/poolmv.h @@ -1,41 +1,16 @@ /* poolmv.h: MANUAL VARIABLE POOL * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .purpose: This is the interface to the manual-variable pool class. * - * .mv: Manual-variable pools manage variably-sized blocks of memory in a - * flexible manner. They have higher overheads than a fixed-size pool. + * .mv: Manual-variable pools manage variably-sized blocks of memory + * in a flexible manner. They have higher overheads than a fixed-size + * pool. * - * .init: This class adds the following arguments to PoolCreate: - * - * Size extendBy - * - * extendBy is the default number of bytes reserved by the pool at a time. - * A large size will make allocation cheaper but have a higher resource - * overhead. A typical value might be 65536. See note 2. - * - * Size avgSize - * - * avgSize is an estimate of the average size of an allocation, and is used - * to choose the size of internal tables. An accurate estimate will - * improve the efficiency of the pool. A low estimate will make the pool - * less space efficient. A high estimate will make the pool less time - * efficient. A typical value might be 32. avgSize must not be less than - * extendBy. - * - * Size maxSize - * - * maxSize is an estimate of the maximum total size that the pool will - * reach. Setting this parameter does not actually contrain the pool, but - * an accurate estimate will improve the efficiency of the pool. maxSize - * must not be less than extendBy. - * - * Notes - * 2. The documentation could suggest a segment size according to the - * distribution of allocation size requests. richard 1994-11-08 + * .design: See */ #ifndef poolmv_h @@ -51,7 +26,7 @@ extern PoolClass PoolClassMV(void); extern Bool MVCheck(MV mv); -#define MV2Pool(mv) (&(mv)->poolStruct) +#define MVPool(mv) (&(mv)->poolStruct) #endif /* poolmv_h */ @@ -59,7 +34,7 @@ extern Bool MVCheck(MV mv); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolmv2.c b/code/poolmv2.c index 27382a91b6..474e2da7c9 100644 --- a/code/poolmv2.c +++ b/code/poolmv2.c @@ -1,7 +1,7 @@ /* poolmv2.c: MANUAL VARIABLE-SIZED TEMPORAL POOL * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: A manual-variable pool designed to take advantage of * placement according to predicted deathtime. @@ -14,6 +14,7 @@ #include "mpscmvt.h" #include "abq.h" #include "cbs.h" +#include "failover.h" #include "freelist.h" #include "meter.h" #include "range.h" @@ -38,7 +39,9 @@ static Res MVTBufferFill(Addr *baseReturn, Addr *limitReturn, Bool withReservoirPermit); static void MVTBufferEmpty(Pool pool, Buffer buffer, Addr base, Addr limit); static void MVTFree(Pool pool, Addr base, Size size); -static Res MVTDescribe(Pool pool, mps_lib_FILE *stream); +static Res MVTDescribe(Pool pool, mps_lib_FILE *stream, Count depth); +static Size MVTTotalSize(Pool pool); +static Size MVTFreeSize(Pool pool); static Res MVTSegAlloc(Seg *segReturn, MVT mvt, Size size, Bool withReservoirPermit); @@ -51,9 +54,9 @@ static Res MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, MVT mvt, Size min); static Bool MVTCheckFit(Addr base, Addr limit, Size min, Arena arena); static ABQ MVTABQ(MVT mvt); -static CBS MVTCBS(MVT mvt); -static Freelist MVTFreelist(MVT mvt); -static SegPref MVTSegPref(MVT mvt); +static Land MVTFreePrimary(MVT mvt); +static Land MVTFreeSecondary(MVT mvt); +static Land MVTFreeLand(MVT mvt); /* Types */ @@ -63,8 +66,8 @@ typedef struct MVTStruct PoolStruct poolStruct; CBSStruct cbsStruct; /* The coalescing block structure */ FreelistStruct flStruct; /* The emergency free list structure */ + FailoverStruct foStruct; /* The fail-over mechanism */ ABQStruct abqStruct; /* The available block queue */ - SegPrefStruct segPrefStruct; /* The preferences for segments */ /* */ Size minSize; /* Pool parameter */ Size meanSize; /* Pool parameter */ @@ -80,7 +83,6 @@ typedef struct MVTStruct Bool abqOverflow; /* ABQ dropped some candidates */ /* .* */ Bool splinter; /* Saved splinter */ - Seg splinterSeg; /* Saved splinter seg */ Addr splinterBase; /* Saved splinter base */ Addr splinterLimit; /* Saved splinter size */ @@ -135,30 +137,26 @@ typedef struct MVTStruct DEFINE_POOL_CLASS(MVTPoolClass, this) { - INHERIT_CLASS(this, AbstractSegBufPoolClass); + INHERIT_CLASS(this, AbstractBufferPoolClass); this->name = "MVT"; this->size = sizeof(MVTStruct); this->offset = offsetof(MVTStruct, poolStruct); - this->attr |= AttrFREE; this->varargs = MVTVarargs; this->init = MVTInit; this->finish = MVTFinish; this->free = MVTFree; this->bufferFill = MVTBufferFill; this->bufferEmpty = MVTBufferEmpty; + this->totalSize = MVTTotalSize; + this->freeSize = MVTFreeSize; this->describe = MVTDescribe; + AVERT(PoolClass, this); } /* Macros */ - -/* .trans.something: the C language sucks */ -#define unless(cond) if (!(cond)) -#define when(cond) if (cond) - - -#define Pool2MVT(pool) PARENT(MVTStruct, poolStruct, pool) -#define MVT2Pool(mvt) (&(mvt)->poolStruct) +#define PoolMVT(pool) PARENT(MVTStruct, poolStruct, pool) +#define MVTPool(mvt) (&(mvt)->poolStruct) /* Accessors */ @@ -170,21 +168,21 @@ static ABQ MVTABQ(MVT mvt) } -static CBS MVTCBS(MVT mvt) +static Land MVTFreePrimary(MVT mvt) { - return &mvt->cbsStruct; + return CBSLand(&mvt->cbsStruct); } -static Freelist MVTFreelist(MVT mvt) +static Land MVTFreeSecondary(MVT mvt) { - return &mvt->flStruct; + return FreelistLand(&mvt->flStruct); } -static SegPref MVTSegPref(MVT mvt) +static Land MVTFreeLand(MVT mvt) { - return &mvt->segPrefStruct; + return FailoverLand(&mvt->foStruct); } @@ -207,7 +205,7 @@ static void MVTVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[4].key = MPS_KEY_MVT_FRAG_LIMIT; args[4].val.d = (double)va_arg(varargs, Count) / 100.0; args[5].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } @@ -217,11 +215,11 @@ static void MVTVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) * minSize, meanSize, maxSize, reserveDepth, fragLimit */ -ARG_DEFINE_KEY(mvt_min_size, Size); -ARG_DEFINE_KEY(mvt_mean_size, Size); -ARG_DEFINE_KEY(mvt_max_size, Size); -ARG_DEFINE_KEY(mvt_reserve_depth, Count); -ARG_DEFINE_KEY(mvt_frag_limit, double); +ARG_DEFINE_KEY(MVT_MIN_SIZE, Size); +ARG_DEFINE_KEY(MVT_MEAN_SIZE, Size); +ARG_DEFINE_KEY(MVT_MAX_SIZE, Size); +ARG_DEFINE_KEY(MVT_RESERVE_DEPTH, Count); +ARG_DEFINE_KEY(MVT_FRAG_LIMIT, double); static Res MVTInit(Pool pool, ArgList args) { @@ -239,7 +237,7 @@ static Res MVTInit(Pool pool, ArgList args) ArgStruct arg; AVERT(Pool, pool); - mvt = Pool2MVT(pool); + mvt = PoolMVT(pool); /* can't AVERT mvt, yet */ arena = PoolArena(pool); AVERT(Arena, arena); @@ -256,11 +254,17 @@ static Res MVTInit(Pool pool, ArgList args) reserveDepth = arg.val.count; if (ArgPick(&arg, args, MPS_KEY_MVT_FRAG_LIMIT)) { /* pending complete fix for job003319 */ - AVER(0 <= arg.val.d && arg.val.d <= 1); + AVER(0 <= arg.val.d); + AVER(arg.val.d <= 1); fragLimit = (Count)(arg.val.d * 100); } - AVER(SizeIsAligned(align, MPS_PF_ALIGN)); + AVERT(Align, align); + /* This restriction on the alignment is necessary because of the use + * of a Freelist to store the free address ranges in low-memory + * situations. See . + */ + AVER(AlignIsAligned(align, FreelistMinimumAlignment)); AVER(0 < minSize); AVER(minSize <= meanSize); AVER(meanSize <= maxSize); @@ -269,7 +273,7 @@ static Res MVTInit(Pool pool, ArgList args) /* TODO: More sanity checks possible? */ /* see */ - fillSize = SizeAlignUp(maxSize, ArenaAlign(arena)); + fillSize = SizeArenaGrains(maxSize, arena); /* see */ reuseSize = 2 * fillSize; abqDepth = (reserveDepth * meanSize + reuseSize - 1) / reuseSize; @@ -277,23 +281,28 @@ static Res MVTInit(Pool pool, ArgList args) if (abqDepth < 3) abqDepth = 3; - res = CBSInit(arena, MVTCBS(mvt), (void *)mvt, align, FALSE, args); + res = LandInit(MVTFreePrimary(mvt), CBSFastLandClassGet(), arena, align, mvt, + mps_args_none); if (res != ResOK) - goto failCBS; + goto failFreePrimaryInit; - res = ABQInit(arena, MVTABQ(mvt), (void *)mvt, abqDepth, sizeof(RangeStruct)); + res = LandInit(MVTFreeSecondary(mvt), FreelistLandClassGet(), arena, align, + mvt, mps_args_none); if (res != ResOK) - goto failABQ; - - FreelistInit(MVTFreelist(mvt), align); + goto failFreeSecondaryInit; + + MPS_ARGS_BEGIN(foArgs) { + MPS_ARGS_ADD(foArgs, FailoverPrimary, MVTFreePrimary(mvt)); + MPS_ARGS_ADD(foArgs, FailoverSecondary, MVTFreeSecondary(mvt)); + res = LandInit(MVTFreeLand(mvt), FailoverLandClassGet(), arena, align, mvt, + foArgs); + } MPS_ARGS_END(foArgs); + if (res != ResOK) + goto failFreeLandInit; - { - ZoneSet zones; - /* --- Loci needed here, what should the pref be? */ - SegPrefInit(MVTSegPref(mvt)); - zones = ZoneSetComp(ArenaDefaultZONESET); - SegPrefExpress(MVTSegPref(mvt), SegPrefZoneSet, (void *)&zones); - } + res = ABQInit(arena, MVTABQ(mvt), (void *)mvt, abqDepth, sizeof(RangeStruct)); + if (res != ResOK) + goto failABQInit; pool->alignment = align; mvt->reuseSize = reuseSize; @@ -304,7 +313,6 @@ static Res MVTInit(Pool pool, ArgList args) mvt->maxSize = maxSize; mvt->fragLimit = fragLimit; mvt->splinter = FALSE; - mvt->splinterSeg = NULL; mvt->splinterBase = (Addr)0; mvt->splinterLimit = (Addr)0; @@ -358,9 +366,13 @@ static Res MVTInit(Pool pool, ArgList args) reserveDepth, fragLimit); return ResOK; -failABQ: - CBSFinish(MVTCBS(mvt)); -failCBS: +failABQInit: + LandFinish(MVTFreeLand(mvt)); +failFreeLandInit: + LandFinish(MVTFreeSecondary(mvt)); +failFreeSecondaryInit: + LandFinish(MVTFreePrimary(mvt)); +failFreePrimaryInit: AVER(res != ResOK); return res; } @@ -368,17 +380,16 @@ static Res MVTInit(Pool pool, ArgList args) /* MVTCheck -- validate an MVT Pool */ +ATTRIBUTE_UNUSED static Bool MVTCheck(MVT mvt) { CHECKS(MVT, mvt); - CHECKD(Pool, &mvt->poolStruct); - CHECKL(mvt->poolStruct.class == MVTPoolClassGet()); + CHECKD(Pool, MVTPool(mvt)); + CHECKL(MVTPool(mvt)->class == MVTPoolClassGet()); CHECKD(CBS, &mvt->cbsStruct); - /* CHECKL(CBSCheck(MVTCBS(mvt))); */ CHECKD(ABQ, &mvt->abqStruct); - /* CHECKL(ABQCheck(MVTABQ(mvt))); */ CHECKD(Freelist, &mvt->flStruct); - CHECKD(SegPref, &mvt->segPrefStruct); + CHECKD(Failover, &mvt->foStruct); CHECKL(mvt->reuseSize >= 2 * mvt->fillSize); CHECKL(mvt->fillSize >= mvt->maxSize); CHECKL(mvt->maxSize >= mvt->meanSize); @@ -391,10 +402,7 @@ static Bool MVTCheck(MVT mvt) if (mvt->splinter) { CHECKL(AddrOffset(mvt->splinterBase, mvt->splinterLimit) >= mvt->minSize); - /* CHECKD(Seg, mvt->splinterSeg); */ - CHECKL(SegCheck(mvt->splinterSeg)); - CHECKL(mvt->splinterBase >= SegBase(mvt->splinterSeg)); - CHECKL(mvt->splinterLimit <= SegLimit(mvt->splinterSeg)); + CHECKL(mvt->splinterBase < mvt->splinterLimit); } CHECKL(mvt->size == mvt->allocated + mvt->available + mvt->unavailable); @@ -415,7 +423,7 @@ static void MVTFinish(Pool pool) Ring node, nextNode; AVERT(Pool, pool); - mvt = Pool2MVT(pool); + mvt = PoolMVT(pool); AVERT(MVT, mvt); arena = PoolArena(pool); AVERT(Arena, arena); @@ -431,10 +439,11 @@ static void MVTFinish(Pool pool) SegFree(SegOfPoolRing(node)); } - /* Finish the Freelist, ABQ and CBS structures */ - FreelistFinish(MVTFreelist(mvt)); + /* Finish the ABQ, Failover, Freelist and CBS structures */ ABQFinish(arena, MVTABQ(mvt)); - CBSFinish(MVTCBS(mvt)); + LandFinish(MVTFreeLand(mvt)); + LandFinish(MVTFreeSecondary(mvt)); + LandFinish(MVTFreePrimary(mvt)); } @@ -490,7 +499,7 @@ static Res MVTOversizeFill(Addr *baseReturn, Addr base, limit; Size alignedSize; - alignedSize = SizeAlignUp(minSize, ArenaAlign(PoolArena(MVT2Pool(mvt)))); + alignedSize = SizeArenaGrains(minSize, PoolArena(MVTPool(mvt))); res = MVTSegAlloc(&seg, mvt, alignedSize, withReservoirPermit); if (res != ResOK) @@ -558,13 +567,13 @@ static Bool MVTSplinterFill(Addr *baseReturn, Addr *limitReturn, static void MVTOneSegOnly(Addr *baseIO, Addr *limitIO, MVT mvt, Size minSize) { Addr base, limit, segLimit; - Seg seg; + Seg seg = NULL; /* suppress uninitialized warning */ Arena arena; base = *baseIO; limit = *limitIO; - arena = PoolArena(MVT2Pool(mvt)); + arena = PoolArena(MVTPool(mvt)); SURELY(SegOfAddr(&seg, arena, base)); segLimit = SegLimit(seg); @@ -624,14 +633,7 @@ static Bool MVTABQFill(Addr *baseReturn, Addr *limitReturn, } -/* MVTContingencyFill -- try to fill a request from the CBS or Freelist - * - * (The CBS and Freelist are lumped together under the heading of - * "contingency" for historical reasons: the Freelist used to be part - * of the CBS. There is no principled reason why these two are - * searched at the same time: if it should prove convenient to - * separate them, go ahead.) - */ +/* MVTContingencyFill -- try to fill a request from the free lists */ static Bool MVTContingencyFill(Addr *baseReturn, Addr *limitReturn, MVT mvt, Size minSize) { @@ -693,13 +695,13 @@ static Res MVTBufferFill(Addr *baseReturn, Addr *limitReturn, AVER(baseReturn != NULL); AVER(limitReturn != NULL); AVERT(Pool, pool); - mvt = Pool2MVT(pool); + mvt = PoolMVT(pool); AVERT(MVT, mvt); AVERT(Buffer, buffer); AVER(BufferIsReset(buffer)); AVER(minSize > 0); AVER(SizeIsAligned(minSize, pool->alignment)); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); /* Allocate oversize blocks exactly, directly from the arena. */ @@ -720,8 +722,7 @@ static Res MVTBufferFill(Addr *baseReturn, Addr *limitReturn, METER_ACC(mvt->underflows, minSize); /* If fragmentation is acceptable, attempt to find a free block from - the CBS or Freelist. - */ + the free lists. */ if (mvt->available >= mvt->availLimit) { METER_ACC(mvt->fragLimitContingencies, minSize); if (MVTContingencyFill(baseReturn, limitReturn, mvt, minSize)) @@ -762,6 +763,7 @@ static Bool MVTDeleteOverlapping(Bool *deleteReturn, void *element, AVER(deleteReturn != NULL); AVER(element != NULL); AVER(closureP != NULL); + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); oldRange = element; @@ -773,9 +775,11 @@ static Bool MVTDeleteOverlapping(Bool *deleteReturn, void *element, /* MVTReserve -- add a range to the available range queue, and if the - * queue is full, return segments to the arena. + * queue is full, return segments to the arena. Return TRUE if it + * succeeded in adding the range to the queue, FALSE if the queue + * overflowed. */ -static Res MVTReserve(MVT mvt, Range range) +static Bool MVTReserve(MVT mvt, Range range) { AVERT(MVT, mvt); AVERT(Range, range); @@ -783,30 +787,30 @@ static Res MVTReserve(MVT mvt, Range range) /* See */ if (!ABQPush(MVTABQ(mvt), range)) { - Arena arena = PoolArena(MVT2Pool(mvt)); + Arena arena = PoolArena(MVTPool(mvt)); RangeStruct oldRange; /* We just failed to push, so the ABQ must be full, and so surely * the peek will succeed. */ SURELY(ABQPeek(MVTABQ(mvt), &oldRange)); AVERT(Range, &oldRange); if (!MVTReturnSegs(mvt, &oldRange, arena)) - goto failOverflow; + goto overflow; METER_ACC(mvt->returns, RangeSize(&oldRange)); if (!ABQPush(MVTABQ(mvt), range)) - goto failOverflow; + goto overflow; } - return ResOK; + return TRUE; -failOverflow: +overflow: mvt->abqOverflow = TRUE; METER_ACC(mvt->overflows, RangeSize(range)); - return ResFAIL; + return FALSE; } -/* MVTInsert -- insert an address range into the CBS (or the Freelist - * if that fails) and update the ABQ accordingly. +/* MVTInsert -- insert an address range into the free lists and update + * the ABQ accordingly. */ static Res MVTInsert(MVT mvt, Addr base, Addr limit) { @@ -815,18 +819,9 @@ static Res MVTInsert(MVT mvt, Addr base, Addr limit) AVERT(MVT, mvt); AVER(base < limit); - - /* Attempt to flush the Freelist to the CBS to give maximum - * opportunities for coalescence. */ - FreelistFlushToCBS(MVTFreelist(mvt), MVTCBS(mvt)); RangeInit(&range, base, limit); - res = CBSInsert(&newRange, MVTCBS(mvt), &range); - if (ResIsAllocFailure(res)) { - /* CBS ran out of memory for splay nodes: add range to emergency - * free list instead. */ - res = FreelistInsert(&newRange, MVTFreelist(mvt), &range); - } + res = LandInsert(&newRange, MVTFreeLand(mvt), &range); if (res != ResOK) return res; @@ -835,16 +830,16 @@ static Res MVTInsert(MVT mvt, Addr base, Addr limit) * with ranges on the ABQ, so ensure that the corresponding ranges * are coalesced on the ABQ. */ - ABQIterate(MVTABQ(mvt), MVTDeleteOverlapping, &newRange, 0); - MVTReserve(mvt, &newRange); + ABQIterate(MVTABQ(mvt), MVTDeleteOverlapping, &newRange, UNUSED_SIZE); + (void)MVTReserve(mvt, &newRange); } return ResOK; } -/* MVTDelete -- delete an address range from the CBS and the Freelist, - * and update the ABQ accordingly. +/* MVTDelete -- delete an address range from the free lists, and + * update the ABQ accordingly. */ static Res MVTDelete(MVT mvt, Addr base, Addr limit) { @@ -855,27 +850,7 @@ static Res MVTDelete(MVT mvt, Addr base, Addr limit) AVER(base < limit); RangeInit(&range, base, limit); - res = CBSDelete(&rangeOld, MVTCBS(mvt), &range); - if (ResIsAllocFailure(res)) { - /* CBS ran out of memory for splay nodes, which must mean that - * there were fragments on both sides: see - * . Handle this by - * deleting the whole of rangeOld (which requires no - * allocation) and re-inserting the fragments. */ - RangeStruct rangeOld2; - res = CBSDelete(&rangeOld2, MVTCBS(mvt), &rangeOld); - AVER(res == ResOK); - AVER(RangesEqual(&rangeOld2, &rangeOld)); - AVER(RangeBase(&rangeOld) != base); - res = MVTInsert(mvt, RangeBase(&rangeOld), base); - AVER(res == ResOK); - AVER(RangeLimit(&rangeOld) != limit); - res = MVTInsert(mvt, limit, RangeLimit(&rangeOld)); - AVER(res == ResOK); - } else if (res == ResFAIL) { - /* Not found in the CBS: try the Freelist. */ - res = FreelistDelete(&rangeOld, MVTFreelist(mvt), &range); - } + res = LandDelete(&rangeOld, MVTFreeLand(mvt), &range); if (res != ResOK) return res; AVER(RangesNest(&rangeOld, &range)); @@ -884,18 +859,18 @@ static Res MVTDelete(MVT mvt, Addr base, Addr limit) * might be on the ABQ, so ensure it is removed. */ if (RangeSize(&rangeOld) >= mvt->reuseSize) - ABQIterate(MVTABQ(mvt), MVTDeleteOverlapping, &rangeOld, 0); + ABQIterate(MVTABQ(mvt), MVTDeleteOverlapping, &rangeOld, UNUSED_SIZE); /* There might be fragments at the left or the right of the deleted * range, and either might be big enough to go back on the ABQ. */ RangeInit(&rangeLeft, RangeBase(&rangeOld), base); if (RangeSize(&rangeLeft) >= mvt->reuseSize) - MVTReserve(mvt, &rangeLeft); + (void)MVTReserve(mvt, &rangeLeft); RangeInit(&rangeRight, limit, RangeLimit(&rangeOld)); if (RangeSize(&rangeRight) >= mvt->reuseSize) - MVTReserve(mvt, &rangeRight); + (void)MVTReserve(mvt, &rangeRight); return ResOK; } @@ -914,7 +889,7 @@ static void MVTBufferEmpty(Pool pool, Buffer buffer, Res res; AVERT(Pool, pool); - mvt = Pool2MVT(pool); + mvt = PoolMVT(pool); AVERT(MVT, mvt); AVERT(Buffer, buffer); AVER(BufferIsReady(buffer)); @@ -963,7 +938,6 @@ static void MVTBufferEmpty(Pool pool, Buffer buffer, } mvt->splinter = TRUE; - mvt->splinterSeg = BufferSeg(buffer); mvt->splinterBase = base; mvt->splinterLimit = limit; } @@ -980,7 +954,7 @@ static void MVTFree(Pool pool, Addr base, Size size) Addr limit; AVERT(Pool, pool); - mvt = Pool2MVT(pool); + mvt = PoolMVT(pool); AVERT(MVT, mvt); AVER(base != (Addr)0); AVER(size > 0); @@ -1001,7 +975,7 @@ static void MVTFree(Pool pool, Addr base, Size size) /* */ /* Return exceptional blocks directly to arena */ if (size > mvt->fillSize) { - Seg seg; + Seg seg = NULL; /* suppress uninitialized warning */ SURELY(SegOfAddr(&seg, PoolArena(pool), base)); AVER(base == SegBase(seg)); AVER(limit <= SegLimit(seg)); @@ -1010,8 +984,6 @@ static void MVTFree(Pool pool, Addr base, Size size) AVER(mvt->size == mvt->allocated + mvt->available + mvt->unavailable); METER_ACC(mvt->exceptionReturns, SegSize(seg)); - if (SegBuffer(seg) != NULL) - BufferDetach(SegBuffer(seg), MVT2Pool(mvt)); MVTSegFree(mvt, seg); return; } @@ -1020,173 +992,140 @@ static void MVTFree(Pool pool, Addr base, Size size) } -/* MVTDescribe -- describe an MVT pool */ +/* MVTTotalSize -- total memory allocated from the arena */ -static Res MVTDescribe(Pool pool, mps_lib_FILE *stream) +static Size MVTTotalSize(Pool pool) { - Res res; MVT mvt; - if (!TESTT(Pool, pool)) return ResFAIL; - mvt = Pool2MVT(pool); - if (!TESTT(MVT, mvt)) return ResFAIL; - if (stream == NULL) return ResFAIL; - - res = WriteF(stream, - "MVT $P\n{\n", (WriteFP)mvt, - " minSize: $U \n", (WriteFU)mvt->minSize, - " meanSize: $U \n", (WriteFU)mvt->meanSize, - " maxSize: $U \n", (WriteFU)mvt->maxSize, - " fragLimit: $U \n", (WriteFU)mvt->fragLimit, - " reuseSize: $U \n", (WriteFU)mvt->reuseSize, - " fillSize: $U \n", (WriteFU)mvt->fillSize, - " availLimit: $U \n", (WriteFU)mvt->availLimit, - " abqOverflow: $S \n", mvt->abqOverflow?"TRUE":"FALSE", - " splinter: $S \n", mvt->splinter?"TRUE":"FALSE", - " splinterSeg: $P \n", (WriteFP)mvt->splinterSeg, - " splinterBase: $A \n", (WriteFA)mvt->splinterBase, - " splinterLimit: $A \n", (WriteFU)mvt->splinterLimit, - " size: $U \n", (WriteFU)mvt->size, - " allocated: $U \n", (WriteFU)mvt->allocated, - " available: $U \n", (WriteFU)mvt->available, - " unavailable: $U \n", (WriteFU)mvt->unavailable, - NULL); - if(res != ResOK) return res; - - res = CBSDescribe(MVTCBS(mvt), stream); - if(res != ResOK) return res; - - res = ABQDescribe(MVTABQ(mvt), (ABQDescribeElement)RangeDescribe, stream); - if(res != ResOK) return res; - - res = FreelistDescribe(MVTFreelist(mvt), stream); - if(res != ResOK) return res; - - res = METER_WRITE(mvt->segAllocs, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->segFrees, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->bufferFills, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->bufferEmpties, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolFrees, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolSize, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolAllocated, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolAvailable, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolUnavailable, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolUtilization, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->finds, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->overflows, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->underflows, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->refills, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->refillPushes, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->returns, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->perfectFits, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->firstFits, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->secondFits, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->failures, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->emergencyContingencies, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->fragLimitContingencies, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->contingencySearches, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->contingencyHardSearches, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->splinters, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->splintersUsed, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->splintersDropped, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->sawdust, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->exceptions, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->exceptionSplinters, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->exceptionReturns, stream); - if (res != ResOK) return res; - - res = WriteF(stream, "}\n", NULL); - return res; -} - + AVERT(Pool, pool); + mvt = PoolMVT(pool); + AVERT(MVT, mvt); -/* Pool Interface */ + return mvt->size; +} -/* PoolClassMVT -- the Pool (sub-)Class for an MVT pool */ +/* MVTFreeSize -- free memory (unused by client program) */ -PoolClass PoolClassMVT(void) +static Size MVTFreeSize(Pool pool) { - return MVTPoolClassGet(); -} + MVT mvt; + AVERT(Pool, pool); + mvt = PoolMVT(pool); + AVERT(MVT, mvt); -/* MPS Interface */ + return mvt->available + mvt->unavailable; +} -/* mps_class_mvt -- the class of an mvt pool */ +/* MVTDescribe -- describe an MVT pool */ -mps_class_t mps_class_mvt(void) +static Res MVTDescribe(Pool pool, mps_lib_FILE *stream, Count depth) { - return (mps_class_t)(PoolClassMVT()); -} + Res res; + MVT mvt; + if (!TESTT(Pool, pool)) + return ResFAIL; + mvt = PoolMVT(pool); + if (!TESTT(MVT, mvt)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "MVT $P {\n", (WriteFP)mvt, + " minSize: $U\n", (WriteFU)mvt->minSize, + " meanSize: $U\n", (WriteFU)mvt->meanSize, + " maxSize: $U\n", (WriteFU)mvt->maxSize, + " fragLimit: $U\n", (WriteFU)mvt->fragLimit, + " reuseSize: $U\n", (WriteFU)mvt->reuseSize, + " fillSize: $U\n", (WriteFU)mvt->fillSize, + " availLimit: $U\n", (WriteFU)mvt->availLimit, + " abqOverflow: $S\n", WriteFYesNo(mvt->abqOverflow), + " splinter: $S\n", WriteFYesNo(mvt->splinter), + " splinterBase: $A\n", (WriteFA)mvt->splinterBase, + " splinterLimit: $A\n", (WriteFU)mvt->splinterLimit, + " size: $U\n", (WriteFU)mvt->size, + " allocated: $U\n", (WriteFU)mvt->allocated, + " available: $U\n", (WriteFU)mvt->available, + " unavailable: $U\n", (WriteFU)mvt->unavailable, + NULL); + if (res != ResOK) + return res; -/* MPS Interface extensions --- should these be pool generics? */ + res = LandDescribe(MVTFreePrimary(mvt), stream, depth + 2); + if (res != ResOK) + return res; + res = LandDescribe(MVTFreeSecondary(mvt), stream, depth + 2); + if (res != ResOK) + return res; + res = LandDescribe(MVTFreeLand(mvt), stream, depth + 2); + if (res != ResOK) + return res; + res = ABQDescribe(MVTABQ(mvt), (ABQDescribeElement)RangeDescribe, stream, + depth + 2); + if (res != ResOK) + return res; + METER_WRITE(mvt->segAllocs, stream, depth + 2); + METER_WRITE(mvt->segFrees, stream, depth + 2); + METER_WRITE(mvt->bufferFills, stream, depth + 2); + METER_WRITE(mvt->bufferEmpties, stream, depth + 2); + METER_WRITE(mvt->poolFrees, stream, depth + 2); + METER_WRITE(mvt->poolSize, stream, depth + 2); + METER_WRITE(mvt->poolAllocated, stream, depth + 2); + METER_WRITE(mvt->poolAvailable, stream, depth + 2); + METER_WRITE(mvt->poolUnavailable, stream, depth + 2); + METER_WRITE(mvt->poolUtilization, stream, depth + 2); + METER_WRITE(mvt->finds, stream, depth + 2); + METER_WRITE(mvt->overflows, stream, depth + 2); + METER_WRITE(mvt->underflows, stream, depth + 2); + METER_WRITE(mvt->refills, stream, depth + 2); + METER_WRITE(mvt->refillPushes, stream, depth + 2); + METER_WRITE(mvt->returns, stream, depth + 2); + METER_WRITE(mvt->perfectFits, stream, depth + 2); + METER_WRITE(mvt->firstFits, stream, depth + 2); + METER_WRITE(mvt->secondFits, stream, depth + 2); + METER_WRITE(mvt->failures, stream, depth + 2); + METER_WRITE(mvt->emergencyContingencies, stream, depth + 2); + METER_WRITE(mvt->fragLimitContingencies, stream, depth + 2); + METER_WRITE(mvt->contingencySearches, stream, depth + 2); + METER_WRITE(mvt->contingencyHardSearches, stream, depth + 2); + METER_WRITE(mvt->splinters, stream, depth + 2); + METER_WRITE(mvt->splintersUsed, stream, depth + 2); + METER_WRITE(mvt->splintersDropped, stream, depth + 2); + METER_WRITE(mvt->sawdust, stream, depth + 2); + METER_WRITE(mvt->exceptions, stream, depth + 2); + METER_WRITE(mvt->exceptionSplinters, stream, depth + 2); + METER_WRITE(mvt->exceptionReturns, stream, depth + 2); + + res = WriteF(stream, depth, "} MVT $P\n", (WriteFP)mvt, NULL); + return res; +} -/* mps_mvt_size -- number of bytes committed to the pool */ -size_t mps_mvt_size(mps_pool_t mps_pool) -{ - Pool pool; - MVT mvt; +/* Pool Interface */ - pool = (Pool)mps_pool; - AVERT(Pool, pool); - mvt = Pool2MVT(pool); - AVERT(MVT, mvt); +/* PoolClassMVT -- the Pool (sub-)Class for an MVT pool */ - return (size_t)mvt->size; +PoolClass PoolClassMVT(void) +{ + return MVTPoolClassGet(); } -/* mps_mvt_free_size -- number of bytes comitted to the pool that are - * available for allocation - */ -size_t mps_mvt_free_size(mps_pool_t mps_pool) -{ - Pool pool; - MVT mvt; +/* MPS Interface */ - pool = (Pool)mps_pool; - AVERT(Pool, pool); - mvt = Pool2MVT(pool); - AVERT(MVT, mvt); +/* mps_class_mvt -- the class of an mvt pool */ - return (size_t)mvt->available; +mps_pool_class_t mps_class_mvt(void) +{ + return (mps_pool_class_t)(PoolClassMVT()); } @@ -1199,11 +1138,8 @@ size_t mps_mvt_free_size(mps_pool_t mps_pool) static Res MVTSegAlloc(Seg *segReturn, MVT mvt, Size size, Bool withReservoirPermit) { - /* Can't use plain old SegClass here because we need to call - * SegBuffer() in MVTFree(). */ - Res res = SegAlloc(segReturn, GCSegClassGet(), - MVTSegPref(mvt), size, MVT2Pool(mvt), withReservoirPermit, - argsNone); + Res res = SegAlloc(segReturn, SegClassGet(), LocusPrefDefault(), size, + MVTPool(mvt), withReservoirPermit, argsNone); if (res == ResOK) { Size segSize = SegSize(*segReturn); @@ -1225,7 +1161,6 @@ static Res MVTSegAlloc(Seg *segReturn, MVT mvt, Size size, */ static void MVTSegFree(MVT mvt, Seg seg) { - Buffer buffer; Size size; size = SegSize(seg); @@ -1235,16 +1170,6 @@ static void MVTSegFree(MVT mvt, Seg seg) mvt->size -= size; mvt->availLimit = mvt->size * mvt->fragLimit / 100; AVER(mvt->size == mvt->allocated + mvt->available + mvt->unavailable); - - /* If the client program allocates the exactly the entire buffer then - frees the allocated memory then we'll try to free the segment with - the buffer still attached. It's safe, but we must detach the buffer - first. See job003520 and job003672. */ - buffer = SegBuffer(seg); - if (buffer != NULL) { - AVER(BufferAP(buffer)->init == SegLimit(seg)); - BufferDetach(buffer, MVT2Pool(mvt)); - } SegFree(seg); METER_ACC(mvt->segFrees, size); @@ -1262,7 +1187,7 @@ static Bool MVTReturnSegs(MVT mvt, Range range, Arena arena) limit = RangeLimit(range); while (base < limit) { - Seg seg; + Seg seg = NULL; /* suppress uninitialized warning */ Addr segBase, segLimit; SURELY(SegOfAddr(&seg, arena, base)); @@ -1280,101 +1205,76 @@ static Bool MVTReturnSegs(MVT mvt, Range range, Arena arena) } -/* MVTRefillCallback -- called from CBSIterate or FreelistIterate at - * the behest of MVTRefillABQIfEmpty +/* MVTRefillABQIfEmpty -- refill the ABQ from the free lists if it is + * empty. */ -static Bool MVTRefillCallback(MVT mvt, Range range) -{ - Res res; - - AVERT(ABQ, MVTABQ(mvt)); - AVERT(Range, range); - - if (RangeSize(range) < mvt->reuseSize) - return TRUE; - METER_ACC(mvt->refillPushes, ABQDepth(MVTABQ(mvt))); - res = MVTReserve(mvt, range); - if (res != ResOK) - return FALSE; - - return TRUE; -} - -static Bool MVTCBSRefillCallback(CBS cbs, Range range, - void *closureP, Size closureS) +static Bool MVTRefillVisitor(Land land, Range range, + void *closureP, Size closureS) { MVT mvt; - AVERT(CBS, cbs); - mvt = closureP; - AVERT(MVT, mvt); - UNUSED(closureS); - return MVTRefillCallback(mvt, range); -} -static Bool MVTFreelistRefillCallback(Bool *deleteReturn, Range range, - void *closureP, Size closureS) -{ - MVT mvt; + AVERT(Land, land); mvt = closureP; AVERT(MVT, mvt); + AVER(closureS == UNUSED_SIZE); UNUSED(closureS); - AVER(deleteReturn != NULL); - *deleteReturn = FALSE; - return MVTRefillCallback(mvt, range); + + if (RangeSize(range) < mvt->reuseSize) + return TRUE; + + METER_ACC(mvt->refillPushes, ABQDepth(MVTABQ(mvt))); + return MVTReserve(mvt, range); } -/* MVTRefillABQIfEmpty -- refill the ABQ from the CBS and the Freelist if - * it is empty - */ static void MVTRefillABQIfEmpty(MVT mvt, Size size) { AVERT(MVT, mvt); AVER(size > 0); /* If there have never been any overflows from the ABQ back to the - * CBS/Freelist, then there cannot be any blocks in the CBS/Freelist + * free lists, then there cannot be any blocks in the free lists * that are worth adding to the ABQ. So as an optimization, we don't * bother to look. */ if (mvt->abqOverflow && ABQIsEmpty(MVTABQ(mvt))) { mvt->abqOverflow = FALSE; METER_ACC(mvt->refills, size); - CBSIterate(MVTCBS(mvt), &MVTCBSRefillCallback, mvt, 0); - FreelistIterate(MVTFreelist(mvt), &MVTFreelistRefillCallback, mvt, 0); + /* The iteration stops if the ABQ overflows, so may finish or not. */ + (void)LandIterate(MVTFreeLand(mvt), MVTRefillVisitor, mvt, UNUSED_SIZE); } } -/* Closure for MVTContingencySearch */ -typedef struct MVTContigencyStruct *MVTContigency; +/* MVTContingencySearch -- search free lists for a block of a given size */ -typedef struct MVTContigencyStruct +typedef struct MVTContigencyClosureStruct { MVT mvt; - Bool found; RangeStruct range; Arena arena; Size min; /* meters */ Count steps; Count hardSteps; -} MVTContigencyStruct; - +} MVTContigencyClosureStruct, *MVTContigencyClosure; -/* MVTContingencyCallback -- called from CBSIterate or FreelistIterate - * at the behest of MVTContingencySearch. - */ -static Bool MVTContingencyCallback(MVTContigency cl, Range range) +static Bool MVTContingencyVisitor(Land land, Range range, + void *closureP, Size closureS) { MVT mvt; Size size; Addr base, limit; + MVTContigencyClosure cl; - AVER(cl != NULL); + AVERT(Land, land); + AVERT(Range, range); + AVER(closureP != NULL); + cl = closureP; mvt = cl->mvt; AVERT(MVT, mvt); - AVERT(Range, range); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); base = RangeBase(range); limit = RangeLimit(range); @@ -1387,7 +1287,6 @@ static Bool MVTContingencyCallback(MVTContigency cl, Range range) /* verify that min will fit when seg-aligned */ if (size >= 2 * cl->min) { RangeInit(&cl->range, base, limit); - cl->found = TRUE; return FALSE; } @@ -1395,7 +1294,6 @@ static Bool MVTContingencyCallback(MVTContigency cl, Range range) cl->hardSteps++; if (MVTCheckFit(base, limit, cl->min, cl->arena)) { RangeInit(&cl->range, base, limit); - cl->found = TRUE; return FALSE; } @@ -1403,46 +1301,18 @@ static Bool MVTContingencyCallback(MVTContigency cl, Range range) return TRUE; } -static Bool MVTCBSContingencyCallback(CBS cbs, Range range, - void *closureP, Size closureS) -{ - MVTContigency cl = closureP; - UNUSED(cbs); - UNUSED(closureS); - return MVTContingencyCallback(cl, range); -} - -static Bool MVTFreelistContingencyCallback(Bool *deleteReturn, Range range, - void *closureP, Size closureS) -{ - MVTContigency cl = closureP; - UNUSED(closureS); - AVER(deleteReturn != NULL); - *deleteReturn = FALSE; - return MVTContingencyCallback(cl, range); -} - -/* MVTContingencySearch -- search the CBS and the Freelist for a block - * of size min */ - static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, MVT mvt, Size min) { - MVTContigencyStruct cls; + MVTContigencyClosureStruct cls; cls.mvt = mvt; - cls.found = FALSE; - cls.arena = PoolArena(MVT2Pool(mvt)); + cls.arena = PoolArena(MVTPool(mvt)); cls.min = min; cls.steps = 0; cls.hardSteps = 0; - FreelistFlushToCBS(MVTFreelist(mvt), MVTCBS(mvt)); - - CBSIterate(MVTCBS(mvt), MVTCBSContingencyCallback, (void *)&cls, 0); - FreelistIterate(MVTFreelist(mvt), MVTFreelistContingencyCallback, - (void *)&cls, 0); - if (!cls.found) + if (LandIterate(MVTFreeLand(mvt), MVTContingencyVisitor, &cls, UNUSED_SIZE)) return FALSE; AVER(RangeSize(&cls.range) >= min); @@ -1459,9 +1329,10 @@ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, /* MVTCheckFit -- verify that segment-aligned block of size min can * fit in a candidate address range. */ + static Bool MVTCheckFit(Addr base, Addr limit, Size min, Arena arena) { - Seg seg; + Seg seg = NULL; /* suppress uninitialized warning */ Addr segLimit; SURELY(SegOfAddr(&seg, arena, base)); @@ -1488,23 +1359,21 @@ static Bool MVTCheckFit(Addr base, Addr limit, Size min, Arena arena) /* Return the CBS of an MVT pool for the benefit of fotest.c. */ -extern CBS _mps_mvt_cbs(mps_pool_t); -CBS _mps_mvt_cbs(mps_pool_t mps_pool) { - Pool pool; +extern Land _mps_mvt_cbs(Pool); +Land _mps_mvt_cbs(Pool pool) { MVT mvt; - pool = (Pool)mps_pool; AVERT(Pool, pool); - mvt = Pool2MVT(pool); + mvt = PoolMVT(pool); AVERT(MVT, mvt); - return MVTCBS(mvt); + return MVTFreePrimary(mvt); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolmvff.c b/code/poolmvff.c index d2c2997e72..22b6d004f6 100644 --- a/code/poolmvff.c +++ b/code/poolmvff.c @@ -1,28 +1,31 @@ /* poolmvff.c: First Fit Manual Variable Pool * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .purpose: This is a pool class for manually managed objects of * variable size where address-ordered first fit is an appropriate - * policy. Provision is made to allocate in reverse. This pool - * can allocate across segment boundaries. + * policy. Provision is made to allocate in reverse. * * .design: * + * NOTE * - * TRANSGRESSIONS - * - * .trans.stat: mps_mvff_stat is a temporary hack for measurement purposes, - * see .stat below. + * There's potential for up to 4% speed improvement by calling Land + * methods statically instead of indirectly via the Land abstraction + * (thus, cbsInsert instead of LandInsert, and so on). See + * */ -#include "mpscmvff.h" -#include "dbgpool.h" #include "cbs.h" +#include "dbgpool.h" +#include "failover.h" #include "freelist.h" #include "mpm.h" +#include "mpscmvff.h" +#include "mpscmfs.h" +#include "poolmfs.h" SRCID(poolmvff, "$Id$"); @@ -42,26 +45,29 @@ extern PoolClass PoolClassMVFF(void); typedef struct MVFFStruct *MVFF; typedef struct MVFFStruct { /* MVFF pool outer structure */ PoolStruct poolStruct; /* generic structure */ - SegPref segPref; /* the preferences for segments */ - Size extendBy; /* segment size to extend pool by */ - Size minSegSize; /* minimum size of segment */ + LocusPrefStruct locusPrefStruct; /* the preferences for allocation */ + Size extendBy; /* size to extend pool by */ Size avgSize; /* client estimate of allocation size */ - Size total; /* total bytes in pool */ - Size free; /* total free bytes in pool */ - CBSStruct cbsStruct; /* free list */ - FreelistStruct flStruct; /* emergency free list */ + double spare; /* spare space fraction, see MVFFReduce */ + MFSStruct cbsBlockPoolStruct; /* stores blocks for CBSs */ + CBSStruct totalCBSStruct; /* all memory allocated from the arena */ + CBSStruct freeCBSStruct; /* free memory (primary) */ + FreelistStruct flStruct; /* free memory (secondary, for emergencies) */ + FailoverStruct foStruct; /* free memory (fail-over mechanism) */ Bool firstFit; /* as opposed to last fit */ Bool slotHigh; /* prefers high part of large block */ Sig sig; /* */ } MVFFStruct; -#define Pool2MVFF(pool) PARENT(MVFFStruct, poolStruct, pool) -#define MVFF2Pool(mvff) (&((mvff)->poolStruct)) -#define CBSOfMVFF(mvff) (&((mvff)->cbsStruct)) -#define MVFFOfCBS(cbs) PARENT(MVFFStruct, cbsStruct, cbs) -#define FreelistOfMVFF(mvff) (&((mvff)->flStruct)) -#define MVFFOfFreelist(fl) PARENT(MVFFStruct, flStruct, fl) +#define PoolMVFF(pool) PARENT(MVFFStruct, poolStruct, pool) +#define MVFFPool(mvff) (&(mvff)->poolStruct) +#define MVFFTotalLand(mvff) CBSLand(&(mvff)->totalCBSStruct) +#define MVFFFreePrimary(mvff) CBSLand(&(mvff)->freeCBSStruct) +#define MVFFFreeSecondary(mvff) FreelistLand(&(mvff)->flStruct) +#define MVFFFreeLand(mvff) FailoverLand(&(mvff)->foStruct) +#define MVFFLocusPref(mvff) (&(mvff)->locusPrefStruct) +#define MVFFBlockPool(mvff) MFSPool(&(mvff)->cbsBlockPoolStruct) static Bool MVFFCheck(MVFF mvff); @@ -80,246 +86,215 @@ typedef MVFFDebugStruct *MVFFDebug; #define MVFFDebug2MVFF(mvffd) (&((mvffd)->mvffStruct)) -/* MVFFAddToFreeList -- Add given range to free list +/* MVFFReduce -- return memory to the arena * - * Updates MVFF counters for additional free space. Returns maximally - * coalesced range containing given range. Does not attempt to free - * segments (see MVFFFreeSegs). + * This is usually called immediately after inserting a range into the + * MVFFFreeLand. (But not in all cases: see MVFFExtend.) */ -static Res MVFFAddToFreeList(Addr *baseIO, Addr *limitIO, MVFF mvff) { - Res res; - RangeStruct range, newRange; +static void MVFFReduce(MVFF mvff) +{ + Arena arena; + Size freeSize, freeLimit, targetFree; + RangeStruct freeRange, oldFreeRange; + Align grainSize; - AVER(baseIO != NULL); - AVER(limitIO != NULL); AVERT(MVFF, mvff); - RangeInit(&range, *baseIO, *limitIO); + arena = PoolArena(MVFFPool(mvff)); - res = CBSInsert(&newRange, CBSOfMVFF(mvff), &range); - if (ResIsAllocFailure(res)) { - /* CBS ran out of memory for splay nodes: add range to emergency - * free list instead. */ - res = FreelistInsert(&newRange, FreelistOfMVFF(mvff), &range); - } + /* NOTE: Memory is returned to the arena in the smallest units + possible (arena grains). There's a possibility that this could + lead to fragmentation in the arena (because allocation is in + multiples of mvff->extendBy). If so, try setting grainSize = + mvff->extendBy here. */ - if (res == ResOK) { - mvff->free += RangeSize(&range); - *baseIO = RangeBase(&newRange); - *limitIO = RangeLimit(&newRange); - } + grainSize = ArenaGrainSize(arena); - return res; -} + /* Try to return memory when the amount of free memory exceeds a + threshold fraction of the total memory. */ + freeLimit = (Size)(LandSize(MVFFTotalLand(mvff)) * mvff->spare); + freeSize = LandSize(MVFFFreeLand(mvff)); + if (freeSize < freeLimit) + return; -/* MVFFFreeSegs -- Free segments from given range - * - * Given a free range, attempts to find entire segments within - * it, and returns them to the arena, updating total size counter. - * - * This is usually called immediately after MVFFAddToFreeList. - * It is not combined with MVFFAddToFreeList because the latter - * is also called when new segments are added under MVFFAlloc. - */ -static void MVFFFreeSegs(MVFF mvff, Addr base, Addr limit) -{ - Seg seg = NULL; /* suppress "may be used uninitialized" */ - Arena arena; - Bool b; - Addr segLimit; /* limit of the current segment when iterating */ - Addr segBase; /* base of the current segment when iterating */ - Res res; + /* For hysteresis, return only a proportion of the free memory. */ - AVERT(MVFF, mvff); - AVER(base < limit); - /* Could profitably AVER that the given range is free, */ - /* but the CBS doesn't provide that facility. */ + targetFree = freeLimit / 2; - if (AddrOffset(base, limit) < mvff->minSegSize) - return; /* not large enough for entire segments */ + /* Each time around this loop we either break, or we free at least + one grain back to the arena, thus ensuring that eventually the + loop will terminate */ - arena = PoolArena(MVFF2Pool(mvff)); - b = SegOfAddr(&seg, arena, base); - AVER(b); + /* NOTE: If this code becomes very hot, then the test of whether there's + a large free block in the CBS could be inlined, since it's a property + stored at the root node. */ - segBase = SegBase(seg); - segLimit = SegLimit(seg); - - while(segLimit <= limit) { /* segment ends in range */ - if (segBase >= base) { /* segment starts in range */ - RangeStruct range, oldRange; - RangeInit(&range, segBase, segLimit); - - res = CBSDelete(&oldRange, CBSOfMVFF(mvff), &range); - if (res == ResOK) { - mvff->free -= RangeSize(&range); - } else if (ResIsAllocFailure(res)) { - /* CBS ran out of memory for splay nodes, which must mean that - * there were fragments on both sides: see - * . Handle this by - * deleting the whole of oldRange (which requires no - * allocation) and re-inserting the fragments. */ - RangeStruct oldRange2; - res = CBSDelete(&oldRange2, CBSOfMVFF(mvff), &oldRange); - AVER(res == ResOK); - AVER(RangesEqual(&oldRange2, &oldRange)); - mvff->free -= RangeSize(&oldRange); - AVER(RangeBase(&oldRange) != segBase); - { - Addr leftBase = RangeBase(&oldRange); - Addr leftLimit = segBase; - res = MVFFAddToFreeList(&leftBase, &leftLimit, mvff); - } - AVER(RangeLimit(&oldRange) != segLimit); - { - Addr rightBase = segLimit; - Addr rightLimit = RangeLimit(&oldRange); - res = MVFFAddToFreeList(&rightBase, &rightLimit, mvff); - } - } else if (res == ResFAIL) { - /* Not found in the CBS: must be found in the Freelist. */ - res = FreelistDelete(&oldRange, FreelistOfMVFF(mvff), &range); - AVER(res == ResOK); - mvff->free -= RangeSize(&range); - } + while (freeSize > targetFree + && LandFindLargest(&freeRange, &oldFreeRange, MVFFFreeLand(mvff), + grainSize, FindDeleteNONE)) + { + RangeStruct grainRange, oldRange; + Size size; + Res res; + Addr base, limit; - AVER(res == ResOK); - AVER(RangesNest(&oldRange, &range)); + AVER(RangesEqual(&freeRange, &oldFreeRange)); - /* Can't free the segment earlier, because if it was on the - * Freelist rather than the CBS then it likely contains data - * that needs to be read in order to update the Freelist. */ - SegFree(seg); - mvff->total -= RangeSize(&range); - } + base = AddrAlignUp(RangeBase(&freeRange), grainSize); + limit = AddrAlignDown(RangeLimit(&freeRange), grainSize); + + /* Give up if this block doesn't contain a whole aligned grain, + even though smaller better-aligned blocks might, because + LandFindLargest won't be able to find those anyway. */ + if (base >= limit) + break; + + size = AddrOffset(base, limit); + + /* Don't return (much) more than we need to. */ + if (size > freeSize - targetFree) + size = SizeAlignUp(freeSize - targetFree, grainSize); + + /* Calculate the range of grains we can return to the arena near the + top end of the free memory (because we're first fit). */ + RangeInit(&grainRange, AddrSub(limit, size), limit); + AVER(!RangeIsEmpty(&grainRange)); + AVER(RangesNest(&freeRange, &grainRange)); + AVER(RangeIsAligned(&grainRange, grainSize)); - /* Avoid calling SegNext if the next segment would fail */ - /* the loop test, mainly because there might not be a */ - /* next segment. */ - if (segLimit == limit) /* segment ends at end of range */ + /* Delete the range from the free list before attempting to delete + it from the total allocated memory, so that we don't have + dangling blocks in the free list, even for a moment. If we fail + to delete from the TotalCBS we add back to the free list, which + can't fail. */ + + res = LandDelete(&oldRange, MVFFFreeLand(mvff), &grainRange); + if (res != ResOK) break; + freeSize -= RangeSize(&grainRange); + AVER(freeSize == LandSize(MVFFFreeLand(mvff))); - b = SegFindAboveAddr(&seg, arena, segBase); - AVER(b); - segBase = SegBase(seg); - segLimit = SegLimit(seg); - } + res = LandDelete(&oldRange, MVFFTotalLand(mvff), &grainRange); + if (res != ResOK) { + RangeStruct coalescedRange; + res = LandInsert(&coalescedRange, MVFFFreeLand(mvff), &grainRange); + AVER(res == ResOK); + break; + } - return; + ArenaFree(RangeBase(&grainRange), RangeSize(&grainRange), MVFFPool(mvff)); + } } -/* MVFFAddSeg -- Allocates a new segment from the arena +/* MVFFExtend -- allocate a new range from the arena * - * Allocates a new segment from the arena (with the given - * withReservoirPermit flag) of at least the specified size. The - * specified size should be pool-aligned. Adds it to the free list. + * Allocate a new range from the arena (with the given + * withReservoirPermit flag) of at least the specified size. The + * specified size should be pool-aligned. Add it to the allocated and + * free lists. */ -static Res MVFFAddSeg(Seg *segReturn, - MVFF mvff, Size size, Bool withReservoirPermit) +static Res MVFFExtend(Range rangeReturn, MVFF mvff, Size size, + Bool withReservoirPermit) { Pool pool; Arena arena; - Size segSize; - Seg seg; + Size allocSize; + RangeStruct range, coalescedRange; + Addr base; Res res; - Align align; - Addr base, limit; AVERT(MVFF, mvff); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); - pool = MVFF2Pool(mvff); + pool = MVFFPool(mvff); arena = PoolArena(pool); - align = ArenaAlign(arena); AVER(SizeIsAligned(size, PoolAlignment(pool))); /* Use extendBy unless it's too small (see */ - /* ). */ + /* ). */ if (size <= mvff->extendBy) - segSize = mvff->extendBy; + allocSize = mvff->extendBy; else - segSize = size; + allocSize = size; - segSize = SizeAlignUp(segSize, align); + allocSize = SizeArenaGrains(allocSize, arena); - res = SegAlloc(&seg, SegClassGet(), mvff->segPref, segSize, pool, - withReservoirPermit, argsNone); + res = ArenaAlloc(&base, MVFFLocusPref(mvff), allocSize, pool, + withReservoirPermit); if (res != ResOK) { - /* try again for a seg just large enough for object */ + /* try again with a range just large enough for object */ /* see */ - segSize = SizeAlignUp(size, align); - res = SegAlloc(&seg, SegClassGet(), mvff->segPref, segSize, pool, - withReservoirPermit, argsNone); - if (res != ResOK) { + allocSize = SizeArenaGrains(size, arena); + res = ArenaAlloc(&base, MVFFLocusPref(mvff), allocSize, pool, + withReservoirPermit); + if (res != ResOK) return res; - } } - mvff->total += segSize; - base = SegBase(seg); - limit = AddrAdd(base, segSize); - DebugPoolFreeSplat(pool, base, limit); - res = MVFFAddToFreeList(&base, &limit, mvff); + RangeInitSize(&range, base, allocSize); + res = LandInsert(&coalescedRange, MVFFTotalLand(mvff), &range); + if (res != ResOK) { + /* Can't record this memory, so return it to the arena and fail. */ + ArenaFree(base, allocSize, pool); + return res; + } + + DebugPoolFreeSplat(pool, RangeBase(&range), RangeLimit(&range)); + res = LandInsert(rangeReturn, MVFFFreeLand(mvff), &range); + /* Insertion must succeed because it fails over to a Freelist. */ AVER(res == ResOK); - AVER(base <= SegBase(seg)); - if (mvff->minSegSize > segSize) mvff->minSegSize = segSize; - /* Don't call MVFFFreeSegs; that would be silly. */ + /* Don't call MVFFReduce; that would be silly. */ - *segReturn = seg; return ResOK; } -/* MVFFFindFirstFree -- Finds the first (or last) suitable free block +/* mvffFindFree -- find a suitable free block or add one * - * Finds a free block of the given (pool aligned) size, according - * to a first (or last) fit policy controlled by the MVFF fields - * firstFit, slotHigh (for whether to allocate the top or bottom - * portion of a larger block). + * Finds a free block of the given (pool aligned) size, using the + * policy (first fit, last fit, or worst fit) specified by findMethod + * and findDelete. * - * Will return FALSE if the free list has no large enough block. - * In particular, will not attempt to allocate a new segment. + * If there is no suitable free block, try extending the pool. */ -static Bool MVFFFindFirstFree(Addr *baseReturn, Addr *limitReturn, - MVFF mvff, Size size) +static Res mvffFindFree(Range rangeReturn, MVFF mvff, Size size, + LandFindMethod findMethod, FindDelete findDelete, + Bool withReservoirPermit) { - Bool foundBlock; - FindDelete findDelete; - RangeStruct range, oldRange; + Bool found; + RangeStruct oldRange; + Land land; - AVER(baseReturn != NULL); - AVER(limitReturn != NULL); + AVER(rangeReturn != NULL); AVERT(MVFF, mvff); AVER(size > 0); - AVER(SizeIsAligned(size, PoolAlignment(MVFF2Pool(mvff)))); - - FreelistFlushToCBS(FreelistOfMVFF(mvff), CBSOfMVFF(mvff)); - - findDelete = mvff->slotHigh ? FindDeleteHIGH : FindDeleteLOW; - - foundBlock = - (mvff->firstFit ? CBSFindFirst : CBSFindLast) - (&range, &oldRange, CBSOfMVFF(mvff), size, findDelete); + AVER(SizeIsAligned(size, PoolAlignment(MVFFPool(mvff)))); + AVER(FUNCHECK(findMethod)); + AVERT(FindDelete, findDelete); + AVERT(Bool, withReservoirPermit); - if (!foundBlock) { - /* Failed to find a block in the CBS: try the emergency free list - * as well. */ - foundBlock = - (mvff->firstFit ? FreelistFindFirst : FreelistFindLast) - (&range, &oldRange, FreelistOfMVFF(mvff), size, findDelete); - } + land = MVFFFreeLand(mvff); + found = (*findMethod)(rangeReturn, &oldRange, land, size, findDelete); + if (!found) { + RangeStruct newRange; + Res res; + res = MVFFExtend(&newRange, mvff, size, withReservoirPermit); + if (res != ResOK) + return res; + found = (*findMethod)(rangeReturn, &oldRange, land, size, findDelete); - if (foundBlock) { - *baseReturn = RangeBase(&range); - *limitReturn = RangeLimit(&range); - mvff->free -= size; + /* We know that the found range must intersect the newly added + * range. But it doesn't necessarily lie entirely within it. */ + AVER(found); + AVER(RangesOverlap(rangeReturn, &newRange)); } + AVER(found); - return foundBlock; + return ResOK; } @@ -330,43 +305,28 @@ static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size, { Res res; MVFF mvff; - Addr base, limit; - Bool foundBlock; + RangeStruct range; + LandFindMethod findMethod; + FindDelete findDelete; + AVER(aReturn != NULL); AVERT(Pool, pool); - mvff = Pool2MVFF(pool); + mvff = PoolMVFF(pool); AVERT(MVFF, mvff); - - AVER(aReturn != NULL); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); size = SizeAlignUp(size, PoolAlignment(pool)); + findMethod = mvff->firstFit ? LandFindFirst : LandFindLast; + findDelete = mvff->slotHigh ? FindDeleteHIGH : FindDeleteLOW; - foundBlock = MVFFFindFirstFree(&base, &limit, mvff, size); - if (!foundBlock) { - Seg seg; - - res = MVFFAddSeg(&seg, mvff, size, withReservoirPermit); - if (res != ResOK) - return res; - foundBlock = MVFFFindFirstFree(&base, &limit, mvff, size); - - /* We know that the found range must intersect the new segment. */ - /* In particular, it doesn't necessarily lie entirely within it. */ - /* The next three AVERs test for intersection of two intervals. */ - AVER(base >= SegBase(seg) || limit <= SegLimit(seg)); - AVER(base < SegLimit(seg)); - AVER(SegBase(seg) < limit); - - /* We also know that the found range is no larger than the segment. */ - AVER(SegSize(seg) >= AddrOffset(base, limit)); - } - AVER(foundBlock); - AVER(AddrOffset(base, limit) == size); - - *aReturn = base; + res = mvffFindFree(&range, mvff, size, findMethod, findDelete, + withReservoirPermit); + if (res != ResOK) + return res; + AVER(RangeSize(&range) == size); + *aReturn = RangeBase(&range); return ResOK; } @@ -376,57 +336,29 @@ static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size, static void MVFFFree(Pool pool, Addr old, Size size) { Res res; - Addr base, limit; + RangeStruct range, coalescedRange; MVFF mvff; AVERT(Pool, pool); - mvff = Pool2MVFF(pool); + mvff = PoolMVFF(pool); AVERT(MVFF, mvff); AVER(old != (Addr)0); AVER(AddrIsAligned(old, PoolAlignment(pool))); AVER(size > 0); - size = SizeAlignUp(size, PoolAlignment(pool)); - base = old; - limit = AddrAdd(base, size); - - res = MVFFAddToFreeList(&base, &limit, mvff); + RangeInitSize(&range, old, SizeAlignUp(size, PoolAlignment(pool))); + res = LandInsert(&coalescedRange, MVFFFreeLand(mvff), &range); + /* Insertion must succeed because it fails over to a Freelist. */ AVER(res == ResOK); - if (res == ResOK) - MVFFFreeSegs(mvff, base, limit); - - return; -} - -/* MVFFFindLargest -- call CBSFindLargest and then fall back to - * FreelistFindLargest if no block in the CBS was big enough. */ - -static Bool MVFFFindLargest(Range range, Range oldRange, MVFF mvff, - Size size, FindDelete findDelete) -{ - AVER(range != NULL); - AVER(oldRange != NULL); - AVERT(MVFF, mvff); - AVER(size > 0); - AVERT(FindDelete, findDelete); - - FreelistFlushToCBS(FreelistOfMVFF(mvff), CBSOfMVFF(mvff)); - - if (CBSFindLargest(range, oldRange, CBSOfMVFF(mvff), size, findDelete)) - return TRUE; - - if (FreelistFindLargest(range, oldRange, FreelistOfMVFF(mvff), - size, findDelete)) - return TRUE; - - return FALSE; + MVFFReduce(mvff); } /* MVFFBufferFill -- Fill the buffer * - * Fill it with the largest block we can find. + * Fill it with the largest block we can find. This is worst-fit + * allocation policy; see . */ static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn, Pool pool, Buffer buffer, Size size, @@ -434,31 +366,23 @@ static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn, { Res res; MVFF mvff; - RangeStruct range, oldRange; - Bool found; - Seg seg = NULL; + RangeStruct range; + AVER(baseReturn != NULL); AVER(limitReturn != NULL); AVERT(Pool, pool); - mvff = Pool2MVFF(pool); + mvff = PoolMVFF(pool); AVERT(MVFF, mvff); AVERT(Buffer, buffer); AVER(size > 0); AVER(SizeIsAligned(size, PoolAlignment(pool))); AVERT(Bool, withReservoirPermit); - found = MVFFFindLargest(&range, &oldRange, mvff, size, FindDeleteENTIRE); - if (!found) { - /* Add a new segment to the free list and try again. */ - res = MVFFAddSeg(&seg, mvff, size, withReservoirPermit); - if (res != ResOK) - return res; - found = MVFFFindLargest(&range, &oldRange, mvff, size, FindDeleteENTIRE); - } - AVER(found); - + res = mvffFindFree(&range, mvff, size, LandFindLargest, FindDeleteENTIRE, + withReservoirPermit); + if (res != ResOK) + return res; AVER(RangeSize(&range) >= size); - mvff->free -= RangeSize(&range); *baseReturn = RangeBase(&range); *limitReturn = RangeLimit(&range); @@ -473,23 +397,21 @@ static void MVFFBufferEmpty(Pool pool, Buffer buffer, { Res res; MVFF mvff; + RangeStruct range, coalescedRange; AVERT(Pool, pool); - mvff = Pool2MVFF(pool); + mvff = PoolMVFF(pool); AVERT(MVFF, mvff); AVERT(Buffer, buffer); AVER(BufferIsReady(buffer)); - AVER(base <= limit); + RangeInit(&range, base, limit); - if (base == limit) + if (RangeIsEmpty(&range)) return; - res = MVFFAddToFreeList(&base, &limit, mvff); + res = LandInsert(&coalescedRange, MVFFFreeLand(mvff), &range); AVER(res == ResOK); - if (res == ResOK) - MVFFFreeSegs(mvff, base, limit); - - return; + MVFFReduce(mvff); } @@ -510,7 +432,7 @@ static void MVFFVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[5].key = MPS_KEY_MVFF_FIRST_FIT; args[5].val.b = va_arg(varargs, Bool); args[6].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } static void MVFFDebugVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) @@ -523,23 +445,22 @@ static void MVFFDebugVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) /* MVFFInit -- initialize method for MVFF */ -ARG_DEFINE_KEY(mvff_slot_high, Bool); -ARG_DEFINE_KEY(mvff_arena_high, Bool); -ARG_DEFINE_KEY(mvff_first_fit, Bool); +ARG_DEFINE_KEY(MVFF_SLOT_HIGH, Bool); +ARG_DEFINE_KEY(MVFF_ARENA_HIGH, Bool); +ARG_DEFINE_KEY(MVFF_FIRST_FIT, Bool); static Res MVFFInit(Pool pool, ArgList args) { Size extendBy = MVFF_EXTEND_BY_DEFAULT; Size avgSize = MVFF_AVG_SIZE_DEFAULT; - Size align = MVFF_ALIGN_DEFAULT; + Align align = MVFF_ALIGN_DEFAULT; Bool slotHigh = MVFF_SLOT_HIGH_DEFAULT; Bool arenaHigh = MVFF_ARENA_HIGH_DEFAULT; Bool firstFit = MVFF_FIRST_FIT_DEFAULT; + double spare = MVFF_SPARE_DEFAULT; MVFF mvff; Arena arena; Res res; - void *p; - ZoneSet zones; ArgStruct arg; AVERT(Pool, pool); @@ -548,7 +469,7 @@ static Res MVFFInit(Pool pool, ArgList args) /* .arg: class-specific additional arguments; see */ /* */ /* .arg.check: we do the same checks here and in MVFFCheck */ - /* except for arenaHigh, which is stored only in the segPref. */ + /* except for arenaHigh, which is stored only in the locusPref. */ if (ArgPick(&arg, args, MPS_KEY_EXTEND_BY)) extendBy = arg.val.size; @@ -559,6 +480,9 @@ static Res MVFFInit(Pool pool, ArgList args) if (ArgPick(&arg, args, MPS_KEY_ALIGN)) align = arg.val.align; + if (ArgPick(&arg, args, MPS_KEY_SPARE)) + spare = arg.val.d; + if (ArgPick(&arg, args, MPS_KEY_MVFF_SLOT_HIGH)) slotHigh = arg.val.b; @@ -571,88 +495,134 @@ static Res MVFFInit(Pool pool, ArgList args) AVER(extendBy > 0); /* .arg.check */ AVER(avgSize > 0); /* .arg.check */ AVER(avgSize <= extendBy); /* .arg.check */ - AVER(SizeIsAligned(align, MPS_PF_ALIGN)); - AVER(BoolCheck(slotHigh)); - AVER(BoolCheck(arenaHigh)); - AVER(BoolCheck(firstFit)); - - mvff = Pool2MVFF(pool); + AVER(spare >= 0.0); /* .arg.check */ + AVER(spare <= 1.0); /* .arg.check */ + AVERT(Align, align); + /* This restriction on the alignment is necessary because of the use + * of a Freelist to store the free address ranges in low-memory + * situations. . + */ + AVER(AlignIsAligned(align, FreelistMinimumAlignment)); + AVERT(Bool, slotHigh); + AVERT(Bool, arenaHigh); + AVERT(Bool, firstFit); + + mvff = PoolMVFF(pool); mvff->extendBy = extendBy; - if (extendBy < ArenaAlign(arena)) - mvff->minSegSize = ArenaAlign(arena); - else - mvff->minSegSize = extendBy; + if (extendBy < ArenaGrainSize(arena)) + mvff->extendBy = ArenaGrainSize(arena); mvff->avgSize = avgSize; pool->alignment = align; mvff->slotHigh = slotHigh; mvff->firstFit = firstFit; + mvff->spare = spare; - res = ControlAlloc(&p, arena, sizeof(SegPrefStruct), FALSE); - if (res != ResOK) - return res; + LocusPrefInit(MVFFLocusPref(mvff)); + LocusPrefExpress(MVFFLocusPref(mvff), + arenaHigh ? LocusPrefHIGH : LocusPrefLOW, NULL); - mvff->segPref = (SegPref)p; - SegPrefInit(mvff->segPref); - SegPrefExpress(mvff->segPref, arenaHigh ? SegPrefHigh : SegPrefLow, NULL); - /* If using zoneset placement, just put it apart from the others. */ - zones = ZoneSetComp(ArenaDefaultZONESET); - SegPrefExpress(mvff->segPref, SegPrefZoneSet, (void *)&zones); + /* An MFS pool is explicitly initialised for the two CBSs partly to + * share space, but mostly to avoid a call to PoolCreate, so that + * MVFF can be used during arena bootstrap as the control pool. */ - mvff->total = 0; - mvff->free = 0; + MPS_ARGS_BEGIN(piArgs) { + MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSFastBlockStruct)); + res = PoolInit(MVFFBlockPool(mvff), arena, PoolClassMFS(), piArgs); + } MPS_ARGS_END(piArgs); + if (res != ResOK) + goto failBlockPoolInit; - res = FreelistInit(FreelistOfMVFF(mvff), align); + MPS_ARGS_BEGIN(liArgs) { + MPS_ARGS_ADD(liArgs, CBSBlockPool, MVFFBlockPool(mvff)); + res = LandInit(MVFFTotalLand(mvff), CBSFastLandClassGet(), arena, align, + mvff, liArgs); + } MPS_ARGS_END(liArgs); if (res != ResOK) - goto failInit; + goto failTotalLandInit; - res = CBSInit(arena, CBSOfMVFF(mvff), (void *)mvff, align, TRUE, args); + MPS_ARGS_BEGIN(liArgs) { + MPS_ARGS_ADD(liArgs, CBSBlockPool, MVFFBlockPool(mvff)); + res = LandInit(MVFFFreePrimary(mvff), CBSFastLandClassGet(), arena, align, + mvff, liArgs); + } MPS_ARGS_END(liArgs); if (res != ResOK) - goto failInit; + goto failFreePrimaryInit; + + res = LandInit(MVFFFreeSecondary(mvff), FreelistLandClassGet(), arena, align, + mvff, mps_args_none); + if (res != ResOK) + goto failFreeSecondaryInit; + + MPS_ARGS_BEGIN(foArgs) { + MPS_ARGS_ADD(foArgs, FailoverPrimary, MVFFFreePrimary(mvff)); + MPS_ARGS_ADD(foArgs, FailoverSecondary, MVFFFreeSecondary(mvff)); + res = LandInit(MVFFFreeLand(mvff), FailoverLandClassGet(), arena, align, + mvff, foArgs); + } MPS_ARGS_END(foArgs); + if (res != ResOK) + goto failFreeLandInit; mvff->sig = MVFFSig; AVERT(MVFF, mvff); EVENT8(PoolInitMVFF, pool, arena, extendBy, avgSize, align, - slotHigh, arenaHigh, firstFit); + BOOLOF(slotHigh), BOOLOF(arenaHigh), BOOLOF(firstFit)); return ResOK; -failInit: - ControlFree(arena, p, sizeof(SegPrefStruct)); +failFreeLandInit: + LandFinish(MVFFFreeSecondary(mvff)); +failFreeSecondaryInit: + LandFinish(MVFFFreePrimary(mvff)); +failFreePrimaryInit: + LandFinish(MVFFTotalLand(mvff)); +failTotalLandInit: + PoolFinish(MVFFBlockPool(mvff)); +failBlockPoolInit: return res; } /* MVFFFinish -- finish method for MVFF */ -static void MVFFFinish(Pool pool) +static Bool mvffFinishVisitor(Bool *deleteReturn, Land land, Range range, + void *closureP, Size closureS) { - MVFF mvff; - Arena arena; - Seg seg; - Ring ring, node, nextNode; + Pool pool; + AVER(deleteReturn != NULL); + AVERT(Land, land); + AVERT(Range, range); + AVER(closureP != NULL); + pool = closureP; AVERT(Pool, pool); - mvff = Pool2MVFF(pool); - AVERT(MVFF, mvff); + AVER(closureS == UNUSED_SIZE); + UNUSED(closureS); - ring = PoolSegRing(pool); - RING_FOR(node, ring, nextNode) { - seg = SegOfPoolRing(node); - AVER(SegPool(seg) == pool); - SegFree(seg); - } + ArenaFree(RangeBase(range), RangeSize(range), pool); + *deleteReturn = TRUE; + return TRUE; +} - /* Could maintain mvff->total here and check it falls to zero, */ - /* but that would just make the function slow. If only we had */ - /* a way to do operations only if AVERs are turned on. */ +static void MVFFFinish(Pool pool) +{ + MVFF mvff; + Bool b; - arena = PoolArena(pool); - ControlFree(arena, mvff->segPref, sizeof(SegPrefStruct)); + AVERT(Pool, pool); + mvff = PoolMVFF(pool); + AVERT(MVFF, mvff); + mvff->sig = SigInvalid; - CBSFinish(CBSOfMVFF(mvff)); - FreelistFinish(FreelistOfMVFF(mvff)); + b = LandIterateAndDelete(MVFFTotalLand(mvff), mvffFinishVisitor, pool, + UNUSED_SIZE); + AVER(b); + AVER(LandSize(MVFFTotalLand(mvff)) == 0); - mvff->sig = SigInvalid; + LandFinish(MVFFFreeLand(mvff)); + LandFinish(MVFFFreeSecondary(mvff)); + LandFinish(MVFFFreePrimary(mvff)); + LandFinish(MVFFTotalLand(mvff)); + PoolFinish(MVFFBlockPool(mvff)); } @@ -663,54 +633,96 @@ static PoolDebugMixin MVFFDebugMixin(Pool pool) MVFF mvff; AVERT(Pool, pool); - mvff = Pool2MVFF(pool); + mvff = PoolMVFF(pool); AVERT(MVFF, mvff); /* Can't check MVFFDebug, because this is called during init */ return &(MVFF2MVFFDebug(mvff)->debug); } +/* MVFFTotalSize -- total memory allocated from the arena */ + +static Size MVFFTotalSize(Pool pool) +{ + MVFF mvff; + + AVERT(Pool, pool); + mvff = PoolMVFF(pool); + AVERT(MVFF, mvff); + + return LandSize(MVFFTotalLand(mvff)); +} + + +/* MVFFFreeSize -- free memory (unused by client program) */ + +static Size MVFFFreeSize(Pool pool) +{ + MVFF mvff; + + AVERT(Pool, pool); + mvff = PoolMVFF(pool); + AVERT(MVFF, mvff); + + return LandSize(MVFFFreeLand(mvff)); +} + + /* MVFFDescribe -- describe an MVFF pool */ -static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream) +static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream, Count depth) { Res res; MVFF mvff; - if (!TESTT(Pool, pool)) return ResFAIL; - mvff = Pool2MVFF(pool); - if (!TESTT(MVFF, mvff)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Pool, pool)) + return ResFAIL; + mvff = PoolMVFF(pool); + if (!TESTT(MVFF, mvff)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = WriteF(stream, + res = WriteF(stream, depth, "MVFF $P {\n", (WriteFP)mvff, " pool $P ($U)\n", (WriteFP)pool, (WriteFU)pool->serial, " extendBy $W\n", (WriteFW)mvff->extendBy, " avgSize $W\n", (WriteFW)mvff->avgSize, - " total $U\n", (WriteFU)mvff->total, - " free $U\n", (WriteFU)mvff->free, + " firstFit $U\n", (WriteFU)mvff->firstFit, + " slotHigh $U\n", (WriteFU)mvff->slotHigh, + " spare $D\n", (WriteFD)mvff->spare, NULL); if (res != ResOK) return res; - res = CBSDescribe(CBSOfMVFF(mvff), stream); + res = LocusPrefDescribe(MVFFLocusPref(mvff), stream, depth + 2); if (res != ResOK) return res; - res = FreelistDescribe(FreelistOfMVFF(mvff), stream); + /* Don't describe MVFFBlockPool(mvff) otherwise it'll appear twice + * in the output of GlobalDescribe. */ + + res = LandDescribe(MVFFTotalLand(mvff), stream, depth + 2); if (res != ResOK) return res; - res = WriteF(stream, "}\n", NULL); + res = LandDescribe(MVFFFreePrimary(mvff), stream, depth + 2); + if (res != ResOK) + return res; + res = LandDescribe(MVFFFreeSecondary(mvff), stream, depth + 2); + if (res != ResOK) + return res; + + res = WriteF(stream, depth, "} MVFF $P\n", (WriteFP)mvff, NULL); return res; } DEFINE_POOL_CLASS(MVFFPoolClass, this) { - INHERIT_CLASS(this, AbstractAllocFreePoolClass); + INHERIT_CLASS(this, AbstractPoolClass); PoolClassMixInBuffer(this); this->name = "MVFF"; this->size = sizeof(MVFFStruct); @@ -722,7 +734,10 @@ DEFINE_POOL_CLASS(MVFFPoolClass, this) this->free = MVFFFree; this->bufferFill = MVFFBufferFill; this->bufferEmpty = MVFFBufferEmpty; + this->totalSize = MVFFTotalSize; + this->freeSize = MVFFFreeSize; this->describe = MVFFDescribe; + AVERT(PoolClass, this); } @@ -742,71 +757,46 @@ DEFINE_POOL_CLASS(MVFFDebugPoolClass, this) this->size = sizeof(MVFFDebugStruct); this->varargs = MVFFDebugVarargs; this->debugMixin = MVFFDebugMixin; + AVERT(PoolClass, this); } /* MPS Interface Extensions. */ -mps_class_t mps_class_mvff(void) +mps_pool_class_t mps_class_mvff(void) { - return (mps_class_t)(MVFFPoolClassGet()); + return (mps_pool_class_t)(MVFFPoolClassGet()); } -mps_class_t mps_class_mvff_debug(void) +mps_pool_class_t mps_class_mvff_debug(void) { - return (mps_class_t)(MVFFDebugPoolClassGet()); -} - - -/* Total free bytes. See */ - -size_t mps_mvff_free_size(mps_pool_t mps_pool) -{ - Pool pool; - MVFF mvff; - - pool = (Pool)mps_pool; - AVERT(Pool, pool); - mvff = Pool2MVFF(pool); - AVERT(MVFF, mvff); - - return (size_t)mvff->free; -} - -/* Total owned bytes. See */ - -size_t mps_mvff_size(mps_pool_t mps_pool) -{ - Pool pool; - MVFF mvff; - - pool = (Pool)mps_pool; - AVERT(Pool, pool); - mvff = Pool2MVFF(pool); - AVERT(MVFF, mvff); - - return (size_t)mvff->total; + return (mps_pool_class_t)(MVFFDebugPoolClassGet()); } /* MVFFCheck -- check the consistency of an MVFF structure */ +ATTRIBUTE_UNUSED static Bool MVFFCheck(MVFF mvff) { CHECKS(MVFF, mvff); - CHECKD(Pool, MVFF2Pool(mvff)); - CHECKL(IsSubclassPoly(MVFF2Pool(mvff)->class, MVFFPoolClassGet())); - CHECKD(SegPref, mvff->segPref); - CHECKL(mvff->extendBy > 0); /* see .arg.check */ - CHECKL(mvff->minSegSize >= ArenaAlign(PoolArena(MVFF2Pool(mvff)))); + CHECKD(Pool, MVFFPool(mvff)); + CHECKL(IsSubclassPoly(MVFFPool(mvff)->class, MVFFPoolClassGet())); + CHECKD(LocusPref, MVFFLocusPref(mvff)); + CHECKL(mvff->extendBy >= ArenaGrainSize(PoolArena(MVFFPool(mvff)))); CHECKL(mvff->avgSize > 0); /* see .arg.check */ CHECKL(mvff->avgSize <= mvff->extendBy); /* see .arg.check */ - CHECKL(mvff->total >= mvff->free); - CHECKL(SizeIsAligned(mvff->free, PoolAlignment(MVFF2Pool(mvff)))); - CHECKL(SizeIsAligned(mvff->total, ArenaAlign(PoolArena(MVFF2Pool(mvff))))); - CHECKD(CBS, CBSOfMVFF(mvff)); - CHECKD(Freelist, FreelistOfMVFF(mvff)); + CHECKL(mvff->spare >= 0.0); /* see .arg.check */ + CHECKL(mvff->spare <= 1.0); /* see .arg.check */ + CHECKD(MFS, &mvff->cbsBlockPoolStruct); + CHECKD(CBS, &mvff->totalCBSStruct); + CHECKD(CBS, &mvff->freeCBSStruct); + CHECKD(Freelist, &mvff->flStruct); + CHECKD(Failover, &mvff->foStruct); + CHECKL(LandSize(MVFFTotalLand(mvff)) >= LandSize(MVFFFreeLand(mvff))); + CHECKL(SizeIsAligned(LandSize(MVFFFreeLand(mvff)), PoolAlignment(MVFFPool(mvff)))); + CHECKL(SizeIsArenaGrains(LandSize(MVFFTotalLand(mvff)), PoolArena(MVFFPool(mvff)))); CHECKL(BoolCheck(mvff->slotHigh)); CHECKL(BoolCheck(mvff->firstFit)); return TRUE; @@ -815,23 +805,21 @@ static Bool MVFFCheck(MVFF mvff) /* Return the CBS of an MVFF pool for the benefit of fotest.c. */ -extern CBS _mps_mvff_cbs(mps_pool_t); -CBS _mps_mvff_cbs(mps_pool_t mps_pool) { - Pool pool; +extern Land _mps_mvff_cbs(Pool); +Land _mps_mvff_cbs(Pool pool) { MVFF mvff; - pool = (Pool)mps_pool; AVERT(Pool, pool); - mvff = Pool2MVFF(pool); + mvff = PoolMVFF(pool); AVERT(MVFF, mvff); - return CBSOfMVFF(mvff); + return MVFFFreePrimary(mvff); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/pooln.c b/code/pooln.c index f0851f56fd..88a7b39222 100644 --- a/code/pooln.c +++ b/code/pooln.c @@ -1,7 +1,7 @@ /* pooln.c: NULL POOL CLASS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "pooln.h" @@ -71,7 +71,7 @@ static Res NAlloc(Addr *pReturn, Pool pool, Size size, AVER(pReturn != NULL); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); return ResLIMIT; /* limit of nil blocks exceeded */ } @@ -110,7 +110,7 @@ static Res NBufferFill(Addr *baseReturn, Addr *limitReturn, AVERT(Buffer, buffer); AVER(BufferIsReset(buffer)); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); NOTREACHED; /* can't create buffers, so shouldn't fill them */ return ResUNIMPL; @@ -133,7 +133,7 @@ static void NBufferEmpty(Pool pool, Buffer buffer, /* NDescribe -- describe method for class N */ -static Res NDescribe(Pool pool, mps_lib_FILE *stream) +static Res NDescribe(Pool pool, mps_lib_FILE *stream, Count depth) { PoolN poolN; @@ -142,6 +142,7 @@ static Res NDescribe(Pool pool, mps_lib_FILE *stream) AVERT(PoolN, poolN); UNUSED(stream); /* TODO: should output something here */ + UNUSED(depth); return ResOK; } @@ -234,7 +235,7 @@ static Res NFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) /* NReclaim -- reclaim method for class N */ -static void NReclaim(Pool pool, Trace trace, Seg seg) +static Bool NReclaim(Pool pool, Trace trace, Seg seg) { PoolN poolN; @@ -245,6 +246,8 @@ static void NReclaim(Pool pool, Trace trace, Seg seg) AVERT(Trace, trace); AVERT(Seg, seg); /* all unmarked and white objects reclaimed */ + + return FALSE; /* segment wasn't deleted */ } @@ -270,8 +273,7 @@ DEFINE_POOL_CLASS(NPoolClass, this) this->name = "N"; this->size = sizeof(PoolNStruct); this->offset = offsetof(PoolNStruct, poolStruct); - this->attr = AttrSCAN | AttrALLOC | AttrFREE | AttrBUF | - AttrBUF_RESERVE | AttrGC; + this->attr |= AttrGC; this->init = NInit; this->finish = NFinish; this->alloc = NAlloc; @@ -287,6 +289,7 @@ DEFINE_POOL_CLASS(NPoolClass, this) this->reclaim = NReclaim; this->traceEnd = NTraceEnd; this->describe = NDescribe; + AVERT(PoolClass, this); } @@ -303,8 +306,8 @@ PoolClass PoolClassN(void) Bool PoolNCheck(PoolN poolN) { CHECKL(poolN != NULL); - CHECKD(Pool, &poolN->poolStruct); - CHECKL(poolN->poolStruct.class == EnsureNPoolClass()); + CHECKD(Pool, PoolNPool(poolN)); + CHECKL(PoolNPool(poolN)->class == EnsureNPoolClass()); UNUSED(poolN); /* */ return TRUE; @@ -313,7 +316,7 @@ Bool PoolNCheck(PoolN poolN) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolncv.c b/code/poolncv.c index deee10715d..a3f85342cb 100644 --- a/code/poolncv.c +++ b/code/poolncv.c @@ -1,14 +1,16 @@ /* poolncv.c: NULL POOL COVERAGE TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" -#include "pooln.h" #include "mpsavm.h" -#include "testlib.h" #include "mpslib.h" +#include "pooln.h" +#include "testlib.h" + +#include /* printf */ static void testit(ArenaClass class, ArgList args) @@ -26,6 +28,7 @@ static void testit(ArenaClass class, ArgList args) error("Error: Unexpectedly succeeded in" "allocating block from PoolN\n"); } + PoolDescribe(pool, mps_lib_get_stdout(), 0); PoolDestroy(pool); ArenaDestroy(arena); } @@ -33,7 +36,7 @@ static void testit(ArenaClass class, ArgList args) int main(int argc, char *argv[]) { - testlib_unused(argc); + testlib_init(argc, argv); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 600000); testit((ArenaClass)mps_arena_class_vm(), args); @@ -45,7 +48,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/poolsnc.c b/code/poolsnc.c index 87133f5571..f507d41222 100644 --- a/code/poolsnc.c +++ b/code/poolsnc.c @@ -1,7 +1,7 @@ /* poolsnc.c: STACK NO CHECKING POOL CLASS * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -24,9 +24,6 @@ SRCID(poolsnc, "$Id$"); -#define SNCGen ((Serial)1) /* "generation" for SNC pools */ - - /* SNCStruct -- structure for an SNC pool * * See design.mps.poolsnc.poolstruct. @@ -37,12 +34,11 @@ SRCID(poolsnc, "$Id$"); typedef struct SNCStruct { PoolStruct poolStruct; Seg freeSegs; - SegPrefStruct segPrefStruct; Sig sig; } SNCStruct, *SNC; -#define Pool2SNC(pool) \ - PARENT(SNCStruct, poolStruct, (pool)) +#define PoolSNC(pool) PARENT(SNCStruct, poolStruct, (pool)) +#define SNCPool(snc) (&(snc)->poolStruct) /* Forward declarations */ @@ -85,15 +81,16 @@ typedef struct SNCBufStruct { /* SNCBufCheck -- check consistency of an SNCBuf */ +ATTRIBUTE_UNUSED static Bool SNCBufCheck(SNCBuf sncbuf) { SegBuf segbuf; CHECKS(SNCBuf, sncbuf); segbuf = &sncbuf->segBufStruct; - CHECKL(SegBufCheck(segbuf)); + CHECKD(SegBuf, segbuf); if (sncbuf->topseg != NULL) { - CHECKL(SegCheck(sncbuf->topseg)); + CHECKD(Seg, sncbuf->topseg); } return TRUE; } @@ -165,7 +162,7 @@ static void SNCBufFinish(Buffer buffer) AVERT(SNCBuf, sncbuf); pool = BufferPool(buffer); - snc = Pool2SNC(pool); + snc = PoolSNC(pool); /* Put any segments which haven't bee popped onto the free list */ sncPopPartialSegChain(snc, buffer, NULL); @@ -186,6 +183,7 @@ DEFINE_BUFFER_CLASS(SNCBufClass, class) class->size = sizeof(SNCBufStruct); class->init = SNCBufInit; class->finish = SNCBufFinish; + AVERT(BufferClass, class); } @@ -214,10 +212,11 @@ typedef struct SNCSegStruct { #define sncSegSetNext(seg, nextseg) \ ((void)(SegSNCSeg(seg)->next = SegSNCSeg(nextseg))) +ATTRIBUTE_UNUSED static Bool SNCSegCheck(SNCSeg sncseg) { CHECKS(SNCSeg, sncseg); - CHECKL(GCSegCheck(&sncseg->gcSegStruct)); + CHECKD(GCSeg, &sncseg->gcSegStruct); if (NULL != sncseg->next) { CHECKS(SNCSeg, sncseg->next); } @@ -238,7 +237,7 @@ static Res sncSegInit(Seg seg, Pool pool, Addr base, Size size, sncseg = SegSNCSeg(seg); AVERT(Pool, pool); /* no useful checks for base and size */ - AVER(BoolCheck(reservoirPermit)); + AVERT(Bool, reservoirPermit); /* Initialize the superclass fields first via next-method call */ super = SEG_SUPERCLASS(SNCSegClass); @@ -262,6 +261,7 @@ DEFINE_SEG_CLASS(SNCSegClass, class) class->name = "SNCSEG"; class->size = sizeof(SNCSegStruct); class->init = sncSegInit; + AVERT(SegClass, class); } @@ -366,7 +366,7 @@ static void SNCVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) args[0].key = MPS_KEY_FORMAT; args[0].val.format = va_arg(varargs, Format); args[1].key = MPS_KEY_ARGS_END; - AVER(ArgListCheck(args)); + AVERT(ArgList, args); } @@ -381,17 +381,15 @@ static Res SNCInit(Pool pool, ArgList args) /* weak check, as half-way through initialization */ AVER(pool != NULL); - snc = Pool2SNC(pool); + snc = PoolSNC(pool); ArgRequire(&arg, args, MPS_KEY_FORMAT); format = arg.val.format; AVERT(Format, format); + AVER(FormatArena(format) == PoolArena(pool)); pool->format = format; snc->freeSegs = NULL; - /* Use the default segpref for the pool. At least this should avoid */ - /* clashes with collected pools */ - SegPrefInit(&snc->segPrefStruct); snc->sig = SNCSig; AVERT(SNC, snc); @@ -408,7 +406,7 @@ static void SNCFinish(Pool pool) Ring ring, node, nextNode; AVERT(Pool, pool); - snc = Pool2SNC(pool); + snc = PoolSNC(pool); AVERT(SNC, snc); ring = &pool->segRing; @@ -435,10 +433,10 @@ static Res SNCBufferFill(Addr *baseReturn, Addr *limitReturn, AVERT(Pool, pool); AVERT(Buffer, buffer); AVER(size > 0); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); AVER(BufferIsReset(buffer)); - snc = Pool2SNC(pool); + snc = PoolSNC(pool); AVERT(SNC, snc); /* Try to find a free segment with enough space already */ @@ -448,8 +446,8 @@ static Res SNCBufferFill(Addr *baseReturn, Addr *limitReturn, /* No free seg, so create a new one */ arena = PoolArena(pool); - asize = SizeAlignUp(size, ArenaAlign(arena)); - res = SegAlloc(&seg, SNCSegClassGet(), &snc->segPrefStruct, + asize = SizeArenaGrains(size, arena); + res = SegAlloc(&seg, SNCSegClassGet(), LocusPrefDefault(), asize, pool, withReservoirPermit, argsNone); if (res != ResOK) return res; @@ -485,7 +483,7 @@ static void SNCBufferEmpty(Pool pool, Buffer buffer, seg = BufferSeg(buffer); AVER(init <= limit); AVER(SegLimit(seg) == limit); - snc = Pool2SNC(pool); + snc = PoolSNC(pool); AVERT(SNC, snc); AVER(BufferFrameState(buffer) == BufferFrameVALID); /* .lw-frame-state */ @@ -514,7 +512,7 @@ static Res SNCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) AVERT(ScanState, ss); AVERT(Seg, seg); AVERT(Pool, pool); - snc = Pool2SNC(pool); + snc = PoolSNC(pool); AVERT(SNC, snc); format = pool->format; @@ -591,7 +589,7 @@ static void SNCFramePopPending(Pool pool, Buffer buf, AllocFrame frame) AVERT(Pool, pool); AVERT(Buffer, buf); /* frame is an Addr and can't be directly checked */ - snc = Pool2SNC(pool); + snc = PoolSNC(pool); AVERT(SNC, snc); AVER(BufferFrameState(buf) == BufferFrameVALID); @@ -627,7 +625,7 @@ static void SNCFramePopPending(Pool pool, Buffer buf, AllocFrame frame) } -static void SNCWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, +static void SNCWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, void *p, size_t s) { AVERT(Pool, pool); @@ -644,7 +642,7 @@ static void SNCWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, SNC snc; Format format; - snc = Pool2SNC(pool); + snc = PoolSNC(pool); AVERT(SNC, snc); format = pool->format; @@ -686,25 +684,26 @@ DEFINE_POOL_CLASS(SNCPoolClass, this) this->framePopPending = SNCFramePopPending; this->walk = SNCWalk; this->bufferClass = SNCBufClassGet; + AVERT(PoolClass, this); } -mps_class_t mps_class_snc(void) +mps_pool_class_t mps_class_snc(void) { - return (mps_class_t)SNCPoolClassGet(); + return (mps_pool_class_t)SNCPoolClassGet(); } /* SNCCheck -- Check an SNC pool */ +ATTRIBUTE_UNUSED static Bool SNCCheck(SNC snc) { CHECKS(SNC, snc); - CHECKD(Pool, &snc->poolStruct); - CHECKD(SegPref, &snc->segPrefStruct); - CHECKL(snc->poolStruct.class == SNCPoolClassGet()); + CHECKD(Pool, SNCPool(snc)); + CHECKL(SNCPool(snc)->class == SNCPoolClassGet()); if (snc->freeSegs != NULL) { - CHECKL(SegCheck(snc->freeSegs)); + CHECKD(Seg, snc->freeSegs); } return TRUE; } @@ -712,7 +711,7 @@ static Bool SNCCheck(SNC snc) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/prmci3fr.c b/code/prmci3fr.c index 6dbe5de09a..75cf94e871 100644 --- a/code/prmci3fr.c +++ b/code/prmci3fr.c @@ -1,7 +1,7 @@ /* prmci3fr.c: PROTECTION MUTATOR CONTEXT INTEL 386 (FREEBSD) * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This module implements the part of the protection module * that decodes the MutatorFaultContext. @@ -15,10 +15,10 @@ * * ASSUMPTIONS * - * .sp: The stack pointer in the context is ESP (x86) or RSP (x86_64). + * .sp: The stack pointer in the context is ESP. * - * .context.regroots: The root regs are EDI, ESI, EBX, EDX, ECX, EAX (or - * their x86_64 equivalents) are assumed to be recorded in the context at + * .context.regroots: The root regs are EDI, ESI, EBX, EDX, ECX, EAX, + * and they are assumed to be recorded in the context at * pointer-aligned boundaries. */ @@ -27,6 +27,10 @@ SRCID(prmci3fr, "$Id$"); +#if !defined(MPS_OS_FR) || !defined(MPS_ARCH_I3) +#error "prmci3fr.c is specific to MPS_OS_FR and MPS_ARCH_I3" +#endif + Addr MutatorFaultContextSP(MutatorFaultContext mfc) { @@ -53,7 +57,7 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/prmci3li.c b/code/prmci3li.c index 8d1b409e2d..08737d363c 100644 --- a/code/prmci3li.c +++ b/code/prmci3li.c @@ -1,7 +1,7 @@ /* prmci3li.c: PROTECTION MUTATOR CONTEXT INTEL 386 (LINUX) * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This module implements the part of the protection module * that decodes the MutatorFaultContext. @@ -17,12 +17,12 @@ * * ASSUMPTIONS * - * .sp: The stack pointer in the context is uc_stack.ss_sp. + * .sp: The stack pointer in the context is ESP. * * .context.regroots: The root regs are assumed to be recorded in the context * at pointer-aligned boundaries. * - * .assume.regref: The resisters in the context can be modified by + * .assume.regref: The registers in the context can be modified by * storing into an MRef pointer. */ @@ -31,35 +31,47 @@ SRCID(prmci3li, "$Id$"); +#if !defined(MPS_OS_LI) || !defined(MPS_ARCH_I3) +#error "prmci3li.c is specific to MPS_OS_LI and MPS_ARCH_I3" +#endif + /* Prmci3AddressHoldingReg -- return an address of a register in a context */ MRef Prmci3AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { + MRef gregs; + + AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 7); + AVER(mfc->ucontext != NULL); + + /* TODO: The current arrangement of the fix operation (taking a Ref *) + forces us to pun these registers (actually `int` on LII3GC). We can + suppress the warning by casting through `void *` and this might make + it safe, but does it really? RB 2012-09-10 */ + AVER(sizeof(void *) == sizeof(*mfc->ucontext->uc_mcontext.gregs)); + gregs = (void *)mfc->ucontext->uc_mcontext.gregs; /* .source.i486 */ /* .assume.regref */ /* The register numbers (REG_EAX etc.) are defined in but only if _GNU_SOURCE is defined: see .feature.li in config.h. */ - /* TODO: The current arrangement of the fix operation (taking a Ref *) - forces us to pun these registers (actually `int` on LII3GC). We can - suppress the warning my casting through `char *` and this might make - it safe, but does it really? RB 2012-09-10 */ switch (regnum) { - case 0: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_EAX]); - case 1: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_ECX]); - case 2: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_EDX]); - case 3: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_EBX]); - case 4: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_ESP]); - case 5: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_EBP]); - case 6: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_ESI]); - case 7: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_EDI]); + case 0: return &gregs[REG_EAX]; + case 1: return &gregs[REG_ECX]; + case 2: return &gregs[REG_EDX]; + case 3: return &gregs[REG_EBX]; + case 4: return &gregs[REG_ESP]; + case 5: return &gregs[REG_EBP]; + case 6: return &gregs[REG_ESI]; + case 7: return &gregs[REG_EDI]; + default: + NOTREACHED; + return NULL; /* Avoids compiler warning. */ } - NOTREACHED; - return (MRef)NULL; /* Avoids compiler warning. */ } @@ -107,7 +119,7 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/prmci3w3.c b/code/prmci3w3.c index f3d2e2386c..4f85f677a8 100644 --- a/code/prmci3w3.c +++ b/code/prmci3w3.c @@ -1,7 +1,7 @@ -/* prmci3w3.c: PROTECTION MUTATOR CONTEXT INTEL 386 (Win32) +/* prmci3w3.c: PROTECTION MUTATOR CONTEXT INTEL 386 (Windows) * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * PURPOSE * @@ -15,7 +15,7 @@ * * ASSUMPTIONS * - * .assume.regref: The resisters in the context can be modified by + * .assume.regref: The registers in the context can be modified by * storing into an MRef pointer. */ @@ -25,6 +25,10 @@ SRCID(prmci3w3, "$Id$"); +#if !defined(MPS_OS_W3) || !defined(MPS_ARCH_I3) +#error "prmci3w3.c is specific to MPS_OS_W3 and MPS_ARCH_I3" +#endif + /* Prmci3AddressHoldingReg -- Return an address for a given machine register */ @@ -46,9 +50,10 @@ MRef Prmci3AddressHoldingReg(MutatorFaultContext context, unsigned int regnum) case 5: return (MRef)&wincont->Ebp; case 6: return (MRef)&wincont->Esi; case 7: return (MRef)&wincont->Edi; + default: + NOTREACHED; + return NULL; /* suppress warning */ } - NOTREACHED; - return NULL; /* suppress warning */ } @@ -80,7 +85,7 @@ void Prmci3StepOverIns(MutatorFaultContext context, Size inslen) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/prmci3xc.c b/code/prmci3xc.c index b08ee3a1c4..67f3f5822d 100644 --- a/code/prmci3xc.c +++ b/code/prmci3xc.c @@ -1,7 +1,7 @@ /* prmci3xc.c: PROTECTION MUTATOR CONTEXT INTEL 386 (MAC OS X) * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This module implements the part of the protection module * that decodes the MutatorFaultContext. @@ -20,22 +20,31 @@ * .context.regroots: The root regs are assumed to be recorded in the context * at pointer-aligned boundaries. * - * .assume.regref: The resisters in the context can be modified by + * .assume.regref: The registers in the context can be modified by * storing into an MRef pointer. */ #include "prmcxc.h" #include "prmci3.h" -SRCID(prmci3li, "$Id$"); +SRCID(prmci3xc, "$Id$"); + +#if !defined(MPS_OS_XC) || !defined(MPS_ARCH_I3) +#error "prmci3xc.c is specific to MPS_OS_XC and MPS_ARCH_I3" +#endif /* Prmci3AddressHoldingReg -- return an address of a register in a context */ MRef Prmci3AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { + THREAD_STATE_S *threadState; + + AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 7); + AVER(mfc->threadState != NULL); + threadState = mfc->threadState; /* .source.i486 */ /* .assume.regref */ @@ -44,20 +53,21 @@ MRef Prmci3AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) config.h. */ /* TODO: The current arrangement of the fix operation (taking a Ref *) forces us to pun these registers (actually `int` on LII3GC). We can - suppress the warning my casting through `char *` and this might make + suppress the warning by casting through `void *` and this might make it safe, but does it really? RB 2012-09-10 */ switch (regnum) { - case 0: return (MRef)((char *)&mfc->threadState->__eax); - case 1: return (MRef)((char *)&mfc->threadState->__ecx); - case 2: return (MRef)((char *)&mfc->threadState->__edx); - case 3: return (MRef)((char *)&mfc->threadState->__ebx); - case 4: return (MRef)((char *)&mfc->threadState->__esp); - case 5: return (MRef)((char *)&mfc->threadState->__ebp); - case 6: return (MRef)((char *)&mfc->threadState->__esi); - case 7: return (MRef)((char *)&mfc->threadState->__edi); + case 0: return (void *)&threadState->__eax; + case 1: return (void *)&threadState->__ecx; + case 2: return (void *)&threadState->__edx; + case 3: return (void *)&threadState->__ebx; + case 4: return (void *)&threadState->__esp; + case 5: return (void *)&threadState->__ebp; + case 6: return (void *)&threadState->__esi; + case 7: return (void *)&threadState->__edi; + default: + NOTREACHED; + return NULL; /* Avoids compiler warning. */ } - NOTREACHED; - return (MRef)NULL; /* Avoids compiler warning. */ } @@ -104,7 +114,7 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/prmci6fr.c b/code/prmci6fr.c index 811d2c8f99..db20d01216 100644 --- a/code/prmci6fr.c +++ b/code/prmci6fr.c @@ -1,7 +1,7 @@ /* prmci6li.c: PROTECTION MUTATOR CONTEXT x64 (FREEBSD) * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This module implements the part of the protection module * that decodes the MutatorFaultContext. @@ -9,10 +9,10 @@ * * ASSUMPTIONS * - * .sp: The stack pointer in the context is ESP (x86) or RSP (x86_64). + * .sp: The stack pointer in the context is RSP. * - * .context.regroots: The root regs are EDI, ESI, EBX, EDX, ECX, EAX (or - * their x86_64 equivalents) are assumed to be recorded in the context at + * .context.regroots: The root regs are RDI, RSI, RBX, RDX, RCX, RAX, + * and they are assumed to be recorded in the context at * pointer-aligned boundaries. */ @@ -21,6 +21,10 @@ SRCID(prmci6fr, "$Id$"); +#if !defined(MPS_OS_FR) || !defined(MPS_ARCH_I6) +#error "prmci6fr.c is specific to MPS_OS_FR and MPS_ARCH_I6" +#endif + Addr MutatorFaultContextSP(MutatorFaultContext mfc) { @@ -47,7 +51,7 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/prmci6li.c b/code/prmci6li.c index 9c15fcdf6f..38b11c3a62 100644 --- a/code/prmci6li.c +++ b/code/prmci6li.c @@ -1,7 +1,7 @@ /* prmci6li.c: PROTECTION MUTATOR CONTEXT x64 (LINUX) * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This module implements the part of the protection module * that decodes the MutatorFaultContext. @@ -14,12 +14,12 @@ * * ASSUMPTIONS * - * .sp: The stack pointer in the context is uc_stack.ss_sp. + * .sp: The stack pointer in the context is RSP. * * .context.regroots: The root regs are assumed to be recorded in the context * at pointer-aligned boundaries. * - * .assume.regref: The resisters in the context can be modified by + * .assume.regref: The registers in the context can be modified by * storing into an MRef pointer. */ @@ -28,17 +28,28 @@ SRCID(prmci6li, "$Id$"); +#if !defined(MPS_OS_LI) || !defined(MPS_ARCH_I6) +#error "prmci6li.c is specific to MPS_OS_LI and MPS_ARCH_I6" +#endif + /* Prmci6AddressHoldingReg -- return an address of a register in a context */ MRef Prmci6AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { - Word *gregs; + MRef gregs; + AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 15); + AVER(mfc->ucontext != NULL); - gregs = (Word *)&mfc->ucontext->uc_mcontext.gregs; + /* TODO: The current arrangement of the fix operation (taking a Ref *) + forces us to pun these registers (actually `int` on LII6GC). We can + suppress the warning by casting through `void *` and this might make + it safe, but does it really? RB 2012-09-10 */ + AVER(sizeof(void *) == sizeof(*mfc->ucontext->uc_mcontext.gregs)); + gregs = (void *)mfc->ucontext->uc_mcontext.gregs; /* .assume.regref */ /* The register numbers (REG_RAX etc.) are defined in @@ -61,13 +72,14 @@ MRef Prmci6AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) case 13: return &gregs[REG_R13]; case 14: return &gregs[REG_R14]; case 15: return &gregs[REG_R15]; + default: + NOTREACHED; + return NULL; /* Avoids compiler warning. */ } - NOTREACHED; - return (MRef)NULL; /* Avoids compiler warning. */ } -/* Prmci3DecodeFaultContext -- decode fault to find faulting address and IP */ +/* Prmci6DecodeFaultContext -- decode fault to find faulting address and IP */ void Prmci6DecodeFaultContext(MRef *faultmemReturn, Byte **insvecReturn, @@ -79,7 +91,7 @@ void Prmci6DecodeFaultContext(MRef *faultmemReturn, } -/* Prmci3StepOverIns -- modify context to step over instruction */ +/* Prmci6StepOverIns -- modify context to step over instruction */ void Prmci6StepOverIns(MutatorFaultContext mfc, Size inslen) { @@ -111,7 +123,7 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/prmci6w3.c b/code/prmci6w3.c index 8faca02545..9fa31ed318 100644 --- a/code/prmci6w3.c +++ b/code/prmci6w3.c @@ -1,7 +1,7 @@ -/* prmci6w3.c: PROTECTION MUTATOR CONTEXT INTEL 386 (Win32) +/* prmci6w3.c: PROTECTION MUTATOR CONTEXT INTEL x64 (Windows) * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * PURPOSE * @@ -13,7 +13,7 @@ * * ASSUMPTIONS * - * .assume.regref: The resisters in the context can be modified by + * .assume.regref: The registers in the context can be modified by * storing into an MRef pointer. */ @@ -23,6 +23,10 @@ SRCID(prmci6w3, "$Id$"); +#if !defined(MPS_OS_W3) || !defined(MPS_ARCH_I6) +#error "prmci6w3.c is specific to MPS_OS_W3 and MPS_ARCH_I6" +#endif + /* Prmci6AddressHoldingReg -- Return an address for a given machine register */ @@ -52,9 +56,10 @@ MRef Prmci6AddressHoldingReg(MutatorFaultContext context, unsigned int regnum) case 13: return (MRef)&wincont->R13; case 14: return (MRef)&wincont->R14; case 15: return (MRef)&wincont->R15; + default: + NOTREACHED; + return NULL; /* suppress warning */ } - NOTREACHED; - return NULL; /* suppress warning */ } @@ -86,7 +91,7 @@ void Prmci6StepOverIns(MutatorFaultContext context, Size inslen) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/prmci6xc.c b/code/prmci6xc.c index f3d0413d1a..4d3a5afe15 100644 --- a/code/prmci6xc.c +++ b/code/prmci6xc.c @@ -1,7 +1,7 @@ -/* prmci6xc.c: PROTECTION MUTATOR CONTEXT x64 (MAC OS X) +/* prmci6xc.c: PROTECTION MUTATOR CONTEXT x64 (OS X) * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This module implements the part of the protection module * that decodes the MutatorFaultContext. @@ -9,62 +9,72 @@ * * SOURCES * - * .source.linux.kernel: Linux kernel source files. - * * * ASSUMPTIONS * + * .sp: The stack pointer in the context is RSP. + * * .context.regroots: The root regs are assumed to be recorded in the context * at pointer-aligned boundaries. * - * .assume.regref: The resisters in the context can be modified by + * .assume.regref: The registers in the context can be modified by * storing into an MRef pointer. */ #include "prmcxc.h" #include "prmci6.h" -SRCID(prmci6li, "$Id$"); +SRCID(prmci6xc, "$Id$"); + +#if !defined(MPS_OS_XC) || !defined(MPS_ARCH_I6) +#error "prmci6xc.c is specific to MPS_OS_XC and MPS_ARCH_I6" +#endif /* Prmci6AddressHoldingReg -- return an address of a register in a context */ MRef Prmci6AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { + THREAD_STATE_S *threadState; + + AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 15); + AVER(mfc->threadState != NULL); + threadState = mfc->threadState; /* .assume.regref */ /* The register numbers (REG_RAX etc.) are defined in but only if _XOPEN_SOURCE is defined: see .feature.xc in config.h. */ /* MRef (a Word *) is not compatible with pointers to the register - types (actually a __uint64_t). To avoid aliasing optimization - problems, The registers are cast through (char *) */ + types (actually a __uint64_t). To avoid aliasing optimization + problems, the registers are cast through (void *). */ switch (regnum) { - case 0: return (MRef)((char *)&mfc->threadState->__rax); - case 1: return (MRef)((char *)&mfc->threadState->__rcx); - case 2: return (MRef)((char *)&mfc->threadState->__rdx); - case 3: return (MRef)((char *)&mfc->threadState->__rbx); - case 4: return (MRef)((char *)&mfc->threadState->__rsp); - case 5: return (MRef)((char *)&mfc->threadState->__rbp); - case 6: return (MRef)((char *)&mfc->threadState->__rsi); - case 7: return (MRef)((char *)&mfc->threadState->__rdi); - case 8: return (MRef)((char *)&mfc->threadState->__r8); - case 9: return (MRef)((char *)&mfc->threadState->__r9); - case 10: return (MRef)((char *)&mfc->threadState->__r10); - case 11: return (MRef)((char *)&mfc->threadState->__r11); - case 12: return (MRef)((char *)&mfc->threadState->__r12); - case 13: return (MRef)((char *)&mfc->threadState->__r13); - case 14: return (MRef)((char *)&mfc->threadState->__r14); - case 15: return (MRef)((char *)&mfc->threadState->__r15); + case 0: return (void *)&threadState->__rax; + case 1: return (void *)&threadState->__rcx; + case 2: return (void *)&threadState->__rdx; + case 3: return (void *)&threadState->__rbx; + case 4: return (void *)&threadState->__rsp; + case 5: return (void *)&threadState->__rbp; + case 6: return (void *)&threadState->__rsi; + case 7: return (void *)&threadState->__rdi; + case 8: return (void *)&threadState->__r8; + case 9: return (void *)&threadState->__r9; + case 10: return (void *)&threadState->__r10; + case 11: return (void *)&threadState->__r11; + case 12: return (void *)&threadState->__r12; + case 13: return (void *)&threadState->__r13; + case 14: return (void *)&threadState->__r14; + case 15: return (void *)&threadState->__r15; + default: + NOTREACHED; + return NULL; /* Avoids compiler warning. */ } - NOTREACHED; - return (MRef)NULL; /* Avoids compiler warning. */ } -/* Prmci3DecodeFaultContext -- decode fault to find faulting address and IP */ +/* Prmci6DecodeFaultContext -- decode fault to find faulting address and IP */ void Prmci6DecodeFaultContext(MRef *faultmemReturn, Byte **insvecReturn, @@ -75,7 +85,7 @@ void Prmci6DecodeFaultContext(MRef *faultmemReturn, } -/* Prmci3StepOverIns -- modify context to step over instruction */ +/* Prmci6StepOverIns -- modify context to step over instruction */ void Prmci6StepOverIns(MutatorFaultContext mfc, Size inslen) { @@ -107,7 +117,7 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/prot.h b/code/prot.h new file mode 100644 index 0000000000..7191cd01e8 --- /dev/null +++ b/code/prot.h @@ -0,0 +1,78 @@ +/* prot.h: MEMORY PROTECTION INTERFACE + * + * $Id: //info.ravenbrook.com/project/mps/master/code/prot.h#1 $ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * See for the design of the generic interface including + * the contracts for these functions. + * + * This interface has several different implementations, typically one + * per platform, see for the various implementations, + * and for the corresponding designs. + */ + +#ifndef prot_h +#define prot_h + +#include "mpmtypes.h" + + +/* Protection Interface */ + +extern void ProtSetup(void); +extern Size ProtGranularity(void); +extern void ProtSet(Addr base, Addr limit, AccessSet mode); +extern void ProtSync(Arena arena); + + +/* Mutator Fault Context */ + +extern Bool ProtCanStepInstruction(MutatorFaultContext context); +extern Res ProtStepInstruction(MutatorFaultContext context); +extern Addr MutatorFaultContextSP(MutatorFaultContext mfc); +extern Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc); + + +#endif /* prot_h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/protan.c b/code/protan.c index 51b2edd6df..5978fb768a 100644 --- a/code/protan.c +++ b/code/protan.c @@ -1,7 +1,7 @@ /* protan.c: ANSI MEMORY PROTECTION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * * * DESIGN @@ -22,13 +22,21 @@ void ProtSetup(void) } +/* ProtGranularity -- return the granularity of protection */ + +Size ProtGranularity(void) +{ + /* Any range of addresses can be "protected" since ProtSet does nothing. */ + return (Size)MPS_PF_ALIGN; +} + + /* ProtSet -- set the protection for a page */ void ProtSet(Addr base, Addr limit, AccessSet pm) { AVER(base < limit); - /* .improve.protset.check: There is nor AccessSetCheck, so we */ - /* don't check it. */ + AVERT(AccessSet, pm); UNUSED(pm); NOOP; } @@ -39,48 +47,42 @@ void ProtSet(Addr base, Addr limit, AccessSet pm) * See . */ -void ProtSync(Arena arena) -{ +typedef struct ProtSyncClosureStruct { + Arena arena; Bool synced; +} ProtSyncClosureStruct, *ProtSyncClosure; - AVERT(Arena, arena); - - do { - Seg seg; - - synced = TRUE; - if (SegFirst(&seg, arena)) { - Addr base; - do { - base = SegBase(seg); - if (SegPM(seg) != AccessSetEMPTY) { /* */ - ShieldEnter(arena); - TraceSegAccess(arena, seg, SegPM(seg)); - ShieldLeave(arena); - synced = FALSE; - } - } while(SegNext(&seg, arena, base)); - } - } while(!synced); +static Bool protSyncVisit(Seg seg, void *closureP, Size closureS) +{ + ProtSyncClosure psc = closureP; + AVER(closureS == sizeof(*psc)); + + if (SegPM(seg) != AccessSetEMPTY) { /* */ + Arena arena = psc->arena; + ShieldEnter(arena); + TraceSegAccess(arena, seg, SegPM(seg)); + ShieldLeave(arena); + psc->synced = FALSE; + } } - -/* ProtTramp -- protection trampoline */ - -void ProtTramp(void **rReturn, void *(*f)(void *, size_t), - void *p, size_t s) +void ProtSync(Arena arena) { - AVER(rReturn != NULL); - AVER(FUNCHECK(f)); - /* Can't check p and s as they are interpreted by the client */ + ProtSyncClosureStruct pscStruct; + + AVERT(Arena, arena); - *(rReturn) = (*(f))(p, s); + pscStruct.arena = arena; + do { + pscStruct.synced = TRUE; + SegTraverse(arena, protSyncVisit, &pscStruct, sizeof(pscStruct)); + } while(!pscStruct.synced); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/proti3.c b/code/proti3.c index d3a13aef29..76f7f7965f 100644 --- a/code/proti3.c +++ b/code/proti3.c @@ -1,7 +1,7 @@ /* proti3.c: PROTECTION MUTATOR CONTEXT (INTEL 386) * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .design: See for the generic design of the interface * which is implemented in this module, including the contracts for the @@ -54,6 +54,10 @@ SRCID(proti3, "$Id$"); +#if !defined(MPS_ARCH_I3) +#error "proti3.c is specific to MPS_ARCH_I3" +#endif + /* DecodeCB -- Decode an Intel x86 control byte into Hi, Medium & Low fields */ @@ -243,7 +247,7 @@ Res ProtStepInstruction(MutatorFaultContext context) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/proti6.c b/code/proti6.c index 37940f1103..a681c51e63 100644 --- a/code/proti6.c +++ b/code/proti6.c @@ -1,7 +1,7 @@ /* proti6.c: PROTECTION MUTATOR CONTEXT (x64) * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .design: See for the generic design of the interface * which is implemented in this module, including the contracts for the @@ -31,6 +31,10 @@ SRCID(proti6, "$Id$"); +#if !defined(MPS_ARCH_I6) +#error "proti6.c is specific to MPS_ARCH_I6" +#endif + static Bool IsSimpleMov(Size *inslenReturn, MRef *srcReturn, @@ -84,7 +88,7 @@ Res ProtStepInstruction(MutatorFaultContext context) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/protix.c b/code/protix.c index 31c272bc5b..a243b00949 100644 --- a/code/protix.c +++ b/code/protix.c @@ -1,14 +1,14 @@ /* protix.c: PROTECTION FOR UNIX * * $Id$ - * Copyright (c) 2001,2007 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * * Somewhat generic across different Unix systems. Shared between - * Darwin (OS X), FreeBSD, and Linux. + * OS X, FreeBSD, and Linux. * - * This file does not contain a signal handler. That's in protsgix.c - * (for FreeBSD and Darwin on Intel); in protxcpp.c (for Darwin on - * PowerPC); in protlii3.c (for Intel Linux). + * This file does not contain a signal handler. That's in protsgix.c for + * historical reasons (there used to be separate implementations for the + * different flavours of Unix). * * * SOURCES @@ -20,12 +20,11 @@ * ASSUMPTIONS * * .assume.mprotect.base: We assume that the first argument to mprotect can - * be safely passed as a void *. Single UNIX Specification Version 2 - * (aka X/OPEN XSH5) says that the parameter is a void *. Some - * Unix-likes may declare this parameter as a caddr_t. FreeBSD used to - * do this (on the now very obsolete FreeBSD 2.2.x series). The - * Darwin man page documents it as caddr_t but it appears to be - * implemented correctly as void *. caddr_t is usually char *. + * be safely passed as a void *. Single UNIX Specification Version 2 (aka + * X/OPEN XSH5) says that the parameter is a void *. Some Unix-likes may + * declare this parameter as a caddr_t. FreeBSD used to do this (on the now + * very obsolete FreeBSD 2.2.x series), as did OS X, but both now implement + * it correctly as void *. caddr_t is usually char *. * * .assume.write-only: More of an anti-assumption really. We * assume that asking the OS for a write-only page (that is, flags = @@ -40,13 +39,11 @@ */ #include "mpm.h" +#include "vm.h" #if !defined(MPS_OS_LI) && !defined(MPS_OS_FR) && !defined(MPS_OS_XC) #error "protix.c is Unix-specific, currently for MPS_OS_LI FR XC" #endif -#ifndef PROTECTION -#error "protix.c implements protection, but PROTECTION is not set" -#endif #include #include @@ -69,6 +66,7 @@ void ProtSet(Addr base, Addr limit, AccessSet mode) AVER(base < limit); AVER(base != 0); AVER(AddrOffset(base, limit) <= INT_MAX); /* should be redundant */ + AVERT(AccessSet, mode); /* Convert between MPS AccessSet and UNIX PROT thingies. In this function, AccessREAD means protect against read accesses @@ -114,28 +112,18 @@ void ProtSync(Arena arena) } -/* ProtTramp -- protection trampoline - * - * The protection trampoline is trivial under Unix, as there is - * nothing that needs to be done in the dynamic context of the mutator in - * order to catch faults. (Contrast this with Win32 Structured Exception - * Handling.) - */ +/* ProtGranularity -- return the granularity of protection */ -void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), - void *p, size_t s) +Size ProtGranularity(void) { - AVER(resultReturn != NULL); - AVER(FUNCHECK(f)); - /* Can't check p and s as they are interpreted by the client */ - - *resultReturn = (*f)(p, s); + /* Individual pages can be protected. */ + return PageSize(); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2007 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/protli.c b/code/protli.c index ba48cab7f5..e157fe9875 100644 --- a/code/protli.c +++ b/code/protli.c @@ -1,13 +1,10 @@ -/* protli.c: PROTECTION FOR LINUX (INTEL 386) +/* protli.c: PROTECTION FOR LINUX * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * SOURCES * - * .source.i486: Intel486 Microprocessor Family Programmer's - * Reference Manual - * * .source.linux.kernel: Linux kernel source files. */ @@ -16,9 +13,6 @@ #ifndef MPS_OS_LI #error "protli.c is Linux-specific, but MPS_OS_LI is not set" #endif -#ifndef PROTECTION -#error "protli.c implements protection, but PROTECTION is not set" -#endif #include #include @@ -28,6 +22,9 @@ SRCID(protli, "$Id$"); +#if !defined(MPS_OS_LI) +#error "protli.c is specific to MPS_OS_LI" +#endif /* The previously-installed signal action, as returned by */ @@ -140,9 +137,10 @@ void ProtSetup(void) AVER(result == 0); } + /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/protocol.c b/code/protocol.c index 6a53613d54..bcc8ae5686 100644 --- a/code/protocol.c +++ b/code/protocol.c @@ -1,7 +1,7 @@ /* pool.c: PROTOCOL IMPLEMENTATION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -18,7 +18,7 @@ SRCID(protocol, "$Id$"); Bool ProtocolClassCheck(ProtocolClass class) { CHECKS(ProtocolClass, class); - CHECKS(ProtocolClass, class->superclass); + CHECKU(ProtocolClass, class->superclass); CHECKL(FUNCHECK(class->coerceInst)); CHECKL(FUNCHECK(class->coerceClass)); return TRUE; @@ -30,7 +30,7 @@ Bool ProtocolClassCheck(ProtocolClass class) Bool ProtocolInstCheck(ProtocolInst inst) { CHECKS(ProtocolInst, inst); - CHECKL(ProtocolClassCheck(inst->class)); + CHECKD(ProtocolClass, inst->class); return TRUE; } @@ -118,6 +118,7 @@ DEFINE_CLASS(ProtocolClass, theClass) theClass->superclass = theClass; theClass->coerceInst = ProtocolCoerceInst; theClass->coerceClass = ProtocolCoerceClass; + AVERT(ProtocolClass, theClass); } @@ -126,7 +127,7 @@ DEFINE_CLASS(ProtocolClass, theClass) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/protsgix.c b/code/protsgix.c index 39f19c90b4..f74a840f6b 100644 --- a/code/protsgix.c +++ b/code/protsgix.c @@ -1,11 +1,13 @@ /* protsgix.c: PROTECTION (SIGNAL HANDLER) FOR UNIX * * $Id$ - * Copyright (c) 2001-2007 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * + * This implements protection exception handling using POSIX signals. + * It is designed to run on any POSIX-compliant Unix, but currently is + * only used on FreeBSD, as we have separate implementions for OS X + * (see protxc.c) and Linux (see protli.c). * - * Would ordinarily be part of protix.c (as the code is common to more - * than one Unix-like operating system), but PowerPC Darwin requires a - * different implementation of this module. * * SOURCES * @@ -18,14 +20,8 @@ #include "mpm.h" -#if !defined(MPS_OS_XC) && !defined(MPS_OS_FR) -#error "protsgix.c is Unix-specific, currently for MPS_OS_FR or XC" -#endif -#if defined(MPS_OS_XC) && defined(MPS_ARCH_PP) -#error "protsgix.c does not work on Darwin on PowerPC. Use protxcpp.c" -#endif -#ifndef PROTECTION -#error "protsgix.c implements protection, but PROTECTION is not set" +#if !defined(MPS_OS_FR) +#error "protsgix.c is Unix-specific, currently for MPS_OS_FR" #endif #include /* for many functions */ @@ -143,7 +139,7 @@ void ProtSetup(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2007 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/protw3.c b/code/protw3.c index 37d886644f..a8dddd74fe 100644 --- a/code/protw3.c +++ b/code/protw3.c @@ -1,20 +1,18 @@ /* protw3.c: PROTECTION FOR WIN32 * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" /* prmcw3.h needed to share MutatorFaultContextStruct declation */ /* with */ #include "prmcw3.h" +#include "vm.h" #ifndef MPS_OS_W3 #error "protw3.c is Win32-specific, but MPS_OS_W3 is not set" #endif -#ifndef PROTECTION -#error "protw3.c implements protection, but PROTECTION is not set" -#endif #include "mpswin.h" @@ -28,6 +26,7 @@ void ProtSet(Addr base, Addr limit, AccessSet mode) AVER(base < limit); AVER(base != 0); + AVERT(AccessSet, mode); newProtect = PAGE_EXECUTE_READWRITE; if((mode & AccessWRITE) != 0) @@ -109,47 +108,40 @@ LONG WINAPI ProtSEHfilter(LPEXCEPTION_POINTERS info) void ProtSetup(void) { + void *handler; /* See "AddVectoredExceptionHandler function (Windows)" */ /* ProtSetup is called only once per process, not once per arena, so this exception handler is only installed once. */ - AddVectoredExceptionHandler(1uL, ProtSEHfilter); + handler = AddVectoredExceptionHandler(1uL, ProtSEHfilter); + AVER(handler != NULL); } -/* ProtSync -- synchronize protection settings with hardware - * - * This does nothing under Win32. See protan.c. - */ +/* ProtGranularity -- return the granularity of protection */ -void ProtSync(Arena arena) +Size ProtGranularity(void) { - UNUSED(arena); - NOOP; + /* Individual pages can be protected. */ + return PageSize(); } -/* ProtTramp -- wrap a mutator thread in a Structured Exception Handler filter +/* ProtSync -- synchronize protection settings with hardware * - * This was the method by which we installed an exception handler on Windows - * prior to MPS 1.111. Now we are using Vectored Exception Handlers, so this - * is deprecated and just calls through to the mutator function. + * This does nothing under Win32. See protan.c. */ -void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), - void *p, size_t s) +void ProtSync(Arena arena) { - AVER(resultReturn != NULL); - AVER(FUNCHECK(f)); - /* Can't check p and s as they are interpreted by the client */ - - *resultReturn = f(p, s); + UNUSED(arena); + NOOP; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/protxc.c b/code/protxc.c index 8f62bb3a5d..7e8f230d06 100644 --- a/code/protxc.c +++ b/code/protxc.c @@ -76,9 +76,6 @@ #if !defined(MPS_OS_XC) #error "protxc.c is OS X specific" #endif -#if !defined(PROTECTION) -#error "protxc.c implements protection, but PROTECTION is not defined" -#endif SRCID(protxc, "$Id$"); @@ -243,7 +240,7 @@ static void protCatchOne(void) different size" warnings in GCC, for the XCI3GC build. */ mfcStruct.address = (Addr)(Word)request.code[1]; AVER(sizeof(*mfcStruct.threadState) == sizeof(THREAD_STATE_S)); - mfcStruct.threadState = (THREAD_STATE_S *)request.old_state; + mfcStruct.threadState = (void *)request.old_state; if (ArenaAccess(mfcStruct.address, AccessREAD | AccessWRITE, @@ -282,6 +279,7 @@ static void protCatchOne(void) * handler won't cause a deadlock. */ +ATTRIBUTE_NORETURN static void *protCatchThread(void *p) { UNUSED(p); for (;;) diff --git a/code/pthrdext.c b/code/pthrdext.c index 7185a0ff44..59d5899c32 100644 --- a/code/pthrdext.c +++ b/code/pthrdext.c @@ -1,7 +1,7 @@ /* pthreadext.c: POSIX THREAD EXTENSIONS * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: Provides extension to Pthreads. * @@ -178,8 +178,8 @@ extern Bool PThreadextCheck(PThreadext pthreadext) CHECKS(PThreadext, pthreadext); /* can't check ID */ - CHECKL(RingCheck(&pthreadext->threadRing)); - CHECKL(RingCheck(&pthreadext->idRing)); + CHECKD_NOSIG(Ring, &pthreadext->threadRing); + CHECKD_NOSIG(Ring, &pthreadext->idRing); if (pthreadext->suspendedMFC == NULL) { /* not suspended */ CHECKL(RingIsSingle(&pthreadext->threadRing)); @@ -366,7 +366,7 @@ Res PThreadextResume(PThreadext target) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/qs.c b/code/qs.c index 13b2070da1..2a62a5ef71 100644 --- a/code/qs.c +++ b/code/qs.c @@ -1,7 +1,7 @@ /* qs.c: QUICKSORT * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * The purpose of this program is to act as a "real" client of the MM. * It is a test, but (hopefully) less contrived than some of the other @@ -29,10 +29,9 @@ #include "mpscamc.h" #include "mpscmv.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif -#include + +#include /* printf */ +#include /* qsort */ #define testArenaSIZE ((size_t)1000*1024) @@ -51,7 +50,7 @@ static mps_addr_t isMoved(mps_addr_t object); static void copy(mps_addr_t object, mps_addr_t to); static void pad(mps_addr_t base, size_t size); -struct mps_fmt_A_s fmt_A_s = +static struct mps_fmt_A_s fmt_A_s = { (mps_align_t)4, scan, skip, copy, @@ -186,7 +185,7 @@ static void swap(void) static void makerndlist(unsigned l) { - unsigned i; + size_t i; mps_word_t r; mps_addr_t addr; @@ -318,15 +317,14 @@ static void validate(void) for(i = 0; i < listl; ++i) { cdie(((QSCell)reg[1])->tag == QSInt, "validate int"); if((mps_word_t)((QSCell)reg[1])->value != list[i]) { - fprintf(stdout, - "mps_res_t: Element %"PRIuLONGEST" of the two lists do not match.\n", - (ulongest_t)i); + printf("mps_res_t: Element %"PRIuLONGEST" of the " + "two lists do not match.\n", (ulongest_t)i); return; } reg[1] = (mps_addr_t)((QSCell)reg[1])->tail; } cdie(reg[1] == (mps_word_t)0, "validate end"); - fprintf(stdout, "Note: Lists compare equal.\n"); + printf("Note: Lists compare equal.\n"); } @@ -335,7 +333,6 @@ static void *go(void *p, size_t s) mps_fmt_t format; mps_chain_t chain; mps_addr_t base; - mps_addr_t *addr; testlib_unused(p); testlib_unused(s); @@ -358,9 +355,7 @@ static void *go(void *p, size_t s) "RootCreateTable"); base = &activationStack; - addr = base; - die(mps_root_create_table(&actroot, arena, mps_rank_ambig(), 0, - addr, sizeof(QSCell)/sizeof(mps_addr_t)), + die(mps_root_create_table(&actroot, arena, mps_rank_ambig(), 0, base, 1), "RootCreateTable"); /* makes a random list */ @@ -372,6 +367,7 @@ static void *go(void *p, size_t s) qsort(list, listl, sizeof(mps_word_t), &compare); validate(); + mps_arena_park(arena); mps_root_destroy(regroot); mps_root_destroy(actroot); mps_ap_destroy(ap); @@ -379,6 +375,7 @@ static void *go(void *p, size_t s) mps_pool_destroy(mpool); mps_chain_destroy(chain); mps_fmt_destroy(format); + mps_arena_release(arena); return NULL; } @@ -528,11 +525,11 @@ int main(int argc, char *argv[]) { void *r; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "mps_arena_create"); + mps_tramp(&r, &go, NULL, 0); mps_arena_destroy(arena); @@ -543,7 +540,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/range.c b/code/range.c index b2d4494454..8b8f1c8bf8 100644 --- a/code/range.c +++ b/code/range.c @@ -15,8 +15,6 @@ SRCID(range, "$Id$"); Bool RangeCheck(Range range) { - CHECKS(Range, range); - CHECKL(range->base != NULL); CHECKL(range->base <= range->limit); return TRUE; @@ -25,37 +23,37 @@ Bool RangeCheck(Range range) void RangeInit(Range range, Addr base, Addr limit) { AVER(range != NULL); - AVER(base != NULL); AVER(base <= limit); range->base = base; range->limit = limit; - range->sig = RangeSig; AVERT(Range, range); } +void RangeInitSize(Range range, Addr base, Size size) +{ + RangeInit(range, base, AddrAdd(base, size)); +} + void RangeFinish(Range range) { AVERT(Range, range); - range->sig = SigInvalid; - - range->base = range->limit = NULL; } -Res RangeDescribe(Range range, mps_lib_FILE *stream) +Res RangeDescribe(Range range, mps_lib_FILE *stream, Count depth) { Res res; AVERT(Range, range); AVER(stream != NULL); - res = WriteF(stream, - "Range $P\n{\n", (WriteFP)range, + res = WriteF(stream, depth, + "Range $P {\n", (WriteFP)range, " base: $P\n", (WriteFP)RangeBase(range), " limit: $P\n", (WriteFP)RangeLimit(range), " size: $U\n", (WriteFU)RangeSize(range), - "}\n", NULL); + "} Range $P\n", (WriteFP)range, NULL); if (res != ResOK) { return res; } @@ -94,21 +92,30 @@ Bool RangeIsAligned(Range range, Align alignment) && AddrIsAligned(RangeLimit(range), alignment); } -Addr (RangeBase)(Range range) { +Addr (RangeBase)(Range range) +{ AVERT(Range, range); return RangeBase(range); } -Addr (RangeLimit)(Range range) { +Addr (RangeLimit)(Range range) +{ AVERT(Range, range); return RangeLimit(range); } -Size (RangeSize)(Range range) { +Size (RangeSize)(Range range) +{ AVERT(Range, range); return RangeSize(range); } +void RangeCopy(Range to, Range from) +{ + AVERT(Range, from); + RangeInit(to, RangeBase(from), RangeLimit(from)); +} + /* C. COPYRIGHT AND LICENSE * diff --git a/code/range.h b/code/range.h index 42ab416771..ac262c98c1 100644 --- a/code/range.h +++ b/code/range.h @@ -14,22 +14,18 @@ #include "mpmtypes.h" -/* Signatures */ - -#define RangeSig ((Sig)0x5196A493) /* SIGnature RANGE */ - - /* Prototypes */ -typedef struct RangeStruct *Range; - #define RangeBase(range) ((range)->base) #define RangeLimit(range) ((range)->limit) #define RangeSize(range) (AddrOffset(RangeBase(range), RangeLimit(range))) +#define RangeContains(range, addr) ((range)->base <= (addr) && (addr) < (range)->limit) +#define RangeIsEmpty(range) (RangeSize(range) == 0) extern void RangeInit(Range range, Addr base, Addr limit); +extern void RangeInitSize(Range range, Addr base, Size size); extern void RangeFinish(Range range); -extern Res RangeDescribe(Range range, mps_lib_FILE *stream); +extern Res RangeDescribe(Range range, mps_lib_FILE *stream, Count depth); extern Bool RangeCheck(Range range); extern Bool RangeIsAligned(Range range, Align align); extern Bool RangesOverlap(Range range1, Range range2); @@ -38,12 +34,12 @@ extern Bool RangesEqual(Range range1, Range range2); extern Addr (RangeBase)(Range range); extern Addr (RangeLimit)(Range range); extern Size (RangeSize)(Range range); +extern void RangeCopy(Range to, Range from); /* Types */ typedef struct RangeStruct { - Sig sig; Addr base; Addr limit; } RangeStruct; diff --git a/code/ref.c b/code/ref.c index fd11743cb1..3f33031555 100644 --- a/code/ref.c +++ b/code/ref.c @@ -81,6 +81,221 @@ ZoneSet ZoneSetOfSeg(Arena arena, Seg seg) } +/* RangeInZoneSetFirst -- find an area of address space within a zone set + * + * Given a range of addresses, find the first sub-range of at least size that + * is also within a zone set. i.e. ZoneSetOfRange is a subset of the zone set. + * Returns FALSE if no range satisfying the conditions could be found. + */ + +static Addr nextStripe(Addr base, Addr limit, Arena arena) +{ + Addr next = AddrAlignUp(AddrAdd(base, 1), ArenaStripeSize(arena)); + AVER(next > base || next == (Addr)0); + if (next >= limit || next < base) + next = limit; + return next; +} + +Bool RangeInZoneSetFirst(Addr *baseReturn, Addr *limitReturn, + Addr base, Addr limit, + Arena arena, ZoneSet zoneSet, Size size) +{ + Size zebra; + Addr searchLimit; + + AVER(baseReturn != NULL); + AVER(limitReturn != NULL); + AVER(base < limit); + AVERT(Arena, arena); + AVER(size > 0); + AVER(zoneSet != ZoneSetEMPTY); + + /* TODO: Consider whether this search is better done by bit twiddling + zone sets, e.g. by constructing a mask of zone bits as wide as the + size and rotating the zoneSet. */ + + if (AddrOffset(base, limit) < size) + return FALSE; + + if (zoneSet == ZoneSetUNIV) { + *baseReturn = base; + *limitReturn = limit; + return TRUE; + } + + /* A "zebra" is the size of a complete set of stripes. */ + zebra = (sizeof(ZoneSet) * CHAR_BIT) << ArenaZoneShift(arena); + if (size >= zebra) { + AVER(zoneSet != ZoneSetUNIV); + return FALSE; + } + + /* There's no point searching through the zoneSet more than once. */ + searchLimit = AddrAdd(AddrAlignUp(base, ArenaStripeSize(arena)), zebra); + if (searchLimit > base && limit > searchLimit) + limit = searchLimit; + + do { + Addr next; + + /* Search for a stripe in the zoneSet and within the block. */ + /* (Find the first set bit in the zoneSet not below the base zone.) */ + while (!ZoneSetHasAddr(arena, zoneSet, base)) { + base = nextStripe(base, limit, arena); + if (base >= limit) + return FALSE; + } + + /* Search for a run stripes in the zoneSet and within the block. */ + /* (Find a run of set bits in the zoneSet.) */ + next = base; + do + next = nextStripe(next, limit, arena); + while (next < limit && ZoneSetHasAddr(arena, zoneSet, next)); + + /* Is the run big enough to satisfy the size? */ + if (AddrOffset(base, next) >= size) { + *baseReturn = base; + *limitReturn = next; + return TRUE; + } + + base = next; + } while (base < limit); + + return FALSE; +} + + +/* RangeInZoneSetLast -- find an area of address space within a zone set + * + * Given a range of addresses, find the last sub-range of at least size that + * is also within a zone set. i.e. ZoneSetOfRange is a subset of the zone set. + * Returns FALSE if no range satisfying the conditions could be found. + */ + +static Addr prevStripe(Addr base, Addr limit, Arena arena) +{ + Addr prev; + AVER(limit != (Addr)0); + prev = AddrAlignDown(AddrSub(limit, 1), ArenaStripeSize(arena)); + AVER(prev < limit); + if (prev < base) + prev = base; + return prev; +} + +Bool RangeInZoneSetLast(Addr *baseReturn, Addr *limitReturn, + Addr base, Addr limit, + Arena arena, ZoneSet zoneSet, Size size) +{ + Size zebra; + Addr searchBase; + + AVER(baseReturn != NULL); + AVER(limitReturn != NULL); + AVER(base < limit); + AVERT(Arena, arena); + AVER(size > 0); + AVER(zoneSet != ZoneSetEMPTY); + + /* TODO: Consider whether this search is better done by bit twiddling + zone sets, e.g. by constructing a mask of zone bits as wide as the + size and rotating the zoneSet. */ + + if (AddrOffset(base, limit) < size) + return FALSE; + + if (zoneSet == ZoneSetUNIV) { + *baseReturn = base; + *limitReturn = limit; + return TRUE; + } + + /* A "zebra" is the size of a complete set of stripes. */ + zebra = (sizeof(ZoneSet) * CHAR_BIT) << ArenaZoneShift(arena); + if (size >= zebra) { + AVER(zoneSet != ZoneSetUNIV); + return FALSE; + } + + /* There's no point searching through the zoneSet more than once. */ + searchBase = AddrSub(AddrAlignDown(limit, ArenaStripeSize(arena)), zebra); + if (searchBase < limit && base < searchBase) + base = searchBase; + + do { + Addr prev; + + /* Search for a stripe in the zoneSet and within the block. */ + /* (Find the last set bit in the zoneSet below the limit zone.) */ + while (!ZoneSetHasAddr(arena, zoneSet, AddrSub(limit, 1))) { + limit = prevStripe(base, limit, arena); + if (base >= limit) + return FALSE; + } + + /* Search for a run stripes in the zoneSet and within the block. */ + /* (Find a run of set bits in the zoneSet.) */ + prev = limit; + do + prev = prevStripe(base, prev, arena); + while (prev > base && ZoneSetHasAddr(arena, zoneSet, AddrSub(prev, 1))); + + /* Is the run big enough to satisfy the size? */ + if (AddrOffset(prev, limit) >= size) { + *baseReturn = prev; + *limitReturn = limit; + return TRUE; + } + + limit = prev; + } while (base < limit); + + return FALSE; +} + + +/* ZoneSetBlacklist() -- calculate a zone set of likely false positives + * + * We blacklist the zones that could be referenced by values likely to be + * found in ambiguous roots (such as the stack) and misinterpreted as + * references, in order to avoid nailing down objects. This isn't a + * perfect simulation, but it should catch the common cases. + */ + +ZoneSet ZoneSetBlacklist(Arena arena) +{ + ZoneSet blacklist; + union { + mps_word_t word; + mps_addr_t addr; + int i; + long l; + } nono; + + AVERT(Arena, arena); + + blacklist = ZoneSetEMPTY; + nono.word = 0; + nono.i = 1; + blacklist = ZoneSetAddAddr(arena, blacklist, nono.addr); + nono.i = -1; + blacklist = ZoneSetAddAddr(arena, blacklist, nono.addr); + nono.l = 1; + blacklist = ZoneSetAddAddr(arena, blacklist, nono.addr); + nono.l = -1; + blacklist = ZoneSetAddAddr(arena, blacklist, nono.addr); + + return blacklist; +} + + + + + + /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2001-2002 Ravenbrook Limited . diff --git a/code/replay.c b/code/replay.c index 974ce5dcd0..957547565f 100644 --- a/code/replay.c +++ b/code/replay.c @@ -1,5 +1,5 @@ /* replay.c: Allocation replayer - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * $Id$ */ @@ -21,7 +21,7 @@ #include "mpstd.h" -#ifdef MPS_PF_W3I6MV +#if defined(MPS_OS_W3) && defined(MPS_ARCH_I6) #define PRIuLONGEST "llu" #define PRIXPTR "016llX" typedef unsigned long long ulongest_t; @@ -47,6 +47,7 @@ static Word eventTime = 0; /* current event time */ /* error -- error signalling */ +ATTRIBUTE_FORMAT((printf, 1, 2)) static void error(const char *format, ...) { va_list args; @@ -126,8 +127,10 @@ static void readLog(EventProc proc) Res res; res = EventRead(&event, proc); - if (res == ResFAIL) break; /* eof */ - if (res != ResOK) error("Truncated log"); + if (res == ResFAIL) + break; /* eof */ + if (res != ResOK) + error("Truncated log"); eventTime = event->any.clock; EventRecord(proc, event, eventTime); EventReplay(event, eventTime); @@ -185,7 +188,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/reserv.c b/code/reserv.c index 6dfcd8543a..91a2fd5255 100644 --- a/code/reserv.c +++ b/code/reserv.c @@ -1,13 +1,13 @@ /* reserv.c: ARENA RESERVOIR * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * IMPROVEMENTS * * .improve.contiguous: There should be a means of grouping contiguous * tracts together so that there's a likelihood of being able to meet - * requests for regions larger than the arena alignment. */ + * requests for regions larger than the arena grain size. */ #include "mpm.h" @@ -16,7 +16,7 @@ SRCID(reserv, "$Id$"); /* The reservoir pool is defined here. See */ -#define Pool2Reservoir(pool) PARENT(ReservoirStruct, poolStruct, pool) +#define PoolReservoir(pool) PARENT(ReservoirStruct, poolStruct, pool) /* Management of tracts @@ -30,7 +30,7 @@ SRCID(reserv, "$Id$"); #define resTractSetNext(tract, next) (TractSetP((tract), (void*)(next))) -#define reservoirArena(reservoir) ((reservoir)->poolStruct.arena) +#define reservoirArena(reservoir) (PoolArena(ReservoirPool(reservoir))) /* ResPoolInit -- Reservoir pool init method */ @@ -58,7 +58,7 @@ static void ResPoolFinish(Pool pool) Reservoir reservoir; AVERT(Pool, pool); - reservoir = Pool2Reservoir(pool); + reservoir = PoolReservoir(pool); AVERT(Reservoir, reservoir); AVER(reservoir->reserve == NULL); /* .reservoir.finish */ } @@ -74,6 +74,7 @@ DEFINE_POOL_CLASS(ReservoirPoolClass, this) this->offset = offsetof(ReservoirStruct, poolStruct); this->init = ResPoolInit; this->finish = ResPoolFinish; + AVERT(PoolClass, this); } @@ -86,19 +87,20 @@ Bool ReservoirCheck(Reservoir reservoir) Tract tract; CHECKS(Reservoir, reservoir); - CHECKD(Pool, &reservoir->poolStruct); - CHECKL(reservoir->poolStruct.class == reservoircl); + CHECKD(Pool, ReservoirPool(reservoir)); + CHECKL(ReservoirPool(reservoir)->class == reservoircl); UNUSED(reservoircl); /* */ arena = reservoirArena(reservoir); CHECKU(Arena, arena); /* could call ReservoirIsConsistent, but it's costly. */ tract = reservoir->reserve; if (tract != NULL) { - CHECKL(TractCheck(tract)); - CHECKL(TractPool(tract) == &reservoir->poolStruct); + CHECKD_NOSIG(Tract, tract); + CHECKL(TractPool(tract) == ReservoirPool(reservoir)); } - CHECKL(SizeIsAligned(reservoir->reservoirLimit, ArenaAlign(arena))); - CHECKL(SizeIsAligned(reservoir->reservoirSize, ArenaAlign(arena))); + CHECKL(SizeIsArenaGrains(reservoir->reservoirLimit, arena)); + CHECKL(SizeIsArenaGrains(reservoir->reservoirSize, arena)); + CHECKL(reservoir->reservoirSize <= reservoir->reservoirLimit); return TRUE; } @@ -106,32 +108,33 @@ Bool ReservoirCheck(Reservoir reservoir) /* reservoirIsConsistent -- returns FALSE if the reservoir is corrupt */ +ATTRIBUTE_UNUSED static Bool reservoirIsConsistent(Reservoir reservoir) { - Size alignment, size = 0; + Size grainSize, size = 0; Tract tract; Pool pool; Arena arena; arena = reservoirArena(reservoir); - pool = &reservoir->poolStruct; + pool = ReservoirPool(reservoir); /* Check that the size of the tracts matches reservoirSize */ - alignment = ArenaAlign(arena); + grainSize = ArenaGrainSize(arena); tract = reservoir->reserve; while (tract != NULL) { AVERT(Tract, tract); AVER(TractPool(tract) == pool); tract = resTractNext(tract); - size += alignment; + size += grainSize; } if (size != reservoir->reservoirSize) return FALSE; /* */ - return SizeIsAligned(reservoir->reservoirLimit, alignment) - && SizeIsAligned(reservoir->reservoirSize, alignment) + return SizeIsAligned(reservoir->reservoirLimit, grainSize) + && SizeIsAligned(reservoir->reservoirSize, grainSize) && (reservoir->reservoirLimit >= reservoir->reservoirSize); } @@ -143,21 +146,21 @@ static Bool reservoirIsConsistent(Reservoir reservoir) Res ReservoirEnsureFull(Reservoir reservoir) { - Size limit, alignment; + Size limit, size; Pool pool; Arena arena; AVERT(Reservoir, reservoir); arena = reservoirArena(reservoir); AVERT(Arena, arena); - alignment = ArenaAlign(arena); + size = ArenaGrainSize(arena); limit = reservoir->reservoirLimit; /* optimize the common case of a full reservoir */ if (reservoir->reservoirSize == limit) return ResOK; - pool = &reservoir->poolStruct; + pool = ReservoirPool(reservoir); /* really ought to try hard to allocate contiguous tracts */ /* see .improve.contiguous */ @@ -165,13 +168,13 @@ Res ReservoirEnsureFull(Reservoir reservoir) Res res; Addr base; Tract tract; - res = (*arena->class->alloc)(&base, &tract, SegPrefDefault(), - alignment, pool); + res = ArenaAlloc(&base, LocusPrefDefault(), size, pool, FALSE); if (res != ResOK) { AVER(reservoirIsConsistent(reservoir)); return res; } - reservoir->reservoirSize += alignment; + tract = TractOfBaseAddr(arena, base); + reservoir->reservoirSize += size; resTractSetNext(tract, reservoir->reserve); reservoir->reserve = tract; } @@ -186,24 +189,24 @@ static void reservoirShrink(Reservoir reservoir, Size want) { Arena arena; Pool pool; - Size alignment; + Size size; - pool = &reservoir->poolStruct; + pool = ReservoirPool(reservoir); arena = reservoirArena(reservoir); - AVER(SizeIsAligned(want, ArenaAlign(arena))); + AVER(SizeIsArenaGrains(want, arena)); AVER(reservoir->reservoirSize >= want); if (reservoir->reservoirSize == want) return; /* Iterate over tracts, freeing them while reservoir is too big */ - alignment = ArenaAlign(arena); + size = ArenaGrainSize(arena); while (reservoir->reservoirSize > want) { Tract tract = reservoir->reserve; AVER(tract != NULL); reservoir->reserve = resTractNext(tract); - (*arena->class->free)(TractBase(tract), alignment, pool); - reservoir->reservoirSize -= alignment; + ArenaFree(TractBase(tract), size, pool); + reservoir->reservoirSize -= size; } AVER(reservoir->reservoirSize == want); AVER(reservoirIsConsistent(reservoir)); @@ -222,14 +225,14 @@ Res ReservoirWithdraw(Addr *baseReturn, Tract *baseTractReturn, AVERT(Reservoir, reservoir); arena = reservoirArena(reservoir); AVERT(Arena, arena); - AVER(SizeIsAligned(size, ArenaAlign(arena))); + AVER(SizeIsArenaGrains(size, arena)); AVER(size > 0); AVERT(Pool, pool); /* @@@@ As a short-term measure, we only permit the reservoir to */ /* allocate single-page regions. */ /* See .improve.contiguous & change.dylan.jackdaw.160125 */ - if (size != ArenaAlign(arena)) + if (size != ArenaGrainSize(arena)) return ResMEMORY; if (size <= reservoir->reservoirSize) { @@ -239,7 +242,7 @@ Res ReservoirWithdraw(Addr *baseReturn, Tract *baseTractReturn, AVER(tract != NULL); base = TractBase(tract); reservoir->reserve = resTractNext(tract); - reservoir->reservoirSize -= ArenaAlign(arena); + reservoir->reservoirSize -= ArenaGrainSize(arena); TractFinish(tract); TractInit(tract, pool, base); AVER(reservoirIsConsistent(reservoir)); @@ -255,41 +258,49 @@ Res ReservoirWithdraw(Addr *baseReturn, Tract *baseTractReturn, /* ReservoirDeposit -- Top up the reservoir */ -void ReservoirDeposit(Reservoir reservoir, Addr base, Size size) +Bool ReservoirDeposit(Reservoir reservoir, Addr *baseIO, Size *sizeIO) { Pool respool; Addr addr, limit; - Size reslimit, alignment; + Size reslimit; Arena arena; Tract tract; + Addr base; + Size size; AVERT(Reservoir, reservoir); arena = reservoirArena(reservoir); AVERT(Arena, arena); - respool = &reservoir->poolStruct; - alignment = ArenaAlign(arena); - AVER(AddrIsAligned(base, alignment)); - AVER(SizeIsAligned(size, alignment)); + respool = ReservoirPool(reservoir); + AVER(baseIO != NULL); + AVER(sizeIO != NULL); + base = *baseIO; + size = *sizeIO; + AVER(AddrIsArenaGrain(base, arena)); + AVER(SizeIsArenaGrains(size, arena)); limit = AddrAdd(base, size); reslimit = reservoir->reservoirLimit; /* put as many pages as necessary into the reserve & free the rest */ TRACT_FOR(tract, addr, arena, base, limit) { - AVER(TractCheck(tract)); + AVERT(Tract, tract); if (reservoir->reservoirSize < reslimit) { /* Reassign the tract to the reservoir pool */ TractFinish(tract); TractInit(tract, respool, addr); - reservoir->reservoirSize += alignment; + reservoir->reservoirSize += ArenaGrainSize(arena); resTractSetNext(tract, reservoir->reserve); reservoir->reserve = tract; } else { - /* free the tract */ - (*arena->class->free)(addr, alignment, TractPool(tract)); + *baseIO = addr; + *sizeIO = AddrOffset(base, limit); + AVER(reservoirIsConsistent(reservoir)); + return TRUE; } } AVER(addr == limit); AVER(reservoirIsConsistent(reservoir)); + return FALSE; } @@ -331,14 +342,14 @@ void ReservoirSetLimit(Reservoir reservoir, Size size) if (size > 0) { Size wastage; /* */ - wastage = ArenaAlign(arena) * mutatorBufferCount(ArenaGlobals(arena)); + wastage = ArenaGrainSize(arena) * mutatorBufferCount(ArenaGlobals(arena)); /* */ - needed = SizeAlignUp(size, ArenaAlign(arena)) + wastage; + needed = SizeArenaGrains(size, arena) + wastage; } else { needed = 0; /* */ } - AVER(SizeIsAligned(needed, ArenaAlign(arena))); + AVER(SizeIsArenaGrains(needed, arena)); /* Emit event now, so subsequent change can be ascribed to it. */ EVENT2(ReservoirLimitSet, arena, size); @@ -387,7 +398,7 @@ Res ReservoirInit(Reservoir reservoir, Arena arena) reservoir->reserve = NULL; reservoir->sig = ReservoirSig; /* initialize the reservoir pool, */ - res = PoolInit(&reservoir->poolStruct, + res = PoolInit(ReservoirPool(reservoir), arena, EnsureReservoirPoolClass(), argsNone); if (res == ResOK) { AVERT(Reservoir, reservoir); @@ -400,14 +411,14 @@ Res ReservoirInit(Reservoir reservoir, Arena arena) void ReservoirFinish (Reservoir reservoir) { - PoolFinish(&reservoir->poolStruct); + PoolFinish(ReservoirPool(reservoir)); reservoir->sig = SigInvalid; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/ring.c b/code/ring.c index ff60149ce4..2f4be5066f 100644 --- a/code/ring.c +++ b/code/ring.c @@ -1,15 +1,14 @@ /* ring.c: RING IMPLEMENTATION * * $Id$ - * Copyright (c) 2001,2003 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .intro: This is a portable implementation of Rings. * * .purpose: Rings are used to manage potentially unbounded collections * of things. * - * .sources: , - * item 6 of mail.richard_brooksby.1996-03-25.16-02 + * .sources: */ #include "ring.h" @@ -21,9 +20,10 @@ SRCID(ring, "$Id$"); /* RingCheck, RingCheckSingle -- check the validity of a ring node * - * RingCheck performs a consistency check on the ring node. - * RingCheckSingle performs the same check, but also checks that - * the ring node is a singleton (). + * RingCheck performs a consistency check on the ring node + * (). RingCheckSingle performs the same check, but + * also checks that the ring node is a singleton + * (). */ Bool RingCheck(Ring ring) @@ -46,6 +46,12 @@ Bool RingCheckSingle(Ring ring) return TRUE; } + +/* RingIsSingle -- return true if ring is a singleton + * + * See + */ + Bool RingIsSingle(Ring ring) { AVERT(Ring, ring); @@ -53,6 +59,23 @@ Bool RingIsSingle(Ring ring) } +/* RingLength -- return the number of nodes in the ring, not including + * this node + * + * See + */ + +Size RingLength(Ring ring) +{ + Size size = 0; + Ring node, next; + AVERT(Ring, ring); + RING_FOR(node, ring, next) + ++ size; + return size; +} + + /* RingInit -- initialize a ring node */ @@ -131,7 +154,7 @@ Ring (RingPrev)(Ring ring) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2003 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/ring.h b/code/ring.h index d5b64076f6..9cb915f4bf 100644 --- a/code/ring.h +++ b/code/ring.h @@ -1,7 +1,7 @@ /* ring.h: RING INTERFACE * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2001 Global Graphics Software. */ @@ -30,8 +30,9 @@ typedef struct RingStruct { /* double-ended queue structure */ extern Bool RingCheck(Ring ring); extern Bool RingCheckSingle(Ring ring); extern Bool RingIsSingle(Ring ring); +extern Size RingLength(Ring ring); -/* .ring.init: */ +/* .ring.init: See */ extern void (RingInit)(Ring ring); #define RingInit(ring) \ BEGIN \ @@ -42,7 +43,7 @@ extern void (RingInit)(Ring ring); AVER(RingCheck(_ring)); \ END -/* .ring.finish: */ +/* .ring.finish: See */ extern void (RingFinish)(Ring ring); #define RingFinish(ring) \ BEGIN \ @@ -52,7 +53,7 @@ extern void (RingFinish)(Ring ring); _ring->prev = RingNONE; \ END -/* .ring.append: */ +/* .ring.append: See */ extern void (RingAppend)(Ring ring, Ring new); #define RingAppend(ring, new) \ BEGIN \ @@ -78,7 +79,7 @@ extern void (RingInsert)(Ring ring, Ring new); _ring->next = _new; \ END -/* .ring.remove: */ +/* .ring.remove: See */ extern void (RingRemove)(Ring old); #define RingRemove(old) \ BEGIN \ @@ -91,11 +92,11 @@ extern void (RingRemove)(Ring old); _old->prev = _old; \ END -/* .ring.next: */ +/* .ring.next: See */ extern Ring (RingNext)(Ring ring); #define RingNext(ring) ((ring)->next) -/* .ring.prev: */ +/* .ring.prev: See */ extern Ring (RingPrev)(Ring ring); #define RingPrev(ring) ((ring)->prev) @@ -115,7 +116,7 @@ extern Ring (RingPrev)(Ring ring); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/root.c b/code/root.c index edb932f800..7a3e1205be 100644 --- a/code/root.c +++ b/code/root.c @@ -1,7 +1,7 @@ /* root.c: ROOT IMPLEMENTATION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * * .purpose: This is the implementation of the root datatype. * @@ -98,7 +98,7 @@ Bool RootCheck(Root root) CHECKS(Root, root); CHECKU(Arena, root->arena); CHECKL(root->serial < ArenaGlobals(root->arena)->rootSerial); - CHECKL(RingCheck(&root->arenaRing)); + CHECKD_NOSIG(Ring, &root->arenaRing); CHECKL(RankCheck(root->rank)); CHECKL(TraceSetCheck(root->grey)); /* Don't need to check var here, because of the switch below */ @@ -121,7 +121,7 @@ Bool RootCheck(Root root) case RootREG: CHECKL(root->the.reg.scan != NULL); - CHECKL(ThreadCheck(root->the.reg.thread)); + CHECKD_NOSIG(Thread, root->the.reg.thread); /* */ break; case RootFMT: @@ -139,7 +139,7 @@ Bool RootCheck(Root root) CHECKL(root->protBase != (Addr)0); CHECKL(root->protLimit != (Addr)0); CHECKL(root->protBase < root->protLimit); - /* there is no AccessSetCheck */ + CHECKL(AccessSetCheck(root->pm)); } else { CHECKL(root->protBase == (Addr)0); CHECKL(root->protLimit == (Addr)0); @@ -218,16 +218,16 @@ static Res rootCreateProtectable(Root *rootReturn, Arena arena, if (mode & RootModePROTECTABLE) { root->protectable = TRUE; if (mode & RootModePROTECTABLE_INNER) { - root->protBase = AddrAlignUp(base, ArenaAlign(arena)); - root->protLimit = AddrAlignDown(limit, ArenaAlign(arena)); + root->protBase = AddrArenaGrainUp(base, arena); + root->protLimit = AddrArenaGrainDown(limit, arena); if (!(root->protBase < root->protLimit)) { /* root had no inner pages */ root->protectable = FALSE; root->mode &=~ (RootModePROTECTABLE|RootModePROTECTABLE_INNER); } } else { - root->protBase = AddrAlignDown(base, ArenaAlign(arena)); - root->protLimit = AddrAlignUp(limit, ArenaAlign(arena)); + root->protBase = AddrArenaGrainDown(base, arena); + root->protLimit = AddrArenaGrainUp(limit, arena); } } @@ -261,9 +261,11 @@ Res RootCreateTable(Root *rootReturn, Arena arena, AVER(rootReturn != NULL); AVERT(Arena, arena); - AVER(RankCheck(rank)); + AVERT(Rank, rank); AVER(base != 0); - AVER(base < limit); + AVER(AddrIsAligned(base, sizeof(Word))); + AVER(base < limit); + AVER(AddrIsAligned(limit, sizeof(Word))); theUnion.table.base = base; theUnion.table.limit = limit; @@ -281,7 +283,7 @@ Res RootCreateTableMasked(Root *rootReturn, Arena arena, AVER(rootReturn != NULL); AVERT(Arena, arena); - AVER(RankCheck(rank)); + AVERT(Rank, rank); AVER(base != 0); AVER(base < limit); /* Can't check anything about mask. */ @@ -302,8 +304,9 @@ Res RootCreateReg(Root *rootReturn, Arena arena, AVER(rootReturn != NULL); AVERT(Arena, arena); - AVER(RankCheck(rank)); + AVERT(Rank, rank); AVERT(Thread, thread); + AVER(ThreadArena(thread) == arena); AVER(scan != NULL); theUnion.reg.scan = scan; @@ -314,6 +317,13 @@ Res RootCreateReg(Root *rootReturn, Arena arena, return rootCreate(rootReturn, arena, rank, (RootMode)0, RootREG, &theUnion); } +/* RootCreateFmt -- create root from block of formatted objects + * + * .fmt.no-align-check: Note that we don't check the alignment of base + * and limit. That's because we're only given the scan function, so we + * don't know the format's alignment requirements. + */ + Res RootCreateFmt(Root *rootReturn, Arena arena, Rank rank, RootMode mode, mps_fmt_scan_t scan, Addr base, Addr limit) @@ -322,7 +332,7 @@ Res RootCreateFmt(Root *rootReturn, Arena arena, AVER(rootReturn != NULL); AVERT(Arena, arena); - AVER(RankCheck(rank)); + AVERT(Rank, rank); AVER(FUNCHECK(scan)); AVER(base != 0); AVER(base < limit); @@ -342,7 +352,7 @@ Res RootCreateFun(Root *rootReturn, Arena arena, Rank rank, AVER(rootReturn != NULL); AVERT(Arena, arena); - AVER(RankCheck(rank)); + AVERT(Rank, rank); AVER(FUNCHECK(scan)); theUnion.fun.scan = scan; @@ -374,9 +384,9 @@ void RootDestroy(Root root) } -/* RootArena -- return the rank of a root +/* RootArena -- return the arena of a root * - * Must be thread-safe. */ + * Must be thread-safe. See */ Arena RootArena(Root root) { @@ -548,7 +558,7 @@ Bool RootOfAddr(Root *rootReturn, Arena arena, Addr addr) void RootAccess(Root root, AccessSet mode) { AVERT(Root, root); - /* Can't AVERT mode. */ + AVERT(AccessSet, mode); AVER((root->pm & mode) != AccessSetEMPTY); AVER(mode == AccessWRITE); /* only write protection supported */ @@ -580,74 +590,99 @@ Res RootsIterate(Globals arena, RootIterateFn f, void *p) /* RootDescribe -- describe a root */ -Res RootDescribe(Root root, mps_lib_FILE *stream) +Res RootDescribe(Root root, mps_lib_FILE *stream, Count depth) { Res res; - if (!TESTT(Root, root)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Root, root)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; - res = WriteF(stream, + res = WriteF(stream, depth, "Root $P ($U) {\n", (WriteFP)root, (WriteFU)root->serial, " arena $P ($U)\n", (WriteFP)root->arena, (WriteFU)root->arena->serial, " rank $U\n", (WriteFU)root->rank, " grey $B\n", (WriteFB)root->grey, " summary $B\n", (WriteFB)root->summary, + " mode", + root->mode == 0 ? " NONE" : "", + root->mode & RootModeCONSTANT ? " CONSTANT" : "", + root->mode & RootModePROTECTABLE ? " PROTECTABLE" : "", + root->mode & RootModePROTECTABLE_INNER ? " INNER" : "", + "\n", + " protectable $S", WriteFYesNo(root->protectable), + " protBase $A", (WriteFA)root->protBase, + " protLimit $A", (WriteFA)root->protLimit, + " pm", + root->pm == AccessSetEMPTY ? " EMPTY" : "", + root->pm & AccessREAD ? " READ" : "", + root->pm & AccessWRITE ? " WRITE" : "", NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; switch(root->var) { case RootTABLE: - res = WriteF(stream, - " table base $A limit $A\n", - root->the.table.base, root->the.table.limit, + res = WriteF(stream, depth + 2, + "table base $A limit $A\n", + (WriteFA)root->the.table.base, + (WriteFA)root->the.table.limit, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; break; case RootTABLE_MASKED: - res = WriteF(stream, " table base $A limit $A mask $B\n", - root->the.tableMasked.base, root->the.tableMasked.limit, - root->the.tableMasked.mask, + res = WriteF(stream, depth + 2, + "table base $A limit $A mask $B\n", + (WriteFA)root->the.tableMasked.base, + (WriteFA)root->the.tableMasked.limit, + (WriteFB)root->the.tableMasked.mask, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; break; case RootFUN: - res = WriteF(stream, - " scan function $F\n", (WriteFF)root->the.fun.scan, - " environment p $P s $W\n", - root->the.fun.p, (WriteFW)root->the.fun.s, + res = WriteF(stream, depth + 2, + "scan function $F\n", (WriteFF)root->the.fun.scan, + "environment p $P s $W\n", + (WriteFP)root->the.fun.p, (WriteFW)root->the.fun.s, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; break; case RootREG: - res = WriteF(stream, - " thread $P\n", (WriteFP)root->the.reg.thread, - " environment p $P", root->the.reg.p, + res = WriteF(stream, depth + 2, + "thread $P\n", (WriteFP)root->the.reg.thread, + "environment p $P", (WriteFP)root->the.reg.p, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; break; case RootFMT: - res = WriteF(stream, - " scan function $F\n", (WriteFF)root->the.fmt.scan, - " format base $A limit $A\n", - root->the.fmt.base, root->the.fmt.limit, + res = WriteF(stream, depth + 2, + "scan function $F\n", (WriteFF)root->the.fmt.scan, + "format base $A limit $A\n", + (WriteFA)root->the.fmt.base, (WriteFA)root->the.fmt.limit, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; break; default: NOTREACHED; } - res = WriteF(stream, + res = WriteF(stream, depth, "} Root $P ($U)\n", (WriteFP)root, (WriteFU)root->serial, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; return ResOK; } @@ -655,15 +690,16 @@ Res RootDescribe(Root root, mps_lib_FILE *stream) /* RootsDescribe -- describe all roots */ -Res RootsDescribe(Globals arenaGlobals, mps_lib_FILE *stream) +Res RootsDescribe(Globals arenaGlobals, mps_lib_FILE *stream, Count depth) { Res res = ResOK; Ring node, next; RING_FOR(node, &arenaGlobals->rootRing, next) { Root root = RING_ELT(Root, arenaRing, node); - res = RootDescribe(root, stream); /* this outputs too much */ - if (res != ResOK) return res; + res = RootDescribe(root, stream, depth); + if (res != ResOK) + return res; } return res; } @@ -671,7 +707,7 @@ Res RootsDescribe(Globals arenaGlobals, mps_lib_FILE *stream) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/sa.c b/code/sa.c index 59ddd4be17..f446a3218c 100644 --- a/code/sa.c +++ b/code/sa.c @@ -7,10 +7,11 @@ #include "sa.h" #include "mpm.h" #include "bt.h" +#include "vm.h" static Index pagesLength(SparseArray sa) { - return (sa->length * sa->elementSize + VMAlign(sa->vm) - 1) >> sa->shift; + return (sa->length * sa->elementSize + VMPageSize(sa->vm) - 1) >> sa->shift; } void SparseArrayInit(SparseArray sa, @@ -25,8 +26,8 @@ void SparseArrayInit(SparseArray sa, sa->mapped = mapped; sa->pages = pages; sa->vm = vm; - AVER(SizeIsP2(VMAlign(vm))); - sa->shift = SizeLog2(VMAlign(vm)); + AVER(SizeIsP2(VMPageSize(vm))); + sa->shift = SizeLog2(VMPageSize(vm)); BTResRange(mapped, 0, length); BTResRange(pages, 0, pagesLength(sa)); @@ -48,14 +49,12 @@ Bool SparseArrayCheck(SparseArray sa) CHECKS(SparseArray, sa); CHECKL(sa->base != NULL); CHECKL(sa->elementSize >= 1); - /* TODO: CHECKD(VM, sa->vm); once VMStruct becomes visible */ - CHECKL(VMCheck(sa->vm)); - CHECKL(sa->elementSize <= VMAlign(sa->vm)); + CHECKD_NOSIG(VM, sa->vm); /* */ + CHECKL(sa->elementSize <= VMPageSize(sa->vm)); CHECKL(sa->length > 0); - /* TODO: Make BTCheck extern and use everywhere. */ - /* CHECKL(BTCheck(sa->mapped)); */ - /* CHECKL(BTCheck(sa->pages)); */ - CHECKL(sa->shift == SizeLog2(VMAlign(sa->vm))); + CHECKD_NOSIG(BT, sa->mapped); + CHECKD_NOSIG(BT, sa->pages); + CHECKL(sa->shift == SizeLog2(VMPageSize(sa->vm))); return TRUE; } @@ -141,7 +140,7 @@ void SparseArrayUnmap(SparseArray sa, Index baseEI, Index limitEI) the page on which the base element resides. If any elements between there and baseMI are defined, we can't unmap that page, so bump up. */ baseMI = (baseEI * sa->elementSize) >> sa->shift; - i = SizeAlignDown(baseEI * sa->elementSize, VMAlign(sa->vm)) / sa->elementSize; + i = SizeAlignDown(baseEI * sa->elementSize, VMPageSize(sa->vm)) / sa->elementSize; if (i < baseEI && !BTIsResRange(sa->mapped, i, baseEI)) ++baseMI; @@ -149,7 +148,7 @@ void SparseArrayUnmap(SparseArray sa, Index baseEI, Index limitEI) the page on which the last element resides. If any elements between limitMI and there are defined, we can't unmap that page, so bump down. */ limitMI = ((limitEI * sa->elementSize - 1) >> sa->shift) + 1; - i = (SizeAlignUp(limitEI * sa->elementSize, VMAlign(sa->vm)) + + i = (SizeAlignUp(limitEI * sa->elementSize, VMPageSize(sa->vm)) + sa->elementSize - 1) / sa->elementSize; if (i > sa->length) i = sa->length; diff --git a/code/sa.h b/code/sa.h index c56ba4b050..2751b35d99 100644 --- a/code/sa.h +++ b/code/sa.h @@ -31,12 +31,12 @@ typedef struct SparseArrayStruct { BT mapped; /* whether elements exist in the array */ BT pages; /* whether underlying pages are mapped */ VM vm; /* where pages are mapped from */ - Shift shift; /* SizeLog2(VMAlign(vm)) TODO: VMShift(vm) */ + Shift shift; /* SizeLog2(VMPageSize(vm)) TODO: VMShift(vm) */ } SparseArrayStruct; extern void SparseArrayInit(SparseArray sa, void *base, Size elementSize, Index length, - BT defined, BT mapped, VM vm); + BT mapped, BT pages, VM vm); extern void SparseArrayFinish(SparseArray sa); extern Bool SparseArrayCheck(SparseArray sa); diff --git a/code/sac.c b/code/sac.c index 2a87f9a281..ab8bf867e3 100644 --- a/code/sac.c +++ b/code/sac.c @@ -1,7 +1,7 @@ /* sac.c: SEGREGATED ALLOCATION CACHES * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" @@ -32,6 +32,7 @@ static Bool sacFreeListBlockCheck(SACFreeListBlock fb) return TRUE; } +ATTRIBUTE_UNUSED static Bool SACCheck(SAC sac) { Index i, j; @@ -48,27 +49,33 @@ static Bool SACCheck(SAC sac) CHECKL(esac->_middle > 0); /* check classes above middle */ prevSize = esac->_middle; - for (j = sac->middleIndex + 1, i = 0; - j <= sac->classesCount; ++j, i += 2) { + for (j = sac->middleIndex + 1, i = 0; j < sac->classesCount; ++j, i += 2) { CHECKL(prevSize < esac->_freelists[i]._size); b = sacFreeListBlockCheck(&(esac->_freelists[i])); - if (!b) return b; + if (!b) + return b; prevSize = esac->_freelists[i]._size; } /* check overlarge class */ - CHECKL(esac->_freelists[i-2]._size == SizeMAX); - CHECKL(esac->_freelists[i-2]._count == 0); - CHECKL(esac->_freelists[i-2]._count_max == 0); - CHECKL(esac->_freelists[i-2]._blocks == NULL); + CHECKL(prevSize < esac->_freelists[i]._size); + b = sacFreeListBlockCheck(&(esac->_freelists[i])); + if (!b) + return b; + CHECKL(esac->_freelists[i]._size == SizeMAX); + CHECKL(esac->_freelists[i]._count == 0); + CHECKL(esac->_freelists[i]._count_max == 0); + CHECKL(esac->_freelists[i]._blocks == NULL); /* check classes below middle */ prevSize = esac->_middle; for (j = sac->middleIndex, i = 1; j > 0; --j, i += 2) { CHECKL(prevSize > esac->_freelists[i]._size); b = sacFreeListBlockCheck(&(esac->_freelists[i])); - if (!b) return b; + if (!b) + return b; prevSize = esac->_freelists[i]._size; } /* check smallest class */ + CHECKL(prevSize > esac->_freelists[i]._size); CHECKL(esac->_freelists[i]._size == 0); b = sacFreeListBlockCheck(&(esac->_freelists[i])); return b; @@ -113,10 +120,10 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount, /* to be large enough, but that gets complicated, if you have to */ /* merge classes because of the adjustment. */ for (i = 0; i < classesCount; ++i) { - AVER(classes[i]._block_size > 0); - AVER(SizeIsAligned(classes[i]._block_size, PoolAlignment(pool))); - AVER(prevSize < classes[i]._block_size); - prevSize = classes[i]._block_size; + AVER(classes[i].mps_block_size > 0); + AVER(SizeIsAligned(classes[i].mps_block_size, PoolAlignment(pool))); + AVER(prevSize < classes[i].mps_block_size); + prevSize = classes[i].mps_block_size; /* no restrictions on count */ /* no restrictions on frequency */ } @@ -124,7 +131,7 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount, /* Calculate frequency scale */ for (i = 0; i < classesCount; ++i) { unsigned oldFreq = totalFreq; - totalFreq += classes[i]._frequency; + totalFreq += classes[i].mps_frequency; AVER(oldFreq <= totalFreq); /* check for overflow */ UNUSED(oldFreq); /* */ } @@ -132,10 +139,11 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount, /* Find middle one */ totalFreq /= 2; for (i = 0; i < classesCount; ++i) { - if (totalFreq < classes[i]._frequency) break; - totalFreq -= classes[i]._frequency; + if (totalFreq < classes[i].mps_frequency) + break; + totalFreq -= classes[i].mps_frequency; } - if (totalFreq <= classes[i]._frequency / 2) + if (totalFreq <= classes[i].mps_frequency / 2) middleIndex = i; else middleIndex = i + 1; /* there must exist another class at i+1 */ @@ -151,9 +159,9 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount, /* It's important this matches SACFind. */ esac = ExternalSACOfSAC(sac); for (j = middleIndex + 1, i = 0; j < classesCount; ++j, i += 2) { - esac->_freelists[i]._size = classes[j]._block_size; + esac->_freelists[i]._size = classes[j].mps_block_size; esac->_freelists[i]._count = 0; - esac->_freelists[i]._count_max = classes[j]._cached_count; + esac->_freelists[i]._count_max = classes[j].mps_cached_count; esac->_freelists[i]._blocks = NULL; } esac->_freelists[i]._size = SizeMAX; @@ -161,19 +169,19 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount, esac->_freelists[i]._count_max = 0; esac->_freelists[i]._blocks = NULL; for (j = middleIndex, i = 1; j > 0; --j, i += 2) { - esac->_freelists[i]._size = classes[j-1]._block_size; + esac->_freelists[i]._size = classes[j-1].mps_block_size; esac->_freelists[i]._count = 0; - esac->_freelists[i]._count_max = classes[j]._cached_count; + esac->_freelists[i]._count_max = classes[j].mps_cached_count; esac->_freelists[i]._blocks = NULL; } esac->_freelists[i]._size = 0; esac->_freelists[i]._count = 0; - esac->_freelists[i]._count_max = classes[j]._cached_count; + esac->_freelists[i]._count_max = classes[j].mps_cached_count; esac->_freelists[i]._blocks = NULL; /* finish init */ esac->_trapped = FALSE; - esac->_middle = classes[middleIndex]._block_size; + esac->_middle = classes[middleIndex].mps_block_size; sac->pool = pool; sac->classesCount = classesCount; sac->middleIndex = middleIndex; @@ -249,7 +257,7 @@ Res SACFill(Addr *p_o, SAC sac, Size size, Bool hasReservoirPermit) AVER(p_o != NULL); AVERT(SAC, sac); AVER(size != 0); - AVER(BoolCheck(hasReservoirPermit)); + AVERT(Bool, hasReservoirPermit); esac = ExternalSACOfSAC(sac); sacFind(&i, &blockSize, sac, size); @@ -384,7 +392,7 @@ void SACFlush(SAC sac) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/sacss.c b/code/sacss.c index 5d74edc62c..d85b3ae507 100644 --- a/code/sacss.c +++ b/code/sacss.c @@ -1,12 +1,13 @@ /* sacss.c: SAC MANUAL ALLOC STRESS TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. */ #include "mpscmv.h" #include "mpscmvff.h" +#include "mpscmfs.h" #include "mpslib.h" #include "mpsavm.h" #include "mps.h" @@ -15,9 +16,7 @@ #include "mpslib.h" #include -#include "mpstd.h" #include -#include #include @@ -28,9 +27,6 @@ #define testSetSIZE 200 #define testLOOPS 10 -#define topClassSIZE 0xA00 -#define classCOUNT 4 - /* make -- allocate an object */ @@ -45,25 +41,36 @@ static mps_res_t make(mps_addr_t *p, mps_sac_t sac, size_t size) /* stress -- create a pool of the requested type and allocate in it */ -static mps_res_t stress(mps_class_t class, - size_t classes_count, mps_sac_classes_s *classes, - size_t (*size)(int i), mps_arena_t arena, ...) +static mps_res_t stress(mps_arena_t arena, mps_align_t align, + size_t (*size)(size_t i), + const char *name, mps_pool_class_t pool_class, + mps_arg_s *args) { mps_res_t res; mps_pool_t pool; mps_sac_t sac; - va_list arg; - int i, k; + size_t i, k; int *ps[testSetSIZE]; size_t ss[testSetSIZE]; + mps_sac_classes_s classes[4] = { + {1, 1, 1}, + {2, 1, 2}, + {16, 9, 5}, + {100, 9, 4}, + }; + size_t classes_count = sizeof classes / sizeof *classes; + for (i = 0; i < classes_count; ++i) { + classes[i].mps_block_size *= alignUp(align, sizeof(void *)); + } + + printf("%s\n", name); - va_start(arg, arena); - res = mps_pool_create_v(&pool, arena, class, arg); - va_end(arg); + res = mps_pool_create_k(&pool, arena, pool_class, args); if (res != MPS_RES_OK) return res; - die(mps_sac_create(&sac, pool, classes_count, classes), "SACCreate"); + die(mps_sac_create(&sac, pool, classes_count, classes), + "SACCreate"); /* allocate a load of objects */ for (i = 0; i < testSetSIZE; ++i) { @@ -89,32 +96,34 @@ static mps_res_t stress(mps_class_t class, ps[j] = ps[i]; ss[j] = ss[i]; ps[i] = tp; ss[i] = ts; } - if (k == (testLOOPS / 2)) mps_sac_flush(sac); + if (k == (testLOOPS / 2)) + mps_sac_flush(sac); /* free half of the objects */ /* upper half, as when allocating them again we want smaller objects */ /* see randomSize() */ switch (k % 2) { - case 0: { + case 0: for (i=testSetSIZE/2; i (b)) ? (a) : (b)) +/* randomSize -- produce sizes both large and small */ - -/* randomSize8 -- produce sizes both latge and small */ - -static size_t randomSize8(int i) +static size_t randomSize(size_t i) { size_t maxSize = 2 * 160 * 0x2000; size_t size; @@ -141,63 +147,97 @@ static size_t randomSize8(int i) } -/* testInArena -- test all the pool classes in the given arena */ +/* fixedSize -- produce always the same size */ + +static size_t fixedSizeSize = 0; + +static size_t fixedSize(size_t i) +{ + testlib_unused(i); + return fixedSizeSize; +} -static mps_pool_debug_option_s debugOptions8 = { - /* .fence_template = */ (const void *)"postpost", - /* .fence_size = */ 8, - /* .free_template = */ (const void *)"DEAD", - /* .free_size = */ 4 -}; -static mps_pool_debug_option_s debugOptions16 = { - /* .fence_template = */ (const void *)"postpostpostpost", - /* .fence_size = */ 16, - /* .free_template = */ (const void *)"DEAD", +static mps_pool_debug_option_s debugOptions = { + /* .fence_template = */ "post", + /* .fence_size = */ 4, + /* .free_template = */ "DEAD", /* .free_size = */ 4 }; -static mps_sac_classes_s classes8[4] = { {8, 1, 1}, {16, 1, 2}, {136, 9, 5}, - {topClassSIZE, 9, 4} }; -static mps_sac_classes_s classes16[4] = { {16, 1, 1}, {32, 1, 2}, {144, 9, 5}, - {topClassSIZE, 9, 4} }; +/* testInArena -- test all the pool classes in the given arena */ -static int testInArena(mps_arena_t arena) +static void testInArena(mps_arena_class_t arena_class, mps_arg_s *arena_args) { - mps_pool_debug_option_s *debugOptions; - mps_sac_classes_s *classes; - - debugOptions = MPS_PF_ALIGN == 8 ? &debugOptions8 : &debugOptions16; - classes = MPS_PF_ALIGN == 8 ? classes8 : classes16; - - printf("MVFF\n\n"); - die(stress(mps_class_mvff(), classCOUNT, classes, randomSize8, arena, - (size_t)65536, (size_t)32, (mps_align_t)MPS_PF_ALIGN, TRUE, TRUE, TRUE), - "stress MVFF"); - printf("MV debug\n\n"); - die(stress(mps_class_mv_debug(), classCOUNT, classes, randomSize8, arena, - debugOptions, (size_t)65536, (size_t)32, (size_t)65536), - "stress MV debug"); - printf("MV\n\n"); - die(stress(mps_class_mv(), classCOUNT, classes, randomSize8, arena, - (size_t)65536, (size_t)32, (size_t)65536), - "stress MV"); - return 0; + mps_arena_t arena; + + die(mps_arena_create_k(&arena, arena_class, arena_args), + "mps_arena_create"); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = sizeof(void *) << (rnd() % 4); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE); + die(stress(arena, align, randomSize, "MVFF", mps_class_mvff(), args), + "stress MVFF"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = sizeof(void *) << (rnd() % 4); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE); + MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &debugOptions); + die(stress(arena, align, randomSize, "MVFF debug", + mps_class_mvff_debug(), args), + "stress MVFF debug"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = (mps_align_t)1 << (rnd() % 6); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + die(stress(arena, align, randomSize, "MV", mps_class_mv(), args), + "stress MV"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + mps_align_t align = (mps_align_t)1 << (rnd() % 6); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); + MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &debugOptions); + die(stress(arena, align, randomSize, "MV debug", + mps_class_mv_debug(), args), + "stress MV debug"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + fixedSizeSize = MPS_PF_ALIGN * (1 + rnd() % 100); + MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, fixedSizeSize); + die(stress(arena, fixedSizeSize, fixedSize, "MFS", mps_class_mfs(), args), + "stress MFS"); + } MPS_ARGS_END(args); + + mps_arena_destroy(arena); } int main(int argc, char *argv[]) { - mps_arena_t arena; + testlib_init(argc, argv); - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE); + testInArena(mps_arena_class_vm(), args); + } MPS_ARGS_END(args); - die(mps_arena_create(&arena, mps_arena_class_vmnz(), testArenaSIZE), - "mps_arena_create"); - testInArena(arena); - mps_arena_destroy(arena); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE); + testInArena(mps_arena_class_vm(), args); + } MPS_ARGS_END(args); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; @@ -206,7 +246,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/seg.c b/code/seg.c index 1ba5041d03..d6c013e77d 100644 --- a/code/seg.c +++ b/code/seg.c @@ -1,7 +1,7 @@ /* seg.c: SEGMENTS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * * .design: The design for this module is . * @@ -54,7 +54,7 @@ static Res SegInit(Seg seg, Pool pool, Addr base, Size size, /* SegAlloc -- allocate a segment from the arena */ -Res SegAlloc(Seg *segReturn, SegClass class, SegPref pref, +Res SegAlloc(Seg *segReturn, SegClass class, LocusPref pref, Size size, Pool pool, Bool withReservoirPermit, ArgList args) { Res res; @@ -65,14 +65,14 @@ Res SegAlloc(Seg *segReturn, SegClass class, SegPref pref, AVER(segReturn != NULL); AVERT(SegClass, class); - AVERT(SegPref, pref); + AVERT(LocusPref, pref); AVER(size > (Size)0); AVERT(Pool, pool); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); arena = PoolArena(pool); AVERT(Arena, arena); - AVER(SizeIsAligned(size, arena->alignment)); + AVER(SizeIsArenaGrains(size, arena)); /* allocate the memory from the arena */ res = ArenaAlloc(&base, pref, size, pool, withReservoirPermit); @@ -137,24 +137,24 @@ void SegFree(Seg seg) static Res SegInit(Seg seg, Pool pool, Addr base, Size size, Bool withReservoirPermit, ArgList args) { - Tract tract; - Addr addr, limit; - Size align; + Addr limit; Arena arena; SegClass class; Res res; + Bool b; AVER(seg != NULL); AVERT(Pool, pool); arena = PoolArena(pool); - align = ArenaAlign(arena); - AVER(AddrIsAligned(base, align)); - AVER(SizeIsAligned(size, align)); + AVER(AddrIsArenaGrain(base, arena)); + AVER(SizeIsArenaGrains(size, arena)); class = seg->class; AVERT(SegClass, class); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); limit = AddrAdd(base, size); + + /* IMPORTANT: Keep in sync with segTrivSplit. */ seg->limit = limit; seg->rankSet = RankSetEMPTY; seg->white = TraceSetEMPTY; @@ -163,26 +163,10 @@ static Res SegInit(Seg seg, Pool pool, Addr base, Size size, seg->pm = AccessSetEMPTY; seg->sm = AccessSetEMPTY; seg->depth = 0; - seg->firstTract = NULL; - - seg->sig = SegSig; /* set sig now so tract checks will see it */ - - TRACT_FOR(tract, addr, arena, base, limit) { - AVER(TractCheck(tract)); /* */ - AVER(TractP(tract) == NULL); - AVER(!TractHasSeg(tract)); - AVER(TractPool(tract) == pool); - AVER(TractWhite(tract) == TraceSetEMPTY); - TRACT_SET_SEG(tract, seg); - if (addr == base) { - AVER(seg->firstTract == NULL); - seg->firstTract = tract; - } - AVER(seg->firstTract != NULL); - } - AVER(addr == seg->limit); - + seg->firstTract = TractOfBaseAddr(arena, base); RingInit(SegPoolRing(seg)); + TreeInit(SegTree(seg)); + seg->sig = SegSig; /* set sig now so tract checks will see it */ /* Class specific initialization comes last */ res = class->init(seg, pool, base, size, withReservoirPermit, args); @@ -191,14 +175,12 @@ static Res SegInit(Seg seg, Pool pool, Addr base, Size size, AVERT(Seg, seg); RingAppend(&pool->segRing, SegPoolRing(seg)); + b = SplayTreeInsert(ArenaSegSplay(arena), SegTree(seg)); + AVER(b); /* not already in tree */ return ResOK; failInit: RingFinish(SegPoolRing(seg)); - TRACT_FOR(tract, addr, arena, base, limit) { - AVER(TractCheck(tract)); /* */ - TRACT_UNSET_SEG(tract); - } seg->sig = SigInvalid; return res; } @@ -209,9 +191,8 @@ static Res SegInit(Seg seg, Pool pool, Addr base, Size size, static void SegFinish(Seg seg) { Arena arena; - Addr addr, limit; - Tract tract; SegClass class; + Bool b; AVERT(Seg, seg); class = seg->class; @@ -230,15 +211,10 @@ static void SegFinish(Seg seg) /* See */ ShieldFlush(PoolArena(SegPool(seg))); - limit = SegLimit(seg); - - TRACT_TRACT_FOR(tract, addr, arena, seg->firstTract, limit) { - AVER(TractCheck(tract)); /* */ - TractSetWhite(tract, TraceSetEMPTY); - TRACT_UNSET_SEG(tract); - } - AVER(addr == seg->limit); - + /* IMPORTANT: Keep in sync with SegFinish. */ + b = SplayTreeDelete(ArenaSegSplay(arena), SegTree(seg)); + AVER(b); /* seg should be in arena splay tree */ + TreeFinish(SegTree(seg)); RingRemove(SegPoolRing(seg)); RingFinish(SegPoolRing(seg)); @@ -251,7 +227,34 @@ static void SegFinish(Seg seg) /* fund are not protected) */ AVER(seg->sm == AccessSetEMPTY); AVER(seg->pm == AccessSetEMPTY); - +} + + +#define segOfTree(_tree) TREE_ELT(Seg, treeStruct, _tree) + +Compare SegCompare(Tree tree, TreeKey key) +{ + Seg seg; + Addr addr; + + AVERT_CRITICAL(Tree, tree); + AVER_CRITICAL(tree != TreeEMPTY); + /* Can't check anything about key -- it's an arbitrary address. */ + + seg = segOfTree(tree); + addr = (Addr)key; /* FIXME: See baseOfKey in cbs.c */ + + if (addr < SegBase(seg)) + return CompareLESS; + else if (addr >= SegLimit(seg)) + return CompareGREATER; + else + return CompareEQUAL; +} + +TreeKey SegKey(Tree tree) +{ + return (TreeKey)SegBase(segOfTree(tree)); /* FIXME: See cbsBlockKey in cbs.c */ } @@ -263,7 +266,7 @@ static void SegFinish(Seg seg) void SegSetGrey(Seg seg, TraceSet grey) { AVERT(Seg, seg); - AVER(TraceSetCheck(grey)); + AVERT(TraceSet, grey); AVER(grey == TraceSetEMPTY || SegRankSet(seg) != RankSetEMPTY); /* Don't dispatch to the class method if there's no actual change in @@ -281,7 +284,7 @@ void SegSetGrey(Seg seg, TraceSet grey) void SegSetWhite(Seg seg, TraceSet white) { AVERT(Seg, seg); - AVER(TraceSetCheck(white)); + AVERT(TraceSet, white); seg->class->setWhite(seg, white); } @@ -296,7 +299,7 @@ void SegSetWhite(Seg seg, TraceSet white) void SegSetRankSet(Seg seg, RankSet rankSet) { AVERT(Seg, seg); - AVER(RankSetCheck(rankSet)); + AVERT(RankSet, rankSet); AVER(rankSet != RankSetEMPTY || SegSummary(seg) == RefSetEMPTY); seg->class->setRankSet(seg, rankSet); } @@ -309,9 +312,12 @@ void SegSetSummary(Seg seg, RefSet summary) AVERT(Seg, seg); AVER(summary == RefSetEMPTY || SegRankSet(seg) != RankSetEMPTY); -#ifdef PROTECTION_NONE +#if defined(REMEMBERED_SET_NONE) + /* Without protection, we can't maintain the remembered set because + there are writes we don't know about. */ summary = RefSetUNIV; #endif + if (summary != SegSummary(seg)) seg->class->setSummary(seg, summary); } @@ -322,13 +328,14 @@ void SegSetSummary(Seg seg, RefSet summary) void SegSetRankAndSummary(Seg seg, RankSet rankSet, RefSet summary) { AVERT(Seg, seg); - AVER(RankSetCheck(rankSet)); + AVERT(RankSet, rankSet); -#ifdef PROTECTION_NONE +#if defined(REMEMBERED_SET_NONE) if (rankSet != RankSetEMPTY) { summary = RefSetUNIV; } #endif + seg->class->setRankSummary(seg, rankSet, summary); } @@ -355,31 +362,58 @@ void SegSetBuffer(Seg seg, Buffer buffer) /* SegDescribe -- describe a segment */ -Res SegDescribe(Seg seg, mps_lib_FILE *stream) +Res SegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) { Res res; Pool pool; - if (!TESTT(Seg, seg)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Seg, seg)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; pool = SegPool(seg); - res = WriteF(stream, + res = WriteF(stream, depth, "Segment $P [$A,$A) {\n", (WriteFP)seg, (WriteFA)SegBase(seg), (WriteFA)SegLimit(seg), " class $P (\"$S\")\n", - (WriteFP)seg->class, seg->class->name, + (WriteFP)seg->class, (WriteFS)seg->class->name, " pool $P ($U)\n", (WriteFP)pool, (WriteFU)pool->serial, + " depth $U\n", seg->depth, + " pm", + seg->pm == AccessSetEMPTY ? " EMPTY" : "", + seg->pm & AccessREAD ? " READ" : "", + seg->pm & AccessWRITE ? " WRITE" : "", + "\n", + " sm", + seg->sm == AccessSetEMPTY ? " EMPTY" : "", + seg->sm & AccessREAD ? " READ" : "", + seg->sm & AccessWRITE ? " WRITE" : "", + "\n", + " grey $B\n", (WriteFB)seg->grey, + " white $B\n", (WriteFB)seg->white, + " nailed $B\n", (WriteFB)seg->nailed, + " rankSet", + seg->rankSet == RankSetEMPTY ? " EMPTY" : "", + BS_IS_MEMBER(seg->rankSet, RankAMBIG) ? " AMBIG" : "", + BS_IS_MEMBER(seg->rankSet, RankEXACT) ? " EXACT" : "", + BS_IS_MEMBER(seg->rankSet, RankFINAL) ? " FINAL" : "", + BS_IS_MEMBER(seg->rankSet, RankWEAK) ? " WEAK" : "", NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; + + res = seg->class->describe(seg, stream, depth + 2); + if (res != ResOK) + return res; - res = seg->class->describe(seg, stream); - if (res != ResOK) return res; + res = WriteF(stream, 0, "\n", NULL); + if (res != ResOK) + return res; - res = WriteF(stream, "\n", - "} Segment $P\n", (WriteFP)seg, NULL); + res = WriteF(stream, depth, "} Segment $P\n", (WriteFP)seg, NULL); return res; } @@ -420,17 +454,59 @@ Size SegSize(Seg seg) Bool SegOfAddr(Seg *segReturn, Arena arena, Addr addr) { - Tract tract; - AVER_CRITICAL(segReturn != NULL); /* .seg.critical */ + Tree tree; + AVER_CRITICAL(segReturn != NULL); /* .seg.critical FIXME: maybe not with white-cuckoo */ AVERT_CRITICAL(Arena, arena); /* .seg.critical */ - if (TractOfAddr(&tract, arena, addr)) { - return TRACT_SEG(segReturn, tract); + if (SplayTreeFind(&tree, ArenaSegSplay(arena), (TreeKey)addr)) { /* FIXME: cast */ + *segReturn = segOfTree(tree); + return TRUE; } else { return FALSE; } } +/* SegTraverse -- visit all segments in the arena in address order */ + +typedef struct SegTraverseClosureStruct { + SegVisitor visit; + void *closureP; + Size closureS; +} SegTraverseClosureStruct, *SegTraverseClosure; + +static Bool segTraverseVisit(Tree tree, void *closureP, Size closureS) +{ + SegTraverseClosure stv = closureP; + AVER_CRITICAL(closureS == sizeof(*stv)); + return stv->visit(segOfTree(tree), stv->closureP, stv->closureS); +} + +Bool SegTraverse(Arena arena, SegVisitor visit, + void *closureP, Size closureS) +{ + SegTraverseClosureStruct stvStruct; + stvStruct.visit = visit; + stvStruct.closureP = closureP; + stvStruct.closureS = closureS; + return TreeTraverse(SplayTreeRoot(ArenaSegSplay(arena)), + SegCompare, SegKey, + segTraverseVisit, + &stvStruct, sizeof(stvStruct)); +} + +void SegTraverseAndDelete(Arena arena, SegVisitor visit, + void *closureP, Size closureS) +{ + SegTraverseClosureStruct stvStruct; + stvStruct.visit = visit; + stvStruct.closureP = closureP; + stvStruct.closureS = closureS; + TreeTraverseAndDelete(&SplayTreeRoot(ArenaSegSplay(arena)), + segTraverseVisit, + &stvStruct, sizeof(stvStruct)); +} + + /* SegFirst -- return the first seg in the arena * * This is used to start an iteration over all segs in the arena. @@ -500,7 +576,7 @@ Bool SegNextOfRing(Seg *segReturn, Arena arena, Pool pool, Ring next) AVER_CRITICAL(segReturn != NULL); /* .seg.critical */ AVERT_CRITICAL(Arena, arena); AVERT_CRITICAL(Pool, pool); - AVER_CRITICAL(RingCheck(next)); + AVERT_CRITICAL(Ring, next); if (next == PoolSegRing(pool)) { if (!PoolNext(&pool, arena, pool) || @@ -522,43 +598,6 @@ Bool SegNext(Seg *segReturn, Arena arena, Seg seg) } -/* SegFindAboveAddr -- return the "next" seg in the arena - * - * Finds the seg with the lowest base address which is - * greater than a specified address. The address must be (or once - * have been) the base address of a seg. - */ - -Bool SegFindAboveAddr(Seg *segReturn, Arena arena, Addr addr) -{ - Tract tract; - Addr base = addr; - AVER_CRITICAL(segReturn != NULL); /* .seg.critical */ - AVERT_CRITICAL(Arena, arena); - - while (TractNext(&tract, arena, base)) { - Seg seg; - if (TRACT_SEG(&seg, tract)) { - if (tract == seg->firstTract) { - *segReturn = seg; - return TRUE; - } else { - /* found the next tract in a large segment */ - /* base & addr must be the base of this segment */ - AVER_CRITICAL(TractBase(seg->firstTract) == addr); - AVER_CRITICAL(addr == base); - /* set base to the last tract in the segment */ - base = AddrSub(seg->limit, ArenaAlign(arena)); - AVER_CRITICAL(base > addr); - } - } else { - base = TractBase(tract); - } - } - return FALSE; -} - - /* SegMerge -- Merge two adjacent segments * * See @@ -571,7 +610,6 @@ Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi, Addr base, mid, limit; Arena arena; Res res; - va_list args; AVER(NULL != mergedSegReturn); AVERT(Seg, segLo); @@ -583,7 +621,7 @@ Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi, mid = SegLimit(segLo); limit = SegLimit(segHi); AVER(SegBase(segHi) == SegLimit(segLo)); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); arena = PoolArena(SegPool(segLo)); ShieldFlush(arena); /* see */ @@ -591,11 +629,10 @@ Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi, /* Invoke class-specific methods to do the merge */ res = class->merge(segLo, segHi, base, mid, limit, withReservoirPermit); - va_end(args); if (ResOK != res) goto failMerge; - EVENT3(SegMerge, segLo, segHi, withReservoirPermit); + EVENT3(SegMerge, segLo, segHi, BOOLOF(withReservoirPermit)); /* Deallocate segHi object */ ControlFree(arena, segHi, class->size); AVERT(Seg, segLo); @@ -633,10 +670,14 @@ Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at, base = SegBase(seg); limit = SegLimit(seg); AVERT(Arena, arena); - AVER(AddrIsAligned(at, arena->alignment)); + AVER(AddrIsArenaGrain(at, arena)); AVER(at > base); AVER(at < limit); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); + + /* Can only split a buffered segment if the entire buffer is below + * the split point. */ + AVER(SegBuffer(seg) == NULL || BufferLimit(SegBuffer(seg)) <= at); ShieldFlush(arena); /* see */ @@ -680,11 +721,8 @@ Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at, Bool SegCheck(Seg seg) { - Tract tract; Arena arena; Pool pool; - Addr addr; - Size align; CHECKS(Seg, seg); CHECKL(TraceSetCheck(seg->white)); @@ -692,34 +730,38 @@ Bool SegCheck(Seg seg) /* can't assume nailed is subset of white - mightn't be during whiten */ /* CHECKL(TraceSetSub(seg->nailed, seg->white)); */ CHECKL(TraceSetCheck(seg->grey)); - CHECKL(TractCheck(seg->firstTract)); /* */ + CHECKD_NOSIG(Tract, seg->firstTract); pool = SegPool(seg); CHECKU(Pool, pool); arena = PoolArena(pool); CHECKU(Arena, arena); - align = ArenaAlign(arena); - CHECKL(AddrIsAligned(TractBase(seg->firstTract), align)); - CHECKL(AddrIsAligned(seg->limit, align)); + CHECKL(AddrIsArenaGrain(TractBase(seg->firstTract), arena)); + CHECKL(AddrIsArenaGrain(seg->limit, arena)); CHECKL(seg->limit > TractBase(seg->firstTract)); - /* Each tract of the segment must agree about white traces */ - TRACT_TRACT_FOR(tract, addr, arena, seg->firstTract, seg->limit) { - Seg trseg = NULL; /* suppress compiler warning */ - - CHECKL(TractCheck(tract)); /* */ - CHECKL(TRACT_SEG(&trseg, tract) && (trseg == seg)); - CHECKL(TractWhite(tract) == seg->white); - CHECKL(TractPool(tract) == pool); + /* Each tract of the segment must agree about white traces. Note + * that even if the CHECKs are compiled away there is still a + * significant cost in looping over the tracts, hence the guard. See + * job003778. */ +#if defined(AVER_AND_CHECK_ALL) + { + Tract tract; + Addr addr; + TRACT_TRACT_FOR(tract, addr, arena, seg->firstTract, seg->limit) { + CHECKD_NOSIG(Tract, tract); + CHECKL(TractPool(tract) == pool); + } + CHECKL(addr == seg->limit); } - CHECKL(addr == seg->limit); +#endif /* AVER_AND_CHECK_ALL */ /* The segment must belong to some pool, so it should be on a */ /* pool's segment ring. (Actually, this isn't true just after */ /* the segment is initialized.) */ /* CHECKL(RingNext(&seg->poolRing) != &seg->poolRing); */ - CHECKL(RingCheck(&seg->poolRing)); - + CHECKD_NOSIG(Ring, &seg->poolRing); + /* "pm", "sm", and "depth" not checked. See .check.shield. */ CHECKL(RankSetCheck(seg->rankSet)); if (seg->rankSet == RankSetEMPTY) { @@ -750,20 +792,18 @@ static Res segTrivInit(Seg seg, Pool pool, Addr base, Size size, Bool reservoirPermit, ArgList args) { /* all the initialization happens in SegInit so checks are safe */ - Size align; Arena arena; AVERT(Seg, seg); AVERT(Pool, pool); arena = PoolArena(pool); - align = ArenaAlign(arena); - AVER(AddrIsAligned(base, align)); - AVER(SizeIsAligned(size, align)); + AVER(AddrIsArenaGrain(base, arena)); + AVER(SizeIsArenaGrains(size, arena)); AVER(SegBase(seg) == base); AVER(SegSize(seg) == size); AVER(SegPool(seg) == pool); - AVER(BoolCheck(reservoirPermit)); - AVER(ArgListCheck(args)); + AVERT(Bool, reservoirPermit); + AVERT(ArgList, args); UNUSED(args); return ResOK; } @@ -783,7 +823,7 @@ static void segTrivFinish(Seg seg) static void segNoSetGrey(Seg seg, TraceSet grey) { AVERT(Seg, seg); - AVER(TraceSetCheck(grey)); + AVERT(TraceSet, grey); AVER(seg->rankSet != RankSetEMPTY); NOTREACHED; } @@ -794,7 +834,7 @@ static void segNoSetGrey(Seg seg, TraceSet grey) static void segNoSetWhite(Seg seg, TraceSet white) { AVERT(Seg, seg); - AVER(TraceSetCheck(white)); + AVERT(TraceSet, white); NOTREACHED; } @@ -804,7 +844,7 @@ static void segNoSetWhite(Seg seg, TraceSet white) static void segNoSetRankSet(Seg seg, RankSet rankSet) { AVERT(Seg, seg); - AVER(RankSetCheck(rankSet)); + AVERT(RankSet, rankSet); NOTREACHED; } @@ -824,7 +864,7 @@ static void segNoSetSummary(Seg seg, RefSet summary) static void segNoSetRankSummary(Seg seg, RankSet rankSet, RefSet summary) { AVERT(Seg, seg); - AVER(RankSetCheck(rankSet)); + AVERT(RankSet, rankSet); UNUSED(summary); NOTREACHED; } @@ -864,7 +904,7 @@ static Res segNoMerge(Seg seg, Seg segHi, AVER(SegLimit(seg) == mid); AVER(SegBase(segHi) == mid); AVER(SegLimit(segHi) == limit); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); NOTREACHED; return ResFAIL; } @@ -881,26 +921,23 @@ static Res segTrivMerge(Seg seg, Seg segHi, Bool withReservoirPermit) { Pool pool; - Size align; Arena arena; - Tract tract; - Addr addr; + Bool b; AVERT(Seg, seg); AVERT(Seg, segHi); pool = SegPool(seg); arena = PoolArena(pool); - align = ArenaAlign(arena); - AVER(AddrIsAligned(base, align)); - AVER(AddrIsAligned(mid, align)); - AVER(AddrIsAligned(limit, align)); + AVER(AddrIsArenaGrain(base, arena)); + AVER(AddrIsArenaGrain(mid, arena)); + AVER(AddrIsArenaGrain(limit, arena)); AVER(base < mid); AVER(mid < limit); AVER(SegBase(seg) == base); AVER(SegLimit(seg) == mid); AVER(SegBase(segHi) == mid); AVER(SegLimit(segHi) == limit); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); /* .similar. */ AVER(seg->rankSet == segHi->rankSet); @@ -916,21 +953,20 @@ static Res segTrivMerge(Seg seg, Seg segHi, /* no need to update fields which match. See .similar */ - seg->limit = limit; - TRACT_FOR(tract, addr, arena, mid, limit) { - AVER(TractCheck(tract)); /* */ - AVER(TractHasSeg(tract)); - AVER(segHi == TractP(tract)); - AVER(TractPool(tract) == pool); - TRACT_SET_SEG(tract, seg); - } - AVER(addr == seg->limit); /* Finish segHi. */ + /* IMPORTANT: Keep in sync with SegFinish. */ + b = SplayTreeDelete(ArenaSegSplay(arena), SegTree(segHi)); + AVER(b); /* seg should be in arena splay tree */ + TreeFinish(SegTree(segHi)); RingRemove(SegPoolRing(segHi)); RingFinish(SegPoolRing(segHi)); segHi->sig = SigInvalid; + /* This does not affect seg's position in the segment tree, since it + is address ordered and no segments overlap. */ + seg->limit = limit; + AVERT(Seg, seg); return ResOK; } @@ -948,7 +984,7 @@ static Res segNoSplit(Seg seg, Seg segHi, AVER(mid < limit); AVER(SegBase(seg) == base); AVER(SegLimit(seg) == limit); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); NOTREACHED; return ResFAIL; @@ -961,32 +997,33 @@ static Res segTrivSplit(Seg seg, Seg segHi, Addr base, Addr mid, Addr limit, Bool withReservoirPermit) { - Tract tract; Pool pool; - Addr addr; - Size align; Arena arena; + Bool b; AVERT(Seg, seg); AVER(segHi != NULL); /* can't check fully, it's not initialized */ pool = SegPool(seg); arena = PoolArena(pool); - align = ArenaAlign(arena); - AVER(AddrIsAligned(base, align)); - AVER(AddrIsAligned(mid, align)); - AVER(AddrIsAligned(limit, align)); + AVER(AddrIsArenaGrain(base, arena)); + AVER(AddrIsArenaGrain(mid, arena)); + AVER(AddrIsArenaGrain(limit, arena)); AVER(base < mid); AVER(mid < limit); AVER(SegBase(seg) == base); AVER(SegLimit(seg) == limit); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); /* Segment may not be exposed, or in the shield cache */ /* See & */ AVER(seg->depth == 0); - /* Full initialization for segHi. Just modify seg. */ + /* Full initialization for segHi. Just modify seg. Note that this + does not affect seg's position in the segment tree, since it is + address-ordered and no segments overlap. */ seg->limit = mid; + + /* IMPORTANT: Keep in sync with SegInit. */ segHi->limit = limit; segHi->rankSet = seg->rankSet; segHi->white = seg->white; @@ -997,85 +1034,49 @@ static Res segTrivSplit(Seg seg, Seg segHi, segHi->depth = seg->depth; segHi->firstTract = NULL; segHi->class = seg->class; - segHi->sig = SegSig; + segHi->firstTract = TractOfBaseAddr(arena, mid); RingInit(SegPoolRing(segHi)); + TreeInit(SegTree(segHi)); + segHi->sig = SegSig; - TRACT_FOR(tract, addr, arena, mid, limit) { - AVER(TractCheck(tract)); /* */ - AVER(TractHasSeg(tract)); - AVER(seg == TractP(tract)); - AVER(TractPool(tract) == pool); - TRACT_SET_SEG(tract, segHi); - if (addr == mid) { - AVER(segHi->firstTract == NULL); - segHi->firstTract = tract; - } - AVER(segHi->firstTract != NULL); - } - AVER(addr == segHi->limit); - - RingAppend(&pool->segRing, SegPoolRing(segHi)); AVERT(Seg, seg); AVERT(Seg, segHi); + + RingAppend(&pool->segRing, SegPoolRing(segHi)); + b = SplayTreeInsert(ArenaSegSplay(arena), SegTree(segHi)); + AVER(b); /* not already in tree */ return ResOK; } /* segTrivDescribe -- Basic Seg description method */ -static Res segTrivDescribe(Seg seg, mps_lib_FILE *stream) +static Res segTrivDescribe(Seg seg, mps_lib_FILE *stream, Count depth) { Res res; - if (!TESTT(Seg, seg)) return ResFAIL; - if (stream == NULL) return ResFAIL; - - res = WriteF(stream, - " shield depth $U\n", (WriteFU)seg->depth, - " protection mode:", - NULL); - if (res != ResOK) return res; - if (SegPM(seg) & AccessREAD) { - res = WriteF(stream, " read", NULL); - if (res != ResOK) return res; - } - if (SegPM(seg) & AccessWRITE) { - res = WriteF(stream, " write", NULL); - if (res != ResOK) return res; - } - res = WriteF(stream, "\n shield mode:", NULL); - if (res != ResOK) return res; - if (SegSM(seg) & AccessREAD) { - res = WriteF(stream, " read", NULL); - if (res != ResOK) return res; - } - if (SegSM(seg) & AccessWRITE) { - res = WriteF(stream, " write", NULL); - if (res != ResOK) return res; - } - res = WriteF(stream, "\n ranks:", NULL); - if (res != ResOK) return res; - /* This bit ought to be in a RankSetDescribe in ref.c. */ - if (RankSetIsMember(seg->rankSet, RankAMBIG)) { - res = WriteF(stream, " ambiguous", NULL); - if (res != ResOK) return res; - } - if (RankSetIsMember(seg->rankSet, RankEXACT)) { - res = WriteF(stream, " exact", NULL); - if (res != ResOK) return res; - } - if (RankSetIsMember(seg->rankSet, RankFINAL)) { - res = WriteF(stream, " final", NULL); - if (res != ResOK) return res; - } - if (RankSetIsMember(seg->rankSet, RankWEAK)) { - res = WriteF(stream, " weak", NULL); - if (res != ResOK) return res; - } - res = WriteF(stream, "\n", - " white $B\n", (WriteFB)seg->white, - " grey $B\n", (WriteFB)seg->grey, - " nailed $B\n", (WriteFB)seg->nailed, + if (!TESTT(Seg, seg)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + res = WriteF(stream, depth, + "shield depth $U\n", (WriteFU)seg->depth, + "protection mode: ", + (SegPM(seg) & AccessREAD) ? "" : "!", "READ", " ", + (SegPM(seg) & AccessWRITE) ? "" : "!", "WRITE", "\n", + "shield mode: ", + (SegSM(seg) & AccessREAD) ? "" : "!", "READ", " ", + (SegSM(seg) & AccessWRITE) ? "" : "!", "WRITE", "\n", + "ranks:", + RankSetIsMember(seg->rankSet, RankAMBIG) ? " ambiguous" : "", + RankSetIsMember(seg->rankSet, RankEXACT) ? " exact" : "", + RankSetIsMember(seg->rankSet, RankFINAL) ? " final" : "", + RankSetIsMember(seg->rankSet, RankWEAK) ? " weak" : "", + "\n", + "white $B\n", (WriteFB)seg->white, + "grey $B\n", (WriteFB)seg->grey, + "nailed $B\n", (WriteFB)seg->nailed, NULL); return res; } @@ -1092,7 +1093,7 @@ Bool GCSegCheck(GCSeg gcseg) Seg seg; CHECKS(GCSeg, gcseg); seg = &gcseg->segStruct; - CHECKL(SegCheck(seg)); + CHECKD(Seg, seg); if (gcseg->buffer != NULL) { CHECKU(Buffer, gcseg->buffer); @@ -1102,7 +1103,7 @@ Bool GCSegCheck(GCSeg gcseg) } /* The segment should be on a grey ring if and only if it is grey. */ - CHECKL(RingCheck(&gcseg->greyRing)); + CHECKD_NOSIG(Ring, &gcseg->greyRing); CHECKL((seg->grey == TraceSetEMPTY) == RingIsSingle(&gcseg->greyRing)); @@ -1123,18 +1124,16 @@ static Res gcSegInit(Seg seg, Pool pool, Addr base, Size size, SegClass super; GCSeg gcseg; Arena arena; - Align align; Res res; AVERT(Seg, seg); AVERT(Pool, pool); arena = PoolArena(pool); - align = ArenaAlign(arena); - AVER(AddrIsAligned(base, align)); - AVER(SizeIsAligned(size, align)); + AVER(AddrIsArenaGrain(base, arena)); + AVER(SizeIsArenaGrains(size, arena)); gcseg = SegGCSeg(seg); AVER(&gcseg->segStruct == seg); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); /* Initialize the superclass fields first via next-method call */ super = SEG_SUPERCLASS(GCSegClass); @@ -1200,7 +1199,7 @@ static void gcSegSetGreyInternal(Seg seg, TraceSet oldGrey, TraceSet grey) /* Internal method. Parameters are checked by caller */ gcseg = SegGCSeg(seg); arena = PoolArena(SegPool(seg)); - seg->grey = grey; + seg->grey = BS_BITFIELD(Trace, grey); /* If the segment is now grey and wasn't before, add it to the */ /* appropriate grey list so that TraceFindGrey can locate it */ @@ -1209,7 +1208,7 @@ static void gcSegSetGreyInternal(Seg seg, TraceSet oldGrey, TraceSet grey) if (oldGrey == TraceSetEMPTY) { if (grey != TraceSetEMPTY) { AVER(RankSetIsSingle(seg->rankSet)); - for(rank = 0; rank < RankLIMIT; ++rank) + for(rank = RankMIN; rank < RankLIMIT; ++rank) if (RankSetIsMember(seg->rankSet, rank)) { /* NOTE: We push the segment onto the front of the queue, so that we preserve some locality of scanning, and so that we tend to @@ -1259,7 +1258,7 @@ static void gcSegSetGrey(Seg seg, TraceSet grey) Arena arena; AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ - AVER_CRITICAL(TraceSetCheck(grey)); /* .seg.method.check */ + AVERT_CRITICAL(TraceSet, grey); /* .seg.method.check */ AVER(seg->rankSet != RankSetEMPTY); gcseg = SegGCSeg(seg); AVERT_CRITICAL(GCSeg, gcseg); @@ -1294,30 +1293,18 @@ static void gcSegSetGrey(Seg seg, TraceSet grey) static void gcSegSetWhite(Seg seg, TraceSet white) { GCSeg gcseg; - Tract tract; Arena arena; - Addr addr, limit; AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ - AVER_CRITICAL(TraceSetCheck(white)); /* .seg.method.check */ + AVERT_CRITICAL(TraceSet, white); /* .seg.method.check */ gcseg = SegGCSeg(seg); AVERT_CRITICAL(GCSeg, gcseg); AVER_CRITICAL(&gcseg->segStruct == seg); arena = PoolArena(SegPool(seg)); AVERT_CRITICAL(Arena, arena); - limit = SegLimit(seg); - /* Each tract of the segment records white traces */ - TRACT_TRACT_FOR(tract, addr, arena, seg->firstTract, limit) { - Seg trseg = NULL; /* suppress compiler warning */ - - AVER_CRITICAL(TractCheck(tract)); /* */ - AVER_CRITICAL(TRACT_SEG(&trseg, tract) && (trseg == seg)); - TractSetWhite(tract, white); - } - AVER(addr == limit); - seg->white = white; + seg->white = BS_BITFIELD(Trace, white); } @@ -1341,7 +1328,7 @@ static void gcSegSetRankSet(Seg seg, RankSet rankSet) Arena arena; AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ - AVER_CRITICAL(RankSetCheck(rankSet)); /* .seg.method.check */ + AVERT_CRITICAL(RankSet, rankSet); /* .seg.method.check */ AVER_CRITICAL(rankSet == RankSetEMPTY || RankSetIsSingle(rankSet)); /* .seg.method.check */ gcseg = SegGCSeg(seg); @@ -1350,7 +1337,7 @@ static void gcSegSetRankSet(Seg seg, RankSet rankSet) arena = PoolArena(SegPool(seg)); oldRankSet = seg->rankSet; - seg->rankSet = rankSet; + seg->rankSet = BS_BITFIELD(Rank, rankSet); if (oldRankSet == RankSetEMPTY) { if (rankSet != RankSetEMPTY) { @@ -1412,7 +1399,7 @@ static void gcSegSetRankSummary(Seg seg, RankSet rankSet, RefSet summary) Arena arena; AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ - AVER_CRITICAL(RankSetCheck(rankSet)); /* .seg.method.check */ + AVERT_CRITICAL(RankSet, rankSet); /* .seg.method.check */ AVER_CRITICAL(rankSet == RankSetEMPTY || RankSetIsSingle(rankSet)); /* .seg.method.check */ gcseg = SegGCSeg(seg); @@ -1427,7 +1414,7 @@ static void gcSegSetRankSummary(Seg seg, RankSet rankSet, RefSet summary) wasShielded = (seg->rankSet != RankSetEMPTY && gcseg->summary != RefSetUNIV); willbeShielded = (rankSet != RankSetEMPTY && summary != RefSetUNIV); - seg->rankSet = rankSet; + seg->rankSet = BS_BITFIELD(Rank, rankSet); gcseg->summary = summary; if (willbeShielded && !wasShielded) { @@ -1499,7 +1486,7 @@ static Res gcSegMerge(Seg seg, Seg segHi, AVER(SegLimit(seg) == mid); AVER(SegBase(segHi) == mid); AVER(SegLimit(segHi) == limit); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); buf = gcsegHi->buffer; /* any buffer on segHi must be reassigned */ AVER(buf == NULL || gcseg->buffer == NULL); /* See .buffer */ @@ -1565,7 +1552,7 @@ static Res gcSegSplit(Seg seg, Seg segHi, AVER(mid < limit); AVER(SegBase(seg) == base); AVER(SegLimit(seg) == limit); - AVER(BoolCheck(withReservoirPermit)); + AVERT(Bool, withReservoirPermit); grey = SegGrey(seg); buf = gcseg->buffer; /* Look for buffer to reassign to segHi */ @@ -1611,34 +1598,39 @@ static Res gcSegSplit(Seg seg, Seg segHi, /* gcSegDescribe -- GCSeg description method */ -static Res gcSegDescribe(Seg seg, mps_lib_FILE *stream) +static Res gcSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) { Res res; SegClass super; GCSeg gcseg; - if (!TESTT(Seg, seg)) return ResFAIL; - if (stream == NULL) return ResFAIL; + if (!TESTT(Seg, seg)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; gcseg = SegGCSeg(seg); - if (!TESTT(GCSeg, gcseg)) return ResFAIL; + if (!TESTT(GCSeg, gcseg)) + return ResFAIL; /* Describe the superclass fields first via next-method call */ super = SEG_SUPERCLASS(GCSegClass); - res = super->describe(seg, stream); - if (res != ResOK) return res; + res = super->describe(seg, stream, depth); + if (res != ResOK) + return res; - res = WriteF(stream, - " summary $W\n", (WriteFW)gcseg->summary, + res = WriteF(stream, depth, + "summary $W\n", (WriteFW)gcseg->summary, NULL); - if (res != ResOK) return res; + if (res != ResOK) + return res; if (gcseg->buffer == NULL) { - res = WriteF(stream, " buffer: NULL\n", NULL); - } - else { - res = BufferDescribe(gcseg->buffer, stream); + res = WriteF(stream, depth, "buffer: NULL\n", NULL); + } else { + res = BufferDescribe(gcseg->buffer, stream, depth); } - if (res != ResOK) return res; + if (res != ResOK) + return res; return ResOK; } @@ -1648,7 +1640,7 @@ static Res gcSegDescribe(Seg seg, mps_lib_FILE *stream) Bool SegClassCheck(SegClass class) { - CHECKL(ProtocolClassCheck(&class->protocol)); + CHECKD(ProtocolClass, &class->protocol); CHECKL(class->name != NULL); /* Should be <= 6 char C identifier */ CHECKL(class->size >= sizeof(SegStruct)); CHECKL(FUNCHECK(class->init)); @@ -1685,6 +1677,7 @@ DEFINE_CLASS(SegClass, class) class->split = segTrivSplit; class->describe = segTrivDescribe; class->sig = SegClassSig; + AVERT(SegClass, class); } @@ -1709,6 +1702,7 @@ DEFINE_CLASS(GCSegClass, class) class->merge = gcSegMerge; class->split = gcSegSplit; class->describe = gcSegDescribe; + AVERT(SegClass, class); } @@ -1728,7 +1722,7 @@ void SegClassMixInNoSplitMerge(SegClass class) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/segsmss.c b/code/segsmss.c index 2e071e6dc0..a271a582f7 100644 --- a/code/segsmss.c +++ b/code/segsmss.c @@ -1,7 +1,7 @@ /* segsmss.c: Segment splitting and merging stress test * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * .design: Adapted from amsss.c (because AMS already supports @@ -21,20 +21,15 @@ #include "mpscams.h" #include "mpsavm.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif #include "mps.h" -#include -#include -#include -#include + +#include /* fflush, printf, puts, stdout */ /* Forward declarations */ -static SegClass AMSTSegClassGet(void); -static PoolClass AMSTPoolClassGet(void); +extern SegClass AMSTSegClassGet(void); +extern PoolClass AMSTPoolClassGet(void); /* Start by defining the AMST pool (AMS Test pool) */ @@ -45,7 +40,6 @@ static PoolClass AMSTPoolClassGet(void); typedef struct AMSTStruct { AMSStruct amsStruct; /* generic AMS structure */ - Chain chain; /* chain to use */ Bool failSegs; /* fail seg splits & merges when true */ Count splits; /* count of successful segment splits */ Count merges; /* count of successful segment merges */ @@ -58,16 +52,17 @@ typedef struct AMSTStruct { typedef struct AMSTStruct *AMST; -#define Pool2AMST(pool) PARENT(AMSTStruct, amsStruct, PARENT(AMSStruct, poolStruct, (pool))) +#define PoolAMST(pool) PARENT(AMSTStruct, amsStruct, PARENT(AMSStruct, poolStruct, (pool))) #define AMST2AMS(amst) (&(amst)->amsStruct) /* AMSTCheck -- the check method for an AMST */ +ATTRIBUTE_UNUSED static Bool AMSTCheck(AMST amst) { CHECKS(AMST, amst); - CHECKL(AMSCheck(AMST2AMS(amst))); + CHECKD_NOSIG(AMS, AMST2AMS(amst)); /* */ return TRUE; } @@ -101,10 +96,11 @@ typedef struct AMSTSegStruct { /* AMSTSegCheck -- check the AMST segment */ +ATTRIBUTE_UNUSED static Bool AMSTSegCheck(AMSTSeg amstseg) { CHECKS(AMSTSeg, amstseg); - CHECKL(AMSSegCheck(&amstseg->amsSegStruct)); + CHECKD_NOSIG(AMSSeg, &amstseg->amsSegStruct); /* */ /* don't bother to do other checks - this is a stress test */ return TRUE; } @@ -126,10 +122,10 @@ static Res amstSegInit(Seg seg, Pool pool, Addr base, Size size, AVERT(Seg, seg); amstseg = Seg2AMSTSeg(seg); AVERT(Pool, pool); - amst = Pool2AMST(pool); + amst = PoolAMST(pool); AVERT(AMST, amst); /* no useful checks for base and size */ - AVER(BoolCheck(reservoirPermit)); + AVERT(Bool, reservoirPermit); /* Initialize the superclass fields first via next-method call */ super = SEG_SUPERCLASS(AMSTSegClass); @@ -194,7 +190,7 @@ static Res amstSegMerge(Seg seg, Seg segHi, amstsegHi = Seg2AMSTSeg(segHi); AVERT(AMSTSeg, amstseg); AVERT(AMSTSeg, amstsegHi); - amst = Pool2AMST(SegPool(seg)); + amst = PoolAMST(SegPool(seg)); /* Merge the superclass fields via direct next-method call */ super = SEG_SUPERCLASS(AMSTSegClass); @@ -245,7 +241,7 @@ static Res amstSegSplit(Seg seg, Seg segHi, amstseg = Seg2AMSTSeg(seg); amstsegHi = Seg2AMSTSeg(segHi); AVERT(AMSTSeg, amstseg); - amst = Pool2AMST(SegPool(seg)); + amst = PoolAMST(SegPool(seg)); /* Split the superclass fields via direct next-method call */ super = SEG_SUPERCLASS(AMSTSegClass); @@ -294,6 +290,7 @@ DEFINE_SEG_CLASS(AMSTSegClass, class) class->finish = amstSegFinish; class->split = amstSegSplit; class->merge = amstSegMerge; + AVERT(SegClass, class); } @@ -310,11 +307,11 @@ static Res AMSTSegSizePolicy(Size *sizeReturn, AVER(sizeReturn != NULL); AVERT(Pool, pool); AVER(size > 0); - AVER(RankSetCheck(rankSet)); + AVERT(RankSet, rankSet); arena = PoolArena(pool); - basic = SizeAlignUp(size, ArenaAlign(arena)); + basic = SizeArenaGrains(size, arena); if (basic == 0) { /* overflow */ return ResMEMORY; @@ -337,25 +334,30 @@ static Res AMSTInit(Pool pool, ArgList args) Format format; Chain chain; Res res; - static GenParamStruct genParam = { 1024, 0.2 }; + unsigned gen = AMS_GEN_DEFAULT; ArgStruct arg; AVERT(Pool, pool); - + AVERT(ArgList, args); + + if (ArgPick(&arg, args, MPS_KEY_CHAIN)) + chain = arg.val.chain; + else { + chain = ArenaGlobals(PoolArena(pool))->defaultChain; + gen = 1; /* avoid the nursery of the default chain by default */ + } + if (ArgPick(&arg, args, MPS_KEY_GEN)) + gen = arg.val.u; ArgRequire(&arg, args, MPS_KEY_FORMAT); format = arg.val.format; - res = ChainCreate(&chain, pool->arena, 1, &genParam); + res = AMSInitInternal(PoolAMS(pool), format, chain, gen, FALSE); if (res != ResOK) return res; - res = AMSInitInternal(Pool2AMS(pool), format, chain, 0, FALSE); - if (res != ResOK) - return res; - amst = Pool2AMST(pool); - ams = Pool2AMS(pool); + amst = PoolAMST(pool); + ams = PoolAMS(pool); ams->segSize = AMSTSegSizePolicy; ams->segClass = AMSTSegClassGet; - amst->chain = chain; amst->failSegs = TRUE; amst->splits = 0; amst->merges = 0; @@ -376,7 +378,7 @@ static void AMSTFinish(Pool pool) AMST amst; AVERT(Pool, pool); - amst = Pool2AMST(pool); + amst = PoolAMST(pool); AVERT(AMST, amst); printf("\nDestroying pool, having performed:\n"); @@ -390,7 +392,6 @@ static void AMSTFinish(Pool pool) AMSFinish(pool); amst->sig = SigInvalid; - ChainDestroy(amst->chain); } @@ -401,7 +402,7 @@ static Bool AMSSegIsFree(Seg seg) AMSSeg amsseg; AVERT(Seg, seg); amsseg = Seg2AMSSeg(seg); - return(amsseg->free == amsseg->grains); + return amsseg->freeGrains == amsseg->grains; } @@ -417,7 +418,7 @@ static Bool AMSSegRegionIsFree(Seg seg, Addr base, Addr limit) AVERT(Seg, seg); amsseg = Seg2AMSSeg(seg); sbase = SegBase(seg); - ams = Pool2AMS(SegPool(seg)); + ams = PoolAMS(SegPool(seg)); bgrain = AMSGrains(ams, AddrOffset(sbase, base)); lgrain = AMSGrains(ams, AddrOffset(sbase, limit)); @@ -435,7 +436,7 @@ static Bool AMSSegRegionIsFree(Seg seg, Addr base, Addr limit) * Used as a means of overriding the behaviour of AMSBufferFill. * The code is similar to AMSBufferEmpty. */ -static void AMSUnallocateRange(Seg seg, Addr base, Addr limit) +static void AMSUnallocateRange(AMS ams, Seg seg, Addr base, Addr limit) { AMSSeg amsseg; Index baseIndex, limitIndex; @@ -463,8 +464,10 @@ static void AMSUnallocateRange(Seg seg, Addr base, Addr limit) BTResRange(amsseg->allocTable, baseIndex, limitIndex); } } - amsseg->free += limitIndex - baseIndex; - amsseg->newAlloc -= limitIndex - baseIndex; + amsseg->freeGrains += limitIndex - baseIndex; + AVER(amsseg->newGrains >= limitIndex - baseIndex); + amsseg->newGrains -= limitIndex - baseIndex; + PoolGenAccountForEmpty(&ams->pgen, AddrOffset(base, limit), FALSE); } @@ -473,7 +476,7 @@ static void AMSUnallocateRange(Seg seg, Addr base, Addr limit) * Used as a means of overriding the behaviour of AMSBufferFill. * The code is similar to AMSUnallocateRange. */ -static void AMSAllocateRange(Seg seg, Addr base, Addr limit) +static void AMSAllocateRange(AMS ams, Seg seg, Addr base, Addr limit) { AMSSeg amsseg; Index baseIndex, limitIndex; @@ -501,9 +504,10 @@ static void AMSAllocateRange(Seg seg, Addr base, Addr limit) BTSetRange(amsseg->allocTable, baseIndex, limitIndex); } } - AVER(amsseg->free >= limitIndex - baseIndex); - amsseg->free -= limitIndex - baseIndex; - amsseg->newAlloc += limitIndex - baseIndex; + AVER(amsseg->freeGrains >= limitIndex - baseIndex); + amsseg->freeGrains -= limitIndex - baseIndex; + amsseg->newGrains += limitIndex - baseIndex; + PoolGenAccountForFill(&ams->pgen, AddrOffset(base, limit), FALSE); } @@ -528,6 +532,7 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, PoolClass super; Addr base, limit; Arena arena; + AMS ams; AMST amst; Bool b; Seg seg; @@ -539,7 +544,8 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, AVER(limitReturn != NULL); /* other parameters are checked by next method */ arena = PoolArena(pool); - amst = Pool2AMST(pool); + ams = PoolAMS(pool); + amst = PoolAMST(pool); /* call next method */ super = POOL_SUPERCLASS(AMSTPoolClass); @@ -560,31 +566,31 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, Seg mergedSeg; Res mres; - AMSUnallocateRange(seg, base, limit); + AMSUnallocateRange(ams, seg, base, limit); mres = SegMerge(&mergedSeg, segLo, seg, withReservoirPermit); if (ResOK == mres) { /* successful merge */ - AMSAllocateRange(mergedSeg, base, limit); + AMSAllocateRange(ams, mergedSeg, base, limit); /* leave range as-is */ } else { /* failed to merge */ AVER(amst->failSegs); /* deliberate fails only */ - AMSAllocateRange(seg, base, limit); + AMSAllocateRange(ams, seg, base, limit); } } } else { Size half = SegSize(seg) / 2; - if (half >= size && SizeIsAligned(half, ArenaAlign(arena))) { + if (half >= size && SizeIsArenaGrains(half, arena)) { /* .split */ Addr mid = AddrAdd(base, half); Seg segLo, segHi; Res sres; - AMSUnallocateRange(seg, mid, limit); + AMSUnallocateRange(ams, seg, mid, limit); sres = SegSplit(&segLo, &segHi, seg, mid, withReservoirPermit); if (ResOK == sres) { /* successful split */ limit = mid; /* range is lower segment */ } else { /* failed to split */ AVER(amst->failSegs); /* deliberate fails only */ - AMSAllocateRange(seg, mid, limit); + AMSAllocateRange(ams, seg, mid, limit); } } @@ -606,9 +612,9 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, * not already attached to a buffer and similar colour) * * .bsplit: Whether or not a merge happpened, a split is performed if - * the limit of the buffered region is arena aligned, and yet does not - * correspond to the segment limit, provided that the part of the segment - * above the buffer is all free. + * the limit of the buffered region is also the limit of an arena + * grain, and yet does not correspond to the segment limit, provided + * that the part of the segment above the buffer is all free. */ static void AMSTStressBufferedSeg(Seg seg, Buffer buffer) { @@ -624,7 +630,7 @@ static void AMSTStressBufferedSeg(Seg seg, Buffer buffer) AVERT(AMSTSeg, amstseg); limit = BufferLimit(buffer); arena = PoolArena(SegPool(seg)); - amst = Pool2AMST(SegPool(seg)); + amst = PoolAMST(SegPool(seg)); AVERT(AMST, amst); if (amstseg->next != NULL) { @@ -645,7 +651,7 @@ static void AMSTStressBufferedSeg(Seg seg, Buffer buffer) } if (SegLimit(seg) != limit && - AddrIsAligned(limit, ArenaAlign(arena)) && + AddrIsArenaGrain(limit, arena) && AMSSegRegionIsFree(seg, limit, SegLimit(seg))) { /* .bsplit */ Seg segLo, segHi; @@ -674,6 +680,7 @@ DEFINE_POOL_CLASS(AMSTPoolClass, this) this->init = AMSTInit; this->finish = AMSTFinish; this->bufferFill = AMSTBufferFill; + AVERT(PoolClass, this); } @@ -695,9 +702,9 @@ static void mps_amst_ap_stress(mps_ap_t ap) /* mps_class_amst -- return the pool class descriptor to the client */ -static mps_class_t mps_class_amst(void) +static mps_pool_class_t mps_class_amst(void) { - return (mps_class_t)AMSTPoolClassGet(); + return (mps_pool_class_t)AMSTPoolClassGet(); } @@ -759,14 +766,19 @@ static void *test(void *arg, size_t s) mps_ap_t busy_ap; mps_addr_t busy_init; const char *indent = " "; + mps_chain_t chain; + static mps_gen_param_s genParam = {1024, 0.2}; arena = (mps_arena_t)arg; (void)s; /* unused */ die(mps_fmt_create_A(&format, arena, dylan_fmt_A()), "fmt_create"); + die(mps_chain_create(&chain, arena, 1, &genParam), "chain_create"); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + MPS_ARGS_ADD(args, MPS_KEY_GEN, 0); die(mps_pool_create_k(&pool, arena, mps_class_amst(), args), "pool_create(amst)"); } MPS_ARGS_END(args); @@ -789,7 +801,7 @@ static void *test(void *arg, size_t s) &ambigRoots[0], ambigRootsCOUNT), "root_create_table(ambig)"); - fputs(indent, stdout); + puts(indent); /* create an ap, and leave it busy */ die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy"); @@ -801,7 +813,7 @@ static void *test(void *arg, size_t s) printf("\nSize %"PRIuLONGEST" bytes, %"PRIuLONGEST" objects.\n", (ulongest_t)totalSize, (ulongest_t)objs); printf("%s", indent); - fflush(stdout); + (void)fflush(stdout); for(i = 0; i < exactRootsCOUNT; ++i) cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]), "all roots check"); @@ -832,16 +844,19 @@ static void *test(void *arg, size_t s) ++objs; if (objs % 256 == 0) { printf("."); - fflush(stdout); + (void)fflush(stdout); } } (void)mps_commit(busy_ap, busy_init, 64); + + mps_arena_park(arena); mps_ap_destroy(busy_ap); mps_ap_destroy(ap); mps_root_destroy(exactRoot); mps_root_destroy(ambigRoot); mps_pool_destroy(pool); + mps_chain_destroy(chain); mps_fmt_destroy(format); return NULL; @@ -854,8 +869,7 @@ int main(int argc, char *argv[]) mps_thr_t thread; void *r; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "arena_create"); @@ -871,7 +885,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/shield.c b/code/shield.c index 35fdcff240..0dfc1945db 100644 --- a/code/shield.c +++ b/code/shield.c @@ -1,7 +1,7 @@ /* shield.c: SHIELD IMPLEMENTATION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * * See: idea.shield, design.mps.shield. * @@ -83,7 +83,7 @@ void (ShieldSuspend)(Arena arena) AVER(arena->insideShield); if (!arena->suspended) { - ThreadRingSuspend(ArenaThreadRing(arena)); + ThreadRingSuspend(ArenaThreadRing(arena), ArenaDeadRing(arena)); arena->suspended = TRUE; } } @@ -105,6 +105,7 @@ static void protLower(Arena arena, Seg seg, AccessSet mode) AVERT_CRITICAL(Arena, arena); UNUSED(arena); AVERT_CRITICAL(Seg, seg); + AVERT_CRITICAL(AccessSet, mode); if (SegPM(seg) & mode) { SegSetPM(seg, SegPM(seg) & ~mode); @@ -133,7 +134,8 @@ static void flush(Arena arena, Size i) AVER(i < arena->shCacheLimit); seg = arena->shCache[i]; - if (seg == NULL) return; + if (seg == NULL) + return; AVERT(Seg, seg); AVER(arena->shDepth > 0); @@ -157,7 +159,8 @@ static void cache(Arena arena, Seg seg) AVERT_CRITICAL(Arena, arena); AVERT_CRITICAL(Seg, seg); - if (SegSM(seg) == SegPM(seg)) return; + if (SegSM(seg) + == SegPM(seg)) return; if (SegDepth(seg) > 0) { ShieldSuspend(arena); return; @@ -189,6 +192,7 @@ void (ShieldRaise) (Arena arena, Seg seg, AccessSet mode) /* can't check seg. Nor can we check arena as that checks the */ /* segs in the cache. */ + AVERT(AccessSet, mode); AVER((SegSM(seg) & mode) == AccessSetEMPTY); SegSetSM(seg, SegSM(seg) | mode); /* inv.prot.shield preserved */ @@ -202,6 +206,7 @@ void (ShieldRaise) (Arena arena, Seg seg, AccessSet mode) void (ShieldLower)(Arena arena, Seg seg, AccessSet mode) { /* Don't check seg or arena, see .seg.broken */ + AVERT(AccessSet, mode); AVER((SegSM(seg) & mode) == mode); /* synced(seg) is not changed by the following * preserving inv.unsynced.suspended @@ -261,7 +266,7 @@ void (ShieldLeave)(Arena arena) /* Ensuring the mutator is running at this point * guarantees inv.outside.running */ if (arena->suspended) { - ThreadRingResume(ArenaThreadRing(arena)); + ThreadRingResume(ArenaThreadRing(arena), ArenaDeadRing(arena)); arena->suspended = FALSE; } arena->insideShield = FALSE; @@ -334,7 +339,7 @@ void (ShieldCover)(Arena arena, Seg seg) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/sp.h b/code/sp.h new file mode 100644 index 0000000000..e2a8bef450 --- /dev/null +++ b/code/sp.h @@ -0,0 +1,69 @@ +/* sp.h: STACK PROBE INTERFACE + * + * $Id: //info.ravenbrook.com/project/mps/master/code/sp.h#1 $ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + */ + +#ifndef sp_h +#define sp_h + +#include "mpmtypes.h" + + +/* StackProbe -- probe above the stack to provoke early stack overflow + * + * This function should check that the stack has at least depth words + * available, and if not, then provoke a stack overflow exception or + * protection fault. The purpose is to ensure that the exception is + * generated before taking the arena lock where it can be handled + * safely, rather than at some later point where the arena lock is + * held and so handling the exception may cause the MPS to be entered + * recursively. + */ + +extern void StackProbe(Size depth); + + +#endif /* sp_h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/splay.c b/code/splay.c index 4042f2c0e0..ed46c7a4ba 100644 --- a/code/splay.c +++ b/code/splay.c @@ -1,16 +1,17 @@ /* splay.c: SPLAY TREE IMPLEMENTATION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. * * .purpose: Splay trees are used to manage potentially unbounded - * collections of ordered things. + * collections of ordered things. In the MPS these are usually + * address-ordered memory blocks. * - * .source: + * .source: * - * .note.stack: It's important that the MPS have a bounded stack - * size, and this is a problem for tree algorithms. Basically, - * we have to avoid recursion. + * .note.stack: It's important that the MPS have a bounded stack size, + * and this is a problem for tree algorithms. Basically, we have to + * avoid recursion. See design.mps.sp.sol.depth.no-recursion. */ @@ -20,646 +21,867 @@ SRCID(splay, "$Id$"); -/* Basic getter and setter methods */ +/* SPLAY_DEBUG -- switch for extra debugging + * + * Define SPLAY_DEBUG to enable extra consistency checking when modifying + * splay tree algorithms, which can be tricky to get right. This will + * check the tree size and ordering frequently. + */ -#define SplayTreeRoot(t) RVALUE((t)->root) -#define SplayTreeSetRoot(t, r) BEGIN ((t)->root = (r)); END -#define SplayNodeLeftChild(n) RVALUE((n)->left) -#define SplayNodeSetLeftChild(n, child) \ - BEGIN ((n)->left = (child)); END -#define SplayNodeRightChild(n) RVALUE((n)->right) -#define SplayNodeSetRightChild(n, child) \ - BEGIN ((n)->right = (child)); END +/* #define SPLAY_DEBUG */ +#define SplayTreeSetRoot(splay, tree) BEGIN ((splay)->root = (tree)); END +#define SplayCompare(tree, key, node) (((tree)->compare)(node, key)) +#define SplayHasUpdate(splay) ((splay)->updateNode != SplayTrivUpdate) -#define SplayCompare(tree, key, node) \ - (((tree)->compare)((key), (node))) +/* SplayTreeCheck -- check consistency of SplayTree + * + * See guide.impl.c.adt.check and . + */ -Bool SplayTreeCheck(SplayTree tree) +Bool SplayTreeCheck(SplayTree splay) { - UNUSED(tree); - CHECKL(tree != NULL); - CHECKL(FUNCHECK(tree->compare)); - CHECKL(tree->updateNode == NULL || FUNCHECK(tree->updateNode)); + UNUSED(splay); + CHECKS(SplayTree, splay); + CHECKL(FUNCHECK(splay->compare)); + CHECKL(FUNCHECK(splay->nodeKey)); + CHECKL(FUNCHECK(splay->updateNode)); + /* Can't use CHECKD_NOSIG because TreeEMPTY is NULL. */ + CHECKL(TreeCheck(splay->root)); return TRUE; } -Bool SplayNodeCheck(SplayNode node) -{ - UNUSED(node); - CHECKL(node != NULL); - return TRUE; -} - +/* SplayTreeInit -- initialise a splay tree + * + * ``compare`` must provide a total ordering on node keys. + * + * ``nodeKey`` extracts a key from a tree node for passing to ``compare``. + * + * ``updateNode`` will be applied to nodes from bottom to top when the + * tree is restructured in order to maintain client properties (see + * design.mps.splay.prop). If SplayTrivUpdate is be passed, faster + * algorithms are chosen for splaying. Compare SplaySplitDown with + * SplaySplitRev. + */ -void SplayTreeInit(SplayTree tree, SplayCompareMethod compare, - SplayUpdateNodeMethod updateNode) +void SplayTreeInit(SplayTree splay, + TreeCompareFunction compare, + TreeKeyFunction nodeKey, + SplayUpdateNodeFunction updateNode) { - AVER(tree != NULL); + AVER(splay != NULL); AVER(FUNCHECK(compare)); - AVER(updateNode == NULL || FUNCHECK(updateNode)); + AVER(FUNCHECK(nodeKey)); + AVER(FUNCHECK(updateNode)); - tree->compare = compare; - tree->updateNode = updateNode; - SplayTreeSetRoot(tree, NULL); + splay->compare = compare; + splay->nodeKey = nodeKey; + splay->updateNode = updateNode; + SplayTreeSetRoot(splay, TreeEMPTY); + splay->sig = SplayTreeSig; - AVERT(SplayTree, tree); + AVERT(SplayTree, splay); } -void SplayNodeInit(SplayNode node) +/* SplayTreeFinish -- finish a splay tree + * + * Does not attempt to descend or finish any tree nodes. + * + * TODO: Should probably fail on non-empty tree, so that client code is + * forced to decide what to do about that. + */ + +void SplayTreeFinish(SplayTree splay) { - AVER(node != NULL); + AVERT(SplayTree, splay); + splay->sig = SigInvalid; + SplayTreeSetRoot(splay, TreeEMPTY); + splay->compare = NULL; + splay->nodeKey = NULL; + splay->updateNode = NULL; +} + - /* We don't try to finish the attached nodes. See .note.stack. */ - SplayNodeSetLeftChild(node, NULL); - SplayNodeSetRightChild(node, NULL); +/* SplayTrivUpdate -- trivial update method + * + * This is passed to SplayTreeInit to indicate that no client property + * maintenance is required. It can also be called to do nothing. + */ - AVERT(SplayNode, node); +void SplayTrivUpdate(SplayTree splay, Tree tree) +{ + AVERT(SplayTree, splay); + AVERT(Tree, tree); } -void SplayNodeFinish(SplayNode node) +/* compareLess, compareGreater -- trivial comparisons + * + * These comparisons can be passed to SplaySplay to find the leftmost + * or rightmost nodes in a tree quickly. + * + * NOTE: It's also possible to make specialised versions of SplaySplit + * that traverse left and right unconditionally. These weren't found + * to have a significant performance advantage when benchmarking. + * RB 2014-02-23 + */ + +static Compare compareLess(Tree tree, TreeKey key) { - AVERT(SplayNode, node); + UNUSED(tree); + UNUSED(key); + return CompareLESS; +} - /* we don't try to do a recursive finish. See .note.stack. */ - SplayNodeSetLeftChild(node, NULL); - SplayNodeSetRightChild(node, NULL); +static Compare compareGreater(Tree tree, TreeKey key) +{ + UNUSED(tree); + UNUSED(key); + return CompareGREATER; } -void SplayTreeFinish(SplayTree tree) +/* SplayDebugUpdate -- force update of client property + * + * A debugging utility to recursively update the client property of + * a subtree. May not be used in production MPS because it has + * indefinite stack usage. See .note.stack. + */ + +void SplayDebugUpdate(SplayTree splay, Tree tree) { - AVERT(SplayTree, tree); - SplayTreeSetRoot(tree, NULL); - tree->compare = NULL; + AVERT(SplayTree, splay); + AVERT(Tree, tree); + if (tree == TreeEMPTY) + return; + SplayDebugUpdate(splay, TreeLeft(tree)); + SplayDebugUpdate(splay, TreeRight(tree)); + splay->updateNode(splay, tree); } -static void SplayNodeUpdate(SplayTree tree, SplayNode node) -{ - AVERT(SplayTree, tree); - AVERT(SplayNode, node); - AVER(tree->updateNode != NULL); +/* SplayDebugCount -- count and check order of tree + * + * This function may be called from a debugger or temporarily inserted + * during development to check a tree's integrity. It may not be called + * from the production MPS because it uses indefinite stack depth. + * See . + */ - (*tree->updateNode)(tree, node, SplayNodeLeftChild(node), - SplayNodeRightChild(node)); - return; +Count SplayDebugCount(SplayTree splay) +{ + AVERT(SplayTree, splay); + return TreeDebugCount(SplayTreeRoot(splay), splay->compare, splay->nodeKey); } -/* SplayLinkRight -- Move top to left child of top +/* SplayZig -- move to left child, prepending to right tree * - * Link the current top node into the left child of the right tree, - * leaving the top node as the left child of the old top node. + * Link the top node of the middle tree into the left child of the + * right tree, then step to the left child. Returns new middle. * * See . + * + * middle rightNext middle + * B E A E + * / \ / \ => / \ + * A C D F rightNext D F + * rightFirst / + * rightFirst B + * \ + * C */ -static void SplayLinkRight(SplayNode *topIO, SplayNode *rightIO) +static Tree SplayZig(Tree middle, Tree *rightFirstIO, Tree *rightNextReturn) { - AVERT(SplayNode, *topIO); - AVERT(SplayNode, *rightIO); + AVERT_CRITICAL(Tree, middle); + AVER_CRITICAL(rightFirstIO != NULL); + AVERT_CRITICAL(Tree, *rightFirstIO); + TreeSetLeft(*rightFirstIO, middle); + *rightNextReturn = *rightFirstIO; + *rightFirstIO = middle; + return TreeLeft(middle); +} + +/* SplayZigZig -- move to left child, rotating on on to right tree + * + * Rotate the top node of the middle tree over the left child of the + * right tree, then step to the left child, completing a splay "zig zig" + * after an initial SplayZig. Returns new middle. + * + * middle rightNext middle rightNext + * B E A E + * / \ / \ => / \ + * A C D F rightFirst B F + * rightFirst \ + * D + * / + * C + */ - /* Don't fix client properties yet. */ +static Tree SplayZigZig(Tree middle, Tree *rightFirstIO, Tree rightNext) +{ + AVERT_CRITICAL(Tree, middle); + AVER_CRITICAL(rightFirstIO != NULL); + AVERT_CRITICAL(Tree, *rightFirstIO); + TreeSetLeft(*rightFirstIO, TreeRight(middle)); + TreeSetRight(middle, *rightFirstIO); + TreeSetLeft(rightNext, middle); + *rightFirstIO = middle; + return TreeLeft(middle); +} + +/* SplayZag -- mirror image of SplayZig */ - /* .link.right.first: *rightIO is always the first node in the */ - /* right tree, so its left child must be null. */ - AVER(SplayNodeLeftChild(*rightIO) == NULL); +static Tree SplayZag(Tree middle, Tree *leftLastIO, Tree *leftPrevReturn) +{ + AVERT_CRITICAL(Tree, middle); + AVER_CRITICAL(leftLastIO != NULL); + AVERT_CRITICAL(Tree, *leftLastIO); + TreeSetRight(*leftLastIO, middle); + *leftPrevReturn = *leftLastIO; + *leftLastIO = middle; + return TreeRight(middle); +} - SplayNodeSetLeftChild(*rightIO, *topIO); - *rightIO = *topIO; - *topIO = SplayNodeLeftChild(*topIO); +/* SplayZagZag -- mirror image of SplayZigZig */ - /* The following line is only required for .link.right.first. */ - SplayNodeSetLeftChild(*rightIO, NULL); +static Tree SplayZagZag(Tree middle, Tree *leftLastIO, Tree leftPrev) +{ + AVERT_CRITICAL(Tree, middle); + AVER_CRITICAL(leftLastIO != NULL); + AVERT_CRITICAL(Tree, *leftLastIO); + TreeSetRight(*leftLastIO, TreeLeft(middle)); + TreeSetLeft(middle, *leftLastIO); + TreeSetRight(leftPrev, middle); + *leftLastIO = middle; + return TreeRight(middle); } -/* SplayLinkLeft -- Move top to right child of top +/* SplayState -- the state of splaying between "split" and "assemble" * - * Link the current top node into the right child of the left tree, - * leaving the top node as the right child of the old top node. + * Splaying is divided into two phases: splitting the tree into three, + * and then assembling a final tree. This allows for optimisation of + * certain operations, the key one being SplayTreeNeighbours, which is + * critical for coalescing memory blocks (see CBSInsert). * - * See . + * Note that SplaySplitDown and SplaySplitRev use the trees slightly + * differently. SplaySplitRev does not provide "left" and "right", and + * "leftLast" and "rightFirst" are pointer-reversed spines. */ -static void SplayLinkLeft(SplayNode *topIO, SplayNode *leftIO) { - AVERT(SplayNode, *topIO); - AVERT(SplayNode, *leftIO); +typedef struct SplayStateStruct { + Tree middle; /* always non-empty, has the found node at the root */ + Tree left; /* nodes less than search key during split */ + Tree leftLast; /* rightmost node on right spine of "left" */ + Tree right; /* nodes greater than search key during split */ + Tree rightFirst; /* leftmost node on left spine of "right" */ +} SplayStateStruct, *SplayState; + - /* Don't fix client properties yet. */ +/* SplaySplitDown -- divide the tree around a key + * + * Split a tree into three according to a key and a comparison, + * splaying nested left and right nodes. Preserves tree ordering. + * This is a top-down splay procedure, and does not use any recursion + * or require any parent pointers (see design.mps.impl.top-down). + * + * Returns cmp, the relationship of the root of the middle tree to the key, + * and a SplayState. + * + * Does *not* call update to maintain client properties. See SplaySplitRev. + */ - /* .link.left.first: *leftIO is always the last node in the */ - /* left tree, so its right child must be null. */ - AVER(SplayNodeRightChild(*leftIO) == NULL); +static Compare SplaySplitDown(SplayStateStruct *stateReturn, + SplayTree splay, TreeKey key, + TreeCompareFunction compare) +{ + TreeStruct sentinel; + Tree middle, leftLast, rightFirst, leftPrev, rightNext; + Compare cmp; - SplayNodeSetRightChild(*leftIO, *topIO); - *leftIO = *topIO; - *topIO = SplayNodeRightChild(*topIO); + AVERT(SplayTree, splay); + AVER(FUNCHECK(compare)); + AVER(!SplayTreeIsEmpty(splay)); + AVER(!SplayHasUpdate(splay)); + + TreeInit(&sentinel); + leftLast = &sentinel; + rightFirst = &sentinel; + middle = SplayTreeRoot(splay); + for (;;) { + cmp = compare(middle, key); + switch(cmp) { + default: + NOTREACHED; + /* defensive fall-through */ + case CompareEQUAL: + goto stop; + + case CompareLESS: + if (!TreeHasLeft(middle)) + goto stop; + middle = SplayZig(middle, &rightFirst, &rightNext); + cmp = compare(middle, key); + switch(cmp) { + default: + NOTREACHED; + /* defensive fall-through */ + case CompareEQUAL: + goto stop; + case CompareLESS: + if (!TreeHasLeft(middle)) + goto stop; + middle = SplayZigZig(middle, &rightFirst, rightNext); + break; + case CompareGREATER: + if (!TreeHasRight(middle)) + goto stop; + middle = SplayZag(middle, &leftLast, &leftPrev); + break; + } + break; + + case CompareGREATER: + if (!TreeHasRight(middle)) + goto stop; + middle = SplayZag(middle, &leftLast, &leftPrev); + cmp = compare(middle, key); + switch(cmp) { + default: + NOTREACHED; + /* defensive fall-through */ + case CompareEQUAL: + goto stop; + case CompareGREATER: + if (!TreeHasRight(middle)) + goto stop; + middle = SplayZagZag(middle, &leftLast, leftPrev); + break; + case CompareLESS: + if (!TreeHasLeft(middle)) + goto stop; + middle = SplayZig(middle, &rightFirst, &rightNext); + break; + } + break; + } + } - /* The following line is only required for .link.left.first. */ - SplayNodeSetRightChild(*leftIO, NULL); +stop: + stateReturn->middle = middle; + stateReturn->left = TreeRight(&sentinel); + stateReturn->leftLast = leftLast == &sentinel ? TreeEMPTY : leftLast; + stateReturn->right = TreeLeft(&sentinel); + stateReturn->rightFirst = rightFirst == &sentinel ? TreeEMPTY : rightFirst; + return cmp; } -/* SplayRotateLeft -- Rotate right child edge of node +/* SplayAssembleDown -- assemble left right and middle trees into one * - * Rotates node, right child of node, and left child of right - * child of node, leftwards in the order stated. + * Takes the result of a SplaySplit and forms a single tree with the + * root of the middle tree as the root. * - * See . + * left middle right middle + * B P V P + * / \ / \ / \ => / \ + * A C N Q U X B V + * leftLast rightFirst / \ / \ + * A C U X + * \ / + * N Q + * + * The children of the middle tree are grafted onto the last and first + * nodes of the side trees, which become the children of the root. + * + * Does *not* maintain client properties. See SplayAssembleRev. + * + * See . */ -static void SplayRotateLeft(SplayNode *nodeIO, SplayTree tree) { - SplayNode nodeRight; - - AVER(nodeIO != NULL); - AVERT(SplayNode, *nodeIO); - AVERT(SplayNode, SplayNodeRightChild(*nodeIO)); - AVERT(SplayTree, tree); - - nodeRight = SplayNodeRightChild(*nodeIO); - SplayNodeSetRightChild(*nodeIO, SplayNodeLeftChild(nodeRight)); - SplayNodeSetLeftChild(nodeRight, *nodeIO); - *nodeIO = nodeRight; - - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, SplayNodeLeftChild(nodeRight)); - /* Don't need to update new root because we know that we will */ - /* do either a link or an assemble next, and that will sort it */ - /* out. */ +static void SplayAssembleDown(SplayTree splay, SplayState state) +{ + AVERT(SplayTree, splay); + AVER(state->middle != TreeEMPTY); + AVER(!SplayHasUpdate(splay)); + + if (state->left != TreeEMPTY) { + AVER_CRITICAL(state->leftLast != TreeEMPTY); + TreeSetRight(state->leftLast, TreeLeft(state->middle)); + TreeSetLeft(state->middle, state->left); } - return; + if (state->right != TreeEMPTY) { + AVER_CRITICAL(state->rightFirst != TreeEMPTY); + TreeSetLeft(state->rightFirst, TreeRight(state->middle)); + TreeSetRight(state->middle, state->right); + } } -/* SplayRotateRight -- Rotate left child edge of node +/* SplayZigRev -- move to left child, prepending to reversed right tree * - * Rotates node, left child of node, and right child of left - * child of node, leftwards in the order stated. + * Same as SplayZig, except that the left spine of the right tree is + * pointer-reversed, so that its left children point at their parents + * instead of their children. This is fixed up in SplayAssembleRev. + */ + +static Tree SplayZigRev(Tree middle, Tree *rightFirstIO) +{ + Tree child; + AVERT_CRITICAL(Tree, middle); + AVER_CRITICAL(rightFirstIO != NULL); + AVERT_CRITICAL(Tree, *rightFirstIO); + child = TreeLeft(middle); + TreeSetLeft(middle, *rightFirstIO); + *rightFirstIO = middle; + return child; +} + +/* SplayZigZigRev -- move to left child, rotating onto reversed right tree * - * See . + * Same as SplayZigZig, except that the right tree is pointer reversed + * (see SplayZigRev) */ -static void SplayRotateRight(SplayNode *nodeIO, SplayTree tree) { - SplayNode nodeLeft; +static Tree SplayZigZigRev(Tree middle, Tree *rightFirstIO) +{ + Tree child; + AVERT_CRITICAL(Tree, middle); + AVER_CRITICAL(rightFirstIO != NULL); + AVERT_CRITICAL(Tree, *rightFirstIO); + child = TreeLeft(middle); + TreeSetLeft(middle, TreeLeft(*rightFirstIO)); + TreeSetLeft(*rightFirstIO, TreeRight(middle)); + TreeSetRight(middle, *rightFirstIO); + *rightFirstIO = middle; + return child; +} - AVER(nodeIO != NULL); - AVERT(SplayNode, *nodeIO); - AVERT(SplayNode, SplayNodeLeftChild(*nodeIO)); - AVERT(SplayTree, tree); +/* SplayZagRev -- mirror image of SplayZigRev */ - nodeLeft = SplayNodeLeftChild(*nodeIO); - SplayNodeSetLeftChild(*nodeIO, SplayNodeRightChild(nodeLeft)); - SplayNodeSetRightChild(nodeLeft, *nodeIO); - *nodeIO = nodeLeft; +static Tree SplayZagRev(Tree middle, Tree *leftLastIO) +{ + Tree child; + AVERT_CRITICAL(Tree, middle); + AVER_CRITICAL(leftLastIO != NULL); + AVERT_CRITICAL(Tree, *leftLastIO); + child = TreeRight(middle); + TreeSetRight(middle, *leftLastIO); + *leftLastIO = middle; + return child; +} - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, SplayNodeRightChild(nodeLeft)); - /* Don't need to update new root because we know that we will */ - /* do either a link or an assemble next, and that will sort it */ - /* out. */ - } +/* SplayZagZagRev -- mirror image of SplayZigZigRev */ - return; +static Tree SplayZagZagRev(Tree middle, Tree *leftLastIO) +{ + Tree child; + AVERT_CRITICAL(Tree, middle); + AVER_CRITICAL(leftLastIO != NULL); + AVERT_CRITICAL(Tree, *leftLastIO); + child = TreeRight(middle); + TreeSetRight(middle, TreeRight(*leftLastIO)); + TreeSetRight(*leftLastIO, TreeLeft(middle)); + TreeSetLeft(middle, *leftLastIO); + *leftLastIO = middle; + return child; } -/* SplayAssemble -- Assemble left right and top trees into one +/* SplaySplitRev -- divide the tree around a key * - * We do this by moving the children of the top tree to the last and - * first nodes in the left and right trees, and then moving the tops - * of the left and right trees to the children of the top tree. - * - * When we reach this function, the nodes between the roots of the - * left and right trees and their last and first nodes respectively - * will have out of date client properties. - * - * See . + * This is the same as SplaySplit, except that: + * - the left and right trees are pointer reversed on their spines + * - client properties for rotated nodes (not on the spines) are + * updated */ -static void SplayAssemble(SplayTree tree, SplayNode top, - SplayNode leftTop, SplayNode leftLast, - SplayNode rightTop, SplayNode rightFirst) { - AVERT(SplayTree, tree); - AVERT(SplayNode, top); - AVER(leftTop == NULL || - (SplayNodeCheck(leftTop) && SplayNodeCheck(leftLast))); - AVER(rightTop == NULL || - (SplayNodeCheck(rightTop) && SplayNodeCheck(rightFirst))); - - if (leftTop != NULL) { - SplayNodeSetRightChild(leftLast, SplayNodeLeftChild(top)); - SplayNodeSetLeftChild(top, leftTop); - - if (tree->updateNode != NULL) { - /* Update client property using pointer reversal (Ugh!). */ - SplayNode node, parent, rightChild; - - /* Reverse the pointers between leftTop and leftLast */ - /* leftLast is not reversed. */ - node = leftTop; - parent = NULL; - while(node != leftLast) { - rightChild = SplayNodeRightChild(node); - SplayNodeSetRightChild(node, parent); /* pointer reversal */ - parent = node; - node = rightChild; - } - - /* Now restore the pointers, updating the client property. */ - /* node is leftLast, parent is the last parent (or NULL). */ - SplayNodeUpdate(tree, node); - while(node != leftTop) { - rightChild = node; - node = parent; - parent = SplayNodeRightChild(node); - SplayNodeSetRightChild(node, rightChild); /* un-reverse pointer */ - SplayNodeUpdate(tree, node); +static Compare SplaySplitRev(SplayStateStruct *stateReturn, + SplayTree splay, TreeKey key, + TreeCompareFunction compare) +{ + Tree middle, leftLast, rightFirst; + Compare cmp; + + AVERT(SplayTree, splay); + AVER(FUNCHECK(compare)); + AVER(!SplayTreeIsEmpty(splay)); + + leftLast = TreeEMPTY; + rightFirst = TreeEMPTY; + middle = SplayTreeRoot(splay); + for (;;) { + cmp = compare(middle, key); + switch(cmp) { + default: + NOTREACHED; + /* defensive fall-through */ + case CompareEQUAL: + goto stop; + + case CompareLESS: + if (!TreeHasLeft(middle)) + goto stop; + middle = SplayZigRev(middle, &rightFirst); + cmp = compare(middle, key); + switch(cmp) { + default: + NOTREACHED; + /* defensive fall-through */ + case CompareEQUAL: + goto stop; + case CompareLESS: + if (!TreeHasLeft(middle)) + goto stop; + middle = SplayZigZigRev(middle, &rightFirst); + splay->updateNode(splay, TreeRight(rightFirst)); + break; + case CompareGREATER: + if (!TreeHasRight(middle)) + goto stop; + middle = SplayZagRev(middle, &leftLast); + break; } - } - } - /* otherwise leave top->left alone */ - - if (rightTop != NULL) { - SplayNodeSetLeftChild(rightFirst, SplayNodeRightChild(top)); - SplayNodeSetRightChild(top, rightTop); - - if (tree->updateNode != NULL) { - /* Update client property using pointer reversal (Ugh!). */ - SplayNode node, parent, leftChild; - - /* Reverse the pointers between rightTop and rightFirst */ - /* ightFirst is not reversed. */ - node = rightTop; - parent = NULL; - while(node != rightFirst) { - leftChild = SplayNodeLeftChild(node); - SplayNodeSetLeftChild(node, parent); /* pointer reversal */ - parent = node; - node = leftChild; - } - - /* Now restore the pointers, updating the client property. */ - /* node is rightFirst, parent is the last parent (or NULL). */ - SplayNodeUpdate(tree, node); - while(node != rightTop) { - leftChild = node; - node = parent; - parent = SplayNodeLeftChild(node); - SplayNodeSetLeftChild(node, leftChild); /* un-reverse pointer */ - SplayNodeUpdate(tree, node); + break; + + case CompareGREATER: + if (!TreeHasRight(middle)) + goto stop; + middle = SplayZagRev(middle, &leftLast); + cmp = compare(middle, key); + switch(cmp) { + default: + NOTREACHED; + /* defensive fall-through */ + case CompareEQUAL: + goto stop; + case CompareGREATER: + if (!TreeHasRight(middle)) + goto stop; + middle = SplayZagZagRev(middle, &leftLast); + splay->updateNode(splay, TreeLeft(leftLast)); + break; + case CompareLESS: + if (!TreeHasLeft(middle)) + goto stop; + middle = SplayZigRev(middle, &rightFirst); + break; } + break; } } - /* otherwise leave top->right alone */ - if (tree->updateNode != NULL) - SplayNodeUpdate(tree, top); +stop: + stateReturn->middle = middle; + stateReturn->leftLast = leftLast; + stateReturn->rightFirst = rightFirst; + return cmp; } -/* SplaySplay -- Splay the tree (top-down) around a given key - * - * If the key is not found, splays around an arbitrary neighbour. - * Returns whether key was found. This is the real logic behind - * splay trees. - * - * See . - */ - -static Bool SplaySplay(SplayNode *nodeReturn, SplayTree tree, - void *key, SplayCompareMethod compareMethod) { - /* The sides structure avoids a boundary case in SplayLink* */ - SplayNodeStruct sides; /* rightTop and leftTop */ - SplayNode top, leftLast, rightFirst; - Bool found; - Compare compareTop; +/* SplayUpdateLeftSpine -- undo pointer reversal, updating client property */ - AVERT(SplayTree, tree); - AVER(nodeReturn != NULL); - AVER(FUNCHECK(compareMethod)); +static Tree SplayUpdateLeftSpine(SplayTree splay, Tree node, Tree child) +{ + AVERT_CRITICAL(SplayTree, splay); + AVERT_CRITICAL(Tree, node); + AVERT_CRITICAL(Tree, child); + while(node != TreeEMPTY) { + Tree parent = TreeLeft(node); + TreeSetLeft(node, child); /* un-reverse pointer */ + splay->updateNode(splay, node); + child = node; + node = parent; + } + return child; +} - top = SplayTreeRoot(tree); /* will be copied back at end */ +/* SplayUpdateRightSpine -- mirror of SplayUpdateLeftSpine */ - if (top == NULL) { - *nodeReturn = NULL; - return FALSE; +static Tree SplayUpdateRightSpine(SplayTree splay, Tree node, Tree child) +{ + AVERT_CRITICAL(SplayTree, splay); + AVERT_CRITICAL(Tree, node); + AVERT_CRITICAL(Tree, child); + while (node != TreeEMPTY) { + Tree parent = TreeRight(node); + TreeSetRight(node, child); /* un-reverse pointer */ + splay->updateNode(splay, node); + child = node; + node = parent; } + return child; +} - /* short-circuit case where node is already top */ - compareTop = (*compareMethod)(key, top); - if (compareTop == CompareEQUAL) { - *nodeReturn = top; - return TRUE; - } - SplayNodeInit(&sides); /* left and right trees now NULL */ - leftLast = &sides; - rightFirst = &sides; - - while(TRUE) { - /* compareTop is already initialised above. */ - switch(compareTop) { - - case CompareLESS: { - SplayNode topLeft = SplayNodeLeftChild(top); - if (topLeft == NULL) { - found = FALSE; - goto assemble; - } else { - Compare compareTopLeft = (*compareMethod)(key, topLeft); - - switch(compareTopLeft) { - - case CompareEQUAL: { /* zig */ - SplayLinkRight(&top, &rightFirst); - found = TRUE; - goto assemble; - } /* break; */ - - case CompareLESS: { /* zig-zig */ - if (SplayNodeLeftChild(topLeft) == NULL) - goto terminalZig; - SplayRotateRight(&top, tree); - SplayLinkRight(&top, &rightFirst); - } break; - - case CompareGREATER: { /* zig-zag */ - if (SplayNodeRightChild(topLeft) == NULL) - goto terminalZig; - SplayLinkRight(&top, &rightFirst); - SplayLinkLeft(&top, &leftLast); - } break; - - default: { - NOTREACHED; - } break; - } - } - } break; - - case CompareGREATER: { - SplayNode topRight = SplayNodeRightChild(top); - if (topRight == NULL) { - found = FALSE; - goto assemble; - } else { - Compare compareTopRight = (*compareMethod)(key, topRight); - - switch(compareTopRight) { - - case CompareEQUAL: { /* zag */ - SplayLinkLeft(&top, &leftLast); - found = TRUE; - goto assemble; - } /* break; */ - - case CompareGREATER: { /* zag-zag */ - if (SplayNodeRightChild(topRight) == NULL) - goto terminalZag; - SplayRotateLeft(&top, tree); - SplayLinkLeft(&top, &leftLast); - } break; - - case CompareLESS: { /* zag-zig */ - if (SplayNodeLeftChild(topRight) == NULL) - goto terminalZag; - SplayLinkLeft(&top, &leftLast); - SplayLinkRight(&top, &rightFirst); - } break; - - default: { - NOTREACHED; - } break; - } - } - } break; +/* SplayAssembleRev -- pointer reversed SplayAssemble + * + * Does the same job as SplayAssemble, but operates on pointer-reversed + * left and right trees, updating client properties. When we reach + * this function, the nodes on the spines of the left and right trees + * will have out of date client properties because their children have + * been changed by SplaySplitRev. + */ - case CompareEQUAL: { - found = TRUE; - goto assemble; - } /* break; */ +static void SplayAssembleRev(SplayTree splay, SplayState state) +{ + Tree left, right; - default: { - NOTREACHED; - } break; - } - compareTop = (*compareMethod)(key, top); - } /* end while(TRUE) */ + AVERT(SplayTree, splay); + AVER(state->middle != TreeEMPTY); + + left = TreeLeft(state->middle); + left = SplayUpdateRightSpine(splay, state->leftLast, left); + TreeSetLeft(state->middle, left); -terminalZig: - SplayLinkRight(&top, &rightFirst); - found = FALSE; - goto assemble; + right = TreeRight(state->middle); + right = SplayUpdateLeftSpine(splay, state->rightFirst, right); + TreeSetRight(state->middle, right); -terminalZag: - SplayLinkLeft(&top, &leftLast); - found = FALSE; - goto assemble; + splay->updateNode(splay, state->middle); +} -assemble: - SplayAssemble(tree, top, - SplayNodeRightChild(&sides), leftLast, - SplayNodeLeftChild(&sides), rightFirst); - SplayTreeSetRoot(tree, top); - *nodeReturn = top; +/* SplaySplit -- call SplaySplitDown or SplaySplitRev as appropriate */ - return found; +static Compare SplaySplit(SplayStateStruct *stateReturn, + SplayTree splay, TreeKey key, + TreeCompareFunction compare) +{ + if (SplayHasUpdate(splay)) + return SplaySplitRev(stateReturn, splay, key, compare); + else + return SplaySplitDown(stateReturn, splay, key, compare); +} + + +/* SplayAssemble -- call SplayAssembleDown or SplayAssembleRev as appropriate */ + +static void SplayAssemble(SplayTree splay, SplayState state) +{ + if (SplayHasUpdate(splay)) + SplayAssembleRev(splay, state); + else + SplayAssembleDown(splay, state); } -/* SplayTreeInsert -- Insert a node into a splay tree +/* SplaySplay -- splay the tree around a given key * - * See and - * . + * Uses SplaySplitRev/SplayAssembleRev or SplaySplitDown/SplayAssembleDown + * as appropriate, but also catches the empty tree case and shortcuts + * the common case where the wanted node is already at the root (due + * to a previous splay). The latter shortcut has a significant effect + * on run time. + * + * If a matching node is found, it is splayed to the root and the function + * returns CompareEQUAL, or if the tree is empty, will also return + * CompareEQUAL. Otherwise, CompareGREATER or CompareLESS is returned + * meaning either the key is greater or less than the new root. In this + * case the new root is the last node visited which is either the closest + * node left or the closest node right of the key. + * + * See . */ -Res SplayTreeInsert(SplayTree tree, SplayNode node, void *key) { - SplayNode neighbour; +static Compare SplaySplay(SplayTree splay, TreeKey key, + TreeCompareFunction compare) +{ + Compare cmp; + SplayStateStruct stateStruct; + +#ifdef SPLAY_DEBUG + Count count = SplayDebugCount(splay); +#endif - AVERT(SplayTree, tree); - AVERT(SplayNode, node); - AVER(SplayNodeLeftChild(node) == NULL); - AVER(SplayNodeRightChild(node) == NULL); + /* Short-circuit common cases. Splay trees often bring recently + acccessed nodes to the root. */ + if (SplayTreeIsEmpty(splay) || + compare(SplayTreeRoot(splay), key) == CompareEQUAL) + return CompareEQUAL; - if (SplayTreeRoot(tree) == NULL) { - SplayTreeSetRoot(tree, node); - } else if (SplaySplay(&neighbour, tree, key, tree->compare)) { - return ResFAIL; + if (SplayHasUpdate(splay)) { + cmp = SplaySplitRev(&stateStruct, splay, key, compare); + SplayAssembleRev(splay, &stateStruct); } else { - AVER(SplayTreeRoot(tree) == neighbour); - switch(SplayCompare(tree, key, neighbour)) { - - case CompareGREATER: { /* left neighbour */ - SplayTreeSetRoot(tree, node); - SplayNodeSetRightChild(node, SplayNodeRightChild(neighbour)); - SplayNodeSetLeftChild(node, neighbour); - SplayNodeSetRightChild(neighbour, NULL); - } break; - - case CompareLESS: { /* right neighbour */ - SplayTreeSetRoot(tree, node); - SplayNodeSetLeftChild(node, SplayNodeLeftChild(neighbour)); - SplayNodeSetRightChild(node, neighbour); - SplayNodeSetLeftChild(neighbour, NULL); - } break; + cmp = SplaySplitDown(&stateStruct, splay, key, compare); + SplayAssembleDown(splay, &stateStruct); + } - case CompareEQUAL: - default: { - NOTREACHED; - } break; - } + SplayTreeSetRoot(splay, stateStruct.middle); - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, neighbour); - SplayNodeUpdate(tree, node); - } - } +#ifdef SPLAY_DEBUG + AVER(count == SplayDebugCount(splay)); +#endif - return ResOK; + return cmp; } -/* SplayTreeDelete -- Delete a node from a splay tree +/* SplayTreeInsert -- insert a node into a splay tree + * * - * See and - * . + * This function is used to insert a node into the tree. Splays the + * tree at the node's key. If an attempt is made to insert a node that + * compares ``CompareEQUAL`` to an existing node in the tree, then + * ``FALSE`` will be returned and the node will not be inserted. + * + * NOTE: It would be possible to use split here, then assemble around + * the new node, leaving the neighbour where it was, but it's probably + * a good thing for key neighbours to be tree neighbours. */ -Res SplayTreeDelete(SplayTree tree, SplayNode node, void *key) { - SplayNode rightHalf, del, leftLast; - Bool found; - - AVERT(SplayTree, tree); - AVERT(SplayNode, node); +Bool SplayTreeInsert(SplayTree splay, Tree node) { + Tree neighbour; - found = SplaySplay(&del, tree, key, tree->compare); - AVER(!found || del == node); + AVERT(SplayTree, splay); + AVERT(Tree, node); + AVER(TreeLeft(node) == TreeEMPTY); + AVER(TreeRight(node) == TreeEMPTY); - if (!found) { - return ResFAIL; - } else if (SplayNodeLeftChild(node) == NULL) { - SplayTreeSetRoot(tree, SplayNodeRightChild(node)); - } else if (SplayNodeRightChild(node) == NULL) { - SplayTreeSetRoot(tree, SplayNodeLeftChild(node)); - } else { - rightHalf = SplayNodeRightChild(node); - SplayTreeSetRoot(tree, SplayNodeLeftChild(node)); - if (SplaySplay(&leftLast, tree, key, tree->compare)) { - return ResFAIL; - } else { - AVER(SplayNodeRightChild(leftLast) == NULL); - SplayNodeSetRightChild(leftLast, rightHalf); - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, leftLast); - } - } + if (SplayTreeIsEmpty(splay)) { + SplayTreeSetRoot(splay, node); + return TRUE; + } + + switch (SplaySplay(splay, splay->nodeKey(node), splay->compare)) { + default: + NOTREACHED; + /* defensive fall-through */ + case CompareEQUAL: /* duplicate node */ + return FALSE; + + case CompareGREATER: /* left neighbour is at root */ + neighbour = SplayTreeRoot(splay); + SplayTreeSetRoot(splay, node); + TreeSetRight(node, TreeRight(neighbour)); + TreeSetLeft(node, neighbour); + TreeSetRight(neighbour, TreeEMPTY); + break; + + case CompareLESS: /* right neighbour is at root */ + neighbour = SplayTreeRoot(splay); + SplayTreeSetRoot(splay, node); + TreeSetLeft(node, TreeLeft(neighbour)); + TreeSetRight(node, neighbour); + TreeSetLeft(neighbour, TreeEMPTY); + break; } - SplayNodeFinish(node); - - return ResOK; + splay->updateNode(splay, neighbour); + splay->updateNode(splay, node); + return TRUE; } -/* SplayTreeSearch -- Search for a node in a splay tree matching a key +/* SplayTreeDelete -- delete a node from a splay tree + * + * Delete a node from the tree. If the tree does not contain the given + * node then ``FALSE`` will be returned. The client must not pass a + * node whose key compares equal to a different node in the tree. + * + * The function first splays the tree at the given key. * - * See and - * . + * TODO: If the node has zero or one children, then the replacement + * would be the leftLast or rightFirst after a SplaySplit, and would + * avoid a search for a replacement in more cases. */ -Res SplayTreeSearch(SplayNode *nodeReturn, SplayTree tree, void *key) { - SplayNode node; +Bool SplayTreeDelete(SplayTree splay, Tree node) { + Tree leftLast; + Compare cmp; - AVERT(SplayTree, tree); - AVER(nodeReturn != NULL); + AVERT(SplayTree, splay); + AVERT(Tree, node); + + if (SplayTreeIsEmpty(splay)) + return FALSE; + + cmp = SplaySplay(splay, splay->nodeKey(node), splay->compare); + AVER(cmp != CompareEQUAL || SplayTreeRoot(splay) == node); - if (SplaySplay(&node, tree, key, tree->compare)) { - *nodeReturn = node; + if (cmp != CompareEQUAL) { + return FALSE; + } else if (!TreeHasLeft(node)) { + SplayTreeSetRoot(splay, TreeRight(node)); + TreeClearRight(node); + } else if (!TreeHasRight(node)) { + SplayTreeSetRoot(splay, TreeLeft(node)); + TreeClearLeft(node); } else { - return ResFAIL; + Tree rightHalf = TreeRight(node); + TreeClearRight(node); + SplayTreeSetRoot(splay, TreeLeft(node)); + TreeClearLeft(node); + (void)SplaySplay(splay, NULL, compareGreater); + leftLast = SplayTreeRoot(splay); + AVER(leftLast != TreeEMPTY); + AVER(!TreeHasRight(leftLast)); + TreeSetRight(leftLast, rightHalf); + splay->updateNode(splay, leftLast); } - return ResOK; + TreeFinish(node); + + return TRUE; } -/* SplayTreePredecessor -- Splays a tree at the root's predecessor +/* SplayTreeFind -- search for a node in a splay tree matching a key * - * Must not be called on en empty tree. Predecessor need not exist, - * in which case NULL is returned, and the tree is unchanged. + * Search the tree for a node that compares ``CompareEQUAL`` to a key + * Splays the tree at the key. Returns ``FALSE`` if there is no such + * node in the tree, otherwise ``*nodeReturn`` will be set to the node. */ -static SplayNode SplayTreePredecessor(SplayTree tree, void *key) { - SplayNode oldRoot, newRoot; - - AVERT(SplayTree, tree); - - oldRoot = SplayTreeRoot(tree); - AVERT(SplayNode, oldRoot); +Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key) { + AVERT(SplayTree, splay); + AVER(nodeReturn != NULL); - if (SplayNodeLeftChild(oldRoot) == NULL) { - newRoot = NULL; /* No predecessor */ - } else { - /* temporarily chop off the right half-tree, inclusive of root */ - SplayTreeSetRoot(tree, SplayNodeLeftChild(oldRoot)); - SplayNodeSetLeftChild(oldRoot, NULL); - if (SplaySplay(&newRoot, tree, key, tree->compare)) { - NOTREACHED; /* Another matching node found */ - } else { - AVER(SplayNodeRightChild(newRoot) == NULL); - SplayNodeSetRightChild(newRoot, oldRoot); - } + if (SplayTreeIsEmpty(splay)) + return FALSE; - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, oldRoot); - SplayNodeUpdate(tree, newRoot); - } - } + if (SplaySplay(splay, key, splay->compare) != CompareEQUAL) + return FALSE; - return newRoot; + *nodeReturn = SplayTreeRoot(splay); + return TRUE; } -/* SplayTreeSuccessor -- Splays a tree at the root's successor +/* SplayTreeSuccessor -- splays a tree at the root's successor * * Must not be called on en empty tree. Successor need not exist, - * in which case NULL is returned, and the tree is unchanged. + * in which case TreeEMPTY is returned, and the tree is unchanged. */ -static SplayNode SplayTreeSuccessor(SplayTree tree, void *key) { - SplayNode oldRoot, newRoot; +static Tree SplayTreeSuccessor(SplayTree splay) { + Tree oldRoot, newRoot; - AVERT(SplayTree, tree); + AVERT(SplayTree, splay); + AVER(!SplayTreeIsEmpty(splay)); - oldRoot = SplayTreeRoot(tree); - AVERT(SplayNode, oldRoot); + oldRoot = SplayTreeRoot(splay); - if (SplayNodeRightChild(oldRoot) == NULL) { - newRoot = NULL; /* No successor */ - } else { - /* temporarily chop off the left half-tree, inclusive of root */ - SplayTreeSetRoot(tree, SplayNodeRightChild(oldRoot)); - SplayNodeSetRightChild(oldRoot, NULL); - if (SplaySplay(&newRoot, tree, key, tree->compare)) { - NOTREACHED; /* Another matching node found */ - } else { - AVER(SplayNodeLeftChild(newRoot) == NULL); - SplayNodeSetLeftChild(newRoot, oldRoot); - } + if (!TreeHasRight(oldRoot)) + return TreeEMPTY; /* No successor */ - if (tree->updateNode != NULL) { - SplayNodeUpdate(tree, oldRoot); - SplayNodeUpdate(tree, newRoot); - } - } + /* temporarily chop off the left half-tree, inclusive of root */ + SplayTreeSetRoot(splay, TreeRight(oldRoot)); + TreeSetRight(oldRoot, TreeEMPTY); + (void)SplaySplay(splay, NULL, compareLess); + newRoot = SplayTreeRoot(splay); + AVER(newRoot != TreeEMPTY); + AVER(TreeLeft(newRoot) == TreeEMPTY); + TreeSetLeft(newRoot, oldRoot); + splay->updateNode(splay, oldRoot); + splay->updateNode(splay, newRoot); return newRoot; } @@ -668,88 +890,129 @@ static SplayNode SplayTreeSuccessor(SplayTree tree, void *key) { /* SplayTreeNeighbours * * Search for the two nodes in a splay tree neighbouring a key. + * Splays the tree at the key. ``*leftReturn`` will be the neighbour + * which compares less than the key if such a neighbour exists; otherwise + * it will be ``TreeEMPTY``. ``*rightReturn`` will be the neighbour which + * compares greater than the key if such a neighbour exists; otherwise + * it will be ``TreeEMPTY``. The function returns ``FALSE`` if any node + * in the tree compares ``CompareEQUAL`` with the given key. * - * See and - * . + * TODO: Change to SplayTreeCoalesce that takes a function that can + * direct the deletion of one of the neighbours, since this is a + * good moment to do it, avoiding another search and splay. + * + * This implementation uses SplaySplit to find both neighbours in a + * single splay (see design.mps.splay.impl.neighbours). */ +Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, + SplayTree splay, TreeKey key) +{ + SplayStateStruct stateStruct; + Bool found; + Compare cmp; +#ifdef SPLAY_DEBUG + Count count = SplayDebugCount(splay); +#endif -Res SplayTreeNeighbours(SplayNode *leftReturn, SplayNode *rightReturn, - SplayTree tree, void *key) { - SplayNode neighbour; - AVERT(SplayTree, tree); + AVERT(SplayTree, splay); AVER(leftReturn != NULL); AVER(rightReturn != NULL); - if (SplaySplay(&neighbour, tree, key, tree->compare)) { - return ResFAIL; - } else if (neighbour == NULL) { - *leftReturn = *rightReturn = NULL; - } else { - switch(SplayCompare(tree, key, neighbour)) { - - case CompareLESS: { - *rightReturn = neighbour; - *leftReturn = SplayTreePredecessor(tree, key); - } break; + if (SplayTreeIsEmpty(splay)) { + *leftReturn = *rightReturn = TreeEMPTY; + return TRUE; + } - case CompareGREATER: { - *leftReturn = neighbour; - *rightReturn = SplayTreeSuccessor(tree, key); - } break; + cmp = SplaySplit(&stateStruct, splay, key, splay->compare); - case CompareEQUAL: - default: { - NOTREACHED; - } break; - } + switch (cmp) { + default: + NOTREACHED; + /* defensive fall-through */ + case CompareEQUAL: + found = FALSE; + break; + + case CompareLESS: + AVER(!TreeHasLeft(stateStruct.middle)); + *rightReturn = stateStruct.middle; + *leftReturn = stateStruct.leftLast; + found = TRUE; + break; + + case CompareGREATER: + AVER(!TreeHasRight(stateStruct.middle)); + *leftReturn = stateStruct.middle; + *rightReturn = stateStruct.rightFirst; + found = TRUE; + break; } - return ResOK; + + SplayAssemble(splay, &stateStruct); + SplayTreeSetRoot(splay, stateStruct.middle); + +#ifdef SPLAY_DEBUG + AVER(count == SplayDebugCount(splay)); +#endif + + return found; } -/* SplayTreeFirst, SplayTreeNext -- Iterators +/* SplayTreeFirst, SplayTreeNext -- iterators + * + * SplayTreeFirst returns TreeEMPTY if the tree is empty. Otherwise, + * it splays the tree to the first node, and returns the new root. * - * SplayTreeFirst receives a key that must precede all - * nodes in the tree. It returns NULL if the tree is empty. - * Otherwise, it splays the tree to the first node, and returns the - * new root. See . + * SplayTreeNext takes a tree and splays it to the successor of a key + * and returns the new root. Returns TreeEMPTY is there are no + * successors. * - * SplayTreeNext takes a tree and splays it to the successor of the - * old root, and returns the new root. Returns NULL is there are - * no successors. It takes a key for the old root. See - * . + * SplayTreeFirst and SplayTreeNext do not require the tree to remain + * 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 TreeTraverse instead. */ -SplayNode SplayTreeFirst(SplayTree tree, void *zeroKey) { - SplayNode node; - AVERT(SplayTree, tree); +Tree SplayTreeFirst(SplayTree splay) { + Tree node; - if (SplayTreeRoot(tree) == NULL) { - node = NULL; - } else if (SplaySplay(&node, tree, zeroKey, tree->compare)) { - NOTREACHED; - } else { - AVER(SplayNodeLeftChild(node) == NULL); - } + AVERT(SplayTree, splay); + + if (SplayTreeIsEmpty(splay)) + return TreeEMPTY; + + (void)SplaySplay(splay, NULL, compareLess); + node = SplayTreeRoot(splay); + AVER(node != TreeEMPTY); + AVER(TreeLeft(node) == TreeEMPTY); return node; } -SplayNode SplayTreeNext(SplayTree tree, SplayNode oldNode, void *oldKey) { - Bool b; - SplayNode node; +Tree SplayTreeNext(SplayTree splay, TreeKey oldKey) { + AVERT(SplayTree, splay); - AVERT(SplayTree, tree); - AVERT(SplayNode, oldNode); - - /* Make old node the root. Probably already is. */ - b = SplaySplay(&node, tree, oldKey, tree->compare); - AVER(b); - AVER(node == oldNode); + if (SplayTreeIsEmpty(splay)) + return TreeEMPTY; + + /* Make old node the root. Probably already is. We don't mind if the + node has been deleted, or replaced by a node with the same key. */ + switch (SplaySplay(splay, oldKey, splay->compare)) { + default: + NOTREACHED; + /* defensive fall-through */ + case CompareLESS: + return SplayTreeRoot(splay); - return SplayTreeSuccessor(tree, oldKey); + case CompareGREATER: + case CompareEQUAL: + return SplayTreeSuccessor(splay); + } } @@ -759,110 +1022,153 @@ SplayNode SplayTreeNext(SplayTree tree, SplayNode oldNode, void *oldKey) { * This is alright as the function is debug only. */ -static Res SplayNodeDescribe(SplayNode node, mps_lib_FILE *stream, - SplayNodeDescribeMethod nodeDescribe) { +static Res SplayNodeDescribe(Tree node, mps_lib_FILE *stream, + TreeDescribeFunction nodeDescribe) +{ Res res; -#if defined(AVER_AND_CHECK) - if (!SplayNodeCheck(node)) return ResFAIL; - /* stream and nodeDescribe checked by SplayTreeDescribe */ -#endif + if (!TreeCheck(node)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + if (nodeDescribe == NULL) + return ResFAIL; - res = WriteF(stream, "( ", NULL); - if (res != ResOK) return res; + res = WriteF(stream, 0, "( ", NULL); + if (res != ResOK) + return res; - if (SplayNodeLeftChild(node) != NULL) { - res = SplayNodeDescribe(SplayNodeLeftChild(node), stream, nodeDescribe); - if (res != ResOK) return res; + if (TreeHasLeft(node)) { + res = SplayNodeDescribe(TreeLeft(node), stream, nodeDescribe); + if (res != ResOK) + return res; - res = WriteF(stream, " / ", NULL); - if (res != ResOK) return res; + res = WriteF(stream, 0, " / ", NULL); + if (res != ResOK) + return res; } res = (*nodeDescribe)(node, stream); - if (res != ResOK) return res; + if (res != ResOK) + return res; - if (SplayNodeRightChild(node) != NULL) { - res = WriteF(stream, " \\ ", NULL); - if (res != ResOK) return res; + if (TreeHasRight(node)) { + res = WriteF(stream, 0, " \\ ", NULL); + if (res != ResOK) + return res; - res = SplayNodeDescribe(SplayNodeRightChild(node), stream, nodeDescribe); - if (res != ResOK) return res; + res = SplayNodeDescribe(TreeRight(node), stream, nodeDescribe); + if (res != ResOK) + return res; } - res = WriteF(stream, " )", NULL); - if (res != ResOK) return res; + res = WriteF(stream, 0, " )", NULL); + if (res != ResOK) + return res; return ResOK; } -typedef struct { - SplayTestNodeMethod testNode; - SplayTestTreeMethod testTree; +/* SplayFindFirstCompare, SplayFindLastCompare -- filtering searches + * + * These are used by SplayFindFirst and SplayFindLast as comparison + * functions to SplaySplit in order to home in on a node using client + * tests. The way to understand them is that the comparison values + * they return have nothing to do with the tree ordering, but are instead + * like commands that tell SplaySplit whether to "go left", "stop", or + * "go right" according to the results of testNode and testTree. + * Since splaying preserves the order of the tree, any tests can be + * applied to navigate to a destination. + * + * In the MPS these are mainly used by the CBS to search for memory + * blocks above a certain size. Their performance is quite critical. + */ + +typedef struct SplayFindClosureStruct { + SplayTestNodeFunction testNode; + SplayTestTreeFunction testTree; void *p; Size s; - SplayTree tree; + SplayTree splay; + Bool found; } SplayFindClosureStruct, *SplayFindClosure; -static Compare SplayFindFirstCompare(void *key, SplayNode node) +static Compare SplayFindFirstCompare(Tree node, TreeKey key) { SplayFindClosure closure; void *closureP; Size closureS; - SplayTestNodeMethod testNode; - SplayTestTreeMethod testTree; - SplayTree tree; + SplayTestNodeFunction testNode; + SplayTestTreeFunction testTree; + SplayTree splay; - AVERT(SplayNode, node); + AVERT(Tree, node); AVER(key != NULL); + /* Lift closure values into variables so that they aren't aliased by + calls to the test functions. */ closure = (SplayFindClosure)key; closureP = closure->p; closureS = closure->s; testNode = closure->testNode; testTree = closure->testTree; - tree = closure->tree; - - if (SplayNodeLeftChild(node) != NULL && - (*testTree)(tree, SplayNodeLeftChild(node), closureP, closureS)) { + splay = closure->splay; + + if (TreeHasLeft(node) && + (*testTree)(splay, TreeLeft(node), closureP, closureS)) { return CompareLESS; - } else if ((*testNode)(tree, node, closureP, closureS)) { + } else if ((*testNode)(splay, node, closureP, closureS)) { + closure->found = TRUE; return CompareEQUAL; } else { - AVER(SplayNodeRightChild(node) != NULL); - AVER((*testTree)(tree, SplayNodeRightChild(node), closureP, closureS)); + /* If there's a right subtree but it doesn't satisfy the tree test + then we want to terminate the splay right now. SplaySplay will + return TRUE, so the caller must check closure->found to find out + whether the result node actually satisfies testNode. */ + if (TreeHasRight(node) && + !(*testTree)(splay, TreeRight(node), closureP, closureS)) { + closure->found = FALSE; + return CompareEQUAL; + } return CompareGREATER; } } -static Compare SplayFindLastCompare(void *key, SplayNode node) +static Compare SplayFindLastCompare(Tree node, TreeKey key) { SplayFindClosure closure; void *closureP; Size closureS; - SplayTestNodeMethod testNode; - SplayTestTreeMethod testTree; - SplayTree tree; + SplayTestNodeFunction testNode; + SplayTestTreeFunction testTree; + SplayTree splay; - AVERT(SplayNode, node); + AVERT(Tree, node); AVER(key != NULL); + /* Lift closure values into variables so that they aren't aliased by + calls to the test functions. */ closure = (SplayFindClosure)key; closureP = closure->p; closureS = closure->s; testNode = closure->testNode; testTree = closure->testTree; - tree = closure->tree; + splay = closure->splay; - if (SplayNodeRightChild(node) != NULL && - (*testTree)(tree, SplayNodeRightChild(node), closureP, closureS)) { + if (TreeHasRight(node) && + (*testTree)(splay, TreeRight(node), closureP, closureS)) { return CompareGREATER; - } else if ((*testNode)(tree, node, closureP, closureS)) { + } else if ((*testNode)(splay, node, closureP, closureS)) { + closure->found = TRUE; return CompareEQUAL; } else { - AVER(SplayNodeLeftChild(node) != NULL); - AVER((*testTree)(tree, SplayNodeLeftChild(node), closureP, closureS)); + /* See SplayFindFirstCompare. */ + if (TreeHasLeft(node) && + !(*testTree)(splay, TreeLeft(node), closureP, closureS)) { + closure->found = FALSE; + return CompareEQUAL; + } return CompareLESS; } } @@ -874,125 +1180,182 @@ static Compare SplayFindLastCompare(void *key, SplayNode node) * tree that satisfies some property defined by the client. The * property is such that the client can detect, given a sub-tree, * whether that sub-tree contains any nodes satisfying the property. + * If there is no satisfactory node, ``FALSE`` is returned, otherwise + * ``*nodeReturn`` is set to the node. * * The given callbacks testNode and testTree detect this property in * a single node or a sub-tree rooted at a node, and both receive the * arbitrary closures closureP and closureS. + * + * TODO: This repeatedly splays failed matches to the root and rotates + * them, so it could have quite an unbalancing effect if size is small. + * Think about a better search, perhaps using TreeTraverse? */ -Bool SplayFindFirst(SplayNode *nodeReturn, SplayTree tree, - SplayTestNodeMethod testNode, - SplayTestTreeMethod testTree, - void *closureP, Size closureS) +Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, + SplayTestNodeFunction testNode, + SplayTestTreeFunction testTree, + void *closureP, Size closureS) { - SplayNode node; SplayFindClosureStruct closureStruct; + Bool found; AVER(nodeReturn != NULL); - AVERT(SplayTree, tree); + AVERT(SplayTree, splay); AVER(FUNCHECK(testNode)); AVER(FUNCHECK(testTree)); - node = SplayTreeRoot(tree); - - if (node == NULL || !(*testTree)(tree, node, closureP, closureS)) + if (SplayTreeIsEmpty(splay) || + !testTree(splay, SplayTreeRoot(splay), closureP, closureS)) return FALSE; /* no suitable nodes in tree */ closureStruct.p = closureP; closureStruct.s = closureS; closureStruct.testNode = testNode; closureStruct.testTree = testTree; - closureStruct.tree = tree; - - if (SplaySplay(&node, tree, (void *)&closureStruct, - &SplayFindFirstCompare)) { - *nodeReturn = node; - return TRUE; - } else { - return FALSE; + closureStruct.splay = splay; + closureStruct.found = FALSE; + + found = SplaySplay(splay, &closureStruct, + SplayFindFirstCompare) == CompareEQUAL && + closureStruct.found; + + while (!found) { + Tree oldRoot, newRoot; + + /* FIXME: Rename to "seen" and "not yet seen" or something. */ + oldRoot = SplayTreeRoot(splay); + newRoot = TreeRight(oldRoot); + + if (newRoot == TreeEMPTY || !(*testTree)(splay, newRoot, closureP, closureS)) + return FALSE; /* no suitable nodes in the rest of the tree */ + + /* Temporarily chop off the left half-tree, inclusive of root, + so that the search excludes any nodes we've seen already. */ + SplayTreeSetRoot(splay, newRoot); + TreeSetRight(oldRoot, TreeEMPTY); + + found = SplaySplay(splay, &closureStruct, + SplayFindFirstCompare) == CompareEQUAL && + closureStruct.found; + + /* Restore the left tree, then rotate left so that the node we + just splayed is at the root. Update both. */ + newRoot = SplayTreeRoot(splay); + TreeSetRight(oldRoot, newRoot); + SplayTreeSetRoot(splay, oldRoot); + TreeRotateLeft(&splay->root); + splay->updateNode(splay, oldRoot); + splay->updateNode(splay, newRoot); } + + *nodeReturn = SplayTreeRoot(splay); + return TRUE; } /* SplayFindLast -- As SplayFindFirst but in reverse address order */ -Bool SplayFindLast(SplayNode *nodeReturn, SplayTree tree, - SplayTestNodeMethod testNode, - SplayTestTreeMethod testTree, - void *closureP, Size closureS) +Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, + SplayTestNodeFunction testNode, + SplayTestTreeFunction testTree, + void *closureP, Size closureS) { - SplayNode node; SplayFindClosureStruct closureStruct; + Bool found; AVER(nodeReturn != NULL); - AVERT(SplayTree, tree); + AVERT(SplayTree, splay); AVER(FUNCHECK(testNode)); AVER(FUNCHECK(testTree)); - node = SplayTreeRoot(tree); - - if (node == NULL || !(*testTree)(tree, node, closureP, closureS)) + if (SplayTreeIsEmpty(splay) || + !testTree(splay, SplayTreeRoot(splay), closureP, closureS)) return FALSE; /* no suitable nodes in tree */ closureStruct.p = closureP; closureStruct.s = closureS; closureStruct.testNode = testNode; closureStruct.testTree = testTree; - closureStruct.tree = tree; - - if (SplaySplay(&node, tree, (void *)&closureStruct, - &SplayFindLastCompare)) { - *nodeReturn = node; - return TRUE; - } else { - return FALSE; + closureStruct.splay = splay; + + found = SplaySplay(splay, &closureStruct, + SplayFindLastCompare) == CompareEQUAL && + closureStruct.found; + + while (!found) { + Tree oldRoot, newRoot; + + oldRoot = SplayTreeRoot(splay); + newRoot = TreeLeft(oldRoot); + + if (newRoot == TreeEMPTY || !(*testTree)(splay, newRoot, closureP, closureS)) + return FALSE; /* no suitable nodes in the rest of the tree */ + + /* Temporarily chop off the right half-tree, inclusive of root, + so that the search excludes any nodes we've seen already. */ + SplayTreeSetRoot(splay, newRoot); + TreeSetLeft(oldRoot, TreeEMPTY); + + found = SplaySplay(splay, &closureStruct, + SplayFindLastCompare) == CompareEQUAL && + closureStruct.found; + + /* Restore the right tree, then rotate right so that the node we + just splayed is at the root. Update both. */ + newRoot = SplayTreeRoot(splay); + TreeSetLeft(oldRoot, newRoot); + SplayTreeSetRoot(splay, oldRoot); + TreeRotateRight(&splay->root); + splay->updateNode(splay, oldRoot); + splay->updateNode(splay, newRoot); } -} - - -/* SplayRoot -- return the root node of the tree */ - -Bool SplayRoot(SplayNode *nodeReturn, SplayTree tree) -{ - SplayNode node; - - AVER(nodeReturn != NULL); - AVERT(SplayTree, tree); - node = SplayTreeRoot(tree); - if (node == NULL) - return FALSE; - else { - *nodeReturn = node; - return TRUE; - } + *nodeReturn = SplayTreeRoot(splay); + return TRUE; } -/* SplayNodeRefresh -- Updates the client property that has changed at a node +/* SplayNodeRefresh -- updates the client property that has changed at a node * * This function undertakes to call the client updateNode callback for each * node affected by the change in properties at the given node (which has * the given key) in an appropriate order. * * The function fullfils its job by first splaying at the given node, and - * updating the single node. This may change. + * updating the single node. In the MPS it is used by the CBS during + * coalescing, when the node is likely to be at (or adjacent to) the top + * of the tree anyway. */ -void SplayNodeRefresh(SplayTree tree, SplayNode node, void *key) +void SplayNodeRefresh(SplayTree splay, Tree node) { - Bool b; - SplayNode node2; + Compare cmp; + + AVERT(SplayTree, splay); + AVERT(Tree, node); + AVER(!SplayTreeIsEmpty(splay)); /* must contain node, at least */ + AVER(SplayHasUpdate(splay)); /* otherwise, why call? */ + + cmp = SplaySplay(splay, splay->nodeKey(node), splay->compare); + AVER(cmp == CompareEQUAL); + AVER(SplayTreeRoot(splay) == node); + + splay->updateNode(splay, node); +} - AVERT(SplayTree, tree); - AVERT(SplayNode, node); - b = SplaySplay(&node2, tree, key, tree->compare); - AVER(b); - AVER(node == node2); +/* SplayNodeInit -- initialize client property without splaying */ - (*tree->updateNode)(tree, node, SplayNodeLeftChild(node), - SplayNodeRightChild(node)); +void SplayNodeInit(SplayTree splay, Tree node) +{ + AVERT(SplayTree, splay); + AVERT(Tree, node); + AVER(!TreeHasLeft(node)); /* otherwise, call SplayNodeRefresh */ + AVER(!TreeHasRight(node)); /* otherwise, call SplayNodeRefresh */ + AVER(SplayHasUpdate(splay)); /* otherwise, why call? */ + + splay->updateNode(splay, node); } @@ -1001,35 +1364,44 @@ void SplayNodeRefresh(SplayTree tree, SplayNode node, void *key) * See . */ -Res SplayTreeDescribe(SplayTree tree, mps_lib_FILE *stream, - SplayNodeDescribeMethod nodeDescribe) { +Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, Count depth, + TreeDescribeFunction nodeDescribe) +{ Res res; -#if defined(AVER_AND_CHECK) - if (!SplayTreeCheck(tree)) return ResFAIL; - if (stream == NULL) return ResFAIL; - if (!FUNCHECK(nodeDescribe)) return ResFAIL; -#endif + if (!TESTT(SplayTree, splay)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + if (nodeDescribe == NULL) + return ResFAIL; - res = WriteF(stream, - "Splay $P {\n", (WriteFP)tree, - " compare $F\n", (WriteFF)tree->compare, + res = WriteF(stream, depth, + "Splay $P {\n", (WriteFP)splay, + " compare $F\n", (WriteFF)splay->compare, + " nodeKey $F\n", (WriteFF)splay->nodeKey, + " updateNode $F\n", (WriteFF)splay->updateNode, NULL); - if (res != ResOK) return res; - - if (SplayTreeRoot(tree) != NULL) { - res = SplayNodeDescribe(SplayTreeRoot(tree), stream, nodeDescribe); - if (res != ResOK) return res; + if (res != ResOK) + return res; + + if (SplayTreeRoot(splay) != TreeEMPTY) { + res = WriteF(stream, depth, " tree ", NULL); + if (res != ResOK) + return res; + res = SplayNodeDescribe(SplayTreeRoot(splay), stream, nodeDescribe); + if (res != ResOK) + return res; } - res = WriteF(stream, "\n}\n", NULL); + res = WriteF(stream, depth, "\n} Splay $P\n", (WriteFP)splay, NULL); return res; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2015 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/splay.h b/code/splay.h index 8fa31af7fd..6a1526876b 100644 --- a/code/splay.h +++ b/code/splay.h @@ -1,7 +1,7 @@ /* splay.h: SPLAY TREE HEADER * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .source: */ @@ -10,75 +10,72 @@ #define splay_h #include "mpmtypes.h" /* for Res, etc. */ +#include "tree.h" typedef struct SplayTreeStruct *SplayTree; -typedef struct SplayNodeStruct *SplayNode; -typedef unsigned Compare; -typedef Compare (*SplayCompareMethod)(void *key, SplayNode node); -typedef Bool (*SplayTestNodeMethod)(SplayTree tree, SplayNode node, - void *closureP, Size closureS); -typedef Bool (*SplayTestTreeMethod)(SplayTree tree, SplayNode node, - void *closureP, Size closureS); -typedef void (*SplayUpdateNodeMethod)(SplayTree tree, SplayNode node, - SplayNode leftChild, - SplayNode rightChild); -typedef Res (*SplayNodeDescribeMethod)(SplayNode node, mps_lib_FILE *stream); -enum { - CompareLESS = 1, - CompareEQUAL, - CompareGREATER -}; +typedef Bool (*SplayTestNodeFunction)(SplayTree splay, Tree node, + void *closureP, Size closureS); +typedef Bool (*SplayTestTreeFunction)(SplayTree splay, Tree node, + void *closureP, Size closureS); + +typedef void (*SplayUpdateNodeFunction)(SplayTree splay, Tree node); +extern void SplayTrivUpdate(SplayTree splay, Tree node); + +#define SplayTreeSig ((Sig)0x5195B1A1) /* SIGnature SPLAY */ typedef struct SplayTreeStruct { - SplayCompareMethod compare; - SplayUpdateNodeMethod updateNode; - SplayNode root; + Sig sig; + TreeCompareFunction compare; + TreeKeyFunction nodeKey; + SplayUpdateNodeFunction updateNode; + Tree root; } SplayTreeStruct; -typedef struct SplayNodeStruct { - SplayNode left; - SplayNode right; -} SplayNodeStruct; +#define SplayTreeRoot(splay) ((splay)->root) +#define SplayTreeIsEmpty(splay) (SplayTreeRoot(splay) == TreeEMPTY) +extern Bool SplayTreeCheck(SplayTree splay); +extern void SplayTreeInit(SplayTree splay, + TreeCompareFunction compare, + TreeKeyFunction nodeKey, + SplayUpdateNodeFunction updateNode); +extern void SplayTreeFinish(SplayTree splay); -extern Bool SplayTreeCheck(SplayTree tree); -extern Bool SplayNodeCheck(SplayNode node); -extern void SplayTreeInit(SplayTree tree, SplayCompareMethod compare, - SplayUpdateNodeMethod updateNode); -extern void SplayNodeInit(SplayNode node); -extern void SplayNodeFinish(SplayNode node); -extern void SplayTreeFinish(SplayTree tree); +extern Bool SplayTreeInsert(SplayTree splay, Tree node); +extern Bool SplayTreeDelete(SplayTree splay, Tree node); -extern Res SplayTreeInsert(SplayTree tree, SplayNode node, void *key); -extern Res SplayTreeDelete(SplayTree tree, SplayNode node, void *key); +extern Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key); -extern Res SplayTreeSearch(SplayNode *nodeReturn, - SplayTree tree, void *key ); -extern Res SplayTreeNeighbours(SplayNode *leftReturn, - SplayNode *rightReturn, - SplayTree tree, void *key); +extern Bool SplayTreeNeighbours(Tree *leftReturn, + Tree *rightReturn, + SplayTree splay, TreeKey key); -extern SplayNode SplayTreeFirst(SplayTree tree, void *zeroKey); -extern SplayNode SplayTreeNext(SplayTree tree, SplayNode oldNode, - void *oldKey); +extern Tree SplayTreeFirst(SplayTree splay); +extern Tree SplayTreeNext(SplayTree splay, TreeKey oldKey); -extern Bool SplayFindFirst(SplayNode *nodeReturn, SplayTree tree, - SplayTestNodeMethod testNode, - SplayTestTreeMethod testTree, +typedef Bool (*SplayFindFunction)(Tree *nodeReturn, SplayTree splay, + SplayTestNodeFunction testNode, + SplayTestTreeFunction testTree, + void *closureP, Size closureS); +extern Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, + SplayTestNodeFunction testNode, + SplayTestTreeFunction testTree, void *closureP, Size closureS); -extern Bool SplayFindLast(SplayNode *nodeReturn, SplayTree tree, - SplayTestNodeMethod testNode, - SplayTestTreeMethod testTree, +extern Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, + SplayTestNodeFunction testNode, + SplayTestTreeFunction testTree, void *closureP, Size closureS); -extern void SplayNodeRefresh(SplayTree tree, SplayNode node, void *key); +extern void SplayNodeRefresh(SplayTree splay, Tree node); +extern void SplayNodeInit(SplayTree splay, Tree node); -extern Res SplayTreeDescribe(SplayTree tree, mps_lib_FILE *stream, - SplayNodeDescribeMethod nodeDescribe); +extern Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, + Count depth, TreeDescribeFunction nodeDescribe); -extern Bool SplayRoot(SplayNode *nodeReturn, SplayTree tree); +extern void SplayDebugUpdate(SplayTree splay, Tree tree); +extern Count SplayDebugCount(SplayTree splay); #endif /* splay_h */ @@ -86,7 +83,7 @@ extern Bool SplayRoot(SplayNode *nodeReturn, SplayTree tree); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/spw3i3mv.c b/code/spw3i3.c similarity index 94% rename from code/spw3i3mv.c rename to code/spw3i3.c index 3bb8ce94d0..1439d069c4 100644 --- a/code/spw3i3mv.c +++ b/code/spw3i3.c @@ -1,4 +1,4 @@ -/* spw3i3mv.c: STACK PROBE FOR 32-BIT WINDOWS +/* spw3i3.c: STACK PROBE FOR 32-BIT WINDOWS * * $Id$ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. @@ -13,6 +13,13 @@ #include "mpm.h" +#ifdef MPS_BUILD_PC + +/* "[ISO] Inline assembly code is not portable." */ +#pragma warn(disable: 2007) + +#endif /* MPS_BUILD_PC */ + void StackProbe(Size depth) { diff --git a/code/spw3i6mv.c b/code/spw3i6.c similarity index 82% rename from code/spw3i6mv.c rename to code/spw3i6.c index 751e9680e4..90997cd158 100644 --- a/code/spw3i6mv.c +++ b/code/spw3i6.c @@ -1,33 +1,28 @@ -/* spw3i6mv.c: STACK PROBE FOR 64-BIT WINDOWS +/* spw3i6.c: STACK PROBE FOR 64-BIT WINDOWS * * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. * * The function StackProbe ensures that the stack has at least depth * words available. It achieves this by exploiting an obscure but * documented feature of Microsoft's function _alloca: "A stack * overflow exception is generated if the space cannot be allocated." * _alloca: http://msdn.microsoft.com/en-us/library/wb1s57t5.aspx - * - * The purpose of this function to ensure that the stack overflow - * exception is generated here (before taking the arena lock) where it - * can be handled safely rather than at some later point where the - * arena lock is held and so handling the exception may cause the MPS - * to be entered recursively. */ +#include /* _alloca */ + #include "mpm.h" -#include void StackProbe(Size depth) { - _alloca(depth*sizeof(Word)); + (void)_alloca(depth*sizeof(Word)); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2013 Ravenbrook Limited . + * Copyright (C) 2013-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/ss.c b/code/ss.c index 084682b2a2..8c5bc44b02 100644 --- a/code/ss.c +++ b/code/ss.c @@ -1,7 +1,7 @@ /* ss.c: STACK SCANNING * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * This is part of the code that scans the stack and fixes the registers * that may contain roots. See @@ -35,7 +35,8 @@ Res StackScanInner(ScanState ss, AVERT(ScanState, ss); AVER(stackTop < stackBot); AVER(AddrIsAligned((Addr)stackTop, sizeof(Addr))); /* .assume.align */ - AVER(0 < nSavedRegs && nSavedRegs < 128); /* sanity check */ + AVER(0 < nSavedRegs); + AVER(nSavedRegs < 128); /* sanity check */ arena = ss->arena; @@ -66,7 +67,7 @@ Res StackScanInner(ScanState ss, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/ssan.c b/code/ssan.c index 6f632dd1d2..27233e7b9f 100644 --- a/code/ssan.c +++ b/code/ssan.c @@ -3,10 +3,16 @@ * $Id$ * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. * - * This module provides zero functionality. It exists to feed the - * linker (prevent linker errors). + * This module makes a best effort to scan the stack and fix the + * registers which may contain roots, using only the features of the + * Standard C library. + * + * .assume.setjmp: The implementation assumes that setjmp stores all + * the registers that need to be scanned in the jmp_buf. */ +#include + #include "mpmtypes.h" #include "misc.h" #include "ss.h" @@ -17,8 +23,19 @@ SRCID(ssan, "$Id$"); Res StackScan(ScanState ss, Addr *stackBot) { - UNUSED(ss); UNUSED(stackBot); - return ResUNIMPL; + jmp_buf jb; + void *stackTop = &jb; + + /* .assume.stack: This implementation assumes that the stack grows + * downwards, so that the address of the jmp_buf is the limit of the + * part of the stack that needs to be scanned. (StackScanInner makes + * the same assumption.) + */ + AVER(stackTop < (void *)stackBot); + + (void)setjmp(jb); + + return StackScanInner(ss, stackBot, stackTop, sizeof jb / sizeof(Addr*)); } diff --git a/code/ssixi6.c b/code/ssixi6.c index 70954edb5d..e61af2ee96 100644 --- a/code/ssixi6.c +++ b/code/ssixi6.c @@ -1,13 +1,13 @@ /* ssixi6.c: UNIX/x64 STACK SCANNING * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * This scans the stack and fixes the registers which may contain * roots. See * * This code was branched from ssixi3.c (32-bit Intel) initially for the - * port to W3I6LL (Mac OS X on x86_64 with Clang). + * port to XCI6LL (Mac OS X on x86_64 with Clang). * * This code is common to more than one Unix implementation on * Intel hardware (but is not portable Unix code). According to Wikipedia, @@ -68,7 +68,7 @@ Res StackScan(ScanState ss, Addr *stackBot) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/ssw3i3mv.c b/code/ssw3i3mv.c index ac0b09fd67..d879780734 100644 --- a/code/ssw3i3mv.c +++ b/code/ssw3i3mv.c @@ -37,6 +37,8 @@ Res StackScan(ScanState ss, Addr *stackBot) AVER(sizeof(((_JUMP_BUFFER *)jb)->Esi) == sizeof(Addr)); AVER(sizeof(((_JUMP_BUFFER *)jb)->Ebx) == sizeof(Addr)); + /* Ensure that the callee-save registers will be found by + StackScanInner when it's passed the address of the Ebx field. */ AVER(offsetof(_JUMP_BUFFER, Edi) == offsetof(_JUMP_BUFFER, Ebx) + 4); AVER(offsetof(_JUMP_BUFFER, Esi) == offsetof(_JUMP_BUFFER, Ebx) + 8); diff --git a/code/ssw3i3pc.c b/code/ssw3i3pc.c new file mode 100644 index 0000000000..e03754c7eb --- /dev/null +++ b/code/ssw3i3pc.c @@ -0,0 +1,104 @@ +/* ssw3i3pc.c: STACK SCANNING FOR WIN32 WITH PELLES C + * + * $Id$ + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * + * This scans the stack and fixes the registers which may contain roots. + * See . + * + * .assume.ms-compat: We rely on the fact that Pelles C's setjmp stores + * the callee-save registers in the jmp_buf and is compatible with Microsoft + * C. The Pelles C 7.00 setjmp.h header has a comment "MS compatible". See + * also "Is Pelles C's jmp_buf compatible with Microsoft C's?" + * + * + * REFERENCES + * + * "Argument Passing and Naming Conventions"; MSDN; Microsoft Corporation; + * . + * + * "Calling conventions for different C++ compilers and operating systems"; + * Agner Fog; Copenhagen University College of Engineering; 2012-02-29; + * . + */ + +#include "mpm.h" +#include + +SRCID(ssw3i3pc, "$Id$"); + + +/* This definition isn't in the Pelles C headers, so we reproduce it here. + * See .assume.ms-compat. */ + +typedef struct __JUMP_BUFFER { + unsigned long Ebp; + unsigned long Ebx; + unsigned long Edi; + unsigned long Esi; + unsigned long Esp; + unsigned long Eip; + unsigned long Registration; + unsigned long TryLevel; + unsigned long Cookie; + unsigned long UnwindFunc; + unsigned long UnwindData[6]; +} _JUMP_BUFFER; + + +Res StackScan(ScanState ss, Addr *stackBot) +{ + jmp_buf jb; + + /* .assume.ms-compat */ + (void)setjmp(jb); + + /* Ensure that the callee-save registers will be found by + StackScanInner when it's passed the address of the Ebx field. */ + AVER(offsetof(_JUMP_BUFFER, Edi) == offsetof(_JUMP_BUFFER, Ebx) + 4); + AVER(offsetof(_JUMP_BUFFER, Esi) == offsetof(_JUMP_BUFFER, Ebx) + 8); + + return StackScanInner(ss, stackBot, (Addr *)&((_JUMP_BUFFER *)jb)->Ebx, 3); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2001-2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/ssw3i6mv.c b/code/ssw3i6mv.c index 807577a89a..16c33e3fb7 100644 --- a/code/ssw3i6mv.c +++ b/code/ssw3i6mv.c @@ -1,7 +1,7 @@ -/* ssw3i6mv.c: STACK SCANNING FOR WIN32 WITH MICROSOFT C +/* ssw3i6mv.c: STACK SCANNING FOR WIN64 WITH MICROSOFT C * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * This scans the stack and fixes the registers which may contain roots. * See . It's unlikely that the callee-save @@ -64,7 +64,7 @@ Res StackScan(ScanState ss, Addr *stackBot) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/ssw3i6pc.c b/code/ssw3i6pc.c new file mode 100644 index 0000000000..89fbbeac42 --- /dev/null +++ b/code/ssw3i6pc.c @@ -0,0 +1,143 @@ +/* ssw3i6pc.c: STACK SCANNING FOR WIN64 WITH PELLES C + * + * $Id$ + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * + * This scans the stack and fixes the registers which may contain roots. + * See . + * + * .assume.ms-compat: We rely on the fact that Pelles C's setjmp stores + * the callee-save registers in the jmp_buf and is compatible with Microsoft + * C. The Pelles C 7.00 setjmp.h header has a comment "MS compatible". See + * also "Is Pelles C's jmp_buf compatible with Microsoft C's?" + * + * + * REFERENCES + * + * "Overview of x64 Calling Conventions"; MSDN; Microsoft Corporation; + * . + * + * "Caller/Callee Saved Registers"; MSDN; Microsoft Corporation; + * . + * + * "Register Usage"; MSDN; Microsoft Corporation; + * . + * + * "Calling conventions for different C++ compilers and operating systems"; + * Agner Fog; Copenhagen University College of Engineering; 2012-02-29; + * . + */ + +#include "mpm.h" +#include + +SRCID(ssw3i6pc, "$Id$"); + + +/* This definition isn't in the Pelles C headers, so we reproduce it here. + * See .assume.ms-compat. */ + +typedef /* _CRT_ALIGN(16) */ struct _SETJMP_FLOAT128 { + unsigned __int64 Part[2]; +} SETJMP_FLOAT128; + +typedef struct _JUMP_BUFFER { + unsigned __int64 Frame; + unsigned __int64 Rbx; + unsigned __int64 Rsp; + unsigned __int64 Rbp; + unsigned __int64 Rsi; + unsigned __int64 Rdi; + unsigned __int64 R12; + unsigned __int64 R13; + unsigned __int64 R14; + unsigned __int64 R15; + unsigned __int64 Rip; + unsigned __int64 Spare; + + SETJMP_FLOAT128 Xmm6; + SETJMP_FLOAT128 Xmm7; + SETJMP_FLOAT128 Xmm8; + SETJMP_FLOAT128 Xmm9; + SETJMP_FLOAT128 Xmm10; + SETJMP_FLOAT128 Xmm11; + SETJMP_FLOAT128 Xmm12; + SETJMP_FLOAT128 Xmm13; + SETJMP_FLOAT128 Xmm14; + SETJMP_FLOAT128 Xmm15; +} _JUMP_BUFFER; + + +Res StackScan(ScanState ss, Addr *stackBot) +{ + jmp_buf jb; + + /* We rely on the fact that Pelles C's setjmp stores the callee-save + registers in the jmp_buf. */ + (void)setjmp(jb); + + /* These checks will just serve to warn us at compile-time if the + setjmp.h header changes to indicate that the registers we want aren't + saved any more. */ + AVER(sizeof(((_JUMP_BUFFER *)jb)->Rdi) == sizeof(Addr)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Rsi) == sizeof(Addr)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->Rbp) == sizeof(Addr)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R12) == sizeof(Addr)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R13) == sizeof(Addr)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R14) == sizeof(Addr)); + AVER(sizeof(((_JUMP_BUFFER *)jb)->R15) == sizeof(Addr)); + + /* The layout of the jmp_buf forces us to harmlessly scan Rsp as well. */ + AVER(offsetof(_JUMP_BUFFER, Rsp) == offsetof(_JUMP_BUFFER, Rbx) + 8); + AVER(offsetof(_JUMP_BUFFER, Rbp) == offsetof(_JUMP_BUFFER, Rbx) + 16); + AVER(offsetof(_JUMP_BUFFER, Rsi) == offsetof(_JUMP_BUFFER, Rbx) + 24); + AVER(offsetof(_JUMP_BUFFER, Rdi) == offsetof(_JUMP_BUFFER, Rbx) + 32); + AVER(offsetof(_JUMP_BUFFER, R12) == offsetof(_JUMP_BUFFER, Rbx) + 40); + AVER(offsetof(_JUMP_BUFFER, R13) == offsetof(_JUMP_BUFFER, Rbx) + 48); + AVER(offsetof(_JUMP_BUFFER, R14) == offsetof(_JUMP_BUFFER, Rbx) + 56); + AVER(offsetof(_JUMP_BUFFER, R15) == offsetof(_JUMP_BUFFER, Rbx) + 64); + + return StackScanInner(ss, stackBot, (Addr *)&((_JUMP_BUFFER *)jb)->Rbx, 9); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2001-2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/steptest.c b/code/steptest.c index c01b81f21e..f857a85d7f 100644 --- a/code/steptest.c +++ b/code/steptest.c @@ -1,7 +1,7 @@ /* steptest.c: TEST FOR ARENA STEPPING * * $Id$ - * Copyright (c) 1998-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 1998-2014 Ravenbrook Limited. See end of file for license. * * Loosely based on . */ @@ -14,12 +14,10 @@ #include "mpscamc.h" #include "mpsavm.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif #include "mps.h" -#include -#include + +#include /* pow */ +#include /* fflush, printf, putchar, stdout */ #define testArenaSIZE ((size_t)((size_t)64 << 20)) #define avLEN 3 @@ -46,24 +44,6 @@ static mps_gen_param_s testChain[genCOUNT] = { {gen3SIZE, gen3MORTALITY}, }; -/* run the test several times, calling mps_arena_step at a different - * frequency each time. When we call it often, tracing is never done - * during allocation. When we call it never, tracing is always done - * during allocation. - */ - -static unsigned long step_frequencies[] = { - 1000, - 5000, - 10000, - 1000000000, /* one billion */ -}; - -#define TESTS (sizeof(step_frequencies) / sizeof(step_frequencies[0])) - -static unsigned test_number = 0; - - /* objNULL needs to be odd so that it's ignored in exactRoots. */ #define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED)) @@ -74,19 +54,19 @@ static mps_addr_t ambigRoots[ambigRootsCOUNT]; /* Things we want to measure. Times are all in microseconds. */ -double alloc_time; /* Time spent allocating */ -double max_alloc_time; /* Max time taken to allocate one object */ -double step_time; /* Time spent in mps_arena_step returning 1 */ -double max_step_time; /* Max time of mps_arena_step returning 1 */ -double no_step_time; /* Time spent in mps_arena_step returning 0 */ -double max_no_step_time; /* Max time of mps_arena_step returning 0 */ +static double alloc_time; /* Time spent allocating */ +static double max_alloc_time; /* Max time taken to allocate one object */ +static double step_time; /* Time spent in mps_arena_step returning 1 */ +static double max_step_time; /* Max time of mps_arena_step returning 1 */ +static double no_step_time; /* Time spent in mps_arena_step returning 0 */ +static double max_no_step_time; /* Max time of mps_arena_step returning 0 */ -double total_clock_time; /* Time spent reading the clock */ -long clock_reads; /* Number of times clock is read */ -long steps; /* # of mps_arena_step calls returning 1 */ -long no_steps; /* # of mps_arena_step calls returning 0 */ -size_t alloc_bytes; /* # of bytes allocated */ -long commit_failures; /* # of times mps_commit fails */ +static double total_clock_time; /* Time spent reading the clock */ +static long clock_reads; /* Number of times clock is read */ +static long steps; /* # of mps_arena_step calls returning 1 */ +static long no_steps; /* # of mps_arena_step calls returning 0 */ +static size_t alloc_bytes; /* # of bytes allocated */ +static long commit_failures; /* # of times mps_commit fails */ /* Operating-system dependent timing. Defines two functions, void @@ -98,6 +78,8 @@ long commit_failures; /* # of times mps_commit fails */ #ifdef MPS_OS_W3 +#include "mpswin.h" + static HANDLE currentProcess; static void prepare_clock(void) @@ -109,7 +91,8 @@ static double my_clock(void) { FILETIME ctime, etime, ktime, utime; double dk, du; - GetProcessTimes(currentProcess, &ctime, &etime, &ktime, &utime); + cdie(GetProcessTimes(currentProcess, &ctime, &etime, &ktime, &utime) != 0, + "GetProcessTimes"); dk = ktime.dwHighDateTime * 4096.0 * 1024.0 * 1024.0 + ktime.dwLowDateTime; dk /= 10.0; @@ -151,7 +134,7 @@ static double my_clock(void) * on thrush.ravenbrook.com on 2002-06-28, clock_time goes from 5.43 * us near process start to 7.45 us later). */ -double clock_time; /* current estimate of time to read the clock */ +static double clock_time; /* current estimate of time to read the clock */ /* take at least this many microseconds to set the clock */ #define CLOCK_TIME_SET 10000 @@ -297,9 +280,8 @@ static void test_step(mps_arena_t arena, double multiplier) /* test -- the body of the test */ -static void *test(void *arg, size_t s) +static void test(mps_arena_t arena, unsigned long step_period) { - mps_arena_t arena; mps_fmt_t format; mps_chain_t chain; mps_root_t exactRoot, ambigRoot; @@ -312,9 +294,6 @@ static void *test(void *arg, size_t s) double total_mps_time, total_time; double t1; - arena = (mps_arena_t)arg; - (void)s; /* unused */ - die(dylan_fmt(&format, arena), "fmt_create"); die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); @@ -338,8 +317,7 @@ static void *test(void *arg, size_t s) &ambigRoots[0], ambigRootsCOUNT), "root_create_table(ambig)"); - printf("Stepping every %lu allocations.\n", - (unsigned long)step_frequencies[test_number]); + printf("Stepping every %lu allocations.\n", step_period); mps_message_type_enable(arena, mps_message_type_gc()); @@ -378,7 +356,7 @@ static void *test(void *arg, size_t s) ++objs; - if (objs % step_frequencies[test_number] == 0) + if (objs % step_period == 0) test_step(arena, 0.0); if (objs % multiStepFREQ == 0) @@ -391,7 +369,7 @@ static void *test(void *arg, size_t s) if (collections > old_collections) { old_collections = collections; putchar('.'); - fflush(stdout); + (void)fflush(stdout); } } @@ -419,8 +397,8 @@ static void *test(void *arg, size_t s) printf("Collection statistics:\n"); printf(" %"PRIuLONGEST" collections\n", (ulongest_t)collections); printf(" %"PRIuLONGEST" bytes condemned.\n", (ulongest_t)condemned); - printf(" %lu bytes not condemned.\n", - (unsigned long)not_condemned); + printf(" %"PRIuLONGEST" bytes not condemned.\n", + (ulongest_t)not_condemned); printf(" %"PRIuLONGEST" bytes survived.\n", (ulongest_t)live); if (condemned) { printf(" Mortality %5.2f%%.\n", @@ -429,10 +407,10 @@ static void *test(void *arg, size_t s) ((double)condemned/(condemned + not_condemned)) * 100.0); } if (collections) { - printf(" Condemned per collection %lu bytes.\n", - (unsigned long)condemned/collections); - printf(" Reclaimed per collection %lu bytes.\n", - (unsigned long)(condemned - live)/collections); + printf(" Condemned per collection %"PRIuLONGEST" bytes.\n", + (ulongest_t)condemned/collections); + printf(" Reclaimed per collection %"PRIuLONGEST" bytes.\n", + (ulongest_t)(condemned - live)/collections); } printf("Allocation statistics:\n"); @@ -478,40 +456,27 @@ static void *test(void *arg, size_t s) printf(" %"PRIuLONGEST" clock reads; ", (ulongest_t)clock_reads); print_time("", total_clock_time / clock_reads, " per read;"); print_time(" recently measured as ", clock_time, ").\n"); + + mps_arena_park(arena); mps_ap_destroy(ap); mps_root_destroy(exactRoot); mps_root_destroy(ambigRoot); mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(format); - - return NULL; } int main(int argc, char *argv[]) { + mps_arena_t arena; prepare_clock(); - - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); - - while (test_number < TESTS) { - mps_arena_t arena; - mps_thr_t thread; - void *r; - - set_clock_timing(); - die(mps_arena_create(&arena, mps_arena_class_vm(), - testArenaSIZE), - "arena_create"); - mps_arena_clamp(arena); - die(mps_thread_reg(&thread, arena), "thread_reg"); - mps_tramp(&r, test, arena, 0); - mps_thread_dereg(thread); - mps_arena_destroy(arena); - ++ test_number; - } - + testlib_init(argc, argv); + set_clock_timing(); + die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), + "arena_create"); + mps_arena_clamp(arena); + test(arena, (unsigned long)pow(10, rnd() % 10)); + mps_arena_destroy(arena); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; } @@ -519,7 +484,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/table.c b/code/table.c index a0d0275c0c..18e5eb8540 100644 --- a/code/table.c +++ b/code/table.c @@ -1,7 +1,7 @@ /* table.h: A dictionary mapping a Word to a void* * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .note.good-hash: As is common in hash table implementations, we * assume that the hash function is good. @@ -11,61 +11,12 @@ #include "mpm.h" #include +#include SRCID(table, "$Id$"); -/* tableHash -- return a hash value from an address - * - * This uses a single cycle of an MLCG, more commonly seen as a - * pseudorandom number generator. It works extremely well as a - * hash function. - * - * (In particular, it is substantially better than simply doing this: - * seed = (unsigned long)addr * 48271; - * Tested by RHSK 2010-12-28.) - * - * This MLCG is a full period generator: it cycles through every - * number from 1 to m-1 before repeating. Therefore, no two numbers - * in that range hash to the same value. Furthermore, it has prime - * modulus, which tends to avoid recurring patterns in the low-order - * bits, which is good because the hash will be used modulus the - * number of slots in the table. - * - * Of course it's only a 31-bit cycle, so we start by losing the top - * bit of the address, but that's hardly a great problem. - * - * See `rnd` in testlib.c for more technical details. - * - * The implementation is quite subtle. See rnd() in testlib.c, where - * it has been exhaustively (ie: totally) tested. RHSK 2010-12-28. - * - * NOTE: According to NB, still a fine function for producing a 31-bit hash - * value, although of course it only hashes on the lower 31 bits of the - * key; we could cheaply make it choose a different 31 bits if we'd prefer - * (e.g. ((key >> 2) & 0x7FFFFFFF)), or combine more of the key bits (e.g. - * ((key ^ (key >> 31)) & 0x7fffffff)). - */ - -#define R_m 2147483647UL -#define R_a 48271UL - -typedef Word Hash; - -static Hash tableHash(Word key) -{ - Hash hash = (Hash)(key & 0x7FFFFFFF); - /* requires m == 2^31-1, a < 2^16 */ - Hash bot = R_a * (hash & 0x7FFF); - Hash top = R_a * (hash >> 15); - hash = bot + ((top & 0xFFFF) << 15) + (top >> 16); - if(hash > R_m) - hash -= R_m; - return hash; -} - - Bool TableCheck(Table table) { CHECKS(Table, table); @@ -86,6 +37,39 @@ static Bool entryIsActive(Table table, TableEntry entry) } +/* */ + +static Word wrand(void) +{ + return ((Word)rand() << 32) | (Word)rand(); +} + +static void tableRandHash(Table table) +{ + table->log2length = SizeLog2(table->length); + do + table->uha = wrand(); + while ((table->uha & 1) == 0); + table->uhb = wrand() & (table->length - 1); + do + table->uhc = wrand(); + while ((table->uhc & 1) == 0); + table->uhd = wrand() & (table->length - 1); +} + +static Index pos0(Table table, Word key) +{ + return (Word)(table->uha * key + table->uhb) >> + (sizeof(Word) * CHAR_BIT - table->log2length); +} + +static Index pos1(Table table, Word key) +{ + return (Word)(table->uhc * key + table->uhd) >> + (sizeof(Word) * CHAR_BIT - table->log2length); +} + + /* tableFind -- finds the entry for this key, or NULL * * .worst: In the worst case, this looks at every slot before giving up, @@ -93,31 +77,64 @@ static Bool entryIsActive(Table table, TableEntry entry) * that all the items still fit in after growing the table. */ -static TableEntry tableFind(Table table, Word key, Bool skip_deleted) +static TableEntry tableFind(Table table, Word key) { - Hash hash; - Index i; - Word mask; - - /* .find.visit: Ensure the length is a power of two so that the stride - is coprime and so visits all entries in the array eventually. */ - AVER(WordIsP2(table->length)); /* .find.visit */ - - mask = table->length - 1; - hash = tableHash(key) & mask; - i = hash; - do { - Word k = table->array[i].key; - if (k == key || - k == table->unusedKey || - (!skip_deleted && key == table->deletedKey)) - return &table->array[i]; - i = (i + (hash | 1)) & mask; /* .find.visit */ - } while(i != hash); + Index pos; + + pos = pos0(table, key); + if (table->array[pos].key == key) + return &table->array[pos]; + pos = pos1(table, key); + if (table->array[pos].key == key) + return &table->array[pos]; return NULL; } +static Res place(Table table, Word *keyIO, void **valueIO) +{ + Index i; + Word key = *keyIO; + void *value = *valueIO; + + for (i = 0; i < table->length; ++i) { + Index pos; + Word tk; + void *tv; + + pos = pos0(table, key); + tk = table->array[pos].key; + if (tk == key) + return ResFAIL; + if (tk == table->unusedKey || tk == table->deletedKey) { + table->array[pos].key = key; + table->array[pos].value = value; + ++table->count; + return ResOK; + } + pos = pos1(table, key); + tk = table->array[pos].key; + if (tk == key) + return ResFAIL; + tv = table->array[pos].value; + /* cuckoo! */ + table->array[pos].key = key; + table->array[pos].value = value; + if (tk == table->unusedKey || tk == table->deletedKey) { + ++table->count; + return ResOK; + } + + key = tk; + value = tv; + } + + *keyIO = key; + *valueIO = value; + + return ResLIMIT; +} + /* TableGrow -- increase the capacity of the table * @@ -145,14 +162,15 @@ static TableEntry tableFind(Table table, Word key, Bool skip_deleted) * occupancy <= capacity < enough <= cSlots */ -#define SPACEFRACTION 0.75 /* .hash.spacefraction */ +#define SPACEFRACTION 0.5 /* .hash.spacefraction */ Res TableGrow(Table table, Count extraCapacity) { TableEntry oldArray, newArray; Count oldLength, newLength; Count required, minimum; - Count i, found; + Count i; + Count oldCount; required = table->count + extraCapacity; if (required < table->count) /* overflow? */ @@ -174,8 +192,10 @@ Res TableGrow(Table table, Count extraCapacity) newLength = doubled; } +#if 0 if (newLength == oldLength) /* already enough space? */ return ResOK; +#endif /* TODO: An event would be good here */ @@ -185,27 +205,30 @@ Res TableGrow(Table table, Count extraCapacity) if(newArray == NULL) return ResMEMORY; - for(i = 0; i < newLength; ++i) { + table->length = newLength; + table->array = newArray; + + oldCount = table->count; +retry: + tableRandHash(table); + + for (i = 0; i < newLength; ++i) { newArray[i].key = table->unusedKey; newArray[i].value = NULL; } - - table->length = newLength; - table->array = newArray; - found = 0; + table->count = 0; for(i = 0; i < oldLength; ++i) { if (entryIsActive(table, &oldArray[i])) { - TableEntry entry; - entry = tableFind(table, oldArray[i].key, FALSE /* none deleted */); - AVER(entry != NULL); - AVER(entry->key == table->unusedKey); - entry->key = oldArray[i].key; - entry->value = oldArray[i].value; - ++found; + Word key = oldArray[i].key; + void *value = oldArray[i].value; + Res res = place(table, &key, &value); + if (res == ResLIMIT) + goto retry; + AVER(res == ResOK); } } - AVER(found == table->count); + AVER(table->count == oldCount); if (oldLength > 0) { AVER(oldArray != NULL); @@ -222,8 +245,8 @@ Res TableGrow(Table table, Count extraCapacity) extern Res TableCreate(Table *tableReturn, Count length, - TableAllocMethod tableAlloc, - TableFreeMethod tableFree, + TableAllocFunction tableAlloc, + TableFreeFunction tableFree, void *allocClosure, Word unusedKey, Word deletedKey) @@ -281,7 +304,7 @@ extern void TableDestroy(Table table) extern Bool TableLookup(void **valueReturn, Table table, Word key) { - TableEntry entry = tableFind(table, key, TRUE /* skip deleted */); + TableEntry entry = tableFind(table, key); if(entry == NULL || !entryIsActive(table, entry)) return FALSE; @@ -294,34 +317,19 @@ extern Bool TableLookup(void **valueReturn, Table table, Word key) extern Res TableDefine(Table table, Word key, void *value) { - TableEntry entry; - AVER(key != table->unusedKey); AVER(key != table->deletedKey); - if (table->count >= table->length * SPACEFRACTION) { - Res res = TableGrow(table, 1); - if(res != ResOK) return res; - entry = tableFind(table, key, FALSE /* no deletions yet */); - AVER(entry != NULL); - if (entryIsActive(table, entry)) - return ResFAIL; - } else { - entry = tableFind(table, key, TRUE /* skip deleted */); - if (entry != NULL && entryIsActive(table, entry)) - return ResFAIL; - /* Search again to find the best slot, deletions included. */ - entry = tableFind(table, key, FALSE /* don't skip deleted */); - AVER(entry != NULL); + for (;;) { + Res res = place(table, &key, &value); + if (res != ResLIMIT) + return res; + res = TableGrow(table, table->count); + AVER(res == ResOK); + /* Should randomize the hash functions and rehash in place. */ } - - entry->key = key; - entry->value = value; - ++table->count; - - return ResOK; } - + /* TableRedefine -- redefine an existing mapping */ @@ -332,7 +340,7 @@ extern Res TableRedefine(Table table, Word key, void *value) AVER(key != table->unusedKey); AVER(key != table->deletedKey); - entry = tableFind(table, key, TRUE /* skip deletions */); + entry = tableFind(table, key); if (entry == NULL || !entryIsActive(table, entry)) return ResFAIL; AVER(entry->key == key); @@ -350,7 +358,7 @@ extern Res TableRemove(Table table, Word key) AVER(key != table->unusedKey); AVER(key != table->deletedKey); - entry = tableFind(table, key, TRUE); + entry = tableFind(table, key); if (entry == NULL || !entryIsActive(table, entry)) return ResFAIL; entry->key = table->deletedKey; @@ -382,7 +390,7 @@ extern Count TableCount(Table table) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/table.h b/code/table.h index 1697be09b9..688e96bc96 100644 --- a/code/table.h +++ b/code/table.h @@ -1,5 +1,5 @@ /* table.h: Interface for a dictionary - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * $Id$ */ @@ -15,8 +15,8 @@ typedef struct TableStruct *Table; #define TableSig ((Sig)0x5192AB13) /* SIGnature TABLE */ -typedef void *(*TableAllocMethod)(void *closure, size_t size); -typedef void (*TableFreeMethod)(void *closure, void *p, size_t size); +typedef void *(*TableAllocFunction)(void *closure, size_t size); +typedef void (*TableFreeFunction)(void *closure, void *p, size_t size); typedef struct TableEntryStruct { Word key; @@ -26,19 +26,22 @@ typedef struct TableEntryStruct { typedef struct TableStruct { Sig sig; /* */ Count length; /* Number of slots in the array */ + Shift log2length; Count count; /* Active entries in the table */ TableEntry array; /* Array of table slots */ - TableAllocMethod alloc; - TableFreeMethod free; + TableAllocFunction alloc; + TableFreeFunction free; void *allocClosure; Word unusedKey; /* key marking unused (undefined) entries */ Word deletedKey; /* key marking deleted entries */ + Word uha, uhb; /* universal hash inputs */ + Word uhc, uhd; /* universal hash inputs */ } TableStruct; extern Res TableCreate(Table *tableReturn, Count length, - TableAllocMethod tableAlloc, - TableFreeMethod tableFree, + TableAllocFunction tableAlloc, + TableFreeFunction tableFree, void *allocClosure, Word unusedKey, Word deletedKey); @@ -60,7 +63,7 @@ extern Res TableGrow(Table table, Count extraCapacity); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/teletest.c b/code/teletest.c index 5f54dc6110..1a6b161455 100644 --- a/code/teletest.c +++ b/code/teletest.c @@ -1,7 +1,7 @@ /* teletest.c: TELEMETRY TEST * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .source: The command parser here was taken and adapted from bttest.c. */ @@ -12,26 +12,18 @@ #include "testlib.h" #include "mpslib.h" -#include +#include /* fflush, fgets, printf, stdin, stdout */ +#include /* exit, EXIT_SUCCESS, strtoul */ SRCID(teletest, "$Id$"); static mps_arena_t arena; - +#define WORD_FORMAT "0x%0" PRIwWORD PRIuLONGEST #define MAX_ARGS 3 #define INPUT_BUFFER_SIZE 512 -#if (MPS_WORD_WIDTH == 32) -#define WORD_FORMAT "0x%08lx" -#elif (MPS_WORD_WIDTH == 64) -#define WORD_FORMAT "0x%016lx" -#else -#error "Unrecognized word width" -#endif - - static mps_word_t args[MAX_ARGS]; static char *stringArg; static Count argCount; @@ -43,7 +35,8 @@ static void callControl(mps_word_t reset, mps_word_t flip) old = mps_telemetry_control(reset, flip); new = mps_telemetry_control((mps_word_t)0, (mps_word_t)0); - (void)printf(WORD_FORMAT " -> " WORD_FORMAT "\n", old, new); + printf(WORD_FORMAT " -> " WORD_FORMAT "\n", + (ulongest_t)old, (ulongest_t)new); } @@ -58,7 +51,7 @@ static void doRead(void) mps_word_t old; old = mps_telemetry_control((mps_word_t)0, (mps_word_t)0); - (void)printf(WORD_FORMAT "\n", old); + (void)printf(WORD_FORMAT "\n", (ulongest_t)old); } @@ -85,7 +78,7 @@ static void doIntern(void) mps_word_t id; id = mps_telemetry_intern(stringArg); - (void)printf(WORD_FORMAT "\n", id); + (void)printf(WORD_FORMAT "\n", (ulongest_t)id); } static void doLabel(void) @@ -200,8 +193,7 @@ static void obeyCommand(char *command) extern int main(int argc, char *argv[]) { - testlib_unused(argc); - testlib_unused(argv); + testlib_init(argc, argv); die(mps_arena_create((mps_arena_t*)&arena, mps_arena_class_vm(), testArenaSIZE), @@ -210,20 +202,21 @@ extern int main(int argc, char *argv[]) while(1) { char input[INPUT_BUFFER_SIZE]; printf("telemetry test> "); - fflush(stdout); + (void)fflush(stdout); if (fgets(input, INPUT_BUFFER_SIZE , stdin)) { obeyCommand(input); } else { - doQuit(); + break; } } + mps_arena_destroy(arena); return EXIT_SUCCESS; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/testlib.c b/code/testlib.c index 2bed23b0f6..401c405993 100644 --- a/code/testlib.c +++ b/code/testlib.c @@ -1,7 +1,7 @@ /* testlib.c: TEST LIBRARY * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .purpose: A library of functions that may be of use to unit tests. @@ -11,26 +11,11 @@ #include "clock.h" /* for EVENT_CLOCK */ #include "mps.h" #include "misc.h" /* for NOOP */ -#include -#include -#include -#ifdef MPS_OS_IA -struct itimerspec; /* stop complaints from time.h */ -#endif -#include - -#ifdef MPS_BUILD_MV -/* MSVC warning 4702 = unreachable code - * - * job000605: believed needed to prevent VC7 warning - * for error() below, in which va_end is mandated by - * ISO C (C99:7.15.1) even though it is unreachable. - */ -#pragma warning(disable: 4702) -/* MSVC warning 4996 = stdio / C runtime 'unsafe' */ -/* Objects to: sscanf. See job001934. */ -#pragma warning( disable : 4996 ) -#endif + +#include /* fmod, log */ +#include /* fflush, printf, stderr, sscanf, vfprintf */ +#include /* abort, exit, getenv */ +#include /* time */ /* fail -- like assert, but (notionally) returns a value, so usable in an expression */ @@ -122,7 +107,7 @@ static unsigned long seed_verify_float = 1; static unsigned long rnd_verify_float(void) { double s; - s = seed_verify_float; + s = (double)seed_verify_float; s *= R_a_float; s = fmod(s, R_m_float); seed_verify_float = (unsigned long)s; @@ -235,6 +220,15 @@ double rnd_double(void) return rnd() / R_m_float; } +size_t rnd_grain(size_t arena_size) +{ + /* The grain size must be small enough to allow for a complete set + * of zones in the initial chunk. */ + size_t s = (size_t)(log((double)arena_size) / log(2.0)); + size_t shift = MPS_WORD_SHIFT; + Insist(s > shift); + return (size_t)1 << (rnd() % (s - shift)); +} rnd_state_t rnd_seed(void) { @@ -285,7 +279,7 @@ void randomize(int argc, char *argv[]) argv[0], seed0); rnd_state_set(seed0); } - fflush(stdout); /* ensure seed is not lost in case of failure */ + (void)fflush(stdout); /* ensure seed is not lost in case of failure */ } unsigned long rnd_state(void) @@ -331,20 +325,22 @@ void rnd_state_set_v2(unsigned long seed0_v2) static struct { const char *ident; const char *doc; -} res_strings[] = { -#define RES_STRINGS_ROW(X, ident, doc) {#ident, #doc}, -_mps_RES_ENUM(RES_STRINGS_ROW, X) +} const res_strings[] = { +#define RES_STRINGS_ROW(X, ident, doc) {#ident, doc}, + _mps_RES_ENUM(RES_STRINGS_ROW, X) }; /* verror -- die with message */ +ATTRIBUTE_FORMAT((printf, 1, 0)) void verror(const char *format, va_list args) { - fflush(stdout); /* synchronize */ - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - fflush(stderr); /* make sure the message is output */ + (void)fflush(stdout); /* synchronize */ + (void)vfprintf(stderr, format, args); + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); /* make sure the message is output */ + mps_telemetry_flush(); /* On Windows, the abort signal pops up a dialog box. This suspends * the test suite until a button is pressed, which is not acceptable * for offline testing, so if the MPS_TESTLIB_NOABORT environment @@ -360,6 +356,7 @@ void verror(const char *format, va_list args) /* error -- die with message */ +ATTRIBUTE_FORMAT((printf, 1, 2)) void error(const char *format, ...) { va_list args; @@ -375,7 +372,7 @@ void error(const char *format, ...) void die_expect(mps_res_t res, mps_res_t expected, const char *s) { if (res != expected) { - if (0 <= res && (unsigned)res < sizeof(res_strings) / sizeof(res_strings[0])) + if (0 <= res && (unsigned)res < NELEMS(res_strings)) error("\n%s: %s: %s\n", s, res_strings[res].ident, res_strings[res].doc); else error("\n%s: %d: unknown result code\n", s, res); @@ -409,10 +406,18 @@ void assert_die(const char *file, unsigned line, const char *condition) } +/* testlib_init -- install assertion handler and seed RNG */ + +void testlib_init(int argc, char *argv[]) +{ + (void)mps_lib_assert_fail_install(assert_die); + randomize(argc, argv); +} + /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/testlib.h b/code/testlib.h index d58655ca24..6f059abcbb 100644 --- a/code/testlib.h +++ b/code/testlib.h @@ -12,61 +12,75 @@ #include "mps.h" #include "misc.h" /* for STR */ - -/* Include system header hackery. */ #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpswin.h" -#endif - -#include -/* Suppress Visual C warnings at warning level 4, */ -/* see mail.richard.1997-09-25.13-26. */ -/* Essentially the same settings are done in config.h. */ +/* Suppress Visual C warnings at /W4 (warning level 4) */ +/* This is also done in config.h. */ #ifdef MPS_BUILD_MV -/* "unreferenced inline function has been removed" (windows.h) */ -#pragma warning(disable: 4514) - -/* "constant conditional" (MPS_END) */ +/* "constant conditional" (provoked by MPS_END) */ #pragma warning(disable: 4127) -/* MSVC 2.0 generates a warning when using NOCHECK or UNUSED */ -#ifdef _MSC_VER -#if _MSC_VER < 1000 -#pragma warning(disable: 4705) -#endif -#else /* _MSC_VER */ -#error "Expected _MSC_VER to be defined for builder.mv" -#endif /* _MSC_VER */ +#endif /* MPS_BUILD_MV */ -/* MSVC 10.00 on PowerPC generates erroneous warnings about */ -/* uninitialized local variables, if you take their address. */ -#ifdef MPS_ARCH_PP -#pragma warning(disable: 4701) -#endif +/* Suppress Pelles C warnings at /W2 (warning level 2) */ +/* This is also done in config.h. */ + +#ifdef MPS_BUILD_PC + +/* "Unreachable code" (provoked by AVER, if condition is constantly true). */ +#pragma warn(disable: 2154) + +#endif /* MPS_BUILD_PC */ + +/* Function attributes */ +/* These are also defined in config.h */ -/* Non-checking varieties give many spurious warnings because parameters - * are suddenly unused, etc. We aren't interested in these. +#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL) + +/* GCC: + * Clang: */ +#define ATTRIBUTE_FORMAT(ARGLIST) __attribute__((__format__ ARGLIST)) + +#else -#if defined(AVER_AND_CHECK_NONE) +#define ATTRIBUTE_FORMAT(ARGLIST) -/* "unreferenced formal parameter" */ -#pragma warning(disable: 4100) +#endif -/* "unreferenced local function has been removed" */ -#pragma warning(disable: 4505) + +/* alloca -- memory allocator + * + * Windows calls this function _alloca() instead of alloca(). + * + */ + +#if defined(MPS_OS_W3) + +#define alloca _alloca #endif -#endif /* MPS_BUILD_MV */ +/* setenv -- set environment variable + * + * Windows lacks setenv(), but _putenv_s() has similar functionality. + * + * + * This macro version may evaluate the name argument twice. + */ + +#if defined(MPS_OS_W3) + +#define setenv(name, value, overwrite) \ + (((overwrite) || !getenv(name)) ? _putenv_s(name, value) : 0) + +#endif /* ulongest_t -- longest unsigned integer type @@ -87,7 +101,7 @@ #error "How many beans make five?" #endif -#ifdef MPS_PF_W3I6MV +#if defined(MPS_OS_W3) && defined(MPS_ARCH_I6) #define PRIuLONGEST "llu" #define SCNuLONGEST "llu" #define SCNXLONGEST "llX" @@ -119,6 +133,23 @@ typedef long longest_t; #define testlib_unused(v) ((void)(v)) +/* max -- return larger value + * + * Note: evaluates its arguments twice. + */ + +#undef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) + + +/* alignUp -- align word to alignment + * + * Note: evaluates argument a twice. + */ + +#define alignUp(w, a) (((w) + (a) - 1) & ~((size_t)(a) - 1)) + + /* die -- succeed or die * * If the first argument is not ResOK then prints the second @@ -167,7 +198,9 @@ void assert_die(const char *file, unsigned line, const char *condition); /* error, verror -- die with message */ +ATTRIBUTE_FORMAT((printf, 1, 2)) extern void error(const char *format, ...); +ATTRIBUTE_FORMAT((printf, 1, 0)) extern void verror(const char *format, va_list args); @@ -217,6 +250,12 @@ extern mps_addr_t rnd_addr(void); extern double rnd_double(void); +/* rnd_grain -- return a random grain size that's not too big for the + * given arena size */ + +extern size_t rnd_grain(size_t arena_size); + + /* randomize -- randomize the generator, or initialize to replay * * randomize(argc, argv) randomizes the rnd generator (using time(3)) @@ -227,6 +266,11 @@ extern double rnd_double(void); extern void randomize(int argc, char *argv[]); +/* testlib_init -- install assertion handler and seed RNG */ + +extern void testlib_init(int argc, char *argv[]); + + #endif /* testlib_h */ diff --git a/code/testthr.h b/code/testthr.h new file mode 100644 index 0000000000..75602bcc56 --- /dev/null +++ b/code/testthr.h @@ -0,0 +1,123 @@ +/* testthr.h: MULTI-THREADED TEST INTERFACE + * + * $Id: //info.ravenbrook.com/project/mps/master/code/testlib.h#30 $ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * .purpose: Simple interface to threads that makes it possible to + * write test cases that are portable between Windows (using the + * implementation in testthrw3.c) and Unix (using the implementation + * in testthrix.c). See . + */ + +#ifndef testthr_h +#define testthr_h + +#include "mpstd.h" + + +/* testthr_routine_t -- type of thread routines + * + * Use the pthread type here and convert back and forth in the Windows + * implementation. + */ +typedef void *(*testthr_routine_t)(void *); + + +/* testthr_t -- type of thread identifiers + * + * It is necessary to define it here (even though it requires some + * #ifdefs) so that clients can allocate storage for them. + */ + +#if defined(MPS_OS_W3) + +#include "mpswin.h" + +/* On Windows, a thread is identified by a HANDLE. + * + * But use a structure so that the thread has somewhere to store its + * result for use by testthr_join. + */ +typedef struct testthr_t { + HANDLE handle; + testthr_routine_t start; + void *arg; /* argument to pass to start */ + void *result; /* result returned from start */ +} testthr_t; + +#elif defined(MPS_OS_FR) || defined(MPS_OS_LI) || defined(MPS_OS_XC) + +#include + +/* In pthreads, a thread is identified by a pthread_t, which is + * allowed "to be defined as a structure" [IEEE Std 1003.1, sys/types.h] + * + */ +typedef pthread_t testthr_t; + +#else +#error "Unknown platform: can't determine a type for testthr_t." +#endif + + +/* testthr_create -- create a thread + * + * Store the identifier of the newly created thread in *thread_o, and + * call start, passing arg as the single parameter. + */ + +void testthr_create(testthr_t *thread_o, testthr_routine_t start, void *arg); + + +/* testthr_join -- wait for a thread to complete + * + * Suspend execution of the calling thread until the target thread + * terminates (if necessary), and if result_o is non-NULL, update + * *result_o with the return value of the thread's start. + */ + +void testthr_join(testthr_t *thread, void **result_o); + +#endif /* testthr_h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/testthrix.c b/code/testthrix.c new file mode 100644 index 0000000000..f4c544e0b8 --- /dev/null +++ b/code/testthrix.c @@ -0,0 +1,66 @@ +/* testthrix.c: MULTI-THREADED TEST IMPLEMENTATION (POSIX THREADS) + * + * $Id: //info.ravenbrook.com/project/mps/master/code/testlib.h#30 $ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + */ + +#include "testlib.h" +#include "testthr.h" + +#include /* strerror */ + +void testthr_create(testthr_t *thread_o, testthr_routine_t start, void *arg) +{ + int res = pthread_create(thread_o, NULL, start, arg); + if (res != 0) + error("pthread_create failed with result %d (%s)", res, strerror(res)); +} + +void testthr_join(testthr_t *thread, void **result_o) +{ + int res = pthread_join(*thread, result_o); + if (res != 0) + error("pthread_join failed with result %d (%s)", res, strerror(res)); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/testthrw3.c b/code/testthrw3.c new file mode 100644 index 0000000000..cd5cf54ae7 --- /dev/null +++ b/code/testthrw3.c @@ -0,0 +1,80 @@ +/* testthrw3.c: MULTI-THREADED TEST IMPLEMENTATION (WINDOWS) + * + * $Id: //info.ravenbrook.com/project/mps/master/code/testlib.h#30 $ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + */ + +#include "testlib.h" +#include "testthr.h" + +static DWORD WINAPI testthr_start(LPVOID arg) +{ + testthr_t *thread = arg; + thread->result = (*thread->start)(thread->arg); + return 0; +} + +void testthr_create(testthr_t *thread_o, testthr_routine_t start, void *arg) +{ + HANDLE res; + thread_o->start = start; + thread_o->arg = arg; + res = CreateThread(NULL, 0, testthr_start, thread_o, 0, NULL); + if (res == NULL) + error("CreateThread failed with error %lu", + (unsigned long)GetLastError()); + else + thread_o->handle = res; +} + +void testthr_join(testthr_t *thread, void **result_o) +{ + DWORD res = WaitForSingleObject(thread->handle, INFINITE); + if (res != WAIT_OBJECT_0) + error("WaitForSingleObject failed with result %lu (error %lu)", + (unsigned long)res, (unsigned long)GetLastError()); + if (result_o) + *result_o = thread->result; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/th.h b/code/th.h index 30a2205a35..8f7996cc0b 100644 --- a/code/th.h +++ b/code/th.h @@ -28,7 +28,7 @@ extern Bool ThreadCheck(Thread thread); extern Bool ThreadCheckSimple(Thread thread); -extern Res ThreadDescribe(Thread thread, mps_lib_FILE *stream); +extern Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth); /* Register/Deregister @@ -47,13 +47,14 @@ extern void ThreadDeregister(Thread thread, Arena arena); /* ThreadRingSuspend/Resume * - * These functions suspend/resume the threads on the ring. - * If the current thread is among them, it is not suspended, - * nor is any attempt to resume it made. + * These functions suspend/resume the threads on the ring. If the + * current thread is among them, it is not suspended, nor is any + * attempt to resume it made. Threads that can't be suspended/resumed + * because they are dead are moved to deadRing. */ -extern void ThreadRingSuspend(Ring threadRing); -extern void ThreadRingResume(Ring threadRing); +extern void ThreadRingSuspend(Ring threadRing, Ring deadRing); +extern void ThreadRingResume(Ring threadRing, Ring deadRing); /* ThreadRingThread diff --git a/code/than.c b/code/than.c index 752ef3ed64..f1fb21006d 100644 --- a/code/than.c +++ b/code/than.c @@ -1,15 +1,11 @@ /* than.c: ANSI THREADS MANAGER * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * This is a single-threaded implementation of the threads manager. * Has stubs for thread suspension. - * See . - * - * .single: We only expect at most one thread on the ring. - * - * This supports the + * See . */ #include "mpm.h" @@ -30,7 +26,7 @@ Bool ThreadCheck(Thread thread) CHECKS(Thread, thread); CHECKU(Arena, thread->arena); CHECKL(thread->serial < thread->arena->threadSerial); - CHECKL(RingCheck(&thread->arenaRing)); + CHECKD_NOSIG(Ring, &thread->arenaRing); return TRUE; } @@ -53,7 +49,8 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) res = ControlAlloc(&p, arena, sizeof(ThreadStruct), /* withReservoirPermit */ FALSE); - if(res != ResOK) return res; + if (res != ResOK) + return res; thread = (Thread)p; thread->arena = arena; @@ -66,7 +63,6 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) AVERT(Thread, thread); ring = ArenaThreadRing(arena); - AVER(RingCheckSingle(ring)); /* .single */ RingAppend(ring, &thread->arenaRing); @@ -90,16 +86,16 @@ void ThreadDeregister(Thread thread, Arena arena) } -void ThreadRingSuspend(Ring threadRing) +void ThreadRingSuspend(Ring threadRing, Ring deadRing) { AVERT(Ring, threadRing); - return; + AVERT(Ring, deadRing); } -void ThreadRingResume(Ring threadRing) +void ThreadRingResume(Ring threadRing, Ring deadRing) { AVERT(Ring, threadRing); - return; + AVERT(Ring, deadRing); } Thread ThreadRingThread(Ring threadRing) @@ -112,11 +108,11 @@ Thread ThreadRingThread(Ring threadRing) } -/* Must be thread-safe. See . */ +/* Must be thread-safe. See . */ + Arena ThreadArena(Thread thread) { - /* Can't AVER thread as that would not be thread-safe */ - /* AVERT(Thread, thread); */ + AVER(TESTT(Thread, thread)); return thread->arena; } @@ -128,17 +124,18 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) } -Res ThreadDescribe(Thread thread, mps_lib_FILE *stream) +Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) { Res res; - res = WriteF(stream, + res = WriteF(stream, depth, "Thread $P ($U) {\n", (WriteFP)thread, (WriteFU)thread->serial, " arena $P ($U)\n", (WriteFP)thread->arena, (WriteFU)thread->arena->serial, "} Thread $P ($U)\n", (WriteFP)thread, (WriteFU)thread->serial, NULL); - if(res != ResOK) return res; + if (res != ResOK) + return res; return ResOK; } @@ -146,7 +143,7 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/thix.c b/code/thix.c index 7760a5e134..c1a31c6e90 100644 --- a/code/thix.c +++ b/code/thix.c @@ -1,7 +1,7 @@ /* thix.c: Threads Manager for Posix threads * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This is a pthreads implementation of the threads manager. * This implements . @@ -12,10 +12,10 @@ * * ASSUMPTIONS * - * .error.resume: PThreadextResume is assumed to succeed unless the thread - * has been destroyed. - * .error.suspend: PThreadextSuspend is assumed to succeed unless the thread - * has been destroyed. In this case, the suspend context is set to NULL; + * .error.resume: PThreadextResume is assumed to succeed unless the + * thread has been terminated. + * .error.suspend: PThreadextSuspend is assumed to succeed unless the + * thread has been terminated. * * .stack.full-descend: assumes full descending stack. * i.e. stack pointer points to the last allocated location; @@ -48,9 +48,10 @@ typedef struct mps_thr_s { /* PThreads thread structure */ Serial serial; /* from arena->threadSerial */ Arena arena; /* owning arena */ RingStruct arenaRing; /* threads attached to arena */ + Bool alive; /* thread believed to be alive? */ PThreadextStruct thrextStruct; /* PThreads extension */ pthread_t id; /* Pthread object of thread */ - MutatorFaultContext mfc; /* Context if thread is suspended */ + MutatorFaultContext mfc; /* Context if suspended, NULL if not */ } ThreadStruct; @@ -61,7 +62,8 @@ Bool ThreadCheck(Thread thread) CHECKS(Thread, thread); CHECKU(Arena, thread->arena); CHECKL(thread->serial < thread->arena->threadSerial); - CHECKL(RingCheck(&thread->arenaRing)); + CHECKD_NOSIG(Ring, &thread->arenaRing); + CHECKL(BoolCheck(thread->alive)); CHECKD(PThreadext, &thread->thrextStruct); return TRUE; } @@ -98,6 +100,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) thread->serial = arena->threadSerial; ++arena->threadSerial; thread->arena = arena; + thread->alive = TRUE; thread->mfc = NULL; PThreadextInit(&thread->thrextStruct, thread->id); @@ -130,69 +133,83 @@ void ThreadDeregister(Thread thread, Arena arena) } -/* mapThreadRing -- map over threads on ring calling a function on each one - * except the current thread +/* mapThreadRing -- map over threads on ring calling a function on + * each one except the current thread. + * + * Threads that are found to be dead (that is, if func returns FALSE) + * are moved to deadRing, in order to implement + * design.thread-manager.sol.thread.term.attempt. */ -static void mapThreadRing(Ring threadRing, void (*func)(Thread)) +static void mapThreadRing(Ring threadRing, Ring deadRing, Bool (*func)(Thread)) { Ring node, next; pthread_t self; AVERT(Ring, threadRing); + AVERT(Ring, deadRing); + AVER(FUNCHECK(func)); self = pthread_self(); RING_FOR(node, threadRing, next) { Thread thread = RING_ELT(Thread, arenaRing, node); AVERT(Thread, thread); - if(! pthread_equal(self, thread->id)) /* .thread.id */ - (*func)(thread); + AVER(thread->alive); + if (!pthread_equal(self, thread->id) /* .thread.id */ + && !(*func)(thread)) + { + thread->alive = FALSE; + RingRemove(&thread->arenaRing); + RingAppend(deadRing, &thread->arenaRing); + } } } -/* ThreadRingSuspend -- suspend all threads on a ring, expect the current one */ - +/* ThreadRingSuspend -- suspend all threads on a ring, except the + * current one. + */ -static void threadSuspend(Thread thread) +static Bool threadSuspend(Thread thread) { - /* .error.suspend */ - /* In the error case (PThreadextSuspend returning ResFAIL), we */ - /* assume the thread has been destroyed. */ - /* In which case we simply continue. */ + /* .error.suspend: if PThreadextSuspend fails, we assume the thread + * has been terminated. */ Res res; + AVER(thread->mfc == NULL); res = PThreadextSuspend(&thread->thrextStruct, &thread->mfc); - if(res != ResOK) - thread->mfc = NULL; + AVER(res == ResOK); + AVER(thread->mfc != NULL); + /* design.thread-manager.sol.thread.term.attempt */ + return res == ResOK; } -void ThreadRingSuspend(Ring threadRing) +void ThreadRingSuspend(Ring threadRing, Ring deadRing) { - mapThreadRing(threadRing, threadSuspend); + mapThreadRing(threadRing, deadRing, threadSuspend); } /* ThreadRingResume -- resume all threads on a ring (expect the current one) */ -static void threadResume(Thread thread) +static Bool threadResume(Thread thread) { - /* .error.resume */ - /* If the previous suspend failed (thread->mfc == NULL), */ - /* or in the error case (PThreadextResume returning ResFAIL), */ - /* assume the thread has been destroyed. */ - /* In which case we simply continue. */ - if(thread->mfc != NULL) { - (void)PThreadextResume(&thread->thrextStruct); - thread->mfc = NULL; - } + Res res; + /* .error.resume: If PThreadextResume fails, we assume the thread + * has been terminated. */ + AVER(thread->mfc != NULL); + res = PThreadextResume(&thread->thrextStruct); + AVER(res == ResOK); + thread->mfc = NULL; + /* design.thread-manager.sol.thread.term.attempt */ + return res == ResOK; } -void ThreadRingResume(Ring threadRing) +void ThreadRingResume(Ring threadRing, Ring deadRing) { - mapThreadRing(threadRing, threadResume); + mapThreadRing(threadRing, deadRing, threadResume); } @@ -210,12 +227,12 @@ Thread ThreadRingThread(Ring threadRing) /* ThreadArena -- get the arena of a thread * - * Must be thread-safe. See . + * Must be thread-safe. See . */ Arena ThreadArena(Thread thread) { - /* Can't check thread as that would not be thread-safe. */ + AVER(TESTT(Thread, thread)); return thread->arena; } @@ -231,20 +248,16 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) self = pthread_self(); if(pthread_equal(self, thread->id)) { /* scan this thread's stack */ + AVER(thread->alive); res = StackScan(ss, stackBot); if(res != ResOK) return res; - } else { + } else if (thread->alive) { MutatorFaultContext mfc; Addr *stackBase, *stackLimit, stackPtr; mfc = thread->mfc; - if(mfc == NULL) { - /* .error.suspend */ - /* We assume that the thread must have been destroyed. */ - /* We ignore the situation by returning immediately. */ - return ResOK; - } + AVER(mfc != NULL); stackPtr = MutatorFaultContextSP(mfc); /* .stack.align */ @@ -272,14 +285,15 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) /* ThreadDescribe -- describe a thread */ -Res ThreadDescribe(Thread thread, mps_lib_FILE *stream) +Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) { Res res; - res = WriteF(stream, + res = WriteF(stream, depth, "Thread $P ($U) {\n", (WriteFP)thread, (WriteFU)thread->serial, " arena $P ($U)\n", (WriteFP)thread->arena, (WriteFU)thread->arena->serial, + " alive $S\n", WriteFYesNo(thread->alive), " id $U\n", (WriteFU)thread->id, "} Thread $P ($U)\n", (WriteFP)thread, (WriteFU)thread->serial, NULL); @@ -292,7 +306,7 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/thw3.c b/code/thw3.c index e1e5c97841..b8b8b10668 100644 --- a/code/thw3.c +++ b/code/thw3.c @@ -1,7 +1,7 @@ /* thw3i3.c: WIN32 THREAD MANAGER * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * Implements thread registration, suspension, and stack * scanning. See . @@ -60,7 +60,7 @@ Bool ThreadCheck(Thread thread) CHECKS(Thread, thread); CHECKU(Arena, thread->arena); CHECKL(thread->serial < thread->arena->threadSerial); - CHECKL(RingCheck(&thread->arenaRing)); + CHECKD_NOSIG(Ring, &thread->arenaRing); return TRUE; } @@ -109,6 +109,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) thread->serial = arena->threadSerial; ++arena->threadSerial; thread->arena = arena; + thread->alive = TRUE; AVERT(Thread, thread); @@ -138,60 +139,66 @@ void ThreadDeregister(Thread thread, Arena arena) } -/* Map over threads on ring calling f on each one except the - * current thread. +/* mapThreadRing -- map over threads on ring calling a function on + * each one except the current thread. + * + * Threads that are found to be dead (that is, if func returns FALSE) + * are moved to deadRing. */ -static void mapThreadRing(Ring ring, void (*f)(Thread thread)) + +static void mapThreadRing(Ring threadRing, Ring deadRing, Bool (*func)(Thread)) { - Ring node; + Ring node, next; DWORD id; - id = GetCurrentThreadId(); - node = RingNext(ring); - while(node != ring) { - Ring next = RingNext(node); - Thread thread; + AVERT(Ring, threadRing); + AVERT(Ring, deadRing); + AVER(FUNCHECK(func)); - thread = RING_ELT(Thread, arenaRing, node); + id = GetCurrentThreadId(); + RING_FOR(node, threadRing, next) { + Thread thread = RING_ELT(Thread, arenaRing, node); AVERT(Thread, thread); - if(id != thread->id) /* .thread.id */ - (*f)(thread); - - node = next; + AVER(thread->alive); + if (id != thread->id /* .thread.id */ + && !(*func)(thread)) + { + thread->alive = FALSE; + RingRemove(&thread->arenaRing); + RingAppend(deadRing, &thread->arenaRing); + } } } -static void suspend(Thread thread) +static Bool suspendThread(Thread thread) { /* .thread.handle.susp-res */ /* .error.suspend */ - /* In the error case (SuspendThread returning 0xFFFFFFFF), we */ - /* assume the thread has been destroyed (as part of process shutdown). */ - /* In which case we simply continue. */ + /* In the error case (SuspendThread returning -1), we */ + /* assume the thread has been terminated. */ /* [GetLastError appears to return 5 when SuspendThread is called */ - /* on a destroyed thread, but I'm not sufficiently confident of this */ + /* on a terminated thread, but I'm not sufficiently confident of this */ /* to check -- drj 1998-04-09] */ - (void)SuspendThread(thread->handle); + return SuspendThread(thread->handle) != (DWORD)-1; } -void ThreadRingSuspend(Ring ring) +void ThreadRingSuspend(Ring threadRing, Ring deadRing) { - mapThreadRing(ring, suspend); + mapThreadRing(threadRing, deadRing, suspendThread); } -static void resume(Thread thread) +static Bool resumeThread(Thread thread) { /* .thread.handle.susp-res */ /* .error.resume */ - /* In the error case (ResumeThread returning 0xFFFFFFFF), we */ - /* assume the thread has been destroyed (as part of process shutdown). */ - /* In which case we simply continue. */ - (void)ResumeThread(thread->handle); + /* In the error case (ResumeThread returning -1), we */ + /* assume the thread has been terminated. */ + return ResumeThread(thread->handle) != (DWORD)-1; } -void ThreadRingResume(Ring ring) +void ThreadRingResume(Ring threadRing, Ring deadRing) { - mapThreadRing(ring, resume); + mapThreadRing(threadRing, deadRing, resumeThread); } @@ -204,22 +211,23 @@ Thread ThreadRingThread(Ring threadRing) return thread; } -/* Must be thread-safe. See . */ +/* Must be thread-safe. See . */ + Arena ThreadArena(Thread thread) { - /* Can't AVER thread as that would not be thread-safe */ - /* AVERT(Thread, thread); */ + AVER(TESTT(Thread, thread)); return thread->arena; } -Res ThreadDescribe(Thread thread, mps_lib_FILE *stream) +Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) { Res res; - res = WriteF(stream, + res = WriteF(stream, depth, "Thread $P ($U) {\n", (WriteFP)thread, (WriteFU)thread->serial, " arena $P ($U)\n", (WriteFP)thread->arena, (WriteFU)thread->arena->serial, + " alive $S\n", WriteFYesNo(thread->alive), " handle $W\n", (WriteFW)thread->handle, " id $U\n", (WriteFU)thread->id, "} Thread $P ($U)\n", (WriteFP)thread, (WriteFU)thread->serial, @@ -233,7 +241,7 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/thw3.h b/code/thw3.h index 7e3cd68e2f..b19bfccadb 100644 --- a/code/thw3.h +++ b/code/thw3.h @@ -26,6 +26,7 @@ typedef struct mps_thr_s { /* Win32 thread structure */ Serial serial; /* from arena->threadSerial */ Arena arena; /* owning arena */ RingStruct arenaRing; /* threads attached to arena */ + Bool alive; /* thread believed to be alive? */ HANDLE handle; /* Handle of thread, see * */ DWORD id; /* Thread id of thread */ diff --git a/code/thw3i3.c b/code/thw3i3.c index 33424b6cf5..20e694ddc8 100644 --- a/code/thw3i3.c +++ b/code/thw3i3.c @@ -74,7 +74,13 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) id = GetCurrentThreadId(); - if(id != thread->id) { /* .thread.id */ + if (id == thread->id) { /* .thread.id */ + /* scan this thread's stack */ + AVER(thread->alive); + res = StackScan(ss, stackBot); + if(res != ResOK) + return res; + } else if (thread->alive) { CONTEXT context; BOOL success; Addr *stackBase, *stackLimit, stackPtr; @@ -116,11 +122,6 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) (Addr *)((char *)&context + sizeof(CONTEXT))); if(res != ResOK) return res; - - } else { /* scan this thread's stack */ - res = StackScan(ss, stackBot); - if(res != ResOK) - return res; } return ResOK; diff --git a/code/thw3i6.c b/code/thw3i6.c index 9b0eae55db..a13a031ec2 100644 --- a/code/thw3i6.c +++ b/code/thw3i6.c @@ -74,7 +74,13 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) id = GetCurrentThreadId(); - if(id != thread->id) { /* .thread.id */ + if (id == thread->id) { /* .thread.id */ + /* scan this thread's stack */ + AVER(thread->alive); + res = StackScan(ss, stackBot); + if(res != ResOK) + return res; + } else if (thread->alive) { CONTEXT context; BOOL success; Addr *stackBase, *stackLimit, stackPtr; @@ -116,11 +122,6 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) (Addr *)((char *)&context + sizeof(CONTEXT))); if(res != ResOK) return res; - - } else { /* scan this thread's stack */ - res = StackScan(ss, stackBot); - if(res != ResOK) - return res; } return ResOK; diff --git a/code/thxc.c b/code/thxc.c index 745669cfc4..6ecc1ea726 100644 --- a/code/thxc.c +++ b/code/thxc.c @@ -1,7 +1,7 @@ /* thxc.c: OS X MACH THREADS MANAGER * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .design: See . * @@ -36,6 +36,7 @@ typedef struct mps_thr_s { /* OS X / Mach thread structure */ Serial serial; /* from arena->threadSerial */ Arena arena; /* owning arena */ RingStruct arenaRing; /* attaches to arena */ + Bool alive; /* thread believed to be alive? */ thread_port_t port; /* thread kernel port */ } ThreadStruct; @@ -45,7 +46,8 @@ Bool ThreadCheck(Thread thread) CHECKS(Thread, thread); CHECKU(Arena, thread->arena); CHECKL(thread->serial < thread->arena->threadSerial); - CHECKL(RingCheck(&thread->arenaRing)); + CHECKD_NOSIG(Ring, &thread->arenaRing); + CHECKL(BoolCheck(thread->alive)); CHECKL(MACH_PORT_VALID(thread->port)); return TRUE; } @@ -69,7 +71,8 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) res = ControlAlloc(&p, arena, sizeof(ThreadStruct), /* withReservoirPermit */ FALSE); - if(res != ResOK) return res; + if (res != ResOK) + return res; thread = (Thread)p; thread->arena = arena; @@ -77,6 +80,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) thread->serial = arena->threadSerial; ++arena->threadSerial; + thread->alive = TRUE; thread->port = mach_thread_self(); thread->sig = ThreadSig; AVERT(Thread, thread); @@ -107,62 +111,80 @@ void ThreadDeregister(Thread thread, Arena arena) } -/* mapThreadRing -- map over threads on ring calling a function on each one - * except the current thread +/* mapThreadRing -- map over threads on ring calling a function on + * each one except the current thread. + * + * Threads that are found to be dead (that is, if func returns FALSE) + * are marked as dead and moved to deadRing, in order to implement + * design.thread-manager.sol.thread.term.attempt. */ -static void mapThreadRing(Ring threadRing, void (*func)(Thread)) +static void mapThreadRing(Ring threadRing, Ring deadRing, Bool (*func)(Thread)) { Ring node, next; mach_port_t self; AVERT(Ring, threadRing); + AVERT(Ring, deadRing); + AVER(FUNCHECK(func)); self = mach_thread_self(); AVER(MACH_PORT_VALID(self)); RING_FOR(node, threadRing, next) { Thread thread = RING_ELT(Thread, arenaRing, node); AVERT(Thread, thread); - if(thread->port != self) - (*func)(thread); + AVER(thread->alive); + if (thread->port != self + && !(*func)(thread)) + { + thread->alive = FALSE; + RingRemove(&thread->arenaRing); + RingAppend(deadRing, &thread->arenaRing); + } } } -static void threadSuspend(Thread thread) +static Bool threadSuspend(Thread thread) { kern_return_t kern_return; kern_return = thread_suspend(thread->port); /* No rendezvous is necessary: thread_suspend "prevents the thread * from executing any more user-level instructions" */ AVER(kern_return == KERN_SUCCESS); + /* Experimentally, values other then KERN_SUCCESS indicate the thread has + terminated . */ + /* design.thread-manager.sol.thread.term.attempt */ + return kern_return == KERN_SUCCESS; } -static void threadResume(Thread thread) +static Bool threadResume(Thread thread) { kern_return_t kern_return; kern_return = thread_resume(thread->port); /* Mach has no equivalent of EAGAIN. */ AVER(kern_return == KERN_SUCCESS); + /* Experimentally, values other then KERN_SUCCESS indicate the thread has + terminated . */ + /* design.thread-manager.sol.thread.term.attempt */ + return kern_return == KERN_SUCCESS; } /* ThreadRingSuspend -- suspend all threads on a ring, except the * current one. */ -void ThreadRingSuspend(Ring threadRing) +void ThreadRingSuspend(Ring threadRing, Ring deadRing) { - AVERT(Ring, threadRing); - mapThreadRing(threadRing, threadSuspend); + mapThreadRing(threadRing, deadRing, threadSuspend); } /* ThreadRingResume -- resume all threads on a ring, except the * current one. */ -void ThreadRingResume(Ring threadRing) +void ThreadRingResume(Ring threadRing, Ring deadRing) { - AVERT(Ring, threadRing); - mapThreadRing(threadRing, threadResume); + mapThreadRing(threadRing, deadRing, threadResume); } Thread ThreadRingThread(Ring threadRing) @@ -175,11 +197,11 @@ Thread ThreadRingThread(Ring threadRing) } -/* Must be thread-safe. See . */ +/* Must be thread-safe. See . */ + Arena ThreadArena(Thread thread) { - /* Can't AVER thread as that would not be thread-safe */ - /* AVERT(Thread, thread); */ + AVER(TESTT(Thread, thread)); return thread->arena; } @@ -198,17 +220,18 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) AVER(MACH_PORT_VALID(self)); if (thread->port == self) { /* scan this thread's stack */ + AVER(thread->alive); res = StackScan(ss, stackBot); if(res != ResOK) return res; - } else { + } else if (thread->alive) { MutatorFaultContextStruct mfcStruct; THREAD_STATE_S threadState; Addr *stackBase, *stackLimit, stackPtr; mach_msg_type_number_t count; kern_return_t kern_return; - /* Note: We could get the thread state and check the suspend cound in + /* Note: We could get the thread state and check the suspend count in order to assert that the thread is suspended, but it's probably unnecessary and is a lot of work to check a static condition. */ @@ -248,18 +271,20 @@ Res ThreadScan(ScanState ss, Thread thread, void *stackBot) } -Res ThreadDescribe(Thread thread, mps_lib_FILE *stream) +Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) { Res res; - res = WriteF(stream, + res = WriteF(stream, depth, "Thread $P ($U) {\n", (WriteFP)thread, (WriteFU)thread->serial, " arena $P ($U)\n", (WriteFP)thread->arena, (WriteFU)thread->arena->serial, + " alive $S\n", WriteFYesNo(thread->alive), " port $U\n", (WriteFU)thread->port, "} Thread $P ($U)\n", (WriteFP)thread, (WriteFU)thread->serial, NULL); - if(res != ResOK) return res; + if (res != ResOK) + return res; return ResOK; } @@ -267,7 +292,7 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/trace.c b/code/trace.c index e77f93cd48..dac2fcf6ed 100644 --- a/code/trace.c +++ b/code/trace.c @@ -1,7 +1,7 @@ /* trace.c: GENERIC TRACER IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. + * Copyright (c) 2001-2015 Ravenbrook Limited. * See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * @@ -9,6 +9,7 @@ #include "chain.h" #include "mpm.h" +#include "table.h" #include /* for LONG_MAX */ SRCID(trace, "$Id$"); @@ -64,9 +65,9 @@ void ScanStateInit(ScanState ss, TraceSet ts, Arena arena, TraceId ti; Trace trace; - AVER(TraceSetCheck(ts)); + AVERT(TraceSet, ts); AVERT(Arena, arena); - AVER(RankCheck(rank)); + AVERT(Rank, rank); /* white is arbitrary and can't be checked */ /* NOTE: We can only currently support scanning for a set of traces with @@ -75,10 +76,12 @@ void ScanStateInit(ScanState ss, TraceSet ts, Arena arena, in TraceFix. */ ss->fix = NULL; ss->fixClosure = NULL; + AVER(TraceSetIsSingle(ts)); TRACE_SET_ITER(ti, trace, ts, arena) { if (ss->fix == NULL) { ss->fix = trace->fix; ss->fixClosure = trace->fixClosure; + ss->whiteTable = trace->whiteTable; } else { AVER(ss->fix == trace->fix); AVER(ss->fixClosure == trace->fixClosure); @@ -233,7 +236,7 @@ Bool traceBandAdvance(Trace trace) ++trace->band; trace->firstStretch = TRUE; if(trace->band >= RankLIMIT) { - trace->band = RankAMBIG; + trace->band = RankMIN; return FALSE; } EVENT3(TraceBandAdvance, trace->arena, trace->ti, trace->band); @@ -344,6 +347,8 @@ Res TraceAddWhite(Trace trace, Seg seg) { Res res; Pool pool; + Addr addr; + Size grainSize; AVERT(Trace, trace); AVERT(Seg, seg); @@ -351,25 +356,41 @@ Res TraceAddWhite(Trace trace, Seg seg) pool = SegPool(seg); AVERT(Pool, pool); + grainSize = ArenaGrainSize(PoolArena(pool)); + + /* Add all the grains in the segment to the white table so that the + segment can be looked up fast in TraceFix. */ + for (addr = SegBase(seg); addr < SegLimit(seg); addr = AddrAdd(addr, grainSize)) { + res = TableDefine(trace->whiteTable, (Word)addr, seg); + AVER(res == ResOK); /* no error path to remove entries */ + if (res != ResOK) + goto failDefine; + } /* Give the pool the opportunity to turn the segment white. */ /* If it fails, unwind. */ res = PoolWhiten(pool, trace, seg); if(res != ResOK) - return res; + goto failWhiten; /* Add the segment to the approximation of the white set if the */ /* pool made it white. */ if(TraceSetIsMember(SegWhite(seg), trace)) { trace->white = ZoneSetUnion(trace->white, ZoneSetOfSeg(trace->arena, seg)); /* if the pool is a moving GC, then condemned objects may move */ - if(pool->class->attr & AttrMOVINGGC) { + if(PoolHasAttr(pool, AttrMOVINGGC)) { trace->mayMove = ZoneSetUnion(trace->mayMove, ZoneSetOfSeg(trace->arena, seg)); } } - + return ResOK; + +failWhiten: + /* FIXME: TableRemove(trace->whiteTable, key); */ +failDefine: + /* FIXME: Remove partial entries in table. */ + return res; } @@ -378,45 +399,79 @@ Res TraceAddWhite(Trace trace, Seg seg) * TraceCondemnZones is passed a trace in state TraceINIT, and a set of * objects to condemn. * - * @@@@ For efficiency, we ought to find the condemned set and the - * foundation in one search of the segment ring. This hasn't been done - * because some pools still use TraceAddWhite for the condemned set. + * TODO: For efficiency, we ought to find the condemned set and the + * foundation in one search of the segment ring. This hasn't been + * done because some pools still use TraceAddWhite for the condemned + * set. + * + * TODO: This function would be more efficient if there were a cheaper + * way to select the segments in a particular zone set. Perhaps using + * a union ZoneSet on the segment splay tree. See + * CBSZonedBlockStruct. * - * @@@@ This function would be more efficient if there were a cheaper - * way to select the segments in a particular zone set. */ + * TODO: This function would be more efficient if there were a way to + * select the subset of segments that are collectible. + */ + +typedef struct TraceCondemnZonesClosureStruct { + Trace trace; + ZoneSet condemnedSet; + Res res; + Bool haveWhiteSegs; +} TraceCondemnZonesClosureStruct, *TraceCondemnZonesClosure; + +static Bool traceCondemnZonesVisit(Seg seg, void *closureP, Size closureS) +{ + TraceCondemnZonesClosure tcz = closureP; + Trace trace = tcz->trace; + Arena arena = tcz->trace->arena; + ZoneSet condemnedSet = tcz->condemnedSet; + Bool haveWhiteSegs = FALSE; + + AVER(closureS == sizeof(*tcz)); + + /* Segment should be black now. */ + AVER(!TraceSetIsMember(SegGrey(seg), trace)); + AVER(!TraceSetIsMember(SegWhite(seg), trace)); + + /* A segment can only be white if it is GC-able. */ + /* This is indicated by the pool having the GC attribute */ + /* We only condemn segments that fall entirely within */ + /* the requested zone set. Otherwise, we would bloat the */ + /* foundation to no gain. Note that this doesn't exclude */ + /* any segments from which the condemned set was derived, */ + if(PoolHasAttr(SegPool(seg), AttrGC) + && ZoneSetSuper(condemnedSet, ZoneSetOfSeg(arena, seg))) + { + Res res = TraceAddWhite(trace, seg); + if(res != ResOK) { + tcz->res = res; + return FALSE; + } + haveWhiteSegs = TRUE; + } + + tcz->haveWhiteSegs = haveWhiteSegs; + return TRUE; +} Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet) { - Seg seg; - Arena arena; - Res res; + TraceCondemnZonesClosureStruct tczStruct; AVERT(Trace, trace); AVER(condemnedSet != ZoneSetEMPTY); AVER(trace->state == TraceINIT); AVER(trace->white == ZoneSetEMPTY); - arena = trace->arena; - - if(SegFirst(&seg, arena)) { - do { - /* Segment should be black now. */ - AVER(!TraceSetIsMember(SegGrey(seg), trace)); - AVER(!TraceSetIsMember(SegWhite(seg), trace)); - - /* A segment can only be white if it is GC-able. */ - /* This is indicated by the pool having the GC attribute */ - /* We only condemn segments that fall entirely within */ - /* the requested zone set. Otherwise, we would bloat the */ - /* foundation to no gain. Note that this doesn't exclude */ - /* any segments from which the condemned set was derived, */ - if((SegPool(seg)->class->attr & AttrGC) != 0 - && ZoneSetSuper(condemnedSet, ZoneSetOfSeg(arena, seg))) { - res = TraceAddWhite(trace, seg); - if(res != ResOK) - return res; - } - } while (SegNext(&seg, arena, seg)); + tczStruct.trace = trace; + tczStruct.condemnedSet = condemnedSet; + tczStruct.haveWhiteSegs = FALSE; + tczStruct.res = ResOK; + if (!SegTraverse(trace->arena, traceCondemnZonesVisit, &tczStruct, sizeof(tczStruct))) { + AVER(tczStruct.res != ResOK); + AVER(!tczStruct.haveWhiteSegs); /* See .whiten.fail. */ + return tczStruct.res; } EVENT3(TraceCondemnZones, trace, condemnedSet, trace->white); @@ -503,9 +558,9 @@ static Res rootFlip(Root root, void *p) AVERT(Root, root); AVER(p != NULL); - AVER(TraceSetCheck(rf->ts)); + AVERT(TraceSet, rf->ts); AVERT(Arena, rf->arena); - AVER(RankCheck(rf->rank)); + AVERT(Rank, rf->rank); AVER(RootRank(root) <= RankEXACT); /* see .root.rank */ @@ -580,7 +635,7 @@ static Res traceFlip(Trace trace) /* early, before the pool contents. @@@@ This isn't correct if there are */ /* higher ranking roots than data in pools. */ - for(rank = RankAMBIG; rank <= RankEXACT; ++rank) { + for(rank = RankMIN; rank <= RankEXACT; ++rank) { rfc.rank = rank; res = RootsIterate(ArenaGlobals(arena), rootFlip, (void *)&rfc); if (res != ResOK) @@ -600,7 +655,7 @@ static Res traceFlip(Trace trace) /* grey objects so that it can't obtain white pointers. This is */ /* achieved by read protecting all segments containing objects */ /* which are grey for any of the flipped traces. */ - for(rank = 0; rank < RankLIMIT; ++rank) + for(rank = RankMIN; rank < RankLIMIT; ++rank) RING_FOR(node, ArenaGreyRing(arena, rank), nextNode) { Seg seg = SegOfGreyRing(node); if(TraceSetInter(SegGrey(seg), arena->flippedTraces) == TraceSetEMPTY @@ -627,33 +682,6 @@ static Res traceFlip(Trace trace) return res; } -/* traceCopySizes -- preserve size information for later use - * - * A PoolGen's newSize is important information that we want to emit in - * a diagnostic message at TraceStart. In order to do that we must copy - * the information before Whiten changes it. This function does that. - */ - -static void traceCopySizes(Trace trace) -{ - Ring node, nextNode; - Index i; - Arena arena = trace->arena; - - RING_FOR(node, &arena->chainRing, nextNode) { - Chain chain = RING_ELT(Chain, chainRing, node); - - for(i = 0; i < chain->genCount; ++i) { - Ring n, nn; - GenDesc desc = &chain->gens[i]; - RING_FOR(n, &desc->locusRing, nn) { - PoolGen gen = RING_ELT(PoolGen, genRing, n); - gen->newSizeAtCreate = gen->newSize; - } - } - } - return; -} /* TraceCreate -- create a Trace object * @@ -670,10 +698,38 @@ static void traceCopySizes(Trace trace) * This code is written to be adaptable to allocating Trace objects * dynamically. */ +static void *whiteTableAlloc(void *closure, Size size) +{ + void *p; + Arena arena = (Arena)closure; + Res res = ControlAlloc(&p, arena, size, FALSE); + if (res != ResOK) + return NULL; + return p; +} + +static void whiteTableFree(void *closure, void *p, Size size) +{ + Arena arena = (Arena)closure; + ControlFree(arena, p, size); +} + +static void TraceCreatePoolGen(GenDesc gen) +{ + Ring n, nn; + RING_FOR(n, &gen->locusRing, nn) { + PoolGen pgen = RING_ELT(PoolGen, genRing, n); + EVENT11(TraceCreatePoolGen, gen, gen->capacity, gen->mortality, gen->zones, + pgen->pool, pgen->totalSize, pgen->freeSize, pgen->newSize, + pgen->oldSize, pgen->newDeferredSize, pgen->oldDeferredSize); + } +} + Res TraceCreate(Trace *traceReturn, Arena arena, int why) { TraceId ti; Trace trace; + Res res; AVER(traceReturn != NULL); AVERT(Arena, arena); @@ -688,13 +744,19 @@ Res TraceCreate(Trace *traceReturn, Arena arena, int why) trace = ArenaTrace(arena, ti); AVER(trace->sig == SigInvalid); /* */ + res = TableCreate(&trace->whiteTable, 4, + whiteTableAlloc, whiteTableFree, + arena, (Word)1, (Word)2); + if (res != ResOK) + return res; + trace->arena = arena; trace->why = why; trace->white = ZoneSetEMPTY; trace->mayMove = ZoneSetEMPTY; trace->ti = ti; trace->state = TraceINIT; - trace->band = RankAMBIG; /* Required to be the earliest rank. */ + trace->band = RankMIN; trace->fix = PoolFix; trace->fixClosure = NULL; trace->chain = NULL; @@ -740,7 +802,24 @@ Res TraceCreate(Trace *traceReturn, Arena arena, int why) /* .. _request.dylan.160098: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/160098 */ ShieldSuspend(arena); - traceCopySizes(trace); + STATISTIC_STAT ({ + /* Iterate over all chains, all GenDescs within a chain, and all + * PoolGens within a GenDesc. */ + Ring node; + Ring nextNode; + + RING_FOR(node, &arena->chainRing, nextNode) { + Chain chain = RING_ELT(Chain, chainRing, node); + Index i; + for (i = 0; i < chain->genCount; ++i) { + GenDesc gen = &chain->gens[i]; + TraceCreatePoolGen(gen); + } + } + + /* Now do topgen GenDesc, and all PoolGens within it. */ + TraceCreatePoolGen(&arena->topGen); + }); *traceReturn = trace; return ResOK; @@ -796,62 +875,70 @@ void TraceDestroy(Trace trace) (TraceStatReclaim, trace, trace->reclaimCount, trace->reclaimSize)); + TableDestroy(trace->whiteTable); + + EVENT1(TraceDestroy, trace); + trace->sig = SigInvalid; trace->arena->busyTraces = TraceSetDel(trace->arena->busyTraces, trace); trace->arena->flippedTraces = TraceSetDel(trace->arena->flippedTraces, trace); - EVENT1(TraceDestroy, trace); } /* traceReclaim -- reclaim the remaining objects white for this trace */ +static Bool traceReclaimVisit(Seg seg, void *closureP, Size closureS) +{ + Trace trace = closureP; + Pool pool; + + AVERT_CRITICAL(Trace, trace); + AVERT_CRITICAL(Seg, seg); + AVER_CRITICAL(closureS == sizeof(*trace)); + + /* There shouldn't be any grey stuff left for this trace. */ + AVER_CRITICAL(!TraceSetIsMember(SegGrey(seg), trace)); + + if (!TraceSetIsMember(SegWhite(seg), trace)) + return FALSE; + + pool = SegPool(seg); + AVER_CRITICAL(PoolHasAttr(pool, AttrGC)); + STATISTIC(++trace->reclaimCount); + + if (PoolReclaim(pool, trace, seg)) + return TRUE; + + /* If the segment still exists, it should no longer be white. */ + /* TODO: The code from the class-specific reclaim methods to + unwhiten the segment could in fact be moved here. */ + AVERT_CRITICAL(Seg, seg); + AVER_CRITICAL(!TraceSetIsMember(SegWhite(seg), trace)); + + return FALSE; +} + static void traceReclaim(Trace trace) { Arena arena; - Seg seg; Ring node, nextNode; + AVERT(Trace, trace); AVER(trace->state == TraceRECLAIM); - EVENT1(TraceReclaim, trace); arena = trace->arena; - if(SegFirst(&seg, arena)) { - Pool pool; - Ring next; - do { - Addr base = SegBase(seg); - pool = SegPool(seg); - next = RingNext(SegPoolRing(seg)); - - /* There shouldn't be any grey stuff left for this trace. */ - AVER_CRITICAL(!TraceSetIsMember(SegGrey(seg), trace)); - - if(TraceSetIsMember(SegWhite(seg), trace)) { - AVER_CRITICAL((pool->class->attr & AttrGC) != 0); - STATISTIC(++trace->reclaimCount); - PoolReclaim(pool, trace, seg); - - /* If the segment still exists, it should no longer be white. */ - /* Note that the seg returned by this SegOfAddr may not be */ - /* the same as the one above, but in that case it's new and */ - /* still shouldn't be white for this trace. */ - - /* The code from the class-specific reclaim methods to */ - /* unwhiten the segment could in fact be moved here. */ - { - Seg nonWhiteSeg = NULL; /* prevents compiler warning */ - AVER_CRITICAL(!(SegOfAddr(&nonWhiteSeg, arena, base) - && TraceSetIsMember(SegWhite(nonWhiteSeg), trace))); - UNUSED(nonWhiteSeg); /* */ - } - } - } while(SegNextOfRing(&seg, arena, pool, next)); - } + + EVENT1(TraceReclaim, trace); + + /* TODO: This isn't very nice, as it rebalances the segment splay + tree and destroys any optimisation discovered by splaying. */ + SegTraverseAndDelete(arena, traceReclaimVisit, trace, sizeof(*trace)); trace->state = TraceFINISHED; + arena = trace->arena; /* Call each pool's TraceEnd method -- do end-of-trace work */ - RING_FOR(node, &ArenaGlobals(arena)->poolRing, nextNode) { + RING_FOR(node, ArenaPoolRing(arena), nextNode) { Pool pool = RING_ELT(Pool, arenaRing, node); PoolTraceEnd(pool, trace); } @@ -901,7 +988,7 @@ Rank TraceRankForAccess(Arena arena, Seg seg) AVERT(Arena, arena); AVERT(Seg, seg); - band = RankAMBIG; /* initialize band to avoid warning */ + band = RankLIMIT; /* initialize with invalid rank */ ts = arena->flippedTraces; AVER(TraceSetIsSingle(ts)); TRACE_SET_ITER(ti, trace, ts, arena) @@ -988,7 +1075,7 @@ static Bool traceFindGrey(Seg *segReturn, Rank *rankReturn, Ring node, nextNode; AVER(segReturn != NULL); - AVER(TraceIdCheck(ti)); + AVERT(TraceId, ti); trace = ArenaTrace(arena, ti); @@ -1179,6 +1266,7 @@ void TraceSegAccess(Arena arena, Seg seg, AccessSet mode) AVERT(Arena, arena); AVERT(Seg, seg); + AVERT(AccessSet, mode); /* If it's a read access, then the segment must be grey for a trace */ /* which is flipped. */ @@ -1251,7 +1339,9 @@ mps_res_t _mps_fix2(mps_ss_t mps_ss, mps_addr_t *mps_ref_io) { ScanState ss = PARENT(ScanStateStruct, ss_s, mps_ss); Ref ref; - Tract tract; + void *value; + Word key; + Res res; /* Special AVER macros are used on the critical path. */ /* See */ @@ -1268,57 +1358,28 @@ mps_res_t _mps_fix2(mps_ss_t mps_ss, mps_addr_t *mps_ref_io) STATISTIC(++ss->fixRefCount); EVENT4(TraceFix, ss, mps_ref_io, ref, ss->rank); - TRACT_OF_ADDR(&tract, ss->arena, ref); - if(tract) { - if(TraceSetInter(TractWhite(tract), ss->traces) != TraceSetEMPTY) { - Seg seg; - if(TRACT_SEG(&seg, tract)) { - Res res; - Pool pool; - STATISTIC(++ss->segRefCount); - STATISTIC(++ss->whiteSegRefCount); - EVENT1(TraceFixSeg, seg); - EVENT0(TraceFixWhite); - pool = TractPool(tract); - res = (*ss->fix)(pool, ss, seg, &ref); - if(res != ResOK) { - /* PoolFixEmergency should never fail. */ - AVER_CRITICAL(ss->fix != PoolFixEmergency); - /* Fix protocol (de facto): if Fix fails, ref must be unchanged - * Justification for this restriction: - * A: it simplifies; - * B: it's reasonable (given what may cause Fix to fail); - * C: the code (here) already assumes this: it returns without - * updating ss->fixedSummary. RHSK 2007-03-21. - */ - AVER(ref == (Ref)*mps_ref_io); - return res; - } - } else { - /* Only tracts with segments ought to have been condemned. */ - /* SegOfAddr FALSE => a ref into a non-seg Tract (poolmv etc) */ - /* .notwhite: ...But it should NOT be white. - * [I assert this both from logic, and from inspection of the - * current condemn code. RHSK 2010-11-30] - */ - NOTREACHED; - } - } else { - /* Tract isn't white. Don't compute seg for non-statistical */ - /* variety. See */ - STATISTIC_STAT - ({ - Seg seg; - if(TRACT_SEG(&seg, tract)) { - ++ss->segRefCount; - EVENT1(TraceFixSeg, seg); - } - }); + key = (Word)AddrAlignDown(ref, ArenaGrainSize(ss->arena)); + if (TableLookup(&value, ss->whiteTable, key)) { + Seg seg = (Seg)value; + Pool pool = SegPool(seg); + STATISTIC(++ss->segRefCount); + STATISTIC(++ss->whiteSegRefCount); + EVENT1(TraceFixSeg, seg); + EVENT0(TraceFixWhite); + res = (*ss->fix)(pool, ss, seg, &ref); + if (res != ResOK) { + /* PoolFixEmergency must not fail. */ + AVER_CRITICAL(ss->fix != PoolFixEmergency); + /* Fix protocol (de facto): if Fix fails, ref must be unchanged + * Justification for this restriction: + * A: it simplifies; + * B: it's reasonable (given what may cause Fix to fail); + * C: the code (here) already assumes this: it returns without + * updating ss->fixedSummary. RHSK 2007-03-21. + */ + AVER_CRITICAL(ref == (Ref)*mps_ref_io); + return res; } - } else { - /* See */ - AVER(ss->rank < RankEXACT - || !ArenaIsReservedAddr(ss->arena, ref)); } /* See */ @@ -1376,10 +1437,10 @@ void TraceScanSingleRef(TraceSet ts, Rank rank, Arena arena, { Res res; - AVER(TraceSetCheck(ts)); - AVER(RankCheck(rank)); + AVERT(TraceSet, ts); + AVERT(Rank, rank); AVERT(Arena, arena); - AVER(SegCheck(seg)); + AVERT(Seg, seg); AVER(refIO != NULL); res = traceScanSingleRefRes(ts, rank, arena, seg, refIO); @@ -1415,7 +1476,8 @@ Res TraceScanArea(ScanState ss, Addr *base, Addr *limit) TRACE_SCAN_BEGIN(ss) { p = base; loop: - if(p >= limit) goto out; + if (p >= limit) + goto out; ref = *p++; if(!TRACE_FIX1(ss, ref)) goto loop; @@ -1462,6 +1524,7 @@ Res TraceScanAreaTagged(ScanState ss, Addr *base, Addr *limit) * This is as TraceScanArea except words are only fixed if they are zero * when masked with a mask. */ +ATTRIBUTE_NO_SANITIZE_ADDRESS Res TraceScanAreaMasked(ScanState ss, Addr *base, Addr *limit, Word mask) { Res res; @@ -1478,10 +1541,13 @@ Res TraceScanAreaMasked(ScanState ss, Addr *base, Addr *limit, Word mask) TRACE_SCAN_BEGIN(ss) { p = base; loop: - if(p >= limit) goto out; + if (p >= limit) + goto out; ref = *p++; - if(((Word)ref & mask) != 0) goto loop; - if(!TRACE_FIX1(ss, ref)) goto loop; + if (((Word)ref & mask) + != 0) goto loop; + if (!TRACE_FIX1(ss, ref)) + goto loop; res = TRACE_FIX2(ss, p-1); if(res == ResOK) goto loop; @@ -1500,21 +1566,31 @@ static Res traceCondemnAll(Trace trace) { Res res; Arena arena; - Ring chainNode, nextChainNode; + Ring poolNode, nextPoolNode, chainNode, nextChainNode; Bool haveWhiteSegs = FALSE; arena = trace->arena; AVERT(Arena, arena); - /* Condemn all the chains. */ - RING_FOR(chainNode, &arena->chainRing, nextChainNode) { - Chain chain = RING_ELT(Chain, chainRing, chainNode); - AVERT(Chain, chain); - res = ChainCondemnAll(chain, trace); - if(res != ResOK) - goto failBegin; - haveWhiteSegs = TRUE; + /* Condemn all segments in pools with the GC attribute. */ + RING_FOR(poolNode, &ArenaGlobals(arena)->poolRing, nextPoolNode) { + Pool pool = RING_ELT(Pool, arenaRing, poolNode); + AVERT(Pool, pool); + + if (PoolHasAttr(pool, AttrGC)) { + Ring segNode, nextSegNode; + RING_FOR(segNode, PoolSegRing(pool), nextSegNode) { + Seg seg = SegOfPoolRing(segNode); + AVERT(Seg, seg); + + res = TraceAddWhite(trace, seg); + if (res != ResOK) + goto failBegin; + haveWhiteSegs = TRUE; + } + } } + /* Notify all the chains. */ RING_FOR(chainNode, &arena->chainRing, nextChainNode) { Chain chain = RING_ELT(Chain, chainRing, chainNode); @@ -1524,7 +1600,14 @@ static Res traceCondemnAll(Trace trace) return ResOK; failBegin: - AVER(!haveWhiteSegs); /* Would leave white sets inconsistent. */ + /* .whiten.fail: If we successfully whitened one or more segments, + * but failed to whiten them all, then the white sets would now be + * inconsistent. This can't happen in practice (at time of writing) + * because all PoolWhiten methods always succeed. If we ever have a + * pool class that fails to whiten a segment, then this assertion + * will be triggered. In that case, we'll have to recover here by + * blackening the segments again. */ + AVER(!haveWhiteSegs); return res; } @@ -1538,9 +1621,9 @@ double TraceWorkFactor = 0.25; * * TraceStart should be passed a trace with state TraceINIT, i.e., * recently returned from TraceCreate, with some condemned segments - * added. mortality is the fraction of the condemned set expected to - * survive. finishingTime is relative to the current polling clock, see - * . + * added. mortality is the fraction of the condemned set expected not + * to survive. finishingTime is relative to the current polling clock, + * see . * * .start.black: All segments are black w.r.t. a newly allocated trace. * However, if TraceStart initialized segments to black when it @@ -1562,19 +1645,6 @@ static Res rootGrey(Root root, void *p) } -static void TraceStartPoolGen(Chain chain, GenDesc desc, Bool top, Index i) -{ - Ring n, nn; - RING_FOR(n, &desc->locusRing, nn) { - PoolGen gen = RING_ELT(PoolGen, genRing, n); - EVENT11(TraceStartPoolGen, chain, top, i, desc, - desc->capacity, desc->mortality, desc->zones, - gen->pool, gen->nr, gen->totalSize, - gen->newSizeAtCreate); - } -} - - /* TraceStart -- start a trace whose white set has been established * * The main job of TraceStart is to set up the grey list for a trace. The @@ -1585,11 +1655,45 @@ static void TraceStartPoolGen(Chain chain, GenDesc desc, Bool top, Index i) * grey-mutator tracing. */ +static Bool traceStartVisit(Seg seg, void *closureP, Size closureS) +{ + Trace trace = closureP; + Size size = SegSize(seg); + + AVER(closureS == sizeof(*trace)); + AVER(!TraceSetIsMember(SegGrey(seg), trace)); + + /* A segment can only be grey if it contains some references. */ + /* This is indicated by the rankSet begin non-empty. Such */ + /* segments may only belong to scannable pools. */ + if (SegRankSet(seg) != RankSetEMPTY) { + /* Turn the segment grey if there might be a reference in it */ + /* to the white set. This is done by seeing if the summary */ + /* of references in the segment intersects with the */ + /* approximation to the white set. */ + if (ZoneSetInter(SegSummary(seg), trace->white) != ZoneSetEMPTY) { + /* Note: can a white seg get greyed as well? At this point */ + /* we still assume it may. (This assumption runs out in */ + /* PoolTrivGrey). */ + PoolGrey(SegPool(seg), trace, seg); + if (TraceSetIsMember(SegGrey(seg), trace)) { + trace->foundation += size; + } + } + + if (PoolHasAttr(SegPool(seg), AttrGC) && + !TraceSetIsMember(SegWhite(seg), trace)) { + trace->notCondemned += size; + } + } + + return TRUE; +} + Res TraceStart(Trace trace, double mortality, double finishingTime) { Arena arena; Res res; - Seg seg; AVERT(Trace, trace); AVER(trace->state == TraceINIT); @@ -1601,64 +1705,9 @@ Res TraceStart(Trace trace, double mortality, double finishingTime) /* From the already set up white set, derive a grey set. */ - /* @@@@ Instead of iterating over all the segments, we could */ - /* iterate over all pools which are scannable and thence over */ - /* all their segments. This might be better if the minority */ - /* of segments are scannable. Perhaps we should choose */ - /* dynamically which method to use. */ - - if(SegFirst(&seg, arena)) { - do { - Size size = SegSize(seg); - AVER(!TraceSetIsMember(SegGrey(seg), trace)); - - /* A segment can only be grey if it contains some references. */ - /* This is indicated by the rankSet begin non-empty. Such */ - /* segments may only belong to scannable pools. */ - if(SegRankSet(seg) != RankSetEMPTY) { - /* Segments with ranks may only belong to scannable pools. */ - AVER((SegPool(seg)->class->attr & AttrSCAN) != 0); - - /* Turn the segment grey if there might be a reference in it */ - /* to the white set. This is done by seeing if the summary */ - /* of references in the segment intersects with the */ - /* approximation to the white set. */ - if(ZoneSetInter(SegSummary(seg), trace->white) != ZoneSetEMPTY) { - /* Note: can a white seg get greyed as well? At this point */ - /* we still assume it may. (This assumption runs out in */ - /* PoolTrivGrey). */ - PoolGrey(SegPool(seg), trace, seg); - if(TraceSetIsMember(SegGrey(seg), trace)) { - trace->foundation += size; - } - } - - if((SegPool(seg)->class->attr & AttrGC) - && !TraceSetIsMember(SegWhite(seg), trace)) { - trace->notCondemned += size; - } - } - } while (SegNext(&seg, arena, seg)); - } - - STATISTIC_BEGIN { - /* @@ */ - /* Iterate over all chains, all GenDescs within a chain, */ - /* (and all PoolGens within a GenDesc). */ - Ring node, nextNode; - Index i; - - RING_FOR(node, &arena->chainRing, nextNode) { - Chain chain = RING_ELT(Chain, chainRing, node); - for(i = 0; i < chain->genCount; ++i) { - GenDesc desc = &chain->gens[i]; - TraceStartPoolGen(chain, desc, FALSE, i); - } - } - - /* Now do topgen GenDesc (and all PoolGens within it). */ - TraceStartPoolGen(NULL, &arena->topGen, TRUE, 0); - } STATISTIC_END; + /* TODO: This might be more efficient if we could select all the + segments that are scannable (non-empty rank set). */ + SegTraverse(arena, traceStartVisit, trace, sizeof(*trace)); res = RootsIterate(ArenaGlobals(arena), rootGrey, (void *)trace); AVER(res == ResOK); @@ -1701,54 +1750,45 @@ Res TraceStart(Trace trace, double mortality, double finishingTime) } -/* traceWorkClock -- a measure of the work done for this trace - * - * .workclock: Segment and root scanning work is the regulator. */ - -#define traceWorkClock(trace) ((trace)->segScanSize + (trace)->rootScanSize) +/* TraceAdvance -- progress a trace by one step */ +void TraceAdvance(Trace trace) +{ + Arena arena; -/* TraceQuantum -- progresses a trace by one quantum */ + AVERT(Trace, trace); + arena = trace->arena; -void TraceQuantum(Trace trace) -{ - Size pollEnd; - Arena arena = trace->arena; + switch (trace->state) { + case TraceUNFLIPPED: + /* all traces are flipped in TraceStart at the moment */ + NOTREACHED; + break; + case TraceFLIPPED: { + Seg seg; + Rank rank; - pollEnd = traceWorkClock(trace) + trace->rate; - do { - switch(trace->state) { - case TraceUNFLIPPED: - /* all traces are flipped in TraceStart at the moment */ - NOTREACHED; - break; - case TraceFLIPPED: { - Seg seg; - Rank rank; - - if(traceFindGrey(&seg, &rank, arena, trace->ti)) { - Res res; - AVER((SegPool(seg)->class->attr & AttrSCAN) != 0); - res = traceScanSeg(TraceSetSingle(trace), rank, arena, seg); - /* Allocation failures should be handled by emergency mode, and we - don't expect any other error in a normal GC trace. */ - AVER(res == ResOK); - } else { - trace->state = TraceRECLAIM; - } - break; - } - case TraceRECLAIM: - traceReclaim(trace); - break; - default: - NOTREACHED; - break; + if (traceFindGrey(&seg, &rank, arena, trace->ti)) { + Res res; + res = traceScanSeg(TraceSetSingle(trace), rank, arena, seg); + /* Allocation failures should be handled by emergency mode, and we + * don't expect any other error in a normal GC trace. */ + AVER(res == ResOK); + } else { + trace->state = TraceRECLAIM; } - } while(trace->state != TraceFINISHED - && (ArenaEmergency(arena) || traceWorkClock(trace) < pollEnd)); + break; + } + case TraceRECLAIM: + traceReclaim(trace); + break; + default: + NOTREACHED; + break; + } } + /* TraceStartCollectAll: start a trace which condemns everything in * the arena. * @@ -1798,109 +1838,104 @@ Res TraceStartCollectAll(Trace *traceReturn, Arena arena, int why) } -/* TracePoll -- Check if there's any tracing work to be done */ +/* traceWorkClock -- a measure of the work done for this trace + * + * .workclock: Segment and root scanning work is the regulator. */ + +#define traceWorkClock(trace) ((trace)->segScanSize + (trace)->rootScanSize) + + +/* TracePoll -- Check if there's any tracing work to be done + * + * Consider starting a trace if none is running; advance the running + * trace (if any) by one quantum. Return a measure of the work done. + */ Size TracePoll(Globals globals) { Trace trace; - Res res; Arena arena; - Size scannedSize; + Size oldScannedSize, scannedSize, pollEnd; AVERT(Globals, globals); arena = GlobalsArena(globals); - scannedSize = (Size)0; - if(arena->busyTraces == TraceSetEMPTY) { - /* If no traces are going on, see if we need to start one. */ - Size sFoundation, sCondemned, sSurvivors, sConsTrace; - double tTracePerScan; /* tTrace/cScan */ - double dynamicDeferral; - - /* Compute dynamic criterion. See strategy.lisp-machine. */ - AVER(arena->topGen.mortality >= 0.0); - AVER(arena->topGen.mortality <= 1.0); - sFoundation = (Size)0; /* condemning everything, only roots @@@@ */ - /* @@@@ sCondemned should be scannable only */ - sCondemned = ArenaCommitted(arena) - ArenaSpareCommitted(arena); - sSurvivors = (Size)(sCondemned * (1 - arena->topGen.mortality)); - tTracePerScan = sFoundation + (sSurvivors * (1 + TraceCopyScanRATIO)); - AVER(TraceWorkFactor >= 0); - AVER(sSurvivors + tTracePerScan * TraceWorkFactor <= (double)SizeMAX); - sConsTrace = (Size)(sSurvivors + tTracePerScan * TraceWorkFactor); - dynamicDeferral = (double)ArenaAvail(arena) - (double)sConsTrace; - - if(dynamicDeferral < 0.0) { /* start full GC */ - res = TraceStartCollectAll(&trace, arena, TraceStartWhyDYNAMICCRITERION); - if(res != ResOK) - goto failStart; - scannedSize = traceWorkClock(trace); - } else { /* Find the nursery most over its capacity. */ - Ring node, nextNode; - double firstTime = 0.0; - Chain firstChain = NULL; - - RING_FOR(node, &arena->chainRing, nextNode) { - Chain chain = RING_ELT(Chain, chainRing, node); - double time; - - AVERT(Chain, chain); - time = ChainDeferral(chain); - if(time < firstTime) { - firstTime = time; firstChain = chain; - } - } + if (arena->busyTraces != TraceSetEMPTY) { + trace = ArenaTrace(arena, (TraceId)0); + } else { + /* No traces are running: consider starting one now. */ + if (!PolicyStartTrace(&trace, arena)) + return (Size)0; + } - /* If one was found, start collection on that chain. */ - if(firstTime < 0) { - double mortality; - - res = TraceCreate(&trace, arena, TraceStartWhyCHAIN_GEN0CAP); - AVER(res == ResOK); - res = ChainCondemnAuto(&mortality, firstChain, trace); - if(res != ResOK) /* should try some other trace, really @@@@ */ - goto failCondemn; - trace->chain = firstChain; - ChainStartGC(firstChain, trace); - res = TraceStart(trace, mortality, trace->condemned * TraceWorkFactor); - /* We don't expect normal GC traces to fail to start. */ - AVER(res == ResOK); - scannedSize = traceWorkClock(trace); - } - } /* (dynamicDeferral > 0.0) */ - } /* (arena->busyTraces == TraceSetEMPTY) */ + AVER(arena->busyTraces == TraceSetSingle(trace)); + oldScannedSize = traceWorkClock(trace); + pollEnd = oldScannedSize + trace->rate; + do { + TraceAdvance(trace); + } while (trace->state != TraceFINISHED + && (ArenaEmergency(arena) || traceWorkClock(trace) < pollEnd)); + scannedSize = traceWorkClock(trace) - oldScannedSize; + if (trace->state == TraceFINISHED) { + TraceDestroy(trace); + /* A trace finished, and hopefully reclaimed some memory, so clear any + * emergency. */ + ArenaSetEmergency(arena, FALSE); + } + return scannedSize; +} - /* If there is a trace, do one quantum of work. */ - if(arena->busyTraces != TraceSetEMPTY) { - Size oldScanned; - trace = ArenaTrace(arena, (TraceId)0); - AVER(arena->busyTraces == TraceSetSingle(trace)); - oldScanned = traceWorkClock(trace); - TraceQuantum(trace); - scannedSize = traceWorkClock(trace) - oldScanned; - if(trace->state == TraceFINISHED) { - TraceDestroy(trace); - /* A trace finished, and hopefully reclaimed some memory, so clear any - emergency. */ - ArenaSetEmergency(arena, FALSE); - } +/* TraceDescribe -- describe a trace */ + +Res TraceDescribe(Trace trace, mps_lib_FILE *stream, Count depth) +{ + Res res; + const char *state; + + if (!TESTT(Trace, trace)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + + switch (trace->state) { + case TraceINIT: state = "INIT"; break; + case TraceUNFLIPPED: state = "UNFLIPPED"; break; + case TraceFLIPPED: state = "FLIPPED"; break; + case TraceRECLAIM: state = "RECLAIM"; break; + case TraceFINISHED: state = "FINISHED"; break; + default: state = "unknown"; break; } - return scannedSize; -failCondemn: - TraceDestroy(trace); - /* This is an unlikely case, but clear the emergency flag so the next attempt - starts normally. */ - ArenaSetEmergency(arena, FALSE); -failStart: - return (Size)0; + res = WriteF(stream, depth, + "Trace $P ($U) {\n", (WriteFP)trace, (WriteFU)trace->ti, + " arena $P ($U)\n", (WriteFP)trace->arena, + (WriteFU)trace->arena->serial, + " why \"$S\"\n", (WriteFS)TraceStartWhyToString(trace->why), + " state $S\n", (WriteFS)state, + " band $U\n", (WriteFU)trace->band, + " white $B\n", (WriteFB)trace->white, + " mayMove $B\n", (WriteFB)trace->mayMove, + " chain $P\n", (WriteFP)trace->chain, + " condemned $U\n", (WriteFU)trace->condemned, + " notCondemned $U\n", (WriteFU)trace->notCondemned, + " foundation $U\n", (WriteFU)trace->foundation, + " rate $U\n", (WriteFU)trace->rate, + " rootScanSize $U\n", (WriteFU)trace->rootScanSize, + " rootCopiedSize $U\n", (WriteFU)trace->rootCopiedSize, + " segScanSize $U\n", (WriteFU)trace->segScanSize, + " segCopiedSize $U\n", (WriteFU)trace->segCopiedSize, + " forwardedSize $U\n", (WriteFU)trace->forwardedSize, + " preservedInPlaceSize $U\n", (WriteFU)trace->preservedInPlaceSize, + "} Trace $P\n", (WriteFP)trace, + NULL); + return res; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited + * Copyright (C) 2001-2015 Ravenbrook Limited * . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. diff --git a/code/traceanc.c b/code/traceanc.c index a383d71955..1ef2139381 100644 --- a/code/traceanc.c +++ b/code/traceanc.c @@ -1,7 +1,7 @@ /* traceanc.c: ANCILLARY SUPPORT FOR TRACER * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. + * Copyright (c) 2001-2014 Ravenbrook Limited. * See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * @@ -239,8 +239,8 @@ void TracePostStartMessage(Trace trace) traceStartWhyToTextBuffer(tsMessage->why, sizeof tsMessage->why, trace->why); - MessagePost(arena, TraceStartMessageMessage(tsMessage)); arena->tsMessage[ti] = NULL; + MessagePost(arena, TraceStartMessageMessage(tsMessage)); } else { arena->droppedMessages += 1; } @@ -406,8 +406,8 @@ void TracePostMessage(Trace trace) tMessage->condemnedSize = trace->condemned; tMessage->notCondemnedSize = trace->notCondemned; - MessagePost(arena, TraceMessageMessage(tMessage)); arena->tMessage[ti] = NULL; + MessagePost(arena, TraceMessageMessage(tMessage)); } else { arena->droppedMessages += 1; } @@ -479,10 +479,11 @@ Res TraceIdMessagesCreate(Arena arena, TraceId ti) traceStartMessageInit(arena, tsMessage); AVERT(TraceStartMessage, tsMessage); - arena->tsMessage[ti] = tsMessage; traceMessageInit(arena, tMessage); AVERT(TraceMessage, tMessage); + + arena->tsMessage[ti] = tsMessage; arena->tMessage[ti] = tMessage; AVER(TraceIdMessagesCheck(arena, ti)); @@ -559,11 +560,12 @@ void ArenaRelease(Globals globals) AVERT(Globals, globals); arenaForgetProtection(globals); globals->clamped = FALSE; - (void)TracePoll(globals); + ArenaPoll(globals); } -/* ArenaPark -- finish all current collections and clamp the arena */ +/* ArenaPark -- finish all current collections and clamp the arena, + * thus leaving the arena parked. */ void ArenaPark(Globals globals) { @@ -577,9 +579,9 @@ void ArenaPark(Globals globals) globals->clamped = TRUE; while(arena->busyTraces != TraceSetEMPTY) { - /* Poll active traces to make progress. */ + /* Advance all active traces. */ TRACE_SET_ITER(ti, trace, arena->busyTraces, arena) - TraceQuantum(trace); + TraceAdvance(trace); if(trace->state == TraceFINISHED) { TraceDestroy(trace); } @@ -615,7 +617,7 @@ Res ArenaStartCollect(Globals globals, int why) return res; } -/* ArenaCollect -- collect everything in arena; leave clamped */ +/* ArenaCollect -- collect everything in arena; leave parked */ Res ArenaCollect(Globals globals, int why) { @@ -672,7 +674,7 @@ static Res arenaRememberSummaryOne(Globals global, Addr base, RefSet summary) RememberedSummaryBlock newBlock; int res; - res = ControlAlloc(&p, arena, sizeof *newBlock, 0); + res = ControlAlloc(&p, arena, sizeof *newBlock, FALSE); if(res != ResOK) { return res; } @@ -698,44 +700,59 @@ static Res arenaRememberSummaryOne(Globals global, Addr base, RefSet summary) } /* ArenaExposeRemember -- park arena and then lift all protection - barriers. Parameter 'remember' specifies whether to remember the - protection state or not (for later restoration with - ArenaRestoreProtection). - */ -void ArenaExposeRemember(Globals globals, int remember) -{ - Seg seg; - Arena arena; + * + * Parameter 'remember' specifies whether to remember the protection + * state or not (for later restoration with ArenaRestoreProtection). + */ - AVERT(Globals, globals); +typedef struct ArenaExposeRememberClosureStruct { + Globals globals; + Bool remember; +} ArenaExposeRememberClosureStruct, *ArenaExposeRememberClosure; - ArenaPark(globals); +static Bool ArenaExposeRememberVisit(Seg seg, void *closureP, Size closureS) +{ + ArenaExposeRememberClosure aer = closureP; + Addr base = SegBase(seg); - arena = GlobalsArena(globals); - if(SegFirst(&seg, arena)) { - Addr base; + AVER(closureS == sizeof(*aer)); - do { - base = SegBase(seg); - if(IsSubclassPoly(ClassOfSeg(seg), GCSegClassGet())) { - if(remember) { - RefSet summary; - - summary = SegSummary(seg); - if(summary != RefSetUNIV) { - Res res = arenaRememberSummaryOne(globals, base, summary); - if(res != ResOK) { - /* If we got an error then stop trying to remember any - protections. */ - remember = 0; - } - } + UNUSED(closureP); + UNUSED(closureS); + + if (IsSubclassPoly(ClassOfSeg(seg), GCSegClassGet())) { + if (aer->remember) { + RefSet summary = SegSummary(seg); + if (summary != RefSetUNIV) { + Res res = arenaRememberSummaryOne(aer->globals, base, summary); + if (res != ResOK) { + /* If we got an error then stop trying to remember any + protections. */ + aer->remember = 0; } - SegSetSummary(seg, RefSetUNIV); - AVER(SegSM(seg) == AccessSetEMPTY); } - } while(SegNext(&seg, arena, seg)); + } + SegSetSummary(seg, RefSetUNIV); + AVER(SegSM(seg) == AccessSetEMPTY); } + + return TRUE; +} + +void ArenaExposeRemember(Globals globals, Bool remember) +{ + ArenaExposeRememberClosureStruct aerStruct; + + AVERT(Globals, globals); + AVERT(Bool, remember); + + ArenaPark(globals); + + aerStruct.globals = globals; + aerStruct.remember = remember; + + SegTraverse(GlobalsArena(globals), ArenaExposeRememberVisit, + &aerStruct, sizeof(aerStruct)); } void ArenaRestoreProtection(Globals globals) @@ -793,7 +810,7 @@ static void arenaForgetProtection(Globals globals) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited + * Copyright (C) 2001-2014 Ravenbrook Limited * . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. diff --git a/code/tract.c b/code/tract.c index 92e16f6fbc..25c5f7de2a 100644 --- a/code/tract.c +++ b/code/tract.c @@ -1,12 +1,28 @@ /* tract.c: PAGE TABLES * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .ullagepages: Pages whose page index is < allocBase are recorded as * free but never allocated as alloc starts searching after the tables. * TractOfAddr uses the fact that these pages are marked as free in order * to detect "references" to these pages as being bogus. + * + * .chunk.at.base: The chunks are stored in a balanced binary tree. + * Looking up an address in this tree is on the critical path, and + * therefore vital that it runs quickly. It is an implementation + * detail of chunks that they are always stored at the base of the + * region of address space they represent. Thus chunk happens to + * always be the same as chunk->base. We take advantage of this in the + * tree search by using chunk as its own key (instead of looking up + * chunk->base): this saves a dereference and perhaps a cache miss. + * See ChunkKey and ChunkCompare for this optimization. The necessary + * property is asserted in ChunkCheck. + * + * .chunk.at.base.no-coalesce: The fact that the chunk structure is + * stored at the base of the base also ensures that free address + * ranges in adjacent chunks are not coalesced by the arena's CBS. See + * */ #include "tract.h" @@ -17,9 +33,6 @@ SRCID(tract, "$Id$"); -static void ChunkDecache(Arena arena, Chunk chunk); - - /* TractArena -- get the arena of a tract */ #define TractArena(tract) PoolArena(TractPool(tract)) @@ -29,13 +42,9 @@ static void ChunkDecache(Arena arena, Chunk chunk); Bool TractCheck(Tract tract) { - CHECKU(Pool, TractPool(tract)); - CHECKL(AddrIsAligned(TractBase(tract), ArenaAlign(TractArena(tract)))); - if (TractHasSeg(tract)) { - CHECKL(TraceSetCheck(TractWhite(tract))); - CHECKU(Seg, (Seg)TractP(tract)); - } else { - CHECKL(TractWhite(tract) == TraceSetEMPTY); + if (TractHasPool(tract)) { + CHECKU(Pool, TractPool(tract)); + CHECKL(AddrIsArenaGrain(TractBase(tract), TractArena(tract))); } return TRUE; } @@ -51,8 +60,6 @@ void TractInit(Tract tract, Pool pool, Addr base) tract->pool.pool = pool; tract->base = base; tract->p = NULL; - tract->white = TraceSetEMPTY; - tract->hasSeg = FALSE; AVERT(Tract, tract); @@ -65,8 +72,6 @@ void TractFinish(Tract tract) { AVERT(Tract, tract); - /* Check that there's no segment - and hence no shielding. */ - AVER(!TractHasSeg(tract)); tract->pool.pool = NULL; } @@ -91,15 +96,13 @@ Addr (TractBase)(Tract tract) } -/* TractLimit -- return the limit address of a segment */ +/* TractLimit -- return the limit address of a tract */ -Addr TractLimit(Tract tract) +Addr TractLimit(Tract tract, Arena arena) { - Arena arena; AVERT_CRITICAL(Tract, tract); /* .tract.critical */ - arena = TractArena(tract); AVERT_CRITICAL(Arena, arena); - return AddrAdd(TractBase(tract), arena->alignment); + return AddrAdd(TractBase(tract), ArenaGrainSize(arena)); } @@ -113,22 +116,22 @@ Bool ChunkCheck(Chunk chunk) CHECKS(Chunk, chunk); CHECKU(Arena, chunk->arena); CHECKL(chunk->serial < chunk->arena->chunkSerial); - CHECKL(RingCheck(&chunk->chunkRing)); + /* Can't use CHECKD_NOSIG because TreeEMPTY is NULL. */ + CHECKL(TreeCheck(&chunk->chunkTree)); CHECKL(ChunkPagesToSize(chunk, 1) == ChunkPageSize(chunk)); CHECKL(ShiftCheck(ChunkPageShift(chunk))); CHECKL(chunk->base != (Addr)0); CHECKL(chunk->base < chunk->limit); - /* check chunk is in itself */ - CHECKL(chunk->base <= (Addr)chunk); + /* check chunk structure is at its own base: see .chunk.at.base. */ + CHECKL(chunk->base == (Addr)chunk); CHECKL((Addr)(chunk+1) <= chunk->limit); - CHECKL(ChunkSizeToPages(chunk, AddrOffset(chunk->base, chunk->limit)) - == chunk->pages); + CHECKL(ChunkSizeToPages(chunk, ChunkSize(chunk)) == chunk->pages); /* check that the tables fit in the chunk */ CHECKL(chunk->allocBase <= chunk->pages); CHECKL(chunk->allocBase >= chunk->pageTablePages); - CHECKL(chunk->allocTable != NULL); + CHECKD_NOSIG(BT, chunk->allocTable); /* check that allocTable is in the chunk overhead */ CHECKL((Addr)chunk->allocTable >= chunk->base); CHECKL(AddrAdd((Addr)chunk->allocTable, BTSize(chunk->pages)) @@ -154,8 +157,8 @@ Bool ChunkCheck(Chunk chunk) /* ChunkInit -- initialize generic part of chunk */ -Res ChunkInit(Chunk chunk, Arena arena, - Addr base, Addr limit, Align pageSize, BootBlock boot) +Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, Size reserved, + BootBlock boot) { Size size; Count pages; @@ -167,23 +170,21 @@ Res ChunkInit(Chunk chunk, Arena arena, /* chunk is supposed to be uninitialized, so don't check it. */ AVERT(Arena, arena); AVER(base != NULL); - AVER(AddrIsAligned(base, pageSize)); + AVER(AddrIsAligned(base, ArenaGrainSize(arena))); AVER(base < limit); - AVER(AddrIsAligned(limit, pageSize)); - AVERT(Align, pageSize); - AVER(pageSize > MPS_PF_ALIGN); + AVER(AddrIsAligned(limit, ArenaGrainSize(arena))); AVERT(BootBlock, boot); chunk->serial = (arena->chunkSerial)++; chunk->arena = arena; - RingInit(&chunk->chunkRing); - RingAppend(&arena->chunkRing, &chunk->chunkRing); + RingInit(&chunk->arenaRing); - chunk->pageSize = pageSize; - chunk->pageShift = pageShift = SizeLog2(pageSize); + chunk->pageSize = ArenaGrainSize(arena); + chunk->pageShift = pageShift = SizeLog2(chunk->pageSize); chunk->base = base; chunk->limit = limit; - size = AddrOffset(base, limit); + chunk->reserved = reserved; + size = ChunkSize(chunk); chunk->pages = pages = size >> pageShift; res = BootAlloc(&p, boot, (size_t)BTSize(pages), MPS_PF_ALIGN); @@ -191,7 +192,7 @@ Res ChunkInit(Chunk chunk, Arena arena, goto failAllocTable; chunk->allocTable = p; - pageTableSize = SizeAlignUp(pages * sizeof(PageUnion), pageSize); + pageTableSize = SizeAlignUp(pages * sizeof(PageUnion), chunk->pageSize); chunk->pageTablePages = pageTableSize >> pageShift; res = (arena->class->chunkInit)(chunk, boot); @@ -202,17 +203,35 @@ Res ChunkInit(Chunk chunk, Arena arena, /* Last thing we BootAlloc'd is pageTable. We requested pageSize */ /* alignment, and pageTableSize is itself pageSize aligned, so */ /* BootAllocated should also be pageSize aligned. */ - AVER(AddrIsAligned(BootAllocated(boot), pageSize)); + AVER(AddrIsAligned(BootAllocated(boot), chunk->pageSize)); chunk->allocBase = (Index)(BootAllocated(boot) >> pageShift); /* Init allocTable after class init, because it might be mapped there. */ BTResRange(chunk->allocTable, 0, pages); + /* Add the chunk's free address space to the arena's freeLand, so that + we can allocate from it. */ + if (arena->hasFreeLand) { + res = ArenaFreeLandInsert(arena, + PageIndexBase(chunk, chunk->allocBase), + chunk->limit); + if (res != ResOK) + goto failLandInsert; + } + + TreeInit(&chunk->chunkTree); + chunk->sig = ChunkSig; AVERT(Chunk, chunk); + + ArenaChunkInsert(arena, chunk); + return ResOK; - /* .no-clean: No clean-ups needed for boot, as we will discard the chunk. */ +failLandInsert: + (arena->class->chunkFinish)(chunk); + /* .no-clean: No clean-ups needed past this point for boot, as we will + discard the chunk. */ failClassInit: failAllocTable: return res; @@ -223,103 +242,65 @@ Res ChunkInit(Chunk chunk, Arena arena, void ChunkFinish(Chunk chunk) { + Arena arena; + AVERT(Chunk, chunk); + AVER(BTIsResRange(chunk->allocTable, 0, chunk->pages)); - ChunkDecache(chunk->arena, chunk); - chunk->sig = SigInvalid; - RingRemove(&chunk->chunkRing); - /* Finish all other fields before class finish, because they might be */ - /* unmapped there. */ - (chunk->arena->class->chunkFinish)(chunk); -} + arena = ChunkArena(chunk); + if (arena->hasFreeLand) + ArenaFreeLandDelete(arena, + PageIndexBase(chunk, chunk->allocBase), + chunk->limit); -/* Chunk Cache - * - * Functions for manipulating the chunk cache in the arena. - */ + ArenaChunkRemoved(arena, chunk); + chunk->sig = SigInvalid; -/* ChunkCacheEntryCheck -- check a chunk cache entry - * - * The cache is EITHER empty: - * - chunk is null; AND - * - base & limit are both null - * OR full: - * - chunk is non-null, points to a ChunkStruct; AND - * - base & limit are not both null; - * - * .chunk.empty.fields: Fields of an empty cache are nonetheless read, - * and must be correct. - */ + TreeFinish(&chunk->chunkTree); + RingRemove(&chunk->arenaRing); -Bool ChunkCacheEntryCheck(ChunkCacheEntry entry) -{ - CHECKS(ChunkCacheEntry, entry); - if (entry->chunk == NULL) { - CHECKL(entry->base == NULL); /* .chunk.empty.fields */ - CHECKL(entry->limit == NULL); /* .chunk.empty.fields */ - } else { - CHECKL(!(entry->base == NULL && entry->limit == NULL)); - CHECKD(Chunk, entry->chunk); - CHECKL(entry->base == entry->chunk->base); - CHECKL(entry->limit == entry->chunk->limit); - } - return TRUE; + /* Finish all other fields before class finish, because they might be */ + /* unmapped there. */ + (*arena->class->chunkFinish)(chunk); } -/* ChunkCacheEntryInit -- initialize a chunk cache entry */ +/* ChunkCompare -- Compare key to [base,limit) */ -void ChunkCacheEntryInit(ChunkCacheEntry entry) +Compare ChunkCompare(Tree tree, TreeKey key) { - entry->chunk = NULL; - entry->base = NULL; /* .chunk.empty.fields */ - entry->limit = NULL; /* .chunk.empty.fields */ - entry->sig = ChunkCacheEntrySig; - AVERT(ChunkCacheEntry, entry); - return; -} - + Addr base1, base2, limit2; + Chunk chunk; -/* ChunkEncache -- cache a chunk */ + AVERT_CRITICAL(Tree, tree); + AVER_CRITICAL(tree != TreeEMPTY); -static void ChunkEncache(Arena arena, Chunk chunk) -{ - /* [Critical path](../design/critical-path.txt); called by ChunkOfAddr */ - AVERT_CRITICAL(Arena, arena); + /* See .chunk.at.base. */ + chunk = ChunkOfTree(tree); AVERT_CRITICAL(Chunk, chunk); - AVER_CRITICAL(arena == chunk->arena); - AVERT_CRITICAL(ChunkCacheEntry, &arena->chunkCache); - - /* check chunk already in cache first */ - if (arena->chunkCache.chunk == chunk) { - return; - } - arena->chunkCache.chunk = chunk; - arena->chunkCache.base = chunk->base; - arena->chunkCache.limit = chunk->limit; + base1 = AddrOfTreeKey(key); + base2 = chunk->base; + limit2 = chunk->limit; - AVERT_CRITICAL(ChunkCacheEntry, &arena->chunkCache); - return; + if (base1 < base2) + return CompareLESS; + else if (base1 >= limit2) + return CompareGREATER; + else + return CompareEQUAL; } -/* ChunkDecache -- make sure a chunk is not in the cache */ +/* ChunkKey -- Return the key corresponding to a chunk */ -static void ChunkDecache(Arena arena, Chunk chunk) +TreeKey ChunkKey(Tree tree) { - AVERT(Arena, arena); - AVERT(Chunk, chunk); - AVER(arena == chunk->arena); - AVERT(ChunkCacheEntry, &arena->chunkCache); - if (arena->chunkCache.chunk == chunk) { - arena->chunkCache.chunk = NULL; - arena->chunkCache.base = NULL; /* .chunk.empty.fields */ - arena->chunkCache.limit = NULL; /* .chunk.empty.fields */ - } - AVERT(ChunkCacheEntry, &arena->chunkCache); + /* See .chunk.at.base. */ + Chunk chunk = ChunkOfTree(tree); + return TreeKeyOfAddrVar(chunk); } @@ -327,88 +308,58 @@ static void ChunkDecache(Arena arena, Chunk chunk) Bool ChunkOfAddr(Chunk *chunkReturn, Arena arena, Addr addr) { - Ring node, next; + Tree tree; AVER_CRITICAL(chunkReturn != NULL); AVERT_CRITICAL(Arena, arena); /* addr is arbitrary */ - /* check cache first; see also .chunk.empty.fields */ - AVERT_CRITICAL(ChunkCacheEntry, &arena->chunkCache); - if (arena->chunkCache.base <= addr && addr < arena->chunkCache.limit) { - *chunkReturn = arena->chunkCache.chunk; - AVER_CRITICAL(*chunkReturn != NULL); + if (TreeFind(&tree, ArenaChunkTree(arena), TreeKeyOfAddrVar(addr), + ChunkCompare) + == CompareEQUAL) + { + Chunk chunk = ChunkOfTree(tree); + AVER_CRITICAL(chunk->base <= addr); + AVER_CRITICAL(addr < chunk->limit); + *chunkReturn = chunk; return TRUE; } - RING_FOR(node, &arena->chunkRing, next) { - Chunk chunk = RING_ELT(Chunk, chunkRing, node); - if (chunk->base <= addr && addr < chunk->limit) { - /* Gotcha! */ - ChunkEncache(arena, chunk); - *chunkReturn = chunk; - return TRUE; - } - } return FALSE; } -/* ChunkOfNextAddr - * - * Finds the next higher chunk in memory which does _not_ contain addr. - * Returns FALSE if there is none. +/* IndexOfAddr -- return the index of the page containing an address * - * [The name is misleading; it should be "NextChunkAboveAddr" -- the - * word "Next" applies to chunks, not to addrs. RHSK 2010-03-20.] + * Function version of INDEX_OF_ADDR, for debugging purposes. */ -static Bool ChunkOfNextAddr(Chunk *chunkReturn, Arena arena, Addr addr) -{ - Addr leastBase; - Chunk leastChunk; - Ring node, next; - - leastBase = (Addr)(Word)-1; - leastChunk = NULL; - RING_FOR(node, &arena->chunkRing, next) { - Chunk chunk = RING_ELT(Chunk, chunkRing, node); - if (addr < chunk->base && chunk->base < leastBase) { - leastBase = chunk->base; - leastChunk = chunk; - } - } - if (leastChunk != NULL) { - *chunkReturn = leastChunk; - return TRUE; - } - return FALSE; -} - - -/* ArenaIsReservedAddr -- is address managed by this arena? */ - -Bool ArenaIsReservedAddr(Arena arena, Addr addr) +Index IndexOfAddr(Chunk chunk, Addr addr) { - Chunk dummy; - - AVERT(Arena, arena); + AVERT(Chunk, chunk); /* addr is arbitrary */ - return ChunkOfAddr(&dummy, arena, addr); + return INDEX_OF_ADDR(chunk, addr); } -/* IndexOfAddr -- return the index of the page containing an address - * - * Function version of INDEX_OF_ADDR, for debugging purposes. +/* ChunkNodeDescribe -- describe a single node in the tree of chunks, + * for SplayTreeDescribe */ -Index IndexOfAddr(Chunk chunk, Addr addr) +Res ChunkNodeDescribe(Tree node, mps_lib_FILE *stream) { - AVERT(Chunk, chunk); - /* addr is arbitrary */ + Chunk chunk; - return INDEX_OF_ADDR(chunk, addr); + if (!TreeCheck(node)) + return ResFAIL; + if (stream == NULL) + return ResFAIL; + chunk = ChunkOfTree(node); + if (!TESTT(Chunk, chunk)) + return ResFAIL; + + return WriteF(stream, 0, "[$P,$P)", (WriteFP)chunk->base, + (WriteFP)chunk->limit, NULL); } @@ -465,7 +416,7 @@ Tract TractOfBaseAddr(Arena arena, Addr addr) Bool found; AVERT_CRITICAL(Arena, arena); - AVER_CRITICAL(AddrIsAligned(addr, arena->alignment)); + AVER_CRITICAL(AddrIsAligned(addr, ArenaGrainSize(arena))); /* Check first in the cache, see . */ if (arena->lastTractBase == addr) { @@ -480,110 +431,6 @@ Tract TractOfBaseAddr(Arena arena, Addr addr) } -/* tractSearchInChunk -- search for a tract - * - * .tract-search: Searches for a tract in the chunk starting at page - * index i, return NULL if there is none. .tract-search.private: This - * function is private to this module and is used in the tract iteration - * protocol (TractFirst and TractNext). - */ - -static Bool tractSearchInChunk(Tract *tractReturn, Chunk chunk, Index i) -{ - AVER_CRITICAL(chunk->allocBase <= i); - AVER_CRITICAL(i <= chunk->pages); - - while (i < chunk->pages - && !(BTGet(chunk->allocTable, i) - && PageIsAllocated(ChunkPage(chunk, i)))) { - ++i; - } - if (i == chunk->pages) - return FALSE; - AVER(i < chunk->pages); - *tractReturn = PageTract(ChunkPage(chunk, i)); - return TRUE; -} - - -/* tractSearch - * - * Searches for the next tract in increasing address order. - * The tract returned is the next one along from addr (i.e., - * it has a base address bigger than addr and no other tract - * with a base address bigger than addr has a smaller base address). - * - * Returns FALSE if there is no tract to find (end of the arena). - */ - -static Bool tractSearch(Tract *tractReturn, Arena arena, Addr addr) -{ - Bool b; - Chunk chunk; - - b = ChunkOfAddr(&chunk, arena, addr); - if (b) { - Index i; - - i = INDEX_OF_ADDR(chunk, addr); - /* There are fewer pages than addresses, therefore the */ - /* page index can never wrap around */ - AVER_CRITICAL(i+1 != 0); - - if (tractSearchInChunk(tractReturn, chunk, i+1)) { - return TRUE; - } - } - while (ChunkOfNextAddr(&chunk, arena, addr)) { - /* If the ring was kept in address order, this could be improved. */ - addr = chunk->base; - /* Start from allocBase to skip the tables. */ - if (tractSearchInChunk(tractReturn, chunk, chunk->allocBase)) { - return TRUE; - } - } - return FALSE; -} - - -/* TractFirst -- return the first tract in the arena - * - * This is used to start an iteration over all tracts in the arena, not - * including the ones used for page tables and other arena structures. - */ - -Bool TractFirst(Tract *tractReturn, Arena arena) -{ - AVER(tractReturn != NULL); - AVERT(Arena, arena); - - /* .tractfirst.assume.nozero: We assume that there is no tract */ - /* with base address (Addr)0. Happily this assumption is sound */ - /* for a number of reasons. */ - return tractSearch(tractReturn, arena, (Addr)0); -} - - -/* TractNext -- return the "next" tract in the arena - * - * TractNext finds the tract with the lowest base address which is - * greater than a specified address. The address must be (or once - * have been) the base address of a tract. - * - * This is used as the iteration step when iterating over all - * tracts in the arena. - */ - -Bool TractNext(Tract *tractReturn, Arena arena, Addr addr) -{ - AVER_CRITICAL(tractReturn != NULL); /* .tract.critical */ - AVERT_CRITICAL(Arena, arena); - AVER_CRITICAL(AddrIsAligned(addr, arena->alignment)); - - return tractSearch(tractReturn, arena, addr); -} - - /* PageAlloc * * Sets up the page descriptor for an allocated page to turn it into a Tract. @@ -624,7 +471,6 @@ void PageInit(Chunk chunk, Index pi) PageSetPool(page, NULL); PageSetType(page, PageStateFREE); RingInit(PageSpareRing(page)); - RingInit(PageFreeRing(page)); } @@ -644,7 +490,7 @@ void PageFree(Chunk chunk, Index pi) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/tract.h b/code/tract.h index 24d8a2af86..cdd18c8f5d 100644 --- a/code/tract.h +++ b/code/tract.h @@ -1,7 +1,7 @@ /* tract.h: PAGE TABLE INTERFACE * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ @@ -9,8 +9,9 @@ #define tract_h #include "mpmtypes.h" -#include "ring.h" #include "bt.h" +#include "ring.h" +#include "tree.h" /* Page states @@ -37,52 +38,30 @@ typedef union PagePoolUnion { * * .tract: Tracts represent the grains of memory allocation from * the arena. See . - * - * .bool: The hasSeg field is a boolean, but can't be represented - * as type Bool. See . */ typedef struct TractStruct { /* Tract structure */ PagePoolUnion pool; /* MUST BE FIRST ( pool) */ void *p; /* pointer for use of owning pool */ Addr base; /* Base address of the tract */ - TraceSet white : TraceLIMIT; /* traces for which tract is white */ - unsigned hasSeg : 1; /* does tract have a seg in p? See .bool */ } TractStruct; extern Addr (TractBase)(Tract tract); #define TractBase(tract) ((tract)->base) -extern Addr TractLimit(Tract tract); +extern Addr TractLimit(Tract tract, Arena arena); +#define TractHasPool(tract) \ + ((tract)->pool.state == PageStateALLOC && TractPool(tract)) #define TractPool(tract) ((tract)->pool.pool) #define TractP(tract) ((tract)->p) #define TractSetP(tract, pp) ((void)((tract)->p = (pp))) -#define TractHasSeg(tract) ((Bool)(tract)->hasSeg) -#define TractSetHasSeg(tract, b) ((void)((tract)->hasSeg = (b))) -#define TractWhite(tract) ((tract)->white) -#define TractSetWhite(tract, w) ((void)((tract)->white = (w))) extern Bool TractCheck(Tract tract); extern void TractInit(Tract tract, Pool pool, Addr base); extern void TractFinish(Tract tract); -/* TRACT_*SEG -- Test / set / unset seg->tract associations - * - * These macros all multiply evaluate the tract parameter - */ - -#define TRACT_SEG(segReturn, tract) \ - (TractHasSeg(tract) && ((*(segReturn) = (Seg)TractP(tract)), TRUE)) - -#define TRACT_SET_SEG(tract, seg) \ - (TractSetHasSeg(tract, TRUE), TractSetP(tract, seg)) - -#define TRACT_UNSET_SEG(tract) \ - (TractSetHasSeg(tract, FALSE), TractSetP(tract, NULL)) - - /* PageUnion -- page descriptor * * .page-table: The page table (defined as a PageUnion array) @@ -96,7 +75,6 @@ extern void TractFinish(Tract tract); typedef struct PageSpareStruct { PagePoolUnion pool; /* spare tract, pool.state == PoolStateSPARE */ RingStruct spareRing; /* link in arena spare ring, LRU order */ - RingStruct freeRing; /* link in free cache ring, FIFO order */ } PageSpareStruct; typedef union PageUnion { /* page structure */ @@ -112,9 +90,7 @@ typedef union PageUnion { /* page structure */ #define PageIsAllocated(page) RVALUE(PagePool(page) != NULL) #define PageState(page) RVALUE((page)->pool.state) #define PageSpareRing(page) RVALUE(&(page)->spare.spareRing) -#define PageFreeRing(page) RVALUE(&(page)->spare.freeRing) #define PageOfSpareRing(node) PARENT(PageUnion, spare, RING_ELT(PageSpare, spareRing, node)) -#define PageOfFreeRing(node) PARENT(PageUnion, spare, RING_ELT(PageSpare, freeRing, node)) #define PageSetPool(page, _pool) \ BEGIN \ @@ -140,7 +116,8 @@ typedef struct ChunkStruct { Sig sig; /* */ Serial serial; /* serial within the arena */ Arena arena; /* parent arena */ - RingStruct chunkRing; /* ring of all chunks in arena */ + RingStruct arenaRing; /* node in ring of all chunks in arena */ + TreeStruct chunkTree; /* node in tree of all chunks in arena */ Size pageSize; /* size of pages */ Shift pageShift; /* log2 of page size, for shifts */ Addr base; /* base address of chunk */ @@ -150,35 +127,32 @@ typedef struct ChunkStruct { BT allocTable; /* page allocation table */ Page pageTable; /* the page table */ Count pageTablePages; /* number of pages occupied by page table */ + Size reserved; /* reserved address space for chunk (including overhead + such as losses due to alignment): must not change + (or arena reserved calculation will break) */ } ChunkStruct; #define ChunkArena(chunk) RVALUE((chunk)->arena) +#define ChunkSize(chunk) AddrOffset((chunk)->base, (chunk)->limit) #define ChunkPageSize(chunk) RVALUE((chunk)->pageSize) #define ChunkPageShift(chunk) RVALUE((chunk)->pageShift) #define ChunkPagesToSize(chunk, pages) ((Size)(pages) << (chunk)->pageShift) #define ChunkSizeToPages(chunk, size) ((Count)((size) >> (chunk)->pageShift)) #define ChunkPage(chunk, pi) (&(chunk)->pageTable[pi]) +#define ChunkOfTree(tree) PARENT(ChunkStruct, chunkTree, tree) +#define ChunkReserved(chunk) RVALUE((chunk)->reserved) extern Bool ChunkCheck(Chunk chunk); -extern Res ChunkInit(Chunk chunk, Arena arena, - Addr base, Addr limit, Align pageSize, BootBlock boot); +extern Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, + Size reserved, BootBlock boot); extern void ChunkFinish(Chunk chunk); - +extern Compare ChunkCompare(Tree tree, TreeKey key); +extern TreeKey ChunkKey(Tree tree); extern Bool ChunkCacheEntryCheck(ChunkCacheEntry entry); extern void ChunkCacheEntryInit(ChunkCacheEntry entry); - extern Bool ChunkOfAddr(Chunk *chunkReturn, Arena arena, Addr addr); - -/* CHUNK_OF_ADDR -- return the chunk containing an address - * - * arena and addr are evaluated multiple times. - */ - -#define CHUNK_OF_ADDR(chunkReturn, arena, addr) \ - (((arena)->chunkCache.base <= (addr) && (addr) < (arena)->chunkCache.limit) \ - ? (*(chunkReturn) = (arena)->chunkCache.chunk, TRUE) \ - : ChunkOfAddr(chunkReturn, arena, addr)) +extern Res ChunkNodeDescribe(Tree node, mps_lib_FILE *stream); /* AddrPageBase -- the base of the page this address is on */ @@ -192,25 +166,6 @@ extern Bool ChunkOfAddr(Chunk *chunkReturn, Arena arena, Addr addr); extern Tract TractOfBaseAddr(Arena arena, Addr addr); extern Bool TractOfAddr(Tract *tractReturn, Arena arena, Addr addr); -/* TRACT_OF_ADDR -- return the tract containing an address */ - -#define TRACT_OF_ADDR(tractReturn, arena, addr) \ - BEGIN \ - Arena _arena = (arena); \ - Addr _addr = (addr); \ - Chunk _chunk; \ - Index _i; \ - \ - if (CHUNK_OF_ADDR(&_chunk, _arena, _addr)) { \ - _i = INDEX_OF_ADDR(_chunk, _addr); \ - if (BTGet(_chunk->allocTable, _i)) \ - *(tractReturn) = PageTract(&_chunk->pageTable[_i]); \ - else \ - *(tractReturn) = NULL; \ - } else \ - *(tractReturn) = NULL; \ - END - /* INDEX_OF_ADDR -- return the index of the page containing an address * @@ -241,15 +196,12 @@ extern Index IndexOfAddr(Chunk chunk, Addr addr); Chunk _ch = NULL; \ \ UNUSED(_ch); \ - AVER(ChunkOfAddr(&_ch, arena, rangeBase) && (rangeLimit) <= _ch->limit); \ + AVER(ChunkOfAddr(&_ch, arena, rangeBase)); \ + AVER((rangeLimit) <= _ch->limit); \ END -extern Bool TractFirst(Tract *tractReturn, Arena arena); -extern Bool TractNext(Tract *tractReturn, Arena arena, Addr addr); - - -/* TRACT_TRACT_FOR -- iterate over a range of tracts +/* TRACT_TRACT_FOR -- iterate over a range of tracts in a chunk * * See . * Parameters arena & limit are evaluated multiple times. @@ -260,13 +212,13 @@ extern Bool TractNext(Tract *tractReturn, Arena arena, Addr addr); tract = (firstTract); addr = TractBase(tract); \ TractAverContiguousRange(arena, addr, limit); \ for(; tract != NULL; \ - (addr = AddrAdd(addr, (arena)->alignment)), \ + (addr = AddrAdd(addr, ArenaGrainSize(arena))), \ (addr < (limit) ? \ (tract = PageTract(PageOfTract(tract) + 1)) : \ (tract = NULL) /* terminate loop */)) -/* TRACT_FOR -- iterate over a range of tracts +/* TRACT_FOR -- iterate over a range of tracts in a chunk * * See . * Parameters arena & limit are evaluated multiple times. @@ -286,7 +238,7 @@ extern void PageFree(Chunk chunk, Index pi); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/tree.c b/code/tree.c new file mode 100644 index 0000000000..f87e8364ea --- /dev/null +++ b/code/tree.c @@ -0,0 +1,608 @@ +/* tree.c: BINARY TREE IMPLEMENTATION + * + * $Id$ + * Copyright (C) 2014-2015 Ravenbrook Limited. See end of file for license. + * + * Simple binary trees with utilities, for use as building blocks. + * Keep it simple, like Rings (see ring.h). + * + * The performance requirements on tree implementation will depend on + * how each individual function is applied in the MPS. + * + * .note.stack: It's important that the MPS have a bounded stack size, + * and this is a problem for tree algorithms. Basically, we have to + * avoid recursion. See design.mps.sp.sol.depth.no-recursion. + */ + +#include "tree.h" +#include "mpm.h" + +SRCID(tree, "$Id$"); + + +Bool TreeCheck(Tree tree) +{ + if (tree != TreeEMPTY) { + CHECKL(tree != NULL); + CHECKL(tree->left == TreeEMPTY || tree->left != NULL); + CHECKL(tree->right == TreeEMPTY || tree->right != NULL); + } + return TRUE; +} + +Bool TreeCheckLeaf(Tree tree) +{ + CHECKL(TreeCheck(tree)); + CHECKL(tree != TreeEMPTY); + CHECKL(tree->left == TreeEMPTY); + CHECKL(tree->right == TreeEMPTY); + return TRUE; +} + + +/* TreeDebugCount -- count and check order of tree + * + * This function may be called from a debugger or temporarily inserted + * during development to check a tree's integrity. It may not be called + * from the production MPS because it uses indefinite stack depth. + * See .note.stack. + */ + +static Count TreeDebugCountBetween(Tree node, + TreeCompareFunction compare, + TreeKeyFunction key, + TreeKey min, TreeKey max) +{ + if (node == TreeEMPTY) + return 0; + AVERT(Tree, node); + AVER(min == NULL || compare(node, min) != CompareGREATER); + AVER(max == NULL || compare(node, max) != CompareLESS); + return TreeDebugCountBetween(TreeLeft(node), compare, key, min, key(node)) + + 1 + + TreeDebugCountBetween(TreeRight(node), compare, key, key(node), max); +} + +Count TreeDebugCount(Tree tree, TreeCompareFunction compare, + TreeKeyFunction key) +{ + AVERT(Tree, tree); + return TreeDebugCountBetween(tree, compare, key, NULL, NULL); +} + + +/* TreeFind -- search for a node matching the key + * + * If a matching node is found, sets *treeReturn to that node and returns + * CompareEQUAL. Otherwise returns values useful for inserting a node with + * the key. If the tree is empty, returns CompareEQUAL and sets *treeReturn + * to NULL. Otherwise, sets *treeReturn to a potential parent for the new + * node and returns CompareLESS if the new node should be its left child, + * or CompareGREATER for its right. + */ + +Compare TreeFind(Tree *treeReturn, Tree root, TreeKey key, + TreeCompareFunction compare) +{ + Tree node, parent; + Compare cmp = CompareEQUAL; + + AVERT_CRITICAL(Tree, root); + AVER_CRITICAL(treeReturn != NULL); + AVER_CRITICAL(FUNCHECK(compare)); + /* key is arbitrary */ + + parent = NULL; + node = root; + while (node != TreeEMPTY) { + parent = node; + cmp = compare(node, key); + switch (cmp) { + case CompareLESS: + node = node->left; + break; + case CompareEQUAL: + *treeReturn = node; + return cmp; + case CompareGREATER: + node = node->right; + break; + default: + NOTREACHED; + *treeReturn = NULL; + return cmp; + } + } + + *treeReturn = parent; + return cmp; +} + + +/* TreeFindNext -- search for node containing key, or next node + * + * 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. + */ + +Bool TreeFindNext(Tree *treeReturn, Tree root, TreeKey key, + TreeCompareFunction compare) +{ + Tree node, best = NULL; + Bool result = FALSE; + + AVERT(Tree, root); + AVER(treeReturn != NULL); + AVER(FUNCHECK(compare)); + /* key is arbitrary */ + + node = root; + while (node != TreeEMPTY) { + Compare cmp = compare(node, key); + switch (cmp) { + case CompareLESS: + best = node; + result = TRUE; + node = node->left; + break; + case CompareEQUAL: + case CompareGREATER: + node = node->right; + break; + default: + NOTREACHED; + return FALSE; + } + } + + *treeReturn = best; + return result; +} + + +/* TreeInsert -- insert a node into a tree + * + * If the key doesn't exist in the tree, inserts a node as a leaf of the + * tree, returning the resulting tree in *treeReturn, and returns TRUE. + * Otherwise, *treeReturn points to the existing matching node, the tree + * is not modified, and returns FALSE. + */ + +Bool TreeInsert(Tree *treeReturn, Tree root, Tree node, + TreeKey key, TreeCompareFunction compare) +{ + Tree parent; + Compare cmp; + + AVER(treeReturn != NULL); + AVERT(Tree, root); + AVER(TreeCheckLeaf(node)); + AVER(FUNCHECK(compare)); + /* key is arbitrary */ + + cmp = TreeFind(&parent, root, key, compare); + switch (cmp) { + case CompareLESS: + parent->left = node; + break; + case CompareEQUAL: + if (parent != NULL) { + *treeReturn = parent; + return FALSE; + } + AVER(root == TreeEMPTY); + root = node; + break; + case CompareGREATER: + parent->right = node; + break; + default: + NOTREACHED; + *treeReturn = NULL; + return FALSE; + } + + *treeReturn = root; + return TRUE; +} + + +#if 0 /* This code is currently not in use in the MPS */ + +/* TreeTraverseMorris -- traverse tree inorder in constant space + * + * The tree may not be accessed or modified during the traversal, and + * the traversal must complete in order to repair the tree. + * + * The visitor should return FALSE to terminate the traversal early, + * in which case FALSE is returned. + * + * TreeTraverse is generally superior if comparisons are cheap, but + * TreeTraverseMorris does not require any comparison function. + * + * + * + * Joseph M. Morris (1979). "Traversing Binary Trees Simply and Cheaply". + * Information Processing Letters 9:5 pp. 197–200. + */ + +Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, + void *closureP, Size closureS) +{ + Tree node; + Bool visiting = TRUE; + + AVERT(Tree, tree); + AVER(FUNCHECK(visit)); + /* closureP, closureS arbitrary */ + + node = tree; + while (node != TreeEMPTY) { + if (node->left == TreeEMPTY) { + if (visiting) + visiting = visit(node, closureP, closureS); + node = node->right; + } else { + Tree pre = node->left; + for (;;) { + if (pre->right == TreeEMPTY) { + pre->right = node; + node = node->left; + break; + } + if (pre->right == node) { + pre->right = TreeEMPTY; + if (visiting) + visiting = visit(node, closureP, closureS); + else if (node == tree) + return FALSE; + node = node->right; + break; + } + pre = pre->right; + } + } + } + + return visiting; +} + +#endif /* not currently in use */ + + +/* TreeTraverse -- traverse tree in-order using pointer reversal + * + * The tree may not be accessed or modified during the traversal, and + * the traversal must complete in order to repair the tree. + * + * The visitor should return FALSE to terminate the traversal early, + * in which case FALSE is returned. + * + * TreeTraverseMorris is an alternative when no cheap comparison is available. + */ + +static Tree stepDownLeft(Tree node, Tree *parentIO) +{ + Tree parent = *parentIO; + Tree child = TreeLeft(node); + TreeSetLeft(node, parent); + *parentIO = node; + return child; +} + +static Tree stepDownRight(Tree node, Tree *parentIO) +{ + Tree parent = *parentIO; + Tree child = TreeRight(node); + TreeSetRight(node, parent); + *parentIO = node; + return child; +} + +static Tree stepUpRight(Tree node, Tree *parentIO) +{ + Tree parent = *parentIO; + Tree grandparent = TreeLeft(parent); + TreeSetLeft(parent, node); + *parentIO = grandparent; + return parent; +} + +static Tree stepUpLeft(Tree node, Tree *parentIO) +{ + Tree parent = *parentIO; + Tree grandparent = TreeRight(parent); + TreeSetRight(parent, node); + *parentIO = grandparent; + return parent; +} + +Bool TreeTraverse(Tree tree, + TreeCompareFunction compare, + TreeKeyFunction key, + TreeVisitor visit, void *closureP, Size closureS) +{ + Tree parent, node; + + AVERT(Tree, tree); + AVER(FUNCHECK(visit)); + /* closureP, closureS arbitrary */ + + parent = TreeEMPTY; + node = tree; + + if (node == TreeEMPTY) + return TRUE; + +down: + if (TreeHasLeft(node)) { + node = stepDownLeft(node, &parent); + AVER(compare(parent, key(node)) == CompareLESS); + goto down; + } + if (!visit(node, closureP, closureS)) + goto abort; + if (TreeHasRight(node)) { + node = stepDownRight(node, &parent); + AVER(compare(parent, key(node)) != CompareLESS); + goto down; + } + +up: + if (parent == TreeEMPTY) + return TRUE; + if (compare(parent, key(node)) != CompareLESS) { + node = stepUpLeft(node, &parent); + goto up; + } + node = stepUpRight(node, &parent); + if (!visit(node, closureP, closureS)) + goto abort; + if (!TreeHasRight(node)) + goto up; + node = stepDownRight(node, &parent); + goto down; + +abort: + if (parent == TreeEMPTY) + return FALSE; + if (compare(parent, key(node)) != CompareLESS) + node = stepUpLeft(node, &parent); + else + node = stepUpRight(node, &parent); + goto abort; +} + + +/* TreeRotateLeft -- Rotate right child edge of node + * + * Rotates node, right child of node, and left child of right + * child of node, leftwards in the order stated. Preserves tree + * ordering. + */ + +void TreeRotateLeft(Tree *treeIO) +{ + Tree tree, right; + + AVER(treeIO != NULL); + tree = *treeIO; + AVERT(Tree, tree); + right = TreeRight(tree); + AVERT(Tree, right); + + TreeSetRight(tree, TreeLeft(right)); + TreeSetLeft(right, tree); + + *treeIO = right; +} + + +/* TreeRotateRight -- Rotate left child edge of node + * + * Rotates node, left child of node, and right child of left + * child of node, leftwards in the order stated. Preserves tree + * ordering. + */ + +void TreeRotateRight(Tree *treeIO) { + Tree tree, left; + + AVER(treeIO != NULL); + tree = *treeIO; + AVERT(Tree, tree); + left = TreeLeft(tree); + AVERT(Tree, left); + + TreeSetLeft(*treeIO, TreeRight(left)); + TreeSetRight(left, *treeIO); + + *treeIO = left; +} + + +/* TreeReverseLeftSpine -- reverse the pointers on the right spine + * + * Descends the left spine of a tree, updating each node's left child + * to point to its parent instead. The root's left child is set to + * TreeEMPTY. Returns the leftmost child, or TreeEMPTY if the tree + * was empty. + */ + +Tree TreeReverseLeftSpine(Tree tree) +{ + Tree node, parent; + + AVERT(Tree, tree); + + parent = TreeEMPTY; + node = tree; + while (node != TreeEMPTY) { + Tree child = TreeLeft(node); + TreeSetLeft(node, parent); + parent = node; + node = child; + } + + return parent; +} + + +/* TreeReverseRightSpine -- reverse the pointers on the right spine + * + * Descends the right spine of a tree, updating each node's right child + * to point to its parent instead. The root's right child is set to + * TreeEMPTY. Returns the rightmost child or TreeEMPTY if the tree + * was empty. + */ + +Tree TreeReverseRightSpine(Tree tree) +{ + Tree node, parent; + + AVERT(Tree, tree); + + parent = TreeEMPTY; + node = tree; + while (node != TreeEMPTY) { + Tree child = TreeRight(node); + TreeSetRight(node, parent); + parent = node; + node = child; + } + + return parent; +} + + +/* TreeToVine -- unbalance a tree into a single right spine */ + +Count TreeToVine(Tree *link) +{ + Count count = 0; + + AVER(link != NULL); + AVERT(Tree, *link); + + while (*link != TreeEMPTY) { + while (TreeHasLeft(*link)) + TreeRotateRight(link); + link = &((*link)->right); + ++count; + } + + return count; +} + + +/* TreeBalance -- rebalance a tree + * + * Linear time, constant space rebalance. + * + * Quentin F. Stout and Bette L. Warren, + * "Tree Rebalancing in Optimal Time and Space", + * Communications of the ACM, Vol. 29, No. 9 (September 1986), p. 902-908 + */ + +void TreeBalance(Tree *treeIO) +{ + Count depth; + + AVER(treeIO != NULL); + AVERT(Tree, *treeIO); + + depth = TreeToVine(treeIO); + + if (depth > 2) { + Count n = depth - 1; + do { + Count m = n / 2, i; + Tree *link = treeIO; + for (i = 0; i < m; ++i) { + TreeRotateLeft(link); + link = &((*link)->right); + } + n = n - m - 1; + } while (n > 1); + } +} + + +/* TreeTraverseAndDelete -- traverse a tree while deleting nodes + * + * The visitor function must return TRUE to delete the current node, + * or FALSE to keep it. + * + * See . + */ +void TreeTraverseAndDelete(Tree *treeIO, TreeVisitor visitor, + void *closureP, Size closureS) +{ + Tree *treeref = treeIO; + + AVER(treeIO != NULL); + AVERT(Tree, *treeIO); + AVER(FUNCHECK(visitor)); + /* closureP and closureS are arbitrary */ + + TreeToVine(treeIO); + + while (*treeref != TreeEMPTY) { + Tree tree = *treeref; /* Current node. */ + Tree *nextref = &tree->right; /* Location of pointer to next node. */ + Tree next = *nextref; /* Next node. */ + if ((*visitor)(tree, closureP, closureS)) { + /* Delete current node. */ + *treeref = next; + } else { + /* Keep current node. */ + treeref = nextref; + } + } + TreeBalance(treeIO); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014-2015 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/tree.h b/code/tree.h new file mode 100644 index 0000000000..7b463067f0 --- /dev/null +++ b/code/tree.h @@ -0,0 +1,185 @@ +/* tree.h: BINARY TREE HEADER + * + * $Id$ + * Copyright (C) 2014 Ravenbrook Limited. See end of file for license. + * + * Simple binary trees with utilities, for use as building blocks. + * Keep it simple, like Rings (see ring.h). + */ + +#ifndef tree_h +#define tree_h + +#include "check.h" +#include "mpmtypes.h" + + +/* TreeStruct -- binary tree structure + * + * The tree structure is used in a field in other structures in order + * to link them together in a binary tree. + */ + +typedef struct TreeStruct *Tree; +typedef struct TreeStruct { + Tree left, right; +} TreeStruct; + +typedef Res (*TreeDescribeFunction)(Tree tree, mps_lib_FILE *stream); + + +/* TreeKeyFunction and TreeCompareFunction -- ordered binary trees + * + * Binary trees are almost always ordered, and these types provide the + * abstraction for ordering. A TreeCompareFunction method returns + * whether a key is less than, equal to, or greater than the key in a + * tree node. A TreeKeyFunction extracts a key from a node, depending + * on how TreeStruct is embedded within its parent structure. + */ + +typedef void *TreeKey; +typedef Compare (*TreeCompareFunction)(Tree tree, TreeKey key); +typedef TreeKey (*TreeKeyFunction)(Tree tree); + + +/* When storing Addrs in a tree, it is fastest to cast the Addr + * directly to a TreeKey. This assumes that Addr and TreeKey are + * compatible, possibly breaking . On an exotic + * platform where the types are not convertible, take the address of + * the variable in TreeKeyOfAddrVar, and dereference the address in + * AddrOfTreeKey. + */ +#define TreeKeyOfAddrVar(var) ((TreeKey)(var)) +#define AddrOfTreeKey(key) ((Addr)(key)) + + +/* TreeEMPTY -- the empty tree + * + * TreeEMPTY is the tree with no nodes, and hence unable to satisfy its + * olfactory senses. Empty trees should not be represented with NULL, + * as this is ambiguous. However, TreeEMPTY is in fact a null pointer for + * performance. To check whether you have it right, try temporarily + * defining TreeEMPTY to (Tree)1 or similar. + */ + +#define TreeEMPTY ((Tree)0) + + +extern Bool TreeCheck(Tree tree); +extern Bool TreeCheckLeaf(Tree tree); +extern Count TreeDebugCount(Tree tree, TreeCompareFunction compare, + TreeKeyFunction key); + +#define TreeInit(tree) \ + BEGIN \ + Tree _tree = (tree); \ + AVER(_tree != NULL); \ + _tree->left = TreeEMPTY; \ + _tree->right = TreeEMPTY; \ + AVERT(Tree, _tree); \ + END + +#define TreeFinish(tree) \ + BEGIN \ + Tree _tree = (tree); \ + AVERT(Tree, _tree); \ + END + +#define TREE_ELT(type, field, node) \ + PARENT(type ## Struct, field, node) + +#define TreeLeft(tree) RVALUE((tree)->left) + +#define TreeRight(tree) RVALUE((tree)->right) + +#define TreeSetLeft(tree, child) \ + BEGIN \ + (tree)->left = (child); \ + END + +#define TreeSetRight(tree, child) \ + BEGIN \ + (tree)->right = (child); \ + END + +#define TreeClearLeft(tree) \ + BEGIN \ + (tree)->left = TreeEMPTY; \ + END + +#define TreeClearRight(tree) \ + BEGIN \ + (tree)->right = TreeEMPTY; \ + END + +#define TreeHasLeft(tree) (TreeLeft(tree) != TreeEMPTY) +#define TreeHasRight(tree) (TreeRight(tree) != TreeEMPTY) + +extern Compare TreeFind(Tree *treeReturn, Tree root, + TreeKey key, TreeCompareFunction compare); +extern Bool TreeFindNext(Tree *treeReturn, Tree root, + TreeKey key, TreeCompareFunction compare); + +extern Bool TreeInsert(Tree *treeReturn, Tree root, Tree node, + TreeKey key, TreeCompareFunction compare); + +typedef Bool TreeVisitor(Tree tree, void *closureP, Size closureS); +extern Bool TreeTraverse(Tree tree, + TreeCompareFunction compare, + TreeKeyFunction key, + TreeVisitor visit, void *closureP, Size closureS); +extern Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, + void *closureP, Size closureS); + +extern void TreeRotateLeft(Tree *nodeIO); +extern void TreeRotateRight(Tree *nodeIO); +extern Tree TreeReverseLeftSpine(Tree tree); +extern Tree TreeReverseRightSpine(Tree tree); +extern Count TreeToVine(Tree *treeIO); +extern void TreeBalance(Tree *treeIO); + +extern void TreeTraverseAndDelete(Tree *treeIO, TreeVisitor visitor, + void *closureP, Size closureS); + +#endif /* tree_h */ + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/version.c b/code/version.c index f2de0e83a8..9a212dbbb7 100644 --- a/code/version.c +++ b/code/version.c @@ -22,14 +22,23 @@ SRCID(version, "$Id$"); /* MPS_RELEASE -- the release name * - * .release: When making a new release, change the expansion of - * MPS_RELEASE to be a string of the form "release/1.106.1" or - * whatever. + * .release.use: This macro is used (i) to prepare MPSVersionString + * (see below) and so identify any binary built using this source + * file; (ii) by the Sphinx documentation (see manual/source/conf.py) + * to identify the documentation; (iii) by the Autoconf script (see + * configure.ac) to identify the configure script. * - * (Note: before 2006-02-01 the style was "release.epcore.chub") + * .release.meaning: This names the next release that is expected to + * be built from these sources. + * + * .release.procedure: After making a version branch, update this + * string in the master sources to name the next version. After making + * a point release, update this string to name the next point release. + * + * .release.old: before 2006-02-01 the style was "release.epcore.chub". */ -#define MPS_RELEASE "release/1.113.0" +#define MPS_RELEASE "release/1.115.0" /* MPSCopyrightNotice -- copyright notice for the binary @@ -38,6 +47,7 @@ SRCID(version, "$Id$"); * (assuming we've made any substantial changes to the library this year). */ +extern char MPSCopyrightNotice[]; char MPSCopyrightNotice[] = "Portions copyright (c) 2010-2014 Ravenbrook Limited and Global Graphics Software."; @@ -50,6 +60,7 @@ char MPSCopyrightNotice[] = * see also guide.mps.version. */ +extern char MPSVersionString[]; char MPSVersionString[] = "@(#)Ravenbrook MPS, " "product." MPS_PROD_STRING ", " MPS_RELEASE ", platform." MPS_PF_STRING diff --git a/code/vm.c b/code/vm.c new file mode 100644 index 0000000000..867db9e74f --- /dev/null +++ b/code/vm.c @@ -0,0 +1,133 @@ +/* vm.c: VIRTUAL MEMORY IMPLEMENTATION + * + * $Id$ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * This is the portable part of the virtual memory implementation. + */ + +#include "mpm.h" +#include "vm.h" + +SRCID(vm, "$Id$"); + + +/* VMCheck -- check a VM structure */ + +Bool VMCheck(VM vm) +{ + CHECKS(VM, vm); + CHECKL(vm->base != (Addr)0); + CHECKL(vm->limit != (Addr)0); + CHECKL(vm->base < vm->limit); + CHECKL(ArenaGrainSizeCheck(vm->pageSize)); + CHECKL(AddrIsAligned(vm->base, vm->pageSize)); + CHECKL(AddrIsAligned(vm->limit, vm->pageSize)); + CHECKL(vm->block != NULL); + CHECKL((Addr)vm->block <= vm->base); + CHECKL(vm->mapped <= vm->reserved); + return TRUE; +} + + +/* VMPageSize -- return the page size cached in the VM */ + +Size (VMPageSize)(VM vm) +{ + AVERT(VM, vm); + + return VMPageSize(vm); +} + + +/* VMBase -- return the base address of the memory reserved */ + +Addr (VMBase)(VM vm) +{ + AVERT(VM, vm); + + return VMBase(vm); +} + + +/* VMLimit -- return the limit address of the memory reserved */ + +Addr (VMLimit)(VM vm) +{ + AVERT(VM, vm); + + return VMLimit(vm); +} + + +/* VMReserved -- return the amount of address space reserved */ + +Size (VMReserved)(VM vm) +{ + AVERT(VM, vm); + + return VMReserved(vm); +} + + +/* VMMapped -- return the amount of memory actually mapped */ + +Size (VMMapped)(VM vm) +{ + AVERT(VM, vm); + + return VMMapped(vm); +} + + +/* VMCopy -- copy VM descriptor */ + +void VMCopy(VM dest, VM src) +{ + AVER(dest != NULL); + AVERT(VM, src); + + (void)mps_lib_memcpy(dest, src, sizeof(VMStruct)); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/vm.h b/code/vm.h new file mode 100644 index 0000000000..992c318298 --- /dev/null +++ b/code/vm.h @@ -0,0 +1,95 @@ +/* vm.h: VIRTUAL MEMORY INTERFACE + * + * $Id$ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + */ + +#ifndef vm_h +#define vm_h + +#include "mpmtypes.h" + + +/* VMStruct -- virtual memory structure + * + * Unlike most other datatypes we permit this structure to be moved + * around in memory, and in particular, allocated temporarily on the + * stack, to help with bootstrapping. Look for uses of VMCopy. + */ + +#define VMSig ((Sig)0x519B3999) /* SIGnature VM */ + +typedef struct VMStruct { + Sig sig; /* */ + Size pageSize; /* operating system page size */ + void *block; /* unaligned base of mmap'd memory */ + Addr base, limit; /* aligned boundaries of reserved space */ + Size reserved; /* total reserved address space */ + Size mapped; /* total mapped memory */ +} VMStruct; + + +#define VMPageSize(vm) RVALUE((vm)->pageSize) +#define VMBase(vm) RVALUE((vm)->base) +#define VMLimit(vm) RVALUE((vm)->limit) +#define VMReserved(vm) RVALUE((vm)->reserved) +#define VMMapped(vm) RVALUE((vm)->mapped) + +extern Size PageSize(void); +extern Size (VMPageSize)(VM vm); +extern Bool VMCheck(VM vm); +extern Res VMParamFromArgs(void *params, size_t paramSize, ArgList args); +extern Res VMInit(VM vmReturn, Size size, Size grainSize, void *params); +extern void VMFinish(VM vm); +extern Addr (VMBase)(VM vm); +extern Addr (VMLimit)(VM vm); +extern Res VMMap(VM vm, Addr base, Addr limit); +extern void VMUnmap(VM vm, Addr base, Addr limit); +extern Size (VMReserved)(VM vm); +extern Size (VMMapped)(VM vm); +extern void VMCopy(VM dest, VM src); + + +#endif /* vm_h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/code/vman.c b/code/vman.c index db7795c9f2..87f057d688 100644 --- a/code/vman.c +++ b/code/vman.c @@ -1,54 +1,22 @@ /* vman.c: ANSI VM: MALLOC-BASED PSEUDO MEMORY MAPPING * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" +#include "vm.h" #include /* for malloc and free */ -#include /* for memset */ SRCID(vman, "$Id$"); -/* VMStruct -- virtual memory structure */ +/* PageSize -- return the page size */ -#define VMSig ((Sig)0x519B3999) /* SIGnature VM */ - -/* ANSI fake VM structure, see */ -typedef struct VMStruct { - Sig sig; /* */ - Addr base, limit; /* boundaries of malloc'd memory */ - void *block; /* pointer to malloc'd block, for free() */ - Size reserved; /* total reserved address space */ - Size mapped; /* total mapped memory */ -} VMStruct; - - -/* VMCheck -- check a VM structure */ - -Bool VMCheck(VM vm) +Size PageSize(void) { - CHECKS(VM, vm); - CHECKL(vm->base != (Addr)0); - CHECKL(vm->limit != (Addr)0); - CHECKL(vm->base < vm->limit); - CHECKL(AddrIsAligned(vm->base, VMANPageALIGNMENT)); - CHECKL(AddrIsAligned(vm->limit, VMANPageALIGNMENT)); - CHECKL(vm->block != NULL); - CHECKL((Addr)vm->block <= vm->base); - CHECKL(vm->mapped <= vm->reserved); - return TRUE; -} - - -/* VMAlign -- return the page size */ - -Align VMAlign(VM vm) -{ - UNUSED(vm); - return VMANPageALIGNMENT; + return VMAN_PAGE_SIZE; } @@ -61,109 +29,74 @@ Res VMParamFromArgs(void *params, size_t paramSize, ArgList args) } -/* VMCreate -- reserve some virtual address space, and create a VM structure */ +/* VMInit -- reserve some virtual address space, and create a VM structure */ -Res VMCreate(VM *vmReturn, Size size) +Res VMInit(VM vm, Size size, Size grainSize, void *params) { - VM vm; - - AVER(vmReturn != NULL); + void *vbase; + Size pageSize, reserved; - /* Note that because we add VMANPageALIGNMENT rather than */ - /* VMANPageALIGNMENT-1 we are not in danger of overflowing */ - /* vm->limit even if malloc were perverse enough to give us */ - /* a block at the end of memory. */ - size = SizeAlignUp(size, VMANPageALIGNMENT) + VMANPageALIGNMENT; - if ((size < VMANPageALIGNMENT) || (size > (Size)(size_t)-1)) - return ResRESOURCE; + AVER(vm != NULL); + AVERT(ArenaGrainSize, grainSize); + AVER(size > 0); + AVER(params != NULL); - vm = (VM)malloc(sizeof(VMStruct)); - if (vm == NULL) - return ResMEMORY; + pageSize = PageSize(); - vm->block = malloc((size_t)size); - if (vm->block == NULL) { - free(vm); - return ResMEMORY; - } + /* Grains must consist of whole pages. */ + AVER(grainSize % pageSize == 0); - vm->base = AddrAlignUp((Addr)vm->block, VMANPageALIGNMENT); - vm->limit = AddrAdd(vm->base, size - VMANPageALIGNMENT); - AVER(vm->limit < AddrAdd((Addr)vm->block, size)); + /* Check that the rounded-up sizes will fit in a Size. */ + size = SizeRoundUp(size, grainSize); + if (size < grainSize || size > (Size)(size_t)-1) + return ResRESOURCE; + /* Note that because we add a whole grainSize here (not grainSize - + * pageSize), we are not in danger of overflowing vm->limit even if + * malloc were perverse enough to give us a block at the end of + * memory. Compare vmix.c#.assume.not-last. */ + reserved = size + grainSize; + if (reserved < grainSize || reserved > (Size)(size_t)-1) + return ResRESOURCE; - memset((void *)vm->block, VMJunkBYTE, size); - - /* Lie about the reserved address space, to simulate real */ - /* virtual memory. */ - vm->reserved = size - VMANPageALIGNMENT; + vbase = malloc((size_t)reserved); + if (vbase == NULL) + return ResMEMORY; + (void)mps_lib_memset(vbase, VMJunkBYTE, reserved); + + vm->pageSize = pageSize; + vm->block = vbase; + vm->base = AddrAlignUp(vbase, grainSize); + vm->limit = AddrAdd(vm->base, size); + AVER(vm->base < vm->limit); /* can't overflow, as discussed above */ + AVER(vm->limit < AddrAdd((Addr)vm->block, reserved)); + vm->reserved = reserved; vm->mapped = (Size)0; vm->sig = VMSig; - AVERT(VM, vm); - EVENT3(VMCreate, vm, vm->base, vm->limit); - *vmReturn = vm; + EVENT3(VMInit, vm, VMBase(vm), VMLimit(vm)); return ResOK; } -/* VMDestroy -- destroy the VM structure */ +/* VMFinish -- release all address space and finish VM structure */ -void VMDestroy(VM vm) +void VMFinish(VM vm) { - /* All vm areas should have been unmapped. */ AVERT(VM, vm); - AVER(vm->mapped == (Size)0); - AVER(vm->reserved == AddrOffset(vm->base, vm->limit)); - - memset((void *)vm->base, VMJunkBYTE, AddrOffset(vm->base, vm->limit)); - free(vm->block); - - vm->sig = SigInvalid; - free(vm); - - EVENT1(VMDestroy, vm); -} - + /* Descriptor must not be stored inside its own VM at this point. */ + AVER(PointerAdd(vm, sizeof *vm) <= vm->block + || PointerAdd(vm->block, VMReserved(vm)) <= (Pointer)vm); + /* All address space must have been unmapped. */ + AVER(VMMapped(vm) == (Size)0); -/* VMBase -- return the base address of the memory reserved */ + EVENT1(VMFinish, vm); -Addr VMBase(VM vm) -{ - AVERT(VM, vm); - - return vm->base; -} - - -/* VMLimit -- return the limit address of the memory reserved */ - -Addr VMLimit(VM vm) -{ - AVERT(VM, vm); - - return vm->limit; -} - - -/* VMReserved -- return the amount of address space reserved */ - -Size VMReserved(VM vm) -{ - AVERT(VM, vm); - - return vm->reserved; -} - - -/* VMMapped -- return the amount of memory actually mapped */ - -Size VMMapped(VM vm) -{ - AVERT(VM, vm); + vm->sig = SigInvalid; - return vm->mapped; + (void)mps_lib_memset(vm->block, VMJunkBYTE, vm->reserved); + free(vm->block); } @@ -174,16 +107,17 @@ Res VMMap(VM vm, Addr base, Addr limit) Size size; AVER(base != (Addr)0); - AVER(vm->base <= base); + AVER(VMBase(vm) <= base); AVER(base < limit); - AVER(limit <= vm->limit); - AVER(AddrIsAligned(base, VMANPageALIGNMENT)); - AVER(AddrIsAligned(limit, VMANPageALIGNMENT)); + AVER(limit <= VMLimit(vm)); + AVER(AddrIsAligned(base, vm->pageSize)); + AVER(AddrIsAligned(limit, vm->pageSize)); size = AddrOffset(base, limit); - memset((void *)base, (int)0, size); + (void)mps_lib_memset((void *)base, VMJunkBYTE, size); vm->mapped += size; + AVER(VMMapped(vm) <= VMReserved(vm)); EVENT3(VMMap, vm, base, limit); return ResOK; @@ -197,16 +131,16 @@ void VMUnmap(VM vm, Addr base, Addr limit) Size size; AVER(base != (Addr)0); - AVER(vm->base <= base); + AVER(VMBase(vm) <= base); AVER(base < limit); - AVER(limit <= vm->limit); - AVER(AddrIsAligned(base, VMANPageALIGNMENT)); - AVER(AddrIsAligned(limit, VMANPageALIGNMENT)); + AVER(limit <= VMLimit(vm)); + AVER(AddrIsAligned(base, vm->pageSize)); + AVER(AddrIsAligned(limit, vm->pageSize)); size = AddrOffset(base, limit); - memset((void *)base, 0xCD, size); + AVER(VMMapped(vm) >= size); - AVER(vm->mapped >= size); + (void)mps_lib_memset((void *)base, VMJunkBYTE, size); vm->mapped -= size; EVENT3(VMUnmap, vm, base, limit); @@ -215,7 +149,7 @@ void VMUnmap(VM vm, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/vmix.c b/code/vmix.c index 01a0919b82..61e093a02e 100644 --- a/code/vmix.c +++ b/code/vmix.c @@ -1,7 +1,7 @@ /* vmix.c: VIRTUAL MEMORY MAPPING FOR UNIX (ISH) * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This is the implementation of the virtual memory mapping * interface (vm.h) for Unix-like operating systems. It was created @@ -24,7 +24,7 @@ * a definition of MAP_ANON requires a _BSD_SOURCE to be defined prior * to ; see config.h. * - * .assume.not-last: The implementation of VMCreate assumes that + * .assume.not-last: The implementation of VMInit assumes that * mmap() will not choose a region which contains the last page * in the address space, so that the limit of the mapped area * is representable. @@ -39,6 +39,7 @@ */ #include "mpm.h" +#include "vm.h" /* for mmap(2), munmap(2) */ #include @@ -58,40 +59,19 @@ SRCID(vmix, "$Id$"); -/* VMStruct -- virtual memory structure */ +/* PageSize -- return operating system page size */ -#define VMSig ((Sig)0x519B3999) /* SIGnature VM */ - -typedef struct VMStruct { - Sig sig; /* */ - Align align; /* page size */ - Addr base, limit; /* boundaries of reserved space */ - Size reserved; /* total reserved address space */ - Size mapped; /* total mapped memory */ -} VMStruct; - - -/* VMAlign -- return page size */ - -Align VMAlign(VM vm) +Size PageSize(void) { - return vm->align; -} + int pageSize; + /* Find out the operating system page size */ + pageSize = getpagesize(); -/* VMCheck -- check a VM */ + /* Check the page size will fit in a Size. */ + AVER((unsigned long)pageSize <= (unsigned long)(Size)-1); -Bool VMCheck(VM vm) -{ - CHECKS(VM, vm); - CHECKL(vm->base != 0); - CHECKL(vm->limit != 0); - CHECKL(vm->base < vm->limit); - CHECKL(vm->mapped <= vm->reserved); - CHECKL(SizeIsP2(vm->align)); - CHECKL(AddrIsAligned(vm->base, vm->align)); - CHECKL(AddrIsAligned(vm->limit, vm->align)); - return TRUE; + return (Size)pageSize; } @@ -104,141 +84,80 @@ Res VMParamFromArgs(void *params, size_t paramSize, ArgList args) } -/* VMCreate -- reserve some virtual address space, and create a VM structure */ +/* VMInit -- reserve some virtual address space, and create a VM structure */ -Res VMCreate(VM *vmReturn, Size size, void *params) +Res VMInit(VM vm, Size size, Size grainSize, void *params) { - Align align; - VM vm; - int pagesize; - void *addr; - Res res; + Size pageSize, reserved; + void *vbase; - AVER(vmReturn != NULL); + AVER(vm != NULL); + AVERT(ArenaGrainSize, grainSize); + AVER(size > 0); AVER(params != NULL); - /* Find out the page size from the OS */ - pagesize = getpagesize(); - /* check the actual returned pagesize will fit in an object of */ - /* type Align. */ - AVER(pagesize > 0); - AVER((unsigned long)pagesize <= (unsigned long)(Align)-1); - align = (Align)pagesize; - AVER(SizeIsP2(align)); - size = SizeAlignUp(size, align); - if((size == 0) || (size > (Size)(size_t)-1)) + pageSize = PageSize(); + + /* Grains must consist of whole pages. */ + AVER(grainSize % pageSize == 0); + + /* Check that the rounded-up sizes will fit in a Size. */ + size = SizeRoundUp(size, grainSize); + if (size < grainSize || size > (Size)(size_t)-1) + return ResRESOURCE; + reserved = size + grainSize - pageSize; + if (reserved < grainSize || reserved > (Size)(size_t)-1) return ResRESOURCE; - /* Map in a page to store the descriptor on. */ - addr = mmap(0, (size_t)SizeAlignUp(sizeof(VMStruct), align), - PROT_READ | PROT_WRITE, - MAP_ANON | MAP_PRIVATE, - -1, 0); + /* See .assume.not-last. */ + vbase = mmap(0, reserved, + PROT_NONE, MAP_ANON | MAP_PRIVATE, + -1, 0); /* On Darwin the MAP_FAILED return value is not documented, but does * work. MAP_FAILED _is_ documented by POSIX. */ - if(addr == MAP_FAILED) { - int e = errno; - AVER(e == ENOMEM); /* .assume.mmap.err */ - return ResMEMORY; - } - vm = (VM)addr; - - vm->align = align; - - /* See .assume.not-last. */ - addr = mmap(0, (size_t)size, - PROT_NONE, MAP_ANON | MAP_PRIVATE, - -1, 0); - if(addr == MAP_FAILED) { + if (vbase == MAP_FAILED) { int e = errno; AVER(e == ENOMEM); /* .assume.mmap.err */ - res = ResRESOURCE; - goto failReserve; + return ResRESOURCE; } - vm->base = (Addr)addr; + vm->pageSize = pageSize; + vm->block = vbase; + vm->base = AddrAlignUp(vbase, grainSize); vm->limit = AddrAdd(vm->base, size); - vm->reserved = size; - vm->mapped = (Size)0; + AVER(vm->base < vm->limit); /* .assume.not-last */ + AVER(vm->limit <= AddrAdd((Addr)vm->block, reserved)); + vm->reserved = reserved; + vm->mapped = 0; vm->sig = VMSig; - AVERT(VM, vm); - EVENT3(VMCreate, vm, vm->base, vm->limit); - - *vmReturn = vm; + EVENT3(VMInit, vm, VMBase(vm), VMLimit(vm)); return ResOK; - -failReserve: - (void)munmap((void *)vm, (size_t)SizeAlignUp(sizeof(VMStruct), align)); - return res; } -/* VMDestroy -- release all address space and destroy VM structure */ +/* VMFinish -- release all address space and finish VM structure */ -void VMDestroy(VM vm) +void VMFinish(VM vm) { int r; AVERT(VM, vm); - AVER(vm->mapped == (Size)0); + /* Descriptor must not be stored inside its own VM at this point. */ + AVER(PointerAdd(vm, sizeof *vm) <= vm->block + || PointerAdd(vm->block, VMReserved(vm)) <= (Pointer)vm); + /* All address space must have been unmapped. */ + AVER(VMMapped(vm) == (Size)0); + + EVENT1(VMFinish, vm); - /* This appears to be pretty pointless, since the descriptor */ - /* page is about to vanish completely. However, munmap might fail */ - /* for some reason, and this would ensure that it was still */ - /* discovered if sigs were being checked. */ vm->sig = SigInvalid; - r = munmap((void *)vm->base, (size_t)AddrOffset(vm->base, vm->limit)); + r = munmap(vm->block, vm->reserved); AVER(r == 0); - r = munmap((void *)vm, - (size_t)SizeAlignUp(sizeof(VMStruct), vm->align)); - AVER(r == 0); - - EVENT1(VMDestroy, vm); -} - - -/* VMBase -- return the base address of the memory reserved */ - -Addr VMBase(VM vm) -{ - AVERT(VM, vm); - - return vm->base; -} - - -/* VMLimit -- return the limit address of the memory reserved */ - -Addr VMLimit(VM vm) -{ - AVERT(VM, vm); - - return vm->limit; -} - - -/* VMReserved -- return the amount of memory reserved */ - -Size VMReserved(VM vm) -{ - AVERT(VM, vm); - - return vm->reserved; -} - - -/* VMMapped -- return the amount of memory actually mapped */ - -Size VMMapped(VM vm) -{ - AVERT(VM, vm); - - return vm->mapped; } @@ -251,10 +170,10 @@ Res VMMap(VM vm, Addr base, Addr limit) AVERT(VM, vm); AVER(sizeof(void *) == sizeof(Addr)); AVER(base < limit); - AVER(base >= vm->base); - AVER(limit <= vm->limit); - AVER(AddrIsAligned(base, vm->align)); - AVER(AddrIsAligned(limit, vm->align)); + AVER(base >= VMBase(vm)); + AVER(limit <= VMLimit(vm)); + AVER(AddrIsAligned(base, vm->pageSize)); + AVER(AddrIsAligned(limit, vm->pageSize)); size = AddrOffset(base, limit); @@ -268,6 +187,7 @@ Res VMMap(VM vm, Addr base, Addr limit) } vm->mapped += size; + AVER(VMMapped(vm) <= VMReserved(vm)); EVENT3(VMMap, vm, base, limit); return ResOK; @@ -283,12 +203,13 @@ void VMUnmap(VM vm, Addr base, Addr limit) AVERT(VM, vm); AVER(base < limit); - AVER(base >= vm->base); - AVER(limit <= vm->limit); - AVER(AddrIsAligned(base, vm->align)); - AVER(AddrIsAligned(limit, vm->align)); + AVER(base >= VMBase(vm)); + AVER(limit <= VMLimit(vm)); + AVER(AddrIsAligned(base, vm->pageSize)); + AVER(AddrIsAligned(limit, vm->pageSize)); size = AddrOffset(base, limit); + AVER(size <= VMMapped(vm)); /* see */ addr = mmap((void *)base, (size_t)size, @@ -304,7 +225,7 @@ void VMUnmap(VM vm, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/vmw3.c b/code/vmw3.c index e82f14ccd5..a6cfc7df6c 100644 --- a/code/vmw3.c +++ b/code/vmw3.c @@ -1,7 +1,7 @@ /* vmw3.c: VIRTUAL MEMORY MAPPING FOR WIN32 * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .design: See . * @@ -39,54 +39,30 @@ */ #include "mpm.h" +#include "vm.h" #ifndef MPS_OS_W3 #error "vmw3.c is Win32 specific, but MPS_OS_W3 is not set" #endif -#ifdef VM_RM -#error "vmw3.c compiled with VM_RM set" -#endif #include "mpswin.h" SRCID(vmw3, "$Id$"); -/* VMStruct -- virtual memory structure */ - -#define VMSig ((Sig)0x519B3999) /* SIGnature VM */ - -typedef struct VMStruct { - Sig sig; /* */ - Align align; /* page size */ - Addr base, limit; /* boundaries of reserved space */ - Size reserved; /* total reserved address space */ - Size mapped; /* total mapped memory */ -} VMStruct; - +/* PageSize -- return the operating system page size */ -/* VMAlign -- return the page size */ - -Align VMAlign(VM vm) +Size PageSize(void) { - AVERT(VM, vm); - - return vm->align; -} + SYSTEM_INFO si; + /* Find out the page size from the OS */ + GetSystemInfo(&si); -/* VMCheck -- check a VM structure */ + /* Check the page size will fit in a Size. */ + AVER(si.dwPageSize <= (Size)(SIZE_T)-1); -Bool VMCheck(VM vm) -{ - CHECKS(VM, vm); - CHECKL(vm->base != 0); - CHECKL(vm->limit != 0); - CHECKL(vm->base < vm->limit); - CHECKL(vm->mapped <= vm->reserved); - CHECKL(AddrIsAligned(vm->base, vm->align)); - CHECKL(AddrIsAligned(vm->limit, vm->align)); - return TRUE; + return (Size)si.dwPageSize; } @@ -114,134 +90,83 @@ Res VMParamFromArgs(void *params, size_t paramSize, ArgList args) } -/* VMCreate -- reserve some virtual address space, and create a VM structure */ +/* VMInit -- reserve some virtual address space, and create a VM structure */ -Res VMCreate(VM *vmReturn, Size size, void *params) +Res VMInit(VM vm, Size size, Size grainSize, void *params) { LPVOID vbase; - SYSTEM_INFO si; - Align align; - VM vm; - Res res; - BOOL b; + Size pageSize, reserved; VMParams vmParams = params; - AVER(vmReturn != NULL); + AVER(vm != NULL); + AVERT(ArenaGrainSize, grainSize); + AVER(size > 0); AVER(params != NULL); /* FIXME: Should have full AVERT? */ AVER(COMPATTYPE(LPVOID, Addr)); /* .assume.lpvoid-addr */ AVER(COMPATTYPE(SIZE_T, Size)); - GetSystemInfo(&si); - align = (Align)si.dwPageSize; - AVER((DWORD)align == si.dwPageSize); /* check it didn't truncate */ - AVER(SizeIsP2(align)); /* see .assume.sysalign */ - size = SizeAlignUp(size, align); - if ((size == 0) || (size > (Size)(SIZE_T)-1)) - return ResRESOURCE; + pageSize = PageSize(); - /* Allocate the vm descriptor. This is likely to be wasteful. */ - vbase = VirtualAlloc(NULL, SizeAlignUp(sizeof(VMStruct), align), - MEM_COMMIT, PAGE_READWRITE); - if (vbase == NULL) - return ResMEMORY; - vm = (VM)vbase; + /* Grains must consist of whole pages. */ + AVER(grainSize % pageSize == 0); + + /* Check that the rounded-up sizes will fit in a Size. */ + size = SizeRoundUp(size, grainSize); + if (size < grainSize || size > (Size)(SIZE_T)-1) + return ResRESOURCE; + reserved = size + grainSize - pageSize; + if (reserved < grainSize || reserved > (Size)(SIZE_T)-1) + return ResRESOURCE; /* Allocate the address space. */ vbase = VirtualAlloc(NULL, - size, + reserved, vmParams->topDown ? MEM_RESERVE | MEM_TOP_DOWN : MEM_RESERVE, PAGE_NOACCESS); - if (vbase == NULL) { - res = ResRESOURCE; - goto failReserve; - } + if (vbase == NULL) + return ResRESOURCE; - AVER(AddrIsAligned(vbase, align)); + AVER(AddrIsAligned(vbase, pageSize)); - vm->align = align; - vm->base = (Addr)vbase; - vm->limit = AddrAdd(vbase, size); - vm->reserved = size; - vm->mapped = 0; + vm->pageSize = pageSize; + vm->block = vbase; + vm->base = AddrAlignUp(vbase, grainSize); + vm->limit = AddrAdd(vm->base, size); AVER(vm->base < vm->limit); /* .assume.not-last */ + AVER(vm->limit <= AddrAdd((Addr)vm->block, reserved)); + vm->reserved = reserved; + vm->mapped = 0; vm->sig = VMSig; AVERT(VM, vm); - EVENT3(VMCreate, vm, vm->base, vm->limit); - *vmReturn = vm; + EVENT3(VMInit, vm, VMBase(vm), VMLimit(vm)); return ResOK; - -failReserve: - b = VirtualFree((LPVOID)vm, (SIZE_T)0, MEM_RELEASE); - AVER(b != 0); - return res; } -/* VMDestroy -- destroy the VM structure */ +/* VMFinish -- release address space and finish the VM structure */ -void VMDestroy(VM vm) +void VMFinish(VM vm) { BOOL b; AVERT(VM, vm); - AVER(vm->mapped == 0); + /* Descriptor must not be stored inside its own VM at this point. */ + AVER(PointerAdd(vm, sizeof *vm) <= vm->block + || PointerAdd(vm->block, VMReserved(vm)) <= (Pointer)vm); + /* All address space must have been unmapped. */ + AVER(VMMapped(vm) == (Size)0); - /* This appears to be pretty pointless, since the vm descriptor page - * is about to vanish completely. However, the VirtualFree might - * fail and it would be nice to have a dead sig there. */ - vm->sig = SigInvalid; + EVENT1(VMFinish, vm); - b = VirtualFree((LPVOID)vm->base, (SIZE_T)0, MEM_RELEASE); - AVER(b != 0); + vm->sig = SigInvalid; - b = VirtualFree((LPVOID)vm, (SIZE_T)0, MEM_RELEASE); + b = VirtualFree((LPVOID)vm->block, (SIZE_T)0, MEM_RELEASE); AVER(b != 0); - EVENT1(VMDestroy, vm); -} - - -/* VMBase -- return the base address of the memory reserved */ - -Addr VMBase(VM vm) -{ - AVERT(VM, vm); - - return vm->base; -} - - -/* VMLimit -- return the limit address of the memory reserved */ - -Addr VMLimit(VM vm) -{ - AVERT(VM, vm); - - return vm->limit; -} - - -/* VMReserved -- return the amount of address space reserved */ - -Size VMReserved(VM vm) -{ - AVERT(VM, vm); - - return vm->reserved; -} - - -/* VMMapped -- return the amount of memory actually mapped */ - -Size VMMapped(VM vm) -{ - AVERT(VM, vm); - - return vm->mapped; } @@ -250,15 +175,13 @@ Size VMMapped(VM vm) Res VMMap(VM vm, Addr base, Addr limit) { LPVOID b; - Align align; AVERT(VM, vm); - align = vm->align; - AVER(AddrIsAligned(base, align)); - AVER(AddrIsAligned(limit, align)); - AVER(vm->base <= base); + AVER(AddrIsAligned(base, vm->pageSize)); + AVER(AddrIsAligned(limit, vm->pageSize)); + AVER(VMBase(vm) <= base); AVER(base < limit); - AVER(limit <= vm->limit); + AVER(limit <= VMLimit(vm)); /* .improve.query-map: We could check that the pages we are about to * map are unmapped using VirtualQuery. */ @@ -270,6 +193,7 @@ Res VMMap(VM vm, Addr base, Addr limit) AVER((Addr)b == base); /* base should've been aligned */ vm->mapped += AddrOffset(base, limit); + AVER(VMMapped(vm) <= VMReserved(vm)); EVENT3(VMMap, vm, base, limit); return ResOK; @@ -280,22 +204,24 @@ Res VMMap(VM vm, Addr base, Addr limit) void VMUnmap(VM vm, Addr base, Addr limit) { - Align align; BOOL b; + Size size; AVERT(VM, vm); - align = vm->align; - AVER(AddrIsAligned(base, align)); - AVER(AddrIsAligned(limit, align)); - AVER(vm->base <= base); + AVER(AddrIsAligned(base, vm->pageSize)); + AVER(AddrIsAligned(limit, vm->pageSize)); + AVER(VMBase(vm) <= base); AVER(base < limit); - AVER(limit <= vm->limit); + AVER(limit <= VMLimit(vm)); + + size = AddrOffset(base, limit); + AVER(size <= VMMapped(vm)); /* .improve.query-unmap: Could check that the pages we are about */ /* to unmap are mapped, using VirtualQuery. */ - b = VirtualFree((LPVOID)base, (SIZE_T)AddrOffset(base, limit), MEM_DECOMMIT); + b = VirtualFree((LPVOID)base, (SIZE_T)size, MEM_DECOMMIT); AVER(b != 0); /* .assume.free.success */ - vm->mapped -= AddrOffset(base, limit); + vm->mapped -= size; EVENT3(VMUnmap, vm, base, limit); } @@ -303,7 +229,7 @@ void VMUnmap(VM vm, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/w3i3mv.nmk b/code/w3i3mv.nmk index 9bea83c3a2..eb8bacce7c 100644 --- a/code/w3i3mv.nmk +++ b/code/w3i3mv.nmk @@ -5,128 +5,20 @@ PFM = w3i3mv -# /Gs appears to be necessary to suppress stack checks. Stack checks -# (if not suppressed) generate a dependency on the C library, __chkesp, -# which causes the linker step to fail when building the DLL, mpsdy.dll. -PFMDEFS = /DCONFIG_PF_STRING="w3i3mv" /DCONFIG_PF_W3I3MV \ - /DWIN32 /D_WINDOWS /Gs +MPMPF = \ + [lockw3] \ + [mpsiw3] \ + [prmci3w3] \ + [proti3] \ + [protw3] \ + [spw3i3] \ + [ssw3i3mv] \ + [thw3] \ + [thw3i3] \ + [vmw3] !INCLUDE commpre.nmk - -# MPM sources: core plus platform-specific. -MPM = $(MPMCOMMON) - - - -# Source to object file mappings and CFLAGS amalgamation -# -# %%VARIETY %%PART: When adding a new variety or part, add new macros which -# expand to the files included in the part for each variety -# -# %%VARIETY: When adding a new variety, add a CFLAGS macro which expands to -# the flags that that variety should use when compiling C. And a LINKFLAGS -# macro which expands to the flags that the variety should use when building -# executables. And a LIBFLAGS macro which expands to the flags that the -# variety should use when building libraries - -!IF "$(VARIETY)" == "hot" -CFLAGS=$(CFLAGSCOMMONPRE) $(CFHOT) $(CFLAGSCOMMONPOST) -CFLAGSSQL=$(CFLAGSSQLPRE) $(CFHOT) $(CFLAGSSQLPOST) -LINKFLAGS=$(LINKFLAGSCOMMON) $(LFHOT) -LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSHOT) -MPMOBJ0 = $(MPM:<=w3i3mv\hot\) -PLINTHOBJ0 = $(PLINTH:<=w3i3mv\hot\) -AMSOBJ0 = $(AMS:<=w3i3mv\hot\) -AMCOBJ0 = $(AMC:<=w3i3mv\hot\) -AWLOBJ0 = $(AWL:<=w3i3mv\hot\) -LOOBJ0 = $(LO:<=w3i3mv\hot\) -SNCOBJ0 = $(SNC:<=w3i3mv\hot\) -MVFFOBJ0 = $(MVFF:<=w3i3mv\hot\) -DWOBJ0 = $(DW:<=w3i3mv\hot\) -FMTTESTOBJ0 = $(FMTTEST:<=w3i3mv\hot\) -POOLNOBJ0 = $(POOLN:<=w3i3mv\hot\) -TESTLIBOBJ0 = $(TESTLIB:<=w3i3mv\hot\) - -!ELSEIF "$(VARIETY)" == "cool" -CFLAGS=$(CFLAGSCOMMONPRE) $(CFCOOL) $(CFLAGSCOMMONPOST) -CFLAGSSQL=$(CFLAGSSQLPRE) $(CFCOOL) $(CFLAGSSQLPOST) -LINKFLAGS=$(LINKFLAGSCOMMON) $(LFCOOL) -LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSCOOL) -MPMOBJ0 = $(MPM:<=w3i3mv\cool\) -PLINTHOBJ0 = $(PLINTH:<=w3i3mv\cool\) -AMSOBJ0 = $(AMS:<=w3i3mv\cool\) -AMCOBJ0 = $(AMC:<=w3i3mv\cool\) -AWLOBJ0 = $(AWL:<=w3i3mv\cool\) -LOOBJ0 = $(LO:<=w3i3mv\cool\) -SNCOBJ0 = $(SNC:<=w3i3mv\cool\) -MVFFOBJ0 = $(MVFF:<=w3i3mv\cool\) -DWOBJ0 = $(DW:<=w3i3mv\cool\) -FMTTESTOBJ0 = $(FMTTEST:<=w3i3mv\cool\) -POOLNOBJ0 = $(POOLN:<=w3i3mv\cool\) -TESTLIBOBJ0 = $(TESTLIB:<=w3i3mv\cool\) - -!ELSEIF "$(VARIETY)" == "rash" -CFLAGS=$(CFLAGSCOMMONPRE) $(CFRASH) $(CFLAGSCOMMONPOST) -CFLAGSSQL=$(CFLAGSSQLPRE) $(CFRASH) $(CFLAGSSQLPOST) -LINKFLAGS=$(LINKFLAGSCOMMON) $(LFRASH) -LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSRASH) -MPMOBJ0 = $(MPM:<=w3i3mv\rash\) -PLINTHOBJ0 = $(PLINTH:<=w3i3mv\rash\) -AMSOBJ0 = $(AMS:<=w3i3mv\rash\) -AMCOBJ0 = $(AMC:<=w3i3mv\rash\) -AWLOBJ0 = $(AWL:<=w3i3mv\rash\) -LOOBJ0 = $(LO:<=w3i3mv\rash\) -SNCOBJ0 = $(SNC:<=w3i3mv\rash\) -MVFFOBJ0 = $(MVFF:<=w3i3mv\rash\) -DWOBJ0 = $(DW:<=w3i3mv\rash\) -FMTTESTOBJ0 = $(FMTTEST:<=w3i3mv\rash\) -POOLNOBJ0 = $(POOLN:<=w3i3mv\rash\) -TESTLIBOBJ0 = $(TESTLIB:<=w3i3mv\rash\) - -#!ELSEIF "$(VARIETY)" == "cv" -#CFLAGS=$(CFLAGSCOMMON) $(CFCV) -#LINKFLAGS=$(LINKFLAGSCOMMON) $(LFCV) -#LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSCV) -#MPMOBJ0 = $(MPM:<=w3i3mv\cv\) -#MPMOBJ = $(MPMOBJ0:>=.obj) -#PLINTHOBJ0 = $(PLINTH:<=w3i3mv\cv\) -#PLINTHOBJ = $(PLINTHOBJ0:>=.obj) -#AMSOBJ0 = $(AMS:<=w3i3mv\cv\) -#AMSOBJ = $(AMSOBJ0:>=.obj) -#AMCOBJ0 = $(AMC:<=w3i3mv\cv\) -#AMCOBJ = $(AMCOBJ0:>=.obj) -#AWLOBJ0 = $(AWL:<=w3i3mv\cv\) -#AWLOBJ = $(AWLOBJ0:>=.obj) -#LOOBJ0 = $(LO:<=w3i3mv\cv\) -#LOOBJ = $(LOOBJ0:>=.obj) -#SNCOBJ0 = $(SNC:<=w3i3mv\cv\) -#SNCOBJ = $(SNCOBJ0:>=.obj) -#DWOBJ0 = $(DW:<=w3i3mv\cv\) -#DWOBJ = $(DWOBJ0:>=.obj) -#POOLNOBJ0 = $(POOLN:<=w3i3mv\cv\) -#POOLNOBJ = $(POOLNOBJ0:>=.obj) -#TESTLIBOBJ0 = $(TESTLIB:<=w3i3mv\cv\) -#TESTLIBOBJ = $(TESTLIBOBJ0:>=.obj) - -!ENDIF - -# %%PART: When adding a new part, add new macros which expand to the object -# files included in the part - -MPMOBJ = $(MPMOBJ0:>=.obj) -PLINTHOBJ = $(PLINTHOBJ0:>=.obj) -AMSOBJ = $(AMSOBJ0:>=.obj) -AMCOBJ = $(AMCOBJ0:>=.obj) -AWLOBJ = $(AWLOBJ0:>=.obj) -LOOBJ = $(LOOBJ0:>=.obj) -SNCOBJ = $(SNCOBJ0:>=.obj) -MVFFOBJ = $(MVFFOBJ0:>=.obj) -DWOBJ = $(DWOBJ0:>=.obj) -FMTTESTOBJ = $(FMTTESTOBJ0:>=.obj) -POOLNOBJ = $(POOLNOBJ0:>=.obj) -TESTLIBOBJ = $(TESTLIBOBJ0:>=.obj) - - +!INCLUDE mv.nmk !INCLUDE commpost.nmk diff --git a/code/w3i3pc.nmk b/code/w3i3pc.nmk new file mode 100644 index 0000000000..82be17e405 --- /dev/null +++ b/code/w3i3pc.nmk @@ -0,0 +1,63 @@ +# w3i3pc.nmk: WINDOWS (IA-32) NMAKE FILE -*- makefile -*- +# +# $Id$ +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + +PFM = w3i3pc + +MPMPF = \ + [lockw3] \ + [mpsiw3] \ + [prmci3w3] \ + [proti3] \ + [protw3] \ + [spw3i3] \ + [ssw3i3pc] \ + [thw3] \ + [thw3i3] \ + [vmw3] + +!INCLUDE commpre.nmk +!INCLUDE pc.nmk +!INCLUDE commpost.nmk + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2001-2014 Ravenbrook Limited . +# All rights reserved. This is an open source license. Contact +# Ravenbrook for commercial licensing options. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Redistributions in any form must be accompanied by information on how +# to obtain complete source code for this software and any accompanying +# software that uses this software. The source code must either be +# included in the distribution or be available for no more than the cost +# of distribution plus a nominal fee, and must be freely redistributable +# under reasonable conditions. For an executable file, complete source +# code means the source code for all modules it contains. It does not +# include source code for modules or files that typically accompany the +# major components of the operating system on which the executable file +# runs. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/w3i6mv.nmk b/code/w3i6mv.nmk index 2a4769a665..2353d4cdda 100644 --- a/code/w3i6mv.nmk +++ b/code/w3i6mv.nmk @@ -1,139 +1,30 @@ # w3i6mv.nmk: WINDOWS (x86-64) NMAKE FILE -*- makefile -*- # # $Id$ -# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. PFM = w3i6mv -# /Gs appears to be necessary to suppress stack checks. Stack checks -# (if not suppressed) generate a dependency on the C library, __chkesp, -# which causes the linker step to fail when building the DLL, mpsdy.dll. -PFMDEFS = /DCONFIG_PF_STRING="w3i6mv" /DCONFIG_PF_W3I6MV \ - /DWIN32 /D_WINDOWS /Gs -MASM = ml64 - -# MPM sources: core plus platform-specific. -MPM = $(MPMCOMMON) - +MPMPF = \ + [lockw3] \ + [mpsiw3] \ + [prmci6w3] \ + [proti6] \ + [protw3] \ + [spw3i6] \ + [ssw3i6mv] \ + [thw3] \ + [thw3i6] \ + [vmw3] !INCLUDE commpre.nmk - - -# Source to object file mappings and CFLAGS amalgamation -# -# %%VARIETY %%PART: When adding a new variety or part, add new macros which -# expand to the files included in the part for each variety -# -# %%VARIETY: When adding a new variety, add a CFLAGS macro which expands to -# the flags that that variety should use when compiling C. And a LINKFLAGS -# macro which expands to the flags that the variety should use when building -# executables. And a LIBFLAGS macro which expands to the flags that the -# variety should use when building libraries - -!IF "$(VARIETY)" == "hot" -CFLAGS=$(CFLAGSCOMMONPRE) $(CFHOT) $(CFLAGSCOMMONPOST) -CFLAGSSQL=$(CFLAGSSQLPRE) $(CFHOT) $(CFLAGSSQLPOST) -LINKFLAGS=$(LINKFLAGSCOMMON) $(LFHOT) -LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSHOT) -MPMOBJ0 = $(MPM:<=w3i6mv\hot\) -PLINTHOBJ0 = $(PLINTH:<=w3i6mv\hot\) -AMSOBJ0 = $(AMS:<=w3i6mv\hot\) -AMCOBJ0 = $(AMC:<=w3i6mv\hot\) -AWLOBJ0 = $(AWL:<=w3i6mv\hot\) -LOOBJ0 = $(LO:<=w3i6mv\hot\) -SNCOBJ0 = $(SNC:<=w3i6mv\hot\) -MVFFOBJ0 = $(MVFF:<=w3i6mv\hot\) -DWOBJ0 = $(DW:<=w3i6mv\hot\) -FMTTESTOBJ0 = $(FMTTEST:<=w3i6mv\hot\) -POOLNOBJ0 = $(POOLN:<=w3i6mv\hot\) -TESTLIBOBJ0 = $(TESTLIB:<=w3i6mv\hot\) - -!ELSEIF "$(VARIETY)" == "cool" -CFLAGS=$(CFLAGSCOMMONPRE) $(CFCOOL) $(CFLAGSCOMMONPOST) -CFLAGSSQL=$(CFLAGSSQLPRE) $(CFCOOL) $(CFLAGSSQLPOST) -LINKFLAGS=$(LINKFLAGSCOMMON) $(LFCOOL) -LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSCOOL) -MPMOBJ0 = $(MPM:<=w3i6mv\cool\) -PLINTHOBJ0 = $(PLINTH:<=w3i6mv\cool\) -AMSOBJ0 = $(AMS:<=w3i6mv\cool\) -AMCOBJ0 = $(AMC:<=w3i6mv\cool\) -AWLOBJ0 = $(AWL:<=w3i6mv\cool\) -LOOBJ0 = $(LO:<=w3i6mv\cool\) -SNCOBJ0 = $(SNC:<=w3i6mv\cool\) -MVFFOBJ0 = $(MVFF:<=w3i6mv\cool\) -DWOBJ0 = $(DW:<=w3i6mv\cool\) -FMTTESTOBJ0 = $(FMTTEST:<=w3i6mv\cool\) -POOLNOBJ0 = $(POOLN:<=w3i6mv\cool\) -TESTLIBOBJ0 = $(TESTLIB:<=w3i6mv\cool\) - -!ELSEIF "$(VARIETY)" == "rash" -CFLAGS=$(CFLAGSCOMMONPRE) $(CFRASH) $(CFLAGSCOMMONPOST) -CFLAGSSQL=$(CFLAGSSQLPRE) $(CFRASH) $(CFLAGSSQLPOST) -LINKFLAGS=$(LINKFLAGSCOMMON) $(LFRASH) -LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSRASH) -MPMOBJ0 = $(MPM:<=w3i6mv\rash\) -PLINTHOBJ0 = $(PLINTH:<=w3i6mv\rash\) -AMSOBJ0 = $(AMS:<=w3i6mv\rash\) -AMCOBJ0 = $(AMC:<=w3i6mv\rash\) -AWLOBJ0 = $(AWL:<=w3i6mv\rash\) -LOOBJ0 = $(LO:<=w3i6mv\rash\) -SNCOBJ0 = $(SNC:<=w3i6mv\rash\) -MVFFOBJ0 = $(MVFF:<=w3i6mv\rash\) -DWOBJ0 = $(DW:<=w3i6mv\rash\) -FMTTESTOBJ0 = $(FMTTEST:<=w3i6mv\rash\) -POOLNOBJ0 = $(POOLN:<=w3i6mv\rash\) -TESTLIBOBJ0 = $(TESTLIB:<=w3i6mv\rash\) - -#!ELSEIF "$(VARIETY)" == "cv" -#CFLAGS=$(CFLAGSCOMMON) $(CFCV) -#LINKFLAGS=$(LINKFLAGSCOMMON) $(LFCV) -#LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSCV) -#MPMOBJ0 = $(MPM:<=w3i6mv\cv\) -#MPMOBJ = $(MPMOBJ0:>=.obj) -#PLINTHOBJ0 = $(PLINTH:<=w3i6mv\cv\) -#PLINTHOBJ = $(PLINTHOBJ0:>=.obj) -#AMSOBJ0 = $(AMS:<=w3i6mv\cv\) -#AMSOBJ = $(AMSOBJ0:>=.obj) -#AMCOBJ0 = $(AMC:<=w3i6mv\cv\) -#AMCOBJ = $(AMCOBJ0:>=.obj) -#AWLOBJ0 = $(AWL:<=w3i6mv\cv\) -#AWLOBJ = $(AWLOBJ0:>=.obj) -#LOOBJ0 = $(LO:<=w3i6mv\cv\) -#LOOBJ = $(LOOBJ0:>=.obj) -#SNCOBJ0 = $(SNC:<=w3i6mv\cv\) -#SNCOBJ = $(SNCOBJ0:>=.obj) -#DWOBJ0 = $(DW:<=w3i6mv\cv\) -#DWOBJ = $(DWOBJ0:>=.obj) -#POOLNOBJ0 = $(POOLN:<=w3i6mv\cv\) -#POOLNOBJ = $(POOLNOBJ0:>=.obj) -#TESTLIBOBJ0 = $(TESTLIB:<=w3i6mv\cv\) -#TESTLIBOBJ = $(TESTLIBOBJ0:>=.obj) - -!ENDIF - -# %%PART: When adding a new part, add new macros which expand to the object -# files included in the part - -MPMOBJ = $(MPMOBJ0:>=.obj) -PLINTHOBJ = $(PLINTHOBJ0:>=.obj) -AMSOBJ = $(AMSOBJ0:>=.obj) -AMCOBJ = $(AMCOBJ0:>=.obj) -AWLOBJ = $(AWLOBJ0:>=.obj) -LOOBJ = $(LOOBJ0:>=.obj) -SNCOBJ = $(SNCOBJ0:>=.obj) -MVFFOBJ = $(MVFFOBJ0:>=.obj) -DWOBJ = $(DWOBJ0:>=.obj) -FMTTESTOBJ = $(FMTTESTOBJ0:>=.obj) -POOLNOBJ = $(POOLNOBJ0:>=.obj) -TESTLIBOBJ = $(TESTLIBOBJ0:>=.obj) - - +!INCLUDE mv.nmk !INCLUDE commpost.nmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2013 Ravenbrook Limited . +# Copyright (C) 2001-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/code/w3i6pc.nmk b/code/w3i6pc.nmk new file mode 100644 index 0000000000..272b96e5a2 --- /dev/null +++ b/code/w3i6pc.nmk @@ -0,0 +1,67 @@ +# -*- makefile -*- +# +# w3i6pc.nmk: NMAKE FILE FOR WINDOWS/x64/PELLES C +# +# $Id: //info.ravenbrook.com/project/mps/branch/2014-03-21/pellesc/code/w3i6pc.nmk#1 $ +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + +PFM = w3i6pc + +CFLAGSTARGETPRE = /Tamd64-coff + +MPMPF = \ + [lockw3] \ + [mpsiw3] \ + [prmci6w3] \ + [proti6] \ + [protw3] \ + [spw3i6] \ + [ssw3i6pc] \ + [thw3] \ + [thw3i6] \ + [vmw3] + +!INCLUDE commpre.nmk +!INCLUDE pc.nmk +!INCLUDE commpost.nmk + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2001-2014 Ravenbrook Limited . +# All rights reserved. This is an open source license. Contact +# Ravenbrook for commercial licensing options. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Redistributions in any form must be accompanied by information on how +# to obtain complete source code for this software and any accompanying +# software that uses this software. The source code must either be +# included in the distribution or be available for no more than the cost +# of distribution plus a nominal fee, and must be freely redistributable +# under reasonable conditions. For an executable file, complete source +# code means the source code for all modules it contains. It does not +# include source code for modules or files that typically accompany the +# major components of the operating system on which the executable file +# runs. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/walk.c b/code/walk.c index b17540e495..cb6c9684b9 100644 --- a/code/walk.c +++ b/code/walk.c @@ -1,7 +1,7 @@ /* walk.c: OBJECT WALKER * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" @@ -10,103 +10,59 @@ SRCID(walk, "$Id$"); -/* Heap Walking - */ - - -#define FormattedObjectsStepClosureSig ((Sig)0x519F05C1) - -typedef struct FormattedObjectsStepClosureStruct *FormattedObjectsStepClosure; - -typedef struct FormattedObjectsStepClosureStruct { - Sig sig; - mps_formatted_objects_stepper_t f; - void *p; - size_t s; -} FormattedObjectsStepClosureStruct; - - -static Bool FormattedObjectsStepClosureCheck(FormattedObjectsStepClosure c) -{ - CHECKS(FormattedObjectsStepClosure, c); - CHECKL(FUNCHECK(c->f)); - /* p and s fields are arbitrary closures which cannot be checked */ - return TRUE; -} +/* mps_arena_formatted_objects_walk -- iterate over all objects */ +typedef struct ObjectsWalkClosureStruct { + Arena arena; + mps_formatted_objects_stepper_t stepper; + void *closureP; + size_t closureS; +} ObjectsWalkClosureStruct, *ObjectsWalkClosure; -static void ArenaFormattedObjectsStep(Addr object, Format format, Pool pool, - void *p, size_t s) +static void objectsWalkStep(Addr addr, Format format, Pool pool, + void *closureP, Size closureS) { - FormattedObjectsStepClosure c; - /* Can't check object */ - AVERT(Format, format); - AVERT(Pool, pool); - c = p; - AVERT(FormattedObjectsStepClosure, c); - AVER(s == 0); - - (*c->f)((mps_addr_t)object, (mps_fmt_t)format, (mps_pool_t)pool, - c->p, c->s); + ObjectsWalkClosure ow = closureP; + AVER(closureS == sizeof(*ow)); + ow->stepper(addr, format, pool, ow->closureP, ow->closureS); } - -/* ArenaFormattedObjectsWalk -- iterate over all objects - * - * So called because it walks all formatted objects in an arena. */ - -static void ArenaFormattedObjectsWalk(Arena arena, FormattedObjectsStepMethod f, - void *p, size_t s) +static Bool objectsWalkVisit(Seg seg, void *closureP, Size closureS) { - Seg seg; - FormattedObjectsStepClosure c; - - AVERT(Arena, arena); - AVER(FUNCHECK(f)); - AVER(f == ArenaFormattedObjectsStep); - /* p and s are arbitrary closures. */ - /* Know that p is a FormattedObjectsStepClosure */ - /* Know that s is 0 */ - AVER(p != NULL); - AVER(s == 0); - - c = p; - AVERT(FormattedObjectsStepClosure, c); - - if (SegFirst(&seg, arena)) { - do { - Pool pool; - pool = SegPool(seg); - if (pool->class->attr & AttrFMT) { - ShieldExpose(arena, seg); - PoolWalk(pool, seg, f, p, s); - ShieldCover(arena, seg); - } - } while(SegNext(&seg, arena, seg)); + ObjectsWalkClosure ow = closureP; + Pool pool; + + AVER(closureS == sizeof(*ow)); + + pool = SegPool(seg); + if (PoolHasAttr(pool, AttrFMT)) { + ShieldExpose(ow->arena, seg); + PoolWalk(pool, seg, objectsWalkStep, closureP, closureS); + ShieldCover(ow->arena, seg); } -} - -/* mps_arena_formatted_objects_walk -- iterate over all objects - * - * Client interface to ArenaFormattedObjectsWalk. */ + return TRUE; +} void mps_arena_formatted_objects_walk(mps_arena_t mps_arena, - mps_formatted_objects_stepper_t f, - void *p, size_t s) + mps_formatted_objects_stepper_t stepper, + void *closureP, size_t closureS) { Arena arena = (Arena)mps_arena; - FormattedObjectsStepClosureStruct c; + ObjectsWalkClosureStruct owStruct; ArenaEnter(arena); + AVERT(Arena, arena); - AVER(FUNCHECK(f)); - /* p and s are arbitrary closures, hence can't be checked */ - c.sig = FormattedObjectsStepClosureSig; - c.f = f; - c.p = p; - c.s = s; - ArenaFormattedObjectsWalk(arena, ArenaFormattedObjectsStep, &c, 0); + AVER(FUNCHECK(stepper)); + /* closureP and closureS are arbitrary closures, hence can't be checked */ + + owStruct.arena = arena; + owStruct.stepper = stepper; + owStruct.closureP = closureP; + owStruct.closureS = closureS; + SegTraverse(arena, objectsWalkVisit, &owStruct, sizeof(owStruct)); + ArenaLeave(arena); } @@ -164,6 +120,7 @@ typedef struct rootsStepClosureStruct { /* rootsStepClosureCheck -- check a rootsStepClosure */ +ATTRIBUTE_UNUSED static Bool rootsStepClosureCheck(rootsStepClosure rsc) { CHECKS(rootsStepClosure, rsc); @@ -171,7 +128,7 @@ static Bool rootsStepClosureCheck(rootsStepClosure rsc) CHECKL(FUNCHECK(rsc->f)); /* p and s fields are arbitrary closures which cannot be checked */ if (rsc->root != NULL) { - CHECKL(RootCheck(rsc->root)); + CHECKD_NOSIG(Root, rsc->root); /* */ } return TRUE; } @@ -190,7 +147,7 @@ static void rootsStepClosureInit(rootsStepClosure rsc, /* First initialize the ScanState superclass */ ss = &rsc->ssStruct; - ScanStateInit(ss, TraceSetSingle(trace), GlobalsArena(arena), RankAMBIG, + ScanStateInit(ss, TraceSetSingle(trace), GlobalsArena(arena), RankMIN, trace->white); /* Initialize the fix method in the ScanState */ @@ -243,7 +200,7 @@ static Res RootsWalkFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) /* If the segment isn't GCable then the ref is not to the heap and */ /* shouldn't be passed to the client. */ - AVER((SegPool(seg)->class->attr & AttrGC) != 0); + AVER(PoolHasAttr(SegPool(seg), AttrGC)); /* Call the client closure - .assume.rootaddr */ rsc->f((mps_addr_t*)refIO, (mps_root_t)rsc->root, rsc->p, rsc->s); @@ -273,8 +230,46 @@ static Res rootWalk(Root root, void *p) } +/* rootWalkGrey -- make the root grey for the trace passed as p */ + +static Res rootWalkGrey(Root root, void *p) +{ + Trace trace = p; + + AVERT(Root, root); + AVERT(Trace, trace); + + RootGrey(root, trace); + return ResOK; +} + + /* ArenaRootsWalk -- walks all the root in the arena */ +static Bool rootsWalkWhiteVisit(Seg seg, void *closureP, Size closureS) +{ + Trace trace = closureP; + AVER(closureS == sizeof(*trace)); + /* TODO: Should use segment class test here? */ + if (PoolHasAttr(SegPool(seg), AttrGC)) { + Res res = TraceAddWhite(trace, seg); + AVER(res == ResOK); + } + return TRUE; +} + +static Bool rootsWalkBlackVisit(Seg seg, void *closureP, Size closureS) +{ + Trace trace = closureP; + AVER(closureS == sizeof(*trace)); + /* TODO: Should use segment class test here? */ + if (PoolHasAttr(SegPool(seg), AttrGC)) { + SegSetGrey(seg, TraceSetDel(SegGrey(seg), trace)); + SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); + } + return TRUE; +} + static Res ArenaRootsWalk(Globals arenaGlobals, mps_roots_stepper_t f, void *p, size_t s) { @@ -285,7 +280,6 @@ static Res ArenaRootsWalk(Globals arenaGlobals, mps_roots_stepper_t f, ScanState ss; Rank rank; Res res; - Seg seg; AVERT(Globals, arenaGlobals); AVER(FUNCHECK(f)); @@ -305,23 +299,17 @@ static Res ArenaRootsWalk(Globals arenaGlobals, mps_roots_stepper_t f, /* ArenaRootsWalk only passes references to GCable pools to the client. */ /* NOTE: I'm not sure why this is. RB 2012-07-24 */ - if (SegFirst(&seg, arena)) { - do { - if ((SegPool(seg)->class->attr & AttrGC) != 0) { - TraceAddWhite(trace, seg); - } - } while (SegNext(&seg, arena, seg)); - } + SegTraverse(arena, rootsWalkWhiteVisit, trace, sizeof(*trace)); /* Make the roots grey so that they are scanned */ - res = RootsIterate(arenaGlobals, (RootIterateFn)RootGrey, (void *)trace); + res = RootsIterate(arenaGlobals, rootWalkGrey, trace); /* Make this trace look like any other trace. */ arena->flippedTraces = TraceSetAdd(arena->flippedTraces, trace); rootsStepClosureInit(rsc, arenaGlobals, trace, RootsWalkFix, f, p, s); ss = rootsStepClosure2ScanState(rsc); - for(rank = RankAMBIG; rank < RankLIMIT; ++rank) { + for(rank = RankMIN; rank < RankLIMIT; ++rank) { ss->rank = rank; AVERT(ScanState, ss); res = RootsIterate(arenaGlobals, rootWalk, (void *)ss); @@ -329,6 +317,9 @@ static Res ArenaRootsWalk(Globals arenaGlobals, mps_roots_stepper_t f, break; } + /* Turn segments black again. */ + SegTraverse(arena, rootsWalkBlackVisit, trace, sizeof(*trace)); + rootsStepClosureFinish(rsc); /* Make this trace look like any other finished trace. */ trace->state = TraceFINISHED; @@ -362,7 +353,7 @@ void mps_arena_roots_walk(mps_arena_t mps_arena, mps_roots_stepper_t f, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/walkt0.c b/code/walkt0.c index 3e8c8ce829..6a378ffe65 100644 --- a/code/walkt0.c +++ b/code/walkt0.c @@ -1,7 +1,7 @@ /* walkt0.c: WALK TEST 0 * * $Id$ - * Copyright (c) 1998-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 1998-2014 Ravenbrook Limited. See end of file for license. * * Loosely based on . */ @@ -11,15 +11,14 @@ #include "testlib.h" #include "mpslib.h" #include "mpscamc.h" +#include "mpscams.h" +#include "mpscawl.h" +#include "mpsclo.h" #include "mpsavm.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif #include "mps.h" -#include -#include +#include /* printf */ #define testArenaSIZE ((size_t)((size_t)64 << 20)) #define avLEN 3 @@ -128,9 +127,8 @@ static void stepper(mps_addr_t object, mps_fmt_t format, /* test -- the body of the test */ -static void *test(void *arg, size_t s) +static void *test(mps_arena_t arena, mps_pool_class_t pool_class) { - mps_arena_t arena; mps_chain_t chain; mps_fmt_t format; mps_pool_t pool; @@ -139,14 +137,14 @@ static void *test(void *arg, size_t s) unsigned long objs; struct stepper_data sdStruct, *sd; - arena = (mps_arena_t)arg; - (void)s; /* unused */ - die(dylan_fmt(&format, arena), "fmt_create"); die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - die(mps_pool_create(&pool, arena, mps_class_amc(), format, chain), - "pool_create(amc)"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create"); + } MPS_ARGS_END(args); die(mps_ap_create(&ap, pool, mps_rank_exact()), "ap_create"); @@ -186,11 +184,14 @@ static void *test(void *arg, size_t s) /* Note: stepper finds more than we expect, due to pad objects */ /* printf("stepper found %ld objs\n", sd->count); */ + + mps_arena_park(arena); mps_ap_destroy(ap); mps_root_destroy(exactRoot); mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(format); + mps_arena_release(arena); return NULL; } @@ -199,16 +200,20 @@ int main(int argc, char *argv[]) { mps_arena_t arena; mps_thr_t thread; - void *r; - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "arena_create"); die(mps_thread_reg(&thread, arena), "thread_reg"); - mps_tramp(&r, test, arena, 0); + + test(arena, mps_class_amc()); + test(arena, mps_class_amcz()); + /* TODO: test(arena, mps_class_ams()); -- see job003738 */ + test(arena, mps_class_awl()); + test(arena, mps_class_lo()); + mps_thread_dereg(thread); mps_arena_destroy(arena); @@ -219,7 +224,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/xci6ll.gmk b/code/xci6ll.gmk new file mode 100644 index 0000000000..597979539e --- /dev/null +++ b/code/xci6ll.gmk @@ -0,0 +1,69 @@ +# -*- makefile -*- +# +# xci6ll.gmk: BUILD FOR MAC OS X/x86_64/Clang PLATFORM +# +# $Id$ +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# +# .prefer.xcode: The documented and preferred way to develop the MPS +# for this platform is to use the Xcode project (mps.xcodeproj). This +# makefile provides a way to compile the MPS one source file at a +# time, rather than all at once via mps.c (which can hide errors due +# to missing headers). + +PFM = xci6ll + +MPMPF = \ + lockix.c \ + prmci6xc.c \ + proti6.c \ + protix.c \ + protxc.c \ + span.c \ + ssixi6.c \ + thxc.c \ + vmix.c + +include ll.gmk +include comm.gmk + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2001-2014 Ravenbrook Limited . +# All rights reserved. This is an open source license. Contact +# Ravenbrook for commercial licensing options. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Redistributions in any form must be accompanied by information on how +# to obtain complete source code for this software and any accompanying +# software that uses this software. The source code must either be +# included in the distribution or be available for no more than the cost +# of distribution plus a nominal fee, and must be freely redistributable +# under reasonable conditions. For an executable file, complete source +# code means the source code for all modules it contains. It does not +# include source code for modules or files that typically accompany the +# major components of the operating system on which the executable file +# runs. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/zcoll.c b/code/zcoll.c index 66b4d268b8..a5d7a0e0d2 100644 --- a/code/zcoll.c +++ b/code/zcoll.c @@ -1,7 +1,7 @@ /* zcoll.c: Collection test * * $Id$ - * Copyright (c) 2008 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2008-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * OBJECTIVE @@ -62,18 +62,8 @@ #include "fmtdy.h" #include "fmtdytst.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif -#include -#include /* clock */ - -#ifdef MPS_BUILD_MV -/* MSVC warning 4996 = stdio / C runtime 'unsafe' */ -/* Objects to: sscanf. See job001934. */ -#pragma warning( disable : 4996 ) -#endif +#include /* fflush, printf, putchar, puts, stdout */ /* testChain -- generation parameters for the test */ @@ -119,12 +109,11 @@ static void showStatsAscii(size_t notcon, size_t con, size_t live, size_t alimit count = (a < 200) ? a + 1 : c; for(i = 0; i < count; i++) { - printf( (i == a) ? "A" - : (i < n) ? "n" - : (i < l) ? "L" - : (i < c) ? "_" - : " " - ); + putchar((i == a) ? 'A' + : (i < n) ? 'n' + : (i < l) ? 'L' + : (i < c) ? '_' + : ' '); } printf("\n"); } @@ -197,8 +186,8 @@ static void get(mps_arena_t arena) switch(type) { case mps_message_type_gc_start(): { mclockBegin = mps_message_clock(arena, message); - printf(" %5lu: (%5lu)", - mclockBegin, mclockBegin - mclockEnd); + printf(" %5"PRIuLONGEST": (%5"PRIuLONGEST")", + (ulongest_t)mclockBegin, (ulongest_t)(mclockBegin - mclockEnd)); printf(" Coll Begin (%s)\n", mps_message_gc_start_why(arena, message)); break; @@ -212,18 +201,20 @@ static void get(mps_arena_t arena) mclockEnd = mps_message_clock(arena, message); - printf(" %5lu: (%5lu)", - mclockEnd, mclockEnd - mclockBegin); + printf(" %5"PRIuLONGEST": (%5"PRIuLONGEST")", + (ulongest_t)mclockEnd, (ulongest_t)(mclockEnd - mclockBegin)); printf(" Coll End "); showStatsText(notcon, con, live); - if(rnd()==0) showStatsAscii(notcon, con, live, alimit); + if (rnd()==0) + showStatsAscii(notcon, con, live, alimit); break; } case mps_message_type_finalization(): { mps_message_finalization_ref(&objaddr, arena, message); obj = objaddr; objind = DYLAN_INT_INT(DYLAN_VECTOR_SLOT(obj, 0)); - printf(" Finalization for object %lu at %p\n", objind, objaddr); + printf(" Finalization for object %"PRIuLONGEST" at %p\n", + (ulongest_t)objind, objaddr); break; } default: { @@ -302,7 +293,7 @@ static void CatalogCheck(void) mps_word_t w; void *Catalog, *Page, *Art, *Poly; unsigned long Catalogs = 0, Pages = 0, Arts = 0, Polys = 0; - int i, j, k; + size_t i, j, k; /* retrieve Catalog from root */ Catalog = myrootExact[CatalogRootIndex]; @@ -360,7 +351,7 @@ static void CatalogDo(mps_arena_t arena, mps_ap_t ap) { mps_word_t v; void *Catalog, *Page, *Art, *Poly; - int i, j, k; + size_t i, j, k; die(make_dylan_vector(&v, ap, CatalogFix + CatalogVar), "Catalog"); DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(CatalogSig); @@ -370,7 +361,7 @@ static void CatalogDo(mps_arena_t arena, mps_ap_t ap) myrootExact[CatalogRootIndex] = Catalog; get(arena); - fflush(stdout); + (void)fflush(stdout); CatalogCheck(); for(i = 0; i < CatalogVar; i += 1) { @@ -382,8 +373,8 @@ static void CatalogDo(mps_arena_t arena, mps_ap_t ap) DYLAN_VECTOR_SLOT(Catalog, CatalogFix + i) = (mps_word_t)Page; get(arena); - printf("Page %d: make articles\n", i); - fflush(stdout); + printf("Page %"PRIuLONGEST": make articles\n", (ulongest_t)i); + (void)fflush(stdout); for(j = 0; j < PageVar; j += 1) { die(make_dylan_vector(&v, ap, ArtFix + ArtVar), "Art"); @@ -405,7 +396,7 @@ static void CatalogDo(mps_arena_t arena, mps_ap_t ap) } } } - fflush(stdout); + (void)fflush(stdout); CatalogCheck(); } @@ -532,7 +523,7 @@ static void Make(mps_arena_t arena, mps_ap_t ap, unsigned randm, unsigned keep1i static void Rootdrop(char rank_char) { - unsigned i; + size_t i; if(rank_char == 'A') { for(i = 0; i < myrootAmbigCOUNT; ++i) { @@ -551,7 +542,7 @@ static void Rootdrop(char rank_char) #define stackwipedepth 50000 static void stackwipe(void) { - unsigned iw; + size_t iw; unsigned long aw[stackwipedepth]; /* Do some pointless work that the compiler won't optimise away, so that @@ -625,7 +616,7 @@ static void testscriptC(mps_arena_t arena, mps_ap_t ap, const char *script) script += sb; printf(" Collect\n"); stackwipe(); - mps_arena_collect(arena); + die(mps_arena_collect(arena), "mps_arena_collect"); mps_arena_release(arena); break; } @@ -735,7 +726,7 @@ static void *testscriptB(void *arg, size_t s) mps_fmt_t fmt; mps_chain_t chain; mps_pool_t amc; - int i; + size_t i; mps_root_t root_table_Ambig; mps_root_t root_table_Exact; mps_ap_t ap; @@ -784,6 +775,7 @@ static void *testscriptB(void *arg, size_t s) testscriptC(arena, ap, script); printf(" Destroy roots, pools, arena etc.\n\n"); + mps_arena_park(arena); mps_root_destroy(root_stackreg); mps_ap_destroy(ap); mps_root_destroy(root_table_Exact); @@ -838,8 +830,7 @@ static void testscriptA(const char *script) */ int main(int argc, char *argv[]) { - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); /* 1<<19 == 524288 == 1/2 Mebibyte */ /* 16<<20 == 16777216 == 16 Mebibyte */ @@ -938,7 +929,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2008-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/code/zmess.c b/code/zmess.c index 0ad57e09c4..53aea54580 100644 --- a/code/zmess.c +++ b/code/zmess.c @@ -1,7 +1,7 @@ /* zmess.c: Message test * * $Id$ - * Copyright (c) 2008 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2008-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * OBJECTIVE @@ -107,15 +107,13 @@ #include "fmtdy.h" #include "fmtdytst.h" #include "mpstd.h" -#ifdef MPS_OS_W3 -#include "mpsw3.h" -#endif -#include + +#include /* printf */ #define testArenaSIZE ((size_t)16<<20) -/* usually (ArenaAlign / sizeof(Ref)) = 1024 */ +/* usually (ArenaGrainSize / sizeof(Ref)) = 1024 */ /* so choose 3000 to force 3 segments of guardians */ #define myrootCOUNT 3000 @@ -179,7 +177,8 @@ static void report(mps_arena_t arena, const char *pm, Bool discard) mps_message_finalization_ref(&objaddr, arena, message); obj = objaddr; objind = DYLAN_INT_INT(DYLAN_VECTOR_SLOT(obj, 0)); - printf(" Finalization for object %lu at %p\n", objind, objaddr); + printf(" Finalization for object %"PRIuLONGEST" at %p\n", + (ulongest_t)objind, objaddr); cdie(myroot[objind] == NULL, "finalized live"); cdie(state[objind] == finalizableSTATE, "not finalizable"); state[objind] = finalizedSTATE; @@ -242,7 +241,7 @@ static void testscriptC(mps_arena_t arena, const char *script) } case 'C': { printf(" Collect\n"); - mps_arena_collect(arena); + die(mps_arena_collect(arena), "mps_arena_collect"); break; } case 'F': { @@ -308,7 +307,7 @@ static void *testscriptB(void *arg, size_t s) mps_root_t root_table; mps_ap_t ap; mps_root_t root_stackreg; - int i; + size_t i; int N = myrootCOUNT - 1; void *stack_starts_here; /* stack scanning starts here */ @@ -382,6 +381,7 @@ static void *testscriptB(void *arg, size_t s) testscriptC(arena, script); + mps_arena_park(arena); mps_ap_destroy(ap); mps_root_destroy(root_table); mps_pool_destroy(amc); @@ -441,6 +441,9 @@ static void testscriptA(const char *script) * * TIMCA_remote returns a Bool, true for let "ControlAlloc succeed". */ + +#ifdef TEST_CONTROLALLOC_FAILURE + static const char *TIMCA_str = ""; static int TIMCA_done = 0; static void TIMCA_setup(const char *string) @@ -480,15 +483,15 @@ Bool TIMCA_remote(void) return succeed; } +#endif /* TEST_CONTROLALLOC_FAILURE */ + /* main -- runs various test scripts * */ int main(int argc, char *argv[]) { - - randomize(argc, argv); - mps_lib_assert_fail_install(assert_die); + testlib_init(argc, argv); /* Scripts that should fail (uncomment to show failure is detected) */ /*testscriptA("C.");*/ @@ -536,7 +539,8 @@ int main(int argc, char *argv[]) * * See . */ - if(0) { +#if TEST_CONTROLALLOC_FAILURE + { /* ArenaCreate unable to pre-allocate: THESE SHOULD FAIL */ /* manually edit if(0) -> if(1) to test these */ if(0) { @@ -563,6 +567,7 @@ int main(int argc, char *argv[]) TIMCA_setup(""); /* must reset it! */ } +#endif printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; @@ -571,7 +576,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . + * Copyright (c) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/configure b/configure index 5f8c328772..edbc732030 100755 --- a/configure +++ b/configure @@ -1,13 +1,11 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for Memory Pool System Kit master. +# Generated by GNU Autoconf 2.69 for Memory Pool System Kit release/1.114.0. # # Report bugs to . # # -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software -# Foundation, Inc. +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation @@ -136,6 +134,31 @@ export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh @@ -169,7 +192,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi -test x\$exitcode = x0 || exit 1" +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && @@ -214,21 +238,25 @@ IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : - # We cannot yet assume a decent shell, so we have to provide a - # neutralization value for shells without unset; and this also - # works around shells that cannot unset nonexistent variables. - # Preserve -v and -x to the replacement shell. - BASH_ENV=/dev/null - ENV=/dev/null - (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV - export CONFIG_SHELL - case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; - esac - exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 fi if test x$as_have_required = xno; then : @@ -331,6 +359,14 @@ $as_echo X"$as_dir" | } # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take @@ -452,6 +488,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). @@ -486,16 +526,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. + # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' + as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -507,28 +547,8 @@ else as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -560,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Memory Pool System Kit' PACKAGE_TARNAME='mps-kit' -PACKAGE_VERSION='master' -PACKAGE_STRING='Memory Pool System Kit master' +PACKAGE_VERSION='release/1.114.0' +PACKAGE_STRING='Memory Pool System Kit release/1.114.0' PACKAGE_BUGREPORT='mps-questions@ravenbrook.com' PACKAGE_URL='http://www.ravenbrook.com/project/mps/' @@ -609,7 +629,9 @@ TEST_TARGET INSTALL_TARGET CLEAN_TARGET BUILD_TARGET -MPS_TARGET_NAME +MPS_BUILD_NAME +MPS_ARCH_NAME +MPS_OS_NAME MAKE host_os host_vendor @@ -1138,8 +1160,6 @@ target=$target_alias if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1225,7 +1245,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures Memory Pool System Kit master to adapt to many kinds of systems. +\`configure' configures Memory Pool System Kit release/1.114.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1290,7 +1310,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of Memory Pool System Kit master:";; + short | recursive ) echo "Configuration of Memory Pool System Kit release/1.114.0:";; esac cat <<\_ACEOF @@ -1371,10 +1391,10 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -Memory Pool System Kit configure master -generated by GNU Autoconf 2.68 +Memory Pool System Kit configure release/1.114.0 +generated by GNU Autoconf 2.69 -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1673,8 +1693,8 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by Memory Pool System Kit $as_me master, which was -generated by GNU Autoconf 2.68. Invocation command line was +It was created by Memory Pool System Kit $as_me release/1.114.0, which was +generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2075,7 +2095,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2115,7 +2135,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2168,7 +2188,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2209,7 +2229,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue @@ -2267,7 +2287,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2311,7 +2331,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2757,8 +2777,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include -#include -#include +struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); @@ -2886,7 +2905,7 @@ case $as_dir/ in #(( # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. @@ -3099,7 +3118,7 @@ do for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue + as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in @@ -3165,7 +3184,7 @@ do for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue + as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in @@ -3438,25 +3457,33 @@ case $host/$CLANG in i*86-*-linux*/no) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Linux x86" >&5 $as_echo "Linux x86" >&6; } - MPS_TARGET_NAME=lii3gc + MPS_OS_NAME=li + MPS_ARCH_NAME=i3 + MPS_BUILD_NAME=gc PFMCFLAGS="$CFLAGS_GC" ;; x86_64-*-linux*/no) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Linux x86_64" >&5 $as_echo "Linux x86_64" >&6; } - MPS_TARGET_NAME=lii6gc + MPS_OS_NAME=li + MPS_ARCH_NAME=i6 + MPS_BUILD_NAME=gc PFMCFLAGS="$CFLAGS_GC" ;; x86_64-*-linux*/yes) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Linux x86_64" >&5 $as_echo "Linux x86_64" >&6; } - MPS_TARGET_NAME=lii6ll + MPS_OS_NAME=li + MPS_ARCH_NAME=i6 + MPS_BUILD_NAME=ll PFMCFLAGS="$CFLAGS_LL" ;; i*86-*-darwin*/*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Mac OS X x86" >&5 $as_echo "Mac OS X x86" >&6; } - MPS_TARGET_NAME=xci3ll + MPS_OS_NAME=xc + MPS_ARCH_NAME=i3 + MPS_BUILD_NAME=ll BUILD_TARGET=build-via-xcode CLEAN_TARGET=clean-xcode-build INSTALL_TARGET=install-xcode-build @@ -3466,7 +3493,9 @@ $as_echo "Mac OS X x86" >&6; } x86_64-apple-darwin*/*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Mac OS X x86_64" >&5 $as_echo "Mac OS X x86_64" >&6; } - MPS_TARGET_NAME=xci6ll + MPS_OS_NAME=xc + MPS_ARCH_NAME=i6 + MPS_BUILD_NAME=ll BUILD_TARGET=build-via-xcode CLEAN_TARGET=clean-xcode-build INSTALL_TARGET=install-xcode-build @@ -3476,7 +3505,9 @@ $as_echo "Mac OS X x86_64" >&6; } i*86-*-freebsd*/no) { $as_echo "$as_me:${as_lineno-$LINENO}: result: FreeBSD x86" >&5 $as_echo "FreeBSD x86" >&6; } - MPS_TARGET_NAME=fri3gc + MPS_OS_NAME=fr + MPS_ARCH_NAME=i3 + MPS_BUILD_NAME=gc # Need /usr/local/include in order to find sqlite3.h CFLAGS="-I/usr/local/include" CPP="$CC -I/usr/local/include -E" @@ -3485,7 +3516,9 @@ $as_echo "FreeBSD x86" >&6; } amd64-*-freebsd*/no | x86_64-*-freebsd*/no) { $as_echo "$as_me:${as_lineno-$LINENO}: result: FreeBSD x86_64" >&5 $as_echo "FreeBSD x86_64" >&6; } - MPS_TARGET_NAME=fri6gc + MPS_OS_NAME=fr + MPS_ARCH_NAME=i6 + MPS_BUILD_NAME=gc # Need /usr/local/include in order to find sqlite3.h CFLAGS="-I/usr/local/include" CPP="$CC -I/usr/local/include -E" @@ -3513,7 +3546,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MAKE="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3564,6 +3597,8 @@ CFLAGS="$CFLAGS $PFMCFLAGS" + + ac_config_files="$ac_config_files Makefile example/scheme/Makefile" @@ -4010,16 +4045,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. + # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' + as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -4079,28 +4114,16 @@ else as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -4121,8 +4144,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by Memory Pool System Kit $as_me master, which was -generated by GNU Autoconf 2.68. Invocation command line was +This file was extended by Memory Pool System Kit $as_me release/1.114.0, which was +generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -4175,11 +4198,11 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -Memory Pool System Kit config.status master -configured by $0, generated by GNU Autoconf 2.68, +Memory Pool System Kit config.status release/1.114.0 +configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -4257,7 +4280,7 @@ fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then - set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' diff --git a/configure.ac b/configure.ac index f401e20f7c..f564f047d5 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # configure.ac -- autoconf configuration for the MPS -*- Autoconf -*- # # $Id$ -# Copyright (C) 2012-2013 Ravenbrook Limited. See end of file for license. +# Copyright (C) 2012-2014 Ravenbrook Limited. See end of file for license. # # YOU DON'T NEED AUTOCONF TO BUILD THE MPS # This is just here for people who want or expect a configure script. @@ -14,9 +14,9 @@ # AC_PREREQ([2.50]) -# http://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.69/html_node/Initializing-configure.html#Initializing-configure +# http://www.gnu.org/software/autoconf/manual/autoconf.html#Initializing-configure AC_INIT([Memory Pool System Kit], - [master], + m4_esyscmd_s([sed -n '/^#define MPS_RELEASE "\(.*\)"$/{s/.*"\(.*\)"/\1/;p;}' code/version.c]), [mps-questions@ravenbrook.com], [mps-kit], [http://www.ravenbrook.com/project/mps/]) @@ -47,22 +47,30 @@ TEST_TARGET=test-make-build case $host/$CLANG in i*86-*-linux*/no) AC_MSG_RESULT([Linux x86]) - MPS_TARGET_NAME=lii3gc + MPS_OS_NAME=li + MPS_ARCH_NAME=i3 + MPS_BUILD_NAME=gc PFMCFLAGS="$CFLAGS_GC" ;; x86_64-*-linux*/no) AC_MSG_RESULT([Linux x86_64]) - MPS_TARGET_NAME=lii6gc + MPS_OS_NAME=li + MPS_ARCH_NAME=i6 + MPS_BUILD_NAME=gc PFMCFLAGS="$CFLAGS_GC" ;; x86_64-*-linux*/yes) AC_MSG_RESULT([Linux x86_64]) - MPS_TARGET_NAME=lii6ll + MPS_OS_NAME=li + MPS_ARCH_NAME=i6 + MPS_BUILD_NAME=ll PFMCFLAGS="$CFLAGS_LL" ;; i*86-*-darwin*/*) AC_MSG_RESULT([Mac OS X x86]) - MPS_TARGET_NAME=xci3ll + MPS_OS_NAME=xc + MPS_ARCH_NAME=i3 + MPS_BUILD_NAME=ll BUILD_TARGET=build-via-xcode CLEAN_TARGET=clean-xcode-build INSTALL_TARGET=install-xcode-build @@ -71,7 +79,9 @@ case $host/$CLANG in ;; x86_64-apple-darwin*/*) AC_MSG_RESULT([Mac OS X x86_64]) - MPS_TARGET_NAME=xci6ll + MPS_OS_NAME=xc + MPS_ARCH_NAME=i6 + MPS_BUILD_NAME=ll BUILD_TARGET=build-via-xcode CLEAN_TARGET=clean-xcode-build INSTALL_TARGET=install-xcode-build @@ -80,7 +90,9 @@ case $host/$CLANG in ;; i*86-*-freebsd*/no) AC_MSG_RESULT([FreeBSD x86]) - MPS_TARGET_NAME=fri3gc + MPS_OS_NAME=fr + MPS_ARCH_NAME=i3 + MPS_BUILD_NAME=gc # Need /usr/local/include in order to find sqlite3.h CFLAGS="-I/usr/local/include" CPP="$CC -I/usr/local/include -E" @@ -88,7 +100,9 @@ case $host/$CLANG in ;; amd64-*-freebsd*/no | x86_64-*-freebsd*/no) AC_MSG_RESULT([FreeBSD x86_64]) - MPS_TARGET_NAME=fri6gc + MPS_OS_NAME=fr + MPS_ARCH_NAME=i6 + MPS_BUILD_NAME=gc # Need /usr/local/include in order to find sqlite3.h CFLAGS="-I/usr/local/include" CPP="$CC -I/usr/local/include -E" @@ -111,7 +125,9 @@ AC_CHECK_HEADER([sqlite3.h], [EXTRA_TARGETS="$EXTRA_TARGETS mpseventsql"]) # those flags. CFLAGS="$CFLAGS $PFMCFLAGS" -AC_SUBST(MPS_TARGET_NAME) +AC_SUBST(MPS_OS_NAME) +AC_SUBST(MPS_ARCH_NAME) +AC_SUBST(MPS_BUILD_NAME) AC_SUBST(BUILD_TARGET) AC_SUBST(CLEAN_TARGET) AC_SUBST(INSTALL_TARGET) diff --git a/contributing.rst b/contributing.rst index d397be35ce..82b5ff4c84 100644 --- a/contributing.rst +++ b/contributing.rst @@ -1,11 +1,5 @@ Contributing to the MPS ======================= -:author: Richard Brooksby -:organization: Ravenbrook Limited -:date: 2013-07-05 -:revision: $Id$ -:confidentiality: public - We are very happy to receive contributions to the Memory Pool System so that we can improve it for everyone. diff --git a/design/abq.txt b/design/abq.txt index c82e2dabbc..ec3c944355 100644 --- a/design/abq.txt +++ b/design/abq.txt @@ -1,7 +1,7 @@ .. mode: -*- rst -*- -Queue design -============ +Fixed-length queues +=================== :Tag: design.mps.abq :Author: Gareth Rees @@ -92,7 +92,7 @@ If the queue is full, return ``TRUE``, otherwise return ``FALSE``. Return the number of elements in the queue. -``typedef Bool (*ABQIterateMethod)(Bool *deleteReturn, void *element, void *closureP, Size closureS)`` +``typedef Bool (*ABQVisitor)(Bool *deleteReturn, void *element, void *closureP, Size closureS)`` A callback function for ``ABQIterate()``. The parameter ``element`` is an element in the queue, and ``closureP`` and ``closureS`` are the @@ -102,10 +102,10 @@ the queue, or ``TRUE`` if ``element`` must be deleted from the queue. It must return ``TRUE`` if the iteration must continue, or ``FALSE`` if the iteration must stop after processing ``element``. -``void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP, Size closureS)`` +``void ABQIterate(ABQ abq, ABQVisitor visitor, void *closureP, Size closureS)`` -Call ``iterate`` for each elements in the queue, passing the element -and ``closureP``. See ``ABQIterateMethod`` for details. +Call ``visitor`` for each element in the queue, passing the element +and ``closureP``. See ``ABQVisitor`` for details. Document History diff --git a/design/an.txt b/design/an.txt new file mode 100644 index 0000000000..7d6cc11740 --- /dev/null +++ b/design/an.txt @@ -0,0 +1,216 @@ +.. mode: -*- rst -*- + +Generic modules +=============== + +:Tag: design.mps.an +:Author: Gareth Rees +:Date: 2014-11-02 +:Status: complete design +:Revision: $Id$ +:Copyright: See `Copyright and License`_. +:Index terms: pair: generic modules; design + + +Introduction +------------ + +_`.intro`: This is the design of generic modules in the MPS. + +_`.readership`: Any MPS developer; anyone porting the MPS to a new +platform. + +_`.overview`: Generic modules provide implementations of functional +modules using only the features of the Standard C Library. These +implementations are partially functional or non-functional, but +provide a basis for ports of the MPS to new platforms. + +_`.name`: The name "ANSI" for the generic modules is historical: the C +language was originally standardized by the American National +Standards Institute, and so Standard C used to be known as "ANSI C". + + +Requirements +------------ + +_`.req.port`: The MPS must be portable to new platforms. (Otherwise we +can't meet the needs of customers using new platforms.) + +_`.req.port.rapid`: The MPS should be portable to new platforms +rapidly. + +_`.req.port.rapid.expert`: An expert MPS developer (who may be a +novice on the new platform) should be able to get a minimally useful +implementation of the MPS running on a new platform within a few +hours. + +_`.req.port.rapid.novice`: A novice MPS developer (who is an expert on +the new platform) should be able to get the MPS running on a new +platform within a few days. + + +Design +------ + +_`.sol.modules`: Features of the MPS which can benefit from +platform-specific implementations are divided into *functional +modules*, with clean interfaces to the MPS and to each other. See +`.mod`_ for a list of these modules. (This helps meet `.req.port`_ by +isolating the platform dependencies, and it helps meet +`.req.port.rapid`_ because a porter can mix and match implementations, +using existing implementations where possible.) + +_`.sol.generic`: Each functional module has a generic implementation +using only features of the Standard C Library. (This helps meet +`.req.port.rapid`_ because the MPS can be ported in stages, starting +with the generic modules and porting the modules needed to meet the +most urgent requirements. The generic implementations help meet +`.req.port.rapid.novice`_ by providing clear and illustrative +examples.) + +_`.sol.fallback`: The interfaces to the modules are designed to make +it possible to implement `.sol.generic`_. When a platform-specific +feature is needed to meet performance (or other attribute) +requirements, the interface also makes it possible to meet the +functional requirements while missing the attribute requirements. See +`.sol.fallback.example`_ for an example. (This helps meet +`.req.port.rapid`_ by allowing the generic implementations to meet +many or most of the functional requirements.) + +_`.sol.fallback.example`: The MPS normally uses incremental collection +to meet requirements on pause times, but this requires barriers. The +interface to the protection module is designed to make it possible to +write an implementation without barriers, via the function +``ProtSync()`` that synchronizes the mutator with the collector. + +_`.sol.test`: There are makefiles for the pseudo-platforms ``anangc``, +``ananll`` and ``ananmv`` that compile and test the generic +implementations. See design.mps.config.opt_ for the configuration +options used to implement these platforms. (This supports +`.req.port.rapid`_ by making sure that the generic implementations are +working when it is time to use them.) + +.. _design.mps.config.opt: config#opt + + +Modules +------- + +_`.mod`: This section lists the functional modules in the MPS. + +_`.mod.lock`: Locks. See design.mps.lock_. + +_`.mod.prmc`: Protection mutator context. See design.mps.prmc_. + +_`.mod.prot`: Memory protection. See design.mps.prot_. + +_`.mod.ss`: Stack and register scanning. See design.mps.ss_. + +_`.mod.sp`: Stack probe. See design.mps.sp_. + +_`.mod.th`: Thread manager. See design.mps.thread-manager_. + +_`.mod.vm`: Virtual mapping. See design.mps.vm_. + +.. _design.mps.lock: lock +.. _design.mps.prot: prot +.. _design.mps.prmc: prmc +.. _design.mps.sp: sp +.. _design.mps.ss: ss +.. _design.mps.thread-manager: thread-manager +.. _design.mps.vm: vm + + +Limitations of generic implementations +-------------------------------------- + +_`.lim`: This section summarizes the limitations of the generic +implementations of the function modules. + +_`.lim.lock`: Requires a single-threaded mutator (see +design.mps.lock.impl.an_). + +_`.lim.prmc`: Does not support single-stepping of accesses (see +design.mps.prmc.impl.an.fault_) and requires a single-threaded mutator +(see design.mps.prmc.impl.an.suspend_). + +_`.lim.prot`: Does not support incremental collection (see +design.mps.prot.impl.an.sync_) and is not compatible with +implementations of the protection mutator context module that support +single-stepping of accesses (see design.mps.prot.impl.an.sync.issue_). + +_`.lim.sp`: Only suitable for use with programs that do not handle +stack overflow faults, or do not call into the MPS from the handler +(see design.mps.sp.issue.an_). + +_`.lim.ss`: Overscans compared to a platform-specific implementation +(see design.mps.ss.impl.an_). + +_`.lim.th`: Requires a single-threaded mutator (see +design.mps.thread-manager.impl.an.single_). + +_`.lim.vm`: Maps all reserved addresses into main memory (see +design.mps.vm.impl.an.reserve_), thus using more main memory than a +platform-specific implementation. + +.. _design.mps.lock.impl.an: lock#impl.an +.. _design.mps.prmc.impl.an.fault: prmc#impl.an.fault +.. _design.mps.prmc.impl.an.suspend: prmc#impl.an.suspend +.. _design.mps.prot.impl.an.sync: prot#impl.an.sync +.. _design.mps.prot.impl.an.sync.issue: prot#impl.an.sync.issue +.. _design.mps.sp.issue.an: sp#issue.an +.. _design.mps.ss.impl.an: ss#impl.an +.. _design.mps.thread-manager.impl.an.single: thread-manager#impl.an.single +.. _design.mps.vm.impl.an.reserve: vm#impl.an.reserve + + + +Document History +---------------- + +- 2014-11-02 GDR_ Initial draft based on design.mps.protan. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 2014 Ravenbrook Limited. All rights reserved. +. This is an open source license. Contact +Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** diff --git a/design/arena.txt b/design/arena.txt index a1fae81ce5..2a94bb05b3 100644 --- a/design/arena.txt +++ b/design/arena.txt @@ -39,12 +39,19 @@ by generic arena code, and some by arena class code. Definitions ----------- -_`.def.tract`: Pools request memory from the arena by calling -``ArenaAlloc()``. This returns a block comprising a contiguous sequence -of "tracts". A tract has a specific size (also known as the "arena -alignment", which typically corresponds to the operating system page -size) and all tracts are aligned to that size. "Tract" is also used -for the data structure used to manage tracts. +_`.def.grain`: The arena manages memory in units called *arena +grains*, whose size is returned by the macro ``ArenaGrainSize()``. +Memory allocated by ``ArenaAlloc()`` is a contiguous sequence of arena +grains, whose base address and size are multiples of the arena grain +size. + +_`.def.tract`: A tract is a data structure containing information +about a region of address space: which pool it belongs to (if any), +for which traces the contents is white, and so on. Tracts are the hook +on which the segment module is implemented. Pools which don't use +segments may use tracts for associating their own data with ranges of +address. + Requirements @@ -56,11 +63,14 @@ Requirements sources of requirements so that they are traceable to client requirements. Most of these come from the architectural design (design.mps.architecture) or the fix function design - (design.mps.fix). Richard Brooksby, 1995-08-28. + (design.mps.fix_). Richard Brooksby, 1995-08-28. - They were copied from design.mps.arena.vm(1) and edited slightly. + They were copied from design.mps.arena.vm_ and edited slightly. David Jones, 1999-06-23. + .. _design.mps.fix: fix + .. _design.mps.arena.vm: arenavm + Block management ................ @@ -69,13 +79,20 @@ _`.req.fun.block.alloc`: The arena must provide allocation of contiguous blocks of memory. _`.req.fun.block.free`: It must also provide freeing of contiguously -allocated blocks owned by a pool - whether or not the block was +allocated blocks owned by a pool, whether or not the block was allocated via a single request. _`.req.attr.block.size.min`: The arena must support management of -blocks down to the size of the grain (page) provided by the virtual -mapping interface if a virtual memory interface is being used, or a -comparable size otherwise. +blocks down to the larger of (i) the grain size of the virtual mapping +interface (if a virtual memory interface is being used); and (ii) the +grain size of the memory protection interface (if protection is used). + +.. note:: + + On all the operating systems we support, these grain sizes are the + same and are equal to the operating system page size. But we want + the MPS to remain flexible enough to be ported to operating + systems where these are different. _`.req.attr.block.size.max`: It must also support management of blocks up to the maximum size allowed by the combination of operating system @@ -87,44 +104,38 @@ than ``MPS_PF_ALIGN`` for the architecture. This is so that pool classes can conveniently guarantee pool allocated blocks are aligned to ``MPS_PF_ALIGN``. (A trivial requirement.) -_`.req.attr.block.grain.max`: The granularity of allocation shall not -be more than the grain size provided by the virtual mapping interface. - Address translation ................... _`.req.fun.trans`: The arena must provide a translation from any -address to either an indication that the address is not in any tract -(if that is so) or the following data associated with the tract -containing that address: +address to the following information: -_`.req.fun.trans.pool`: The pool that allocated the tract. +_`.req.fun.trans.arena`: Whether the address is managed by the arena. -_`.req.fun.trans.arbitrary`: An arbitrary pointer value that the pool -can associate with the tract at any time. +_`.req.fun.trans.pool`: Whether the address is managed by a pool +within the arena, and if it is, the pool. -_`.req.fun.trans.white`: The tracer whiteness information. That is, a -bit for each active trace that indicates whether this tract is white -(contains white objects). This is required so that the "fix" protocol -can run very quickly. - -_`.req.attr.trans.time`: The translation shall take no more than @@@@ -[something not very large -- drj 1999-06-23] +_`.req.fun.trans.arbitrary`: If the address is managed by a pool, an +arbitrary pointer value that the pool can associate with a group of +contiguous addresses at any time. +_`.req.fun.trans.white`: If the address is managed by an automatic +pool, the set of traces for which the address is white. This is +required so that the second-stage fix protocol can reject non-white +addresses quickly. See design.mps.critical-path_. -Iteration protocol -.................. +.. _design.mps.critical-path: critical-path -_`.req.iter`: er, there's a tract iteration protocol which is -presumably required for some reason? +_`.req.attr.trans.time`: The translation shall take no more than @@@@ +[something not very large -- drj 1999-06-23] Arena partition ............... -_`.req.fun.set`: The arena must provide a method for approximating sets -of addresses. +_`.req.fun.set`: The arena must provide a method for approximating +sets of addresses. _`.req.fun.set.time`: The determination of membership shall take no more than @@@@ [something very small indeed]. (the non-obvious @@ -185,9 +196,11 @@ Arena classes ``typedef mps_arena_s *Arena`` _`.class`: The ``Arena`` data structure is designed to be subclassable -(see design.mps.protocol(0)). Clients can select what arena class -they'd like when instantiating one with ``mps_arena_create()``. The -arguments to ``mps_arena_create()`` are class-dependent. +(see design.mps.protocol_). Clients can select what arena class +they'd like when instantiating one with ``mps_arena_create_k()``. The +arguments to ``mps_arena_create_k()`` are class-dependent. + +.. _design.mps.protocol: protocol _`.class.init`: However, the generic ``ArenaInit()`` is called from the class-specific method, rather than vice versa, because the method is @@ -195,7 +208,7 @@ responsible for allocating the memory for the arena descriptor and the arena lock in the first place. Likewise, ``ArenaFinish()`` is called from the finish method. -_`.class.fields`: The ``alignment`` (for tract allocations) and +_`.class.fields`: The ``grainSize`` (for allocation and freeing) and ``zoneShift`` (for computing zone sizes and what zone an address is in) fields in the arena are the responsibility of the each class, and are initialized by the ``init`` method. The responsibility for @@ -220,15 +233,54 @@ 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_. + +_`.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 possible, if care is taken) and then calling +``TreeBalance()`` on the remaining tree. The function +``TreeTraverseAndDelete()`` implements this. + +_`.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 over the chunks (so the +best we can do is O(*n*) time in any case). + +_`.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. The function +``TreeTraverseAndDelete()`` ensures that this is done. + + Tracts ...... -_`.tract`: The arena allocation function ``ArenaAlloc()`` allocates a -block of memory to pools, of a size which is aligned to the arena -alignment. Each alignment unit (grain) of allocation is represented by -a tract. Tracts are the hook on which the segment module is -implemented. Pools which don't use segments may use tracts for -associating their own data with each allocation grain. +_`.tract.table`: The arena maintains tables of tract structures such +that every address managed by the arena belongs to exactly one tract. + +_`.tract.size`: Each tract covers exactly one arena grain. This is an +implementation detail, not a requirement. _`.tract.structure`: The tract structure definition looks like this:: @@ -237,7 +289,7 @@ _`.tract.structure`: The tract structure definition looks like this:: 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? */ + BOOLFIELD(hasSeg); /* does tract have a seg in p? */ } TractStruct; _`.tract.field.pool`: The pool.pool field indicates to which pool the tract @@ -262,10 +314,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. +If this field is ``TRUE``, then the value of ``p`` is a ``Seg``. See +design.mps.type.bool.bitfield_ for why this is declared using the +``BOOLFIELD`` macro. + +.. _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. @@ -273,19 +326,20 @@ memory represented by the tract. _`.tract.field.white`: The white bit-field indicates for which traces the tract is white (`.req.fun.trans.white`_). This information is also stored in the segment, but is duplicated here for efficiency during a -call to ``TraceFix`` (see design.mps.trace.fix). +call to ``TraceFix()`` (see design.mps.trace.fix_). + +.. _design.mps.trace.fix: trace#fix _`.tract.limit`: The limit of the tract's memory may be determined by -adding the arena alignment to the base address. +adding the arena grain size to the base address. _`.tract.iteration`: Iteration over tracts is described in design.mps.arena.tract-iter(0). ``Bool TractOfAddr(Tract *tractReturn, Arena arena, Addr addr)`` -_`.tract.if.tractofaddr`: The function ``TractOfAddr()`` finds the tract -corresponding to an address in memory. (See `.req.fun.trans`_.) - +_`.tract.if.tractofaddr`: The function ``TractOfAddr()`` finds the +tract corresponding to an address in memory. (See `.req.fun.trans`_.) If ``addr`` is an address which has been allocated to some pool, then ``TractOfAddr()`` returns ``TRUE``, and sets ``*tractReturn`` to the tract corresponding to that address. Otherwise, it returns ``FALSE``. @@ -293,10 +347,6 @@ This function is similar to ``TractOfBaseAddr()`` (see design.mps.arena.tract-iter.if.contig-base) but serves a more general purpose and is less efficient. -_`.tract.if.TRACT_OF_ADDR`: ``TRACT_OF_ADDR()`` is a macro version of -``TractOfAddr()``. It's provided for efficiency during a call to -``TraceFix()`` (see design.mps.trace.fix.tractofaddr). - Control pool ............ @@ -382,13 +432,13 @@ bytes; ``spareCommitLimit`` records the limit (set by the user) on the amount of spare committed memory. ``spareCommitted`` is modified by the arena class but its value is used by the generic arena code. There are two uses: a getter function for this value is provided through the -MPS interface (``mps_arena_spare_commit_limit_set()``), and by the -``SetSpareCommitLimit()`` function to determine whether the amount of -spare committed memory needs to be reduced. ``spareCommitLimit`` is -manipulated by generic arena code, however the associated semantics -are the responsibility of the class. It is the class's responsibility -to ensure that it doesn't use more spare committed bytes than the -value in ``spareCommitLimit``. +MPS interface (``mps_arena_spare_commit_limit()``), and by the +``ArenaSetSpareCommitLimit()`` function to determine whether the +amount of spare committed memory needs to be reduced. +``spareCommitLimit`` is manipulated by generic arena code, however the +associated semantics are the responsibility of the class. It is the +class's responsibility to ensure that it doesn't use more spare +committed bytes than the value in ``spareCommitLimit``. _`.spare-commit-limit`: The function ``ArenaSetSpareCommitLimit()`` sets the ``spareCommitLimit`` field. If the limit is set to a value lower @@ -510,12 +560,12 @@ _`.ld.epoch`: ``arena->epoch`` is the "current epoch". This is the number of 'flips' of traces in the arena since the arena was created. From the mutator's point of view locations change atomically at flip. -_`.ld.history`: ``arena->history`` is an array of ``ARENA_LD_LENGTH`` -elements of type ``RefSet``. These are the summaries of moved objects -since the last ``ARENA_LD_LENGTH`` epochs. If ``e`` is one of these -recent epochs, then :: +_`.ld.history`: ``arena->history`` is a circular buffer of +``LDHistoryLENGTH`` elements of type ``RefSet``. These are the +summaries of moved objects since the last ``LDHistoryLENGTH`` epochs. +If ``e`` is one of these recent epochs, then :: - arena->history[e % ARENA_LD_LENGTH] + arena->history[e % LDHistoryLENGTH] is a summary of (the original locations of) objects moved since epoch ``e``. diff --git a/design/arenavm.txt b/design/arenavm.txt index 92b24d8234..d746f18d88 100644 --- a/design/arenavm.txt +++ b/design/arenavm.txt @@ -17,10 +17,11 @@ Virtual Memory Arena Introduction ------------ -_`.intro`: This document describes the detailed design of the Virtual -Memory Arena Class of the Memory Pool System. The VM Arena Class is -just one class available in the MPS. The generic arena part is -described in design.mps.arena. +_`.intro`: This is the design of the Virtual Memory Arena Class of the +Memory Pool System. The VM Arena Class is just one class available in +the MPS. The generic arena part is described in design.mps.arena_. + +.. _design.mps.arena: arena Overview @@ -28,23 +29,30 @@ Overview _`.overview`: VM arenas provide blocks of memory to all other parts of the MPS in the form of "tracts" using the virtual mapping interface -(design.mps.vm) to the operating system. The VM Arena Class is not +(design.mps.vm_) to the operating system. The VM Arena Class is not expected to be provided on platforms that do not have virtual memory -(like MacOS, os.s7(1)). +(for example, Macintosh System 7). + +.. _design.mps.vm: vm _`.overview.gc`: The VM Arena Class provides some special services on these blocks in order to facilitate garbage collection: _`.overview.gc.zone`: Allocation of blocks with specific zones. This -means that the generic fix function (design.mps.fix) can use a fast +means that the generic fix function (design.mps.fix_) can use a fast refset test to eliminate references to addresses that are not in the condemned set. This assumes that a pool class that uses this placement appropriately is being used (such as the generation placement policy -used by AMC: see design.mps.poolamc(1)) and that the pool selects the +used by AMC: see design.mps.poolamc_) and that the pool selects the condemned sets to coincide with zone stripes. +.. _design.mps.fix: fix +.. _design.mps.poolamc: poolamc + _`.overview.gc.tract`: A fast translation from addresses to tract. -(See design.mps.arena.req.fun.trans) +(See design.mps.arena.req.fun.trans_.) + +.. _design.mps.arena.req.fun.trans: arena#req.fun.trans Notes @@ -52,16 +60,20 @@ Notes _`.note.refset`: Some of this document simply assumes that RefSets (see the horribly incomplete design.mps.refset) have been chosen as -the solution for design.mps.arena.req.fun.set. It's a lot simpler that -way. Both to write and understand. +the solution for design.mps.arena.req.fun.set_. It's a lot simpler +that way. Both to write and understand. + +.. _design.mps.arena.req.fun.set: arena#req.fun.set Requirements ------------ Most of the requirements are in fact on the generic arena (see -design.mps.arena.req). However, many of those requirements can only be -met by a suitable arena class design. +design.mps.arena.req_). However, many of those requirements can only +be met by a suitable arena class design. + +.. _design.mps.arena.req: arena#req Requirements particular to this arena class: @@ -78,17 +90,17 @@ of suitable addresses. Arena partition ............... -_`.req.fun.set`: See design.mps.arena.req.fun.set. The approximation -to sets of address must cooperate with the placement mechanism in the -way required by `.req.fun.place`_ (above). +_`.req.fun.set`: See design.mps.arena.req.fun.set_. The +approximation to sets of address must cooperate with the placement +mechanism in the way required by `.req.fun.place`_ (above). Architecture ------------ _`.arch.memory`: The underlying memory is obtained from whatever -Virtual Memory interface (see design.mps.vm). @@@@ Explain why this is -used. +Virtual Memory interface (see design.mps.vm_). @@@@ Explain why this +is used. Solution ideas @@ -172,10 +184,14 @@ _`.table.page.partial`: The table is partially mapped on an _`.table.page.tract`: Each page table entry contains a tract, which is only valid if it is allocated to a pool. If it is not allocated to a pool, the fields of the tract are used for other purposes. (See -design.mps.arena.tract.field.pool) +design.mps.arena.tract.field.pool_) + +.. _design.mps.arena.tract.field.pool: arena#tract.field.pool _`.table.alloc`: The alloc table is a simple bit table (implemented -using the BT module, design.mps.bt). +using the BT module, design.mps.bt_). + +.. _design.mps.bt: bt _`.table.alloc.map`: Each page in the VM has a corresponding alloc table entry. diff --git a/design/bootstrap.txt b/design/bootstrap.txt new file mode 100644 index 0000000000..8615aee0f9 --- /dev/null +++ b/design/bootstrap.txt @@ -0,0 +1,141 @@ +.. mode: -*- rst -*- + +Bootstrapping +============= + +:Tag: design.mps.bootstrap +:Author: Gareth Rees +:Date: 2015-09-01 +:Status: incomplete design +:Revision: $Id$ +:Copyright: See section `Copyright and License`_. +:Index terms: pair: bootsrap; design + + +Introduction +------------ + +_`.intro`: This explains how the MPS gets started. + +_`.readership`: Any MPS developer. + +_`.overview`: The job of the MPS is to allocate memory to a program. +Before it can allocate memory, the MPS needs to create data structures +to represent its internal state. But before it can create those data +structures, it needs to allocate memory to store them in. This +bootstrapping problem affects the MPS at several points, which are +listed here, together with their solutions. + + +Bootstrapping problems +---------------------- + +Virtual memory descriptor +......................... + +_`.vm`: Before address space can be mapped into main memory, the +virtual memory descriptor must be initialized. But before the virtual +memory descriptor can be initialized, some address space must be +mapped into main memory in order to store it. See +`design.vm.req.bootstrap`_. + +_`.vm.sol`: The virtual memory descriptor is allocated initially on +the stack, and then copied into its place in the chunk after the +memory for it has been mapped. See `design.vm.sol.bootstrap`_. + +.. _design.vm.req.bootstrap: vm#req.bootstrap +.. _design.vm.sol.bootstrap: vm#sol.bootstrap + + +Arena descriptor +................ + +_`.arena`: Before chunks of address space can be reserved and mapped, +the virtual memory arena descriptor must be initialized (so that the +chunks can be added to the arena's chunk tree). But before a virtual +memory arena descriptor can be initialized, address space must be +reserved and mapped in order to store it. + +_`.arena.sol`: A small amount of address space is reserved and mapped +directly via ``VMInit()`` and ``VMMap()`` (not via the chunk system) +in order to provide enough memory for the arena descriptor. + + +Arena's free land +................. + +_`.land`: Before the arena can allocate memory, a range of addresses +must be inserted into the arena's free land (so that the free land can +hand out memory from this range). But before addresses can be inserted +into the arena's free land, the free land's block pool must have +memory from the arena to store the nodes in the tree representing +those addresses. + +_`.land.sol`: The arena has two "back door" mechanisms and uses them +in combination. + +_`.land.sol.alloc`: First, there is a mechanism for allocating a +page of memory directly from a chunk, bypassing the free land. + +_`.land.sol.pool`: Second, the free land's block pool has an option to +prevent it extending itself by allocating memory from the arena. +Instead, it fails allocations with ``ResLIMIT``. The free land's +block pool also has a mechanism, ``MFSExtend`` to extend it with a +block of memory. When the free land fails with ``ResLIMIT`` the arena +uses `.land.sol.alloc`_ to provide it with memory. + + + +Document History +---------------- + +- 2015-09-01 GDR_ Initial draft. + +- 2016-02-25 RB_ Improving description of arena free land bootstrap + and cross-referencing from source code. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ +.. _RB: http://www.ravenbrook.com/consultants/rb/ + + +Copyright and License +--------------------- + +Copyright © 2015-2016 Ravenbrook Limited +. All rights reserved. This is an open +source license. Contact Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** diff --git a/design/bt.txt b/design/bt.txt index d83d9af6c4..bc069efd2a 100644 --- a/design/bt.txt +++ b/design/bt.txt @@ -15,14 +15,14 @@ Bit tables Introduction ------------ -_`.intro`: This is the design of the Bit Tables module. A Bit Table is a -linear array of bits. A Bit Table of length *n* is indexed using an +_`.intro`: This is the design of the Bit Tables module. A Bit Table is +a linear array of bits. A Bit Table of length *n* is indexed using an integer from 0 up to (but not including) *n*. Each bit in a Bit Table -can hold either the value 0 (aka ``FALSE``) or 1 (aka ``TRUE``). A -variety of operations are provided including: set, reset, and -retrieve, individual bits; set and reset a contiguous range of bits; -search for a contiguous range of reset bits; making a "negative image" -copy of a range. +can hold either the value 0 (``FALSE``) or 1 (``TRUE``). A variety of +operations are provided including: get, set, and reset individual +bits; set and reset a contiguous range of bits; search for a +contiguous range of reset bits; making a "negative image" copy of a +range. _`.readership`: MPS developers. @@ -34,15 +34,13 @@ _`.def.set`: **Set** Used as a verb meaning to assign the value 1 or ``TRUE`` to a bit. Used descriptively to denote a bit containing the value 1. Note 1 - and ``TRUE`` are synonyms in MPS C code (see - design.mps.type(0).bool.value). + and ``TRUE`` are synonyms in MPS C code (see ``Bool``). _`.def.reset`: **Reset** Used as a verb meaning to assign the value 0 or ``FALSE`` to a bit. Used descriptively to denote a bit containing the value 0. - Note 0 and ``FALSE`` are synonyms in MPS C code (see - design.mps.type(0).bool.value). + Note 0 and ``FALSE`` are synonyms in MPS C code (see ``Bool``). .. note:: @@ -55,7 +53,7 @@ _`.def.bt`: **Bit Table** A Bit Table is a mapping from [0, *n*) to {0,1} for some *n*, represented as a linear array of bits. - _`..def.bt.justify`: They are called *bit tables* because a single + _`.def.bt.justify`: They are called *Bit Tables* because a single bit is used to encode whether the image of a particular integer under the map is 0 or 1. @@ -68,22 +66,22 @@ _`.def.range`: **Range** [*base*, *limit*), is used. - Requirements ------------ -_`.req.bit`: The storage for a Bit Table of *n* bits shall take no more -than a small constant addition to the storage required for *n* bits. -_`..req.bit.why`: This is so that clients can make some predictions -about how much storage their algorithms use. A small constant is -allowed over the minimal for two reasons: inevitable implementation -overheads (such as only being able to allocate storage in multiples of -32 bits), extra storage for robustness or speed (such as signature and -length fields). +_`.req.bit`: The storage for a Bit Table of *n* bits shall take no +more than a small constant addition to the storage required for *n* +bits. _`.req.bit.why`: This is so that clients can make some +predictions about how much storage their algorithms use. A small +constant is allowed over the minimal for two reasons: inevitable +implementation overheads (such as only being able to allocate storage +in multiples of 32 bits), extra storage for robustness or speed (such +as signature and length fields). -_`.req.create`: A means to create Bit Tables. _`.req.create.why`: Obvious. +_`.req.create`: A means to create Bit Tables. _`.req.create.why`: +Obvious. -_`.req.destroy`: A means to destroy Bit Tables. _`.req.destroy.why` +_`.req.destroy`: A means to destroy Bit Tables. _`.req.destroy.why`: Obvious. _`.req.ops`: The following operations shall be supported: @@ -118,7 +116,7 @@ operations. range of bits are all reset. _`.req.ops.test.range.reset.why` As for IsSetRange, see `.req.ops.test.range.set.why`_. -* _`.req.ops.find`: Find a range (which we'll denote [*i*, *j*)) of at +* _`.req.ops.find`: Find a range, which we'll denote [*i*, *j*), of at least *L* reset bits that lies in a specified subrange of the entire Bit Table. Various find operations are required according to the (additional) properties of the required range: @@ -176,8 +174,8 @@ operations. manipulation code in ``PoolClassAMS`` and ``PoolClassLO``. _`.req.speed`: Operations shall take no more than a few memory -operations per bit manipulated. _`.req.speed.why`: Any slower -would be gratuitous. +operations per bit manipulated. _`.req.speed.why`: Any slower would be +gratuitous. _`.req.speed.fast`: The following operations shall be very fast: @@ -188,7 +186,7 @@ _`.req.speed.fast`: The following operations shall be very fast: _`.req.speed.fast.find.short.why`: These two are used by the client arena (design.mps.arena.client) and the VM arena - (design.mps.arena.vm) for finding segments in page tables. The + (design.mps.arena.vm_) for finding segments in page tables. The operation will be used sufficiently often that its speed will noticeably affect the overall speed of the MPS. They will be called with a length equal to the number of pages in a segment. Typical @@ -198,18 +196,24 @@ _`.req.speed.fast`: The following operations shall be very fast: it is populated at all, that is set bits will tend to be clustered together in subranges. + .. _design.mps.arena.vm: arenavm + * _`.req.speed.fast.find.long`: FindLongResRange (the operation used to meet `.req.ops.find.long.low`_) _`.req.speed.fast.find.long.why`: Used in the allocator for - ``PoolClassAWL`` (design.mps.poolawl(1)), ``PoolClassAMS`` - (design.mps.poolams(2)), ``PoolClassEPVM`` (design.mps.poolepvm(0)). + ``PoolClassAWL`` (design.mps.poolawl_), ``PoolClassAMS`` + (design.mps.poolams_), ``PoolClassEPVM`` (design.mps.poolepvm(0)). Of these AWL and EPVM have speed requirements. For AWL the length of range to be found will be the length of a Dylan table in words. - According to mail.tony.1999-05-05.11-36(0), only - objects are allocated in AWL (though not all objects - are allocated in AWL), and the mean length of an - object is 486 Words. No data for EPVM alas. + According to `mail.tony.1999-05-05.11-36`_, only ```` + objects are allocated in AWL (though not all ```` + objects are allocated in AWL), and the mean length of an + ```` object is 486 Words. No data for EPVM alas. + + .. _design.mps.poolawl: poolawl + .. _design.mps.poolams: poolams + .. _mail.tony.1999-05-05.11-36: https://info.ravenbrook.com/project/mps/mail/1999/05/05/11-36/0.txt _`.req.speed.fast.other.why`: We might expect mark and sweep pools to make use of Bit Tables, the MPS has general requirements to support @@ -242,10 +246,14 @@ Background ---------- _`.background`: Originally Bit Tables were used and implemented -by ``PoolClassLO`` (design.mps.poollo). It was +by ``PoolClassLO`` (design.mps.poollo_). It was decided to lift them out into a separate module when designing the Pool to manage Dylan Weak Tables which is also a mark and sweep pool -and will make use of Bit Tables (see design.mps.poolawl). +and will make use of Bit Tables (see design.mps.poolawl_). + +.. _design.mps.poollo: poollo +.. _design.mps.poolawl: poolawl + _`.background.analysis`: analysis.mps.bt(0) contains some of the analysis of the design decisions that were and were not made in this document. @@ -256,7 +264,7 @@ Clients _`.clients`: Bit Tables are used throughout the MPS but the important uses are in the client and VM arenas (design.mps.arena.client(0) and -design.mps.arena.vm(1)) a bit table is used to record whether each +design.mps.arena.vm_) a bit table is used to record whether each page is free or not; several pool classes (``PoolClassLO``, ``PoolClassEPVM``, ``PoolClassAMS``) use bit tables to record which locations are free and also to store colour. @@ -366,7 +374,7 @@ _`.if.test.range.reset`: Returns ``TRUE`` if all the bits in the range [``base``, ``limit``) are reset, ``FALSE`` otherwise. Meets `.req.ops.test.range.reset`_. -``Bool BTRangesSame(BT BTx, BT BTy, Index base, Index limit);`` +``Bool BTRangesSame(BT BTx, BT BTy, Index base, Index limit)`` _`.if.test.range.same`: returns ``TRUE`` if ``BTGet(BTx,i)`` equals ``BTGet(BTy,i)`` for ``i`` in [``base``, ``limit``), and ``FALSE`` @@ -693,10 +701,12 @@ _`.test.btcv`: ``btcv.c``. This is supposed to be a coverage test, intended to execute all of the module's code in at least some minimal way. -_`.test.cbstest`: ``cbstest.c``. This was written as a test of the -``CBS`` module (design.mps.cbs(2)). It compares the functional -operation of a ``CBS`` with that of a ``BT`` so is a good functional -test of either module. +_`.test.landtest`: ``landtest.c``. This is a test of the ``Land`` +module (design.mps.land_) and its concrete implementations. It +compares the functional operation of a ``Land`` with that of a ``BT`` +so is a good functional test of either module. + +.. _design.mps.land: land _`.test.mmqa.120`: MMQA_test_function!210.c. This is used because it has a fair amount of segment allocation and freeing so exercises the arena diff --git a/design/buffer.txt b/design/buffer.txt index 391e86b7c6..ae55fa3420 100644 --- a/design/buffer.txt +++ b/design/buffer.txt @@ -15,8 +15,8 @@ Allocation buffers and allocation points Introduction ------------ -_`.scope`: This document describes the design of allocation buffers -and allocation points. +_`.scope`: This is the design of allocation buffers and allocation +points. _`.purpose`: The purpose of this document is to record design decisions made concerning allocation buffers and allocation points and @@ -48,11 +48,12 @@ the archives if you can't find what you want here. 2006-06-09. _`.source.synchronize`: For a discussion of the synchronization -issues: +issues, see `mail.richard.1995-05-19.17-10`_, +`mail.ptw.1995-05-19.19-15`_, and `mail.richard.1995-05-24.10-18`_. -* `mail.richard.1995-05-19.17-10 `_ -* `mail.ptw.1995-05-19.19-15 `_ -* `mail.richard.1995-05-24.10-18 `_ +.. _mail.richard.1995-05-19.17-10: https://info.ravenbrook.com/project/mps/mail/1995/05/19/17-10/0.txt +.. _mail.ptw.1995-05-19.19-15: https://info.ravenbrook.com/project/mps/mail/1995/05/19/19-15/0.txt +.. _mail.richard.1995-05-24.10-18: https://info.ravenbrook.com/project/mps/mail/1995/05/24/10-18/0.txt .. note:: @@ -60,33 +61,39 @@ issues: incorrect. The operations should be in the other order. DRJ. _`.source.interface`: For a description of the buffer interface in C -prototypes: +prototypes, see `mail.richard.1997-04-28.09-25`_. -* `mail.richard.1997-04-28.09-25(0) `_ +.. _mail.richard.1997-04-28.09-25: https://info.ravenbrook.com/project/mps/mail/1997/04/28/09-25/0.txt _`.source.qa`: Discussions with QA were useful in pinning down the semantics and understanding of some obscure but important boundary -cases. See the thread starting -`mail.richard.tucker.1997-05-12.09-45(0) -`_ with subject "notes on -our allocation points discussion": - -* `mail.richard.tucker.1997-05-12.09-45 `_ -* `mail.ptw.1997-05-12.12-46(1) `_ -* `mail.richard.1997-05-12.13-15 `_ -* `mail.richard.1997-05-12.13-28 `_ -* `mail.ptw.1997-05-13.15-15 `_ -* `mail.sheep.1997-05-14.11-52 `_ -* `mail.rit.1997-05-15.09-19 `_ -* `mail.ptw.1997-05-15.21-22 `_ -* `mail.ptw.1997-05-15.21-35 `_ -* `mail.rit.1997-05-16.08-02 `_ -* `mail.rit.1997-05-16.08-42 `_ -* `mail.ptw.1997-05-16.12-36 `_ -* `mail.ptw.1997-05-16.12-47 `_ -* `mail.richard.1997-05-19.15-46 `_ -* `mail.richard.1997-05-19.15-56 `_ -* `mail.ptw.1997-05-20.20-47 `_ +cases. See the thread with subject "notes on our allocation points +discussion" and messages `mail.richard.tucker.1997-05-12.09-45`_, +`mail.ptw.1997-05-12.12-46`_, `mail.richard.1997-05-12.13-15`_, +`mail.richard.1997-05-12.13-28`_, `mail.ptw.1997-05-13.15-15`_, +`mail.sheep.1997-05-14.11-52`_, `mail.rit.1997-05-15.09-19`_, +`mail.ptw.1997-05-15.21-22`_, `mail.ptw.1997-05-15.21-35`_, +`mail.rit.1997-05-16.08-02`_, `mail.rit.1997-05-16.08-42`_, +`mail.ptw.1997-05-16.12-36`_, `mail.ptw.1997-05-16.12-47`_, +`mail.richard.1997-05-19.15-46`_, `mail.richard.1997-05-19.15-56`_, +and `mail.ptw.1997-05-20.20-47`_. + +.. _mail.richard.tucker.1997-05-12.09-45: https://info.ravenbrook.com/project/mps/mail/1997/05/12/09-45/0.txt +.. _mail.ptw.1997-05-12.12-46: https://info.ravenbrook.com/project/mps/mail/1997/05/12/12-46/1.txt +.. _mail.richard.1997-05-12.13-15: https://info.ravenbrook.com/project/mps/mail/1997/05/12/13-15/0.txt +.. _mail.richard.1997-05-12.13-28: https://info.ravenbrook.com/project/mps/mail/1997/05/12/13-28/0.txt +.. _mail.ptw.1997-05-13.15-15: https://info.ravenbrook.com/project/mps/mail/1997/05/13/15-15/0.txt +.. _mail.sheep.1997-05-14.11-52: https://info.ravenbrook.com/project/mps/mail/1997/05/14/11-52/0.txt +.. _mail.rit.1997-05-15.09-19: https://info.ravenbrook.com/project/mps/mail/1997/05/15/09-19/0.txt +.. _mail.ptw.1997-05-15.21-22: https://info.ravenbrook.com/project/mps/mail/1997/05/15/21-22/0.txt +.. _mail.ptw.1997-05-15.21-35: https://info.ravenbrook.com/project/mps/mail/1997/05/15/21-35/0.txt +.. _mail.rit.1997-05-16.08-02: https://info.ravenbrook.com/project/mps/mail/1997/05/16/08-02/0.txt +.. _mail.rit.1997-05-16.08-42: https://info.ravenbrook.com/project/mps/mail/1997/05/16/08-42/0.txt +.. _mail.ptw.1997-05-16.12-36: https://info.ravenbrook.com/project/mps/mail/1997/05/16/12-36/0.txt +.. _mail.ptw.1997-05-16.12-47: https://info.ravenbrook.com/project/mps/mail/1997/05/16/12-47/0.txt +.. _mail.richard.1997-05-19.15-46: https://info.ravenbrook.com/project/mps/mail/1997/05/19/15-46/0.txt +.. _mail.richard.1997-05-19.15-56: https://info.ravenbrook.com/project/mps/mail/1997/05/19/15-56/0.txt +.. _mail.ptw.1997-05-20.20-47: https://info.ravenbrook.com/project/mps/mail/1997/05/20/20-47/0.txt @@ -121,7 +128,9 @@ Classes ------- _`.class.hierarchy`: The ``Buffer`` data structure is designed to be -subclassable (see design.mps.protocol). +subclassable (see design.mps.protocol_). + +.. _design.mps.protocol: protocol _`.class.hierarchy.buffer`: The basic buffer class (``BufferClass``) supports basic allocation-point buffering, and is appropriate for @@ -171,15 +180,19 @@ _`.subclassing`: Pools may create their own subclasses of the standard buffer classes. This is sometimes useful if the pool needs to add an extra field to the buffer. The convenience macro ``DEFINE_BUFFER_CLASS()`` may be used to define subclasses of buffer -classes. See design.mps.protocol.int.define-special. +classes. See design.mps.protocol.int.define-special_. + +.. _design.mps.protocol.int.define-special: protocol#int.define-special _`.replay`: To work with the allocation replayer (see -design.mps.telemetry.replayer), the buffer class has to emit an event +design.mps.telemetry.replayer_), the buffer class has to emit an event for each call to an external interface, containing all the parameters passed by the user. If a new event type is required to carry this information, the replayer (impl.c.eventrep) must then be extended to recreate the call. +.. _design.mps.telemetry.replayer: telemetry#replayer + _`.replay.pool-buffer`: The replayer must also be updated if the association of buffer class to pool or the buffer class hierarchy is changed. @@ -243,10 +256,10 @@ setter method which sets the rank set of a buffer. It is called from ``BufferSetRankSet()``. Clients should not need to define their own methods for this. -``typedef Res (*BufferDescribeMethod)(Buffer buffer, mps_lib_FILE *stream)`` +``typedef Res (*BufferDescribeMethod)(Buffer buffer, mps_lib_FILE *stream, Count depth)`` _`.class.method.describe`: ``describe()`` is a class-specific method -called to describe a buffer, via BufferDescribe. Client-defined +called to describe a buffer, via ``BufferDescribe()``. Client-defined methods must call their superclass method (via a next-method call) before describing any class-specific state. @@ -281,13 +294,11 @@ pool using the ``PoolClass`` ``BufferEmpty()`` method). _`.count.generic`: These fields are maintained by the generic buffer code in ``BufferAttach()`` and ``BufferDetach()``. -_`.count.other`: Similar count fields are maintained in the pool and -the arena. They are maintained on an internal (buffers used internally -by the MPS) and external (buffers used for mutator allocation points) -basis. The fields are also updated by the buffer code. The fields are: +_`.count.other`: Similar count fields are maintained in the arena. +They are maintained on an internal (buffers used internally by the +MPS) and external (buffers used for mutator allocation points) basis. +The fields are also updated by the buffer code. The fields are: -- in the pool: ``fillMutatorSize``, ``fillInternalSize``, - ``emptyMutatorSize``, and ``emptyInternalSize`` (4 fields); - in the arena, ``fillMutatorSize``, ``fillInternalSize``, ``emptyMutatorSize``, ``emptyInternalSize``, and ``allocMutatorSize`` (5 fields). @@ -398,8 +409,6 @@ Memory Barriers will need to be placed at the points indicated. * DESIGN * - * design.mps.buffer. - * * An allocation buffer is an interface to a pool which provides * very fast allocation, and defers the need for synchronization in * a multi-threaded environment. @@ -606,7 +615,9 @@ _`.method.arena`: Returns the arena which owns a buffer. _`.method.arena.thread-safe`: ``BufferArena()`` must be thread safe (see impl.c.mpsi.thread-safety). This is achieved simple because the underlying operation is a read of shared-non-mutable data (see -design.mps.thread-safety). +design.mps.thread-safety_). + +.. _design.mps.thread-safety: thread-safety ``Pool BufferPool(Buffer buffer)`` @@ -714,7 +725,9 @@ Document History ``BufferSpace()``, ``BufferSet()`` and ``BufferReset()`` are now ``BufferArena()``, ``BufferAttach()`` and ``BufferDetach()`` respectively; ``BufferExpose()`` and ``BufferCover()`` have been - moved to the Shield interface; see design.mps.shield). + moved to the Shield interface; see design.mps.shield_). + + .. _design.mps.shield: shield .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/cbs.txt b/design/cbs.txt index 6531cd0209..34c2eedd4c 100644 --- a/design/cbs.txt +++ b/design/cbs.txt @@ -1,7 +1,7 @@ .. mode: -*- rst -*- -Coalescing block structure -========================== +Coalescing block structures +=========================== :Tag: design.mps.cbs :Author: Gavin Matthews @@ -20,7 +20,10 @@ eager coalescence. _`.readership`: This document is intended for any MM developer. -_`.source`: design.mps.poolmv2, design.mps.poolmvff. +_`.source`: design.mps.poolmvt_, design.mps.poolmvff_. + +.. _design.mps.poolmvt: poolmvt +.. _design.mps.poolmvff: poolmvff _`.overview`: The "coalescing block structure" is a set of addresses (or a subset of address space), with provision for efficient @@ -29,50 +32,27 @@ high level communication with the client about the size of contiguous ranges, and detection of protocol violations. -Definitions ------------ - -_`.def.range`: A (contiguous) *range* of addresses is a semi-open -interval on address space. - -_`.def.isolated`: A contiguous range is *isolated* with respect to -some property it has, if adjacent elements do not have that property. - - Requirements ------------ -_`.req.set`: Must maintain a set of addresses. - -_`.req.fast`: Common operations must have a low amortized cost. - -_`.req.add`: Must be able to add address ranges to the set. +In addition to the generic land requirements (see +design.mps.land_), the CBS must satisfy: -_`.req.remove`: Must be able to remove address ranges from the set. +.. _design.mps.land: land -_`.req.size`: Must report concisely to the client when isolated -contiguous ranges of at least a certain size appear and disappear. - -_`.req.iterate`: Must support the iteration of all isolated -contiguous ranges. This will not be a common operation. - -_`.req.protocol`: Must detect protocol violations. - -_`.req.debug`: Must support debugging of client code. +_`.req.fast`: Common operations must have a low amortized cost. _`.req.small`: Must have a small space overhead for the storage of typical subsets of address space and not have abysmal overhead for the storage of any subset of address space. -_`.req.align`: Must support an alignment (the alignment of all -addresses specifying ranges) of down to ``sizeof(void *)`` without -losing memory. - Interface --------- -_`.header`: CBS is used through impl.h.cbs. +_`.land`: CBS is an implementation of the *land* abstract data type, +so the interface consists of the generic functions for lands. See +design.mps.land_. External types @@ -80,180 +60,111 @@ External types ``typedef struct CBSStruct *CBS`` -_`.type.cbs`: ``CBS`` is the main data structure for manipulating a -CBS. It is intended that a ``CBSStruct`` be embedded in another -structure. No convenience functions are provided for the allocation or -deallocation of the CBS. - -``typedef Bool (*CBSIterateMethod)(CBS cbs, Range range, void *closureP, Size closureS)`` - -_`.type.cbs.iterate.method`: Type ``CBSIterateMethod`` is a callback -function that may be passed to ``CBSIterate()``. It is called for -every isolated contiguous range in address order. The function must -returns a ``Bool`` indicating whether to continue with the iteration. +_`.type.cbs`: The type of coalescing block structures. A ``CBSStruct`` +may be embedded in another structure, or you can create it using +``LandCreate()``. External functions .................. -``Res CBSInit(Arena arena, CBS cbs, void *owner, Align alignment, Bool fastFind, ArgList args)`` - -_`.function.cbs.init`: ``CBSInit()`` is the function that initialises -the CBS structure. It performs allocation in the supplied arena. The -parameter ``owner`` is passed to ``MeterInit()``, an ``alignment`` -indicates the alignment of ranges to be maintained. An initialised CBS -contains no ranges. - -``fastFind``, if set, causes the CBS to maintain, for each subtree, -the size of the largest block in that subtree. This must be true if -any of the ``CBSFindFirst()``, ``CBSFindLast()``, or -``CBSFindLargest()`` functions are going to be used on the CBS. - -``CBSInit()`` may take one keyword argument: +``LandClass CBSLandClassGet(void)`` -* ``MPS_KEY_CBS_EXTEND_BY`` (type ``Size``; default 4096) is the size - of segment that the CBS will request from the arena in which to - allocate its ``CBSBlock`` structures. - -``void CBSFinish(CBS cbs)`` +_`.function.class`: The function ``CBSLandClassGet()`` returns the CBS +class, a subclass of ``LandClass`` suitable for passing to +``LandCreate()`` or ``LandInit()``. -_`.function.cbs.finish`: ``CBSFinish()`` is the function that finishes -the CBS structure and discards any other resources associated with the -CBS. - -``Res CBSInsert(Range rangeReturn, CBS cbs, Range range)`` +``LandClass CBSFastLandClassGet(void)`` -_`.function.cbs.insert`: If any part of ``range`` is already in the -CBS, then leave it unchanged and return ``ResFAIL``. Otherwise, -attempt to insert ``range`` into the CBS. If the insertion succeeds, -then update ``rangeReturn`` to describe the contiguous isolated range -containing the inserted range (this may differ from ``range`` if there -was coalescence on either side) and return ``ResOK``. If the insertion -fails, return a result code indicating allocation failure. +_`.function.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. -_`.function.cbs.insert.fail`: Insertion of a valid range (that is, one -that does not overlap with any range in the CBS) can only fail if the -new range is isolated and the allocation of the necessary data -structure to represent it failed. +``LandClass CBSZonedLandClassGet(void)`` +_`.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. -``Res CBSDelete(Range rangeReturn, CBS cbs, Range range)`` -_`.function.cbs.delete`: If any part of the range is not in the CBS, -then leave the CBS unchanged and return ``ResFAIL``. Otherwise, update -``rangeReturn`` to describe the contiguous isolated range that -contains ``range`` (this may differ from ``range`` if there are -fragments on either side) and attempt to delete the range from the -CBS. If the deletion succeeds, return ``ResOK``. If the deletion -fails, return a result code indicating allocation failure. -_`.function.cbs.delete.fail`: Deletion of a valid range (that is, one -that is wholly contained in the CBS) can only fail if there are -fragments on both sides and the allocation of the necessary data -structures to represent them fails. +Keyword arguments +................. -_`.function.cbs.delete.return`: ``CBSDelete()`` returns the contiguous -isolated range that contains ``range`` even if the deletion fails. -This is so that the caller can try deleting the whole block (which is -guaranteed to succeed) and managing the fragments using a fallback -strategy. +When initializing a CBS, ``LandCreate()`` and ``LandInit()`` take the +following optional keyword arguments: -``void CBSIterate(CBS cbs, CBSIterateMethod iterate, void *closureP, Size closureS)`` +* ``CBSBlockPool`` (type ``Pool``) is the pool from which the CBS + block descriptors will be allocated. If omitted, a new MFS pool is + created for this purpose. -_`.function.cbs.iterate`: ``CBSIterate()`` is the function used to -iterate all isolated contiguous ranges in a CBS. It receives a -pointer, ``Size`` closure pair to pass on to the iterator method, -and an iterator method to invoke on every range in address order. If -the iterator method returns ``FALSE``, then the iteration is -terminated. +* ``MPS_KEY_CBS_EXTEND_BY`` (type ``Size``; default 4096) is passed as + the ``MPS_KEY_EXTEND_BY`` keyword argument to ``PoolCreate()`` if a + block descriptor pool is created. It specifies the size of segment + that the block descriptor pool will request from the arena. -``Res CBSDescribe(CBS cbs, mps_lib_FILE *stream)`` +* ``MFSExtendSelf`` (type ``Bool``; default ``TRUE``) is passed to + ``PoolCreate()`` if a block descriptor pool is created. If ``TRUE``, + the block descriptor pool automatically extends itself when out of + space; if ``FALSE``, the pool returns ``ResLIMIT`` in this case. + (This feature is used by the arena to bootstrap its own CBS of free + memory.) -_`.function.cbs.describe`: ``CBSDescribe()`` is a function that prints -a textual representation of the CBS to the given stream, indicating -the contiguous ranges in order, as well as the structure of the -underlying splay tree implementation. It is provided for debugging -purposes only. -``Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn, CBS cbs, Size size, FindDelete findDelete)`` +Limitations +........... -_`.function.cbs.find.first`: Locate the first block (in address order) -within the CBS of at least the specified size, update ``rangeReturn`` -to describe that range, and return ``TRUE``. If there is no such -block, it returns ``FALSE``. +_`.limit.find`: ``CBSLandClass`` does not support the +``LandFindFirst()``, ``LandFindLast()``, and ``LandFindLargest()`` +generic functions (the subclasses do support these operations). -In addition, optionally delete the top, bottom, or all of the found -range, depending on the ``findDelete`` argument. This saves a separate -call to ``CBSDelete()``, and uses the knowledge of exactly where we -found the range. The value of ``findDelete`` must come from this -enumeration:: +_`.limit.zones`: ``CBSLandClass`` and ``CBSFastLandClass`` do not +support the ``LandFindInZones()`` generic function (the subclass +``CBSZonedLandClass`` does support this operation). - enum { - FindDeleteNONE, /* don't delete after finding */ - FindDeleteLOW, /* delete size bytes from low end of block */ - FindDeleteHIGH, /* delete size bytes from high end of block */ - FindDeleteENTIRE /* delete entire range */ - }; +_`.limit.iterate`: CBS does not provide an implementation for the +``LandIterateAndDelete()`` generic function. This is because +``TreeTraverse()`` does not permit modification, for speed and to +avoid perturbing the splay tree balance. -The original contiguous isolated range in which the range was found is -returned via the ``oldRangeReturn`` argument. (If ``findDelete`` is -``FindDeleteNONE`` or ``FindDeleteENTIRE``, then this will be -identical to the range returned via the ``rangeReturn`` argument.) - -``CBSFindFirst()`` requires that ``fastFind`` was true when -``CBSInit()`` was called. - -``Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn, CBS cbs, Size size, FindDelete findDelete)`` - -_`.function.cbs.find.last`: Like ``CBSFindFirst()``, except that it -finds the last block in address order. - -``Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn, CBS cbs, Size size, FindDelete findDelete)`` - -_`.function.cbs.find.largest`: Locate the largest block within the -CBS, and if that block is at least as big as ``size``, return its -range via the ``rangeReturn`` argument, and return ``TRUE``. If there -are no blocks in the CBS at least as large as ``size``, return -``FALSE``. Pass 0 for ``size`` if you want the largest block -unconditionally. - -Like ``CBSFindFirst()``, optionally delete the range (specifying -``FindDeleteLOW`` or ``FindDeleteHIGH`` has the same effect as -``FindDeleteENTIRE``). This feature requires that ``fastFind`` was -true when ``CBSInit()`` was called. +_`.limit.flush`: CBS cannot be used as the source in a call to +``LandFlush()``. (Because of `.limit.iterate`_.) Implementation -------------- -_`.impl`: This section is concerned with describing various aspects of -the implementation. It does not form part of the interface definition. - - - Splay tree .......... -_`.impl.splay`: The CBS is principally implemented using a splay tree -(see design.mps.splay_). Each splay tree node is embedded in a -``CBSBlock`` that represents a semi-open address range. The key passed +_`.impl.splay`: The CBS is implemented using a splay tree (see +design.mps.splay_). Each splay tree node is embedded in a block +structure that represents a semi-open address range. The key passed for comparison is the base of another range. .. _design.mps.splay: splay -_`.impl.splay.fast-find`: ``CBSFindFirst()`` and ``CBSFindLast()`` use -the update/refresh facility of splay trees to store, in each -``CBSBlock``, an accurate summary of the maximum block size in the -tree rooted at the corresponding splay node. This allows rapid -location of the first or last suitable block, and very rapid failure -if there is no suitable block. +_`.impl.splay.fast-find`: In the ``CBSFastLandClass`` class, +``cbsFindFirst()`` and ``cbsFindLast()`` use the update/refresh +facility of splay trees to store, in each block, an accurate summary +of the maximum block size in the tree rooted at the corresponding +splay node. This allows rapid location of the first or last suitable +block, and very rapid failure if there is no suitable block. -_`.impl.find-largest`: ``CBSFindLargest()`` simply finds out the size +_`.impl.find-largest`: ``cbsFindLargest()`` simply finds out the size of the largest block in the CBS from the root of the tree, using ``SplayRoot()``, and does ``SplayFindFirst()`` for a block of that size. This takes time proportional to the logarithm of the size of the free list, so it's about the best you can do without maintaining a -separate priority queue, just to do ``CBSFindLargest()``. +separate priority queue, just to do ``cbsFindLargest()``. + +_`.impl.splay.zones`: In the ``CBSZonedLandClass`` class, +``cbsFindInZones()`` uses the update/refresh facility of splay trees +to store, in each block, the union of the zones of the ranges in the +tree rooted at the corresponding splay node. This allows rapid +location of a block in a set of zones. Low memory behaviour @@ -261,10 +172,10 @@ Low memory behaviour _`.impl.low-mem`: When the CBS tries to allocate a new ``CBSBlock`` structure for a new isolated range as a result of either -``CBSInsert()`` or ``CBSDelete()``, and there is insufficient memory -to allocation the ``CBSBlock`` structure, then the range is not added -to the CBS or deleted from it, and the call to ``CBSInsert()`` or -``CBSDelete()`` returns ``ResMEMORY``. +``LandInsert()`` or ``LandDelete()``, and there is insufficient memory +to allocate the block structure, then the range is not added to the +CBS or deleted from it, and the call to ``LandInsert()`` or +``LandDelete()`` returns ``ResMEMORY``. The CBS block @@ -285,19 +196,14 @@ Testing _`.test`: The following testing will be performed on this module: -_`.test.cbstest`: There is a stress test for this module in -impl.c.cbstest. This allocates a large block of memory and then -simulates the allocation and deallocation of ranges within this block -using both a ``CBS`` and a ``BT``. It makes both valid and invalid -requests, and compares the ``CBS`` response to the correct behaviour -as determined by the ``BT``. It also iterates the ranges in the -``CBS``, comparing them to the ``BT``. It also invokes the -``CBSDescribe()`` method, but makes no automatic test of the resulting -output. It does not currently test the callbacks. +_`.test.land`: A generic test for land implementations. See +design.mps.land.test_. + +.. _design.mps.land.test: land#test -_`.test.pool`: Several pools (currently MVT_ and MVFF_) are implemented -on top of a CBS. These pool are subject to testing in development, QA, -and are/will be heavily exercised by customers. +_`.test.pool`: The arena and two pools (MVT_ and MVFF_) are +implemented on top of a CBS. These are subject to testing in +development, QA, and are heavily exercised by customers. .. _MVT: poolmvt .. _MVFF: poolmvff @@ -306,9 +212,9 @@ and are/will be heavily exercised by customers. Notes for future development ---------------------------- -_`.future.not-splay`: The initial implementation of CBSs is based on -splay trees. It could be revised to use any other data structure that -meets the requirements (especially `.req.fast`_). +_`.future.not-splay`: The implementation of CBSs is based on splay +trees. It could be revised to use other data structures that meet the +requirements (especially `.req.fast`_). _`.future.hybrid`: It would be possible to attenuate the problem of `.risk.overhead`_ (below) by using a single word bit set to represent @@ -318,6 +224,11 @@ converting them when they reach all free in the bit set. Note that this would make coalescence slightly less eager, by up to ``(word-width - 1)``. +_`.future.iterate.and.delete`: It would be possible to provide an +implementation for the ``LandIterateAndDelete()`` generic function by +calling ``TreeToVine()`` first, and then iterating over the vine +(where deletion is straightforward). + Risks ----- @@ -330,12 +241,13 @@ the size of that area. [Four words per two grains.] The CBS structure is thus suitable only for managing large enough ranges. - Document History ---------------- - 1998-05-01 Gavin Matthews. This document was derived from the - outline in design.mps.poolmv2(2). + outline in design.mps.poolmv2_. + +.. _design.mps.poolmv2: poolmv2 - 1998-07-22 Gavin Matthews. Updated in response to approval comments in change.epcore.anchovy.160040. There is too much fragmentation in @@ -359,6 +271,9 @@ Document History talking about the deleted "emergency" free list allocator. Documented ``fastFind`` argument to ``CBSInit()``. +- 2014-04-01 GDR_ Moved generic material to design.mps.land_. + Documented new keyword arguments. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/check.txt b/design/check.txt index c0d2a8a97b..138c2a7fc1 100644 --- a/design/check.txt +++ b/design/check.txt @@ -26,9 +26,10 @@ Implementation _`.level`: There are three levels of checking: -#. _`.level.sig`: The lowest level checks only that the - structure has a valid ``Signature`` (see - design.mps.sig). +#. _`.level.sig`: The lowest level checks only that the structure has + a valid ``Signature`` (see design.mps.sig_). + + .. _design.mps.sig: sig #. _`.level.shallow`: Shallow checking checks all local fields (including signature) and also checks the signatures of any parent @@ -95,17 +96,30 @@ type structure pointer, possibly invoking ``Check`` (depending on level; see `.level`_). It should be called with the child type and pointer. -_`.full-type`: ``CHECKS()``, ``CHECKD()``, ``CHECKU()``, all operate -only on fully fledged types. This means the type has to provide a -function ``Bool TypeCheck(Type type)`` where ``Type`` is substituted -for the name of the type (for example, ``PoolCheck()``), and the -expression ``obj->sig`` must be a valid value of type ``Sig`` whenever -``obj`` is a valid value of type ``Type``. - -_`.type.no-sig`: This tag is to be referenced in implementations -whenever the form ``CHECKL(ThingCheck(thing))`` is used instead of -``CHECK{U,D}(Thing, thing)`` because ``Thing`` is not a fully fledged -type (`.full-type`_). +_`.full-type`: Use ``CHECKS()``, ``CHECKD()`` or ``CHECKU()`` on all +types that satisfy these three requirements: + +_`.full-type.pointer`: The type is a pointer type. + +_`.full-type.check`: The type provides a function ``Bool TypeCheck(Type +type)`` where ``Type`` is substituted for the name of the type (for +example, ``PoolCheck()``). + +_`.full-type.sig`: The expression ``obj->sig`` is a valid value of +type ``Sig`` whenever ``obj`` is a valid value of type ``Type``. + +_`.partial-type`: Where the type satisfies `.full-type.pointer`_ and +`.full-type.check`_ but not `.full-type.sig`_ because the type lacks a +signature in order to save space (this applies to small structures +that are embedded many times in other structures, for example +``Ring``), use ``CHECKD_NOSIG()``. + +_`.hidden-type`: Where the type satisfies `.full-type.pointer`_ and +`.full-type.check`_ but not `.full-type.sig`_ because the structure +has a signature but the structure definition is not visible at point +of checking (for example ``Root``), use ``CHECKD_NOSIG()`` and +reference this tag. The structure could be considered for addition to +``mpmst.h``. Document History diff --git a/design/class-interface.txt b/design/class-interface.txt index 1f716703df..83e839cdc2 100644 --- a/design/class-interface.txt +++ b/design/class-interface.txt @@ -20,231 +20,216 @@ the MPM and the pool class implementations. .. note:: - This document should be merged into design.mps.pool. Pekka P. + This document should be merged into design.mps.pool_. Pekka P. Pirinen, 1999-07-20. + .. _design.mps.pool: pool -Methods -------- -_`.methods`: These methods are provided by pool classes as part of the -``PoolClass`` object (see impl.h.mpmst.class). They form the interface -which allows the MPM to treat pools in a uniform manner. +Fields +------ -The following description is based on the definition of the -``PoolClassStruct`` (impl.h.mpmst.class). +_`.field`: These fields are provided by pool classes as part of the +``PoolClass`` object (see impl.h.mpmst.class). They form part of the +interface which allows the MPM to treat pools in a uniform manner. -If a class is not required to provide a certain method then it should -set the appropriate ``PoolNo*`` method for that method. It is not -acceptable to use ``NULL``. +_`.field.name`: The ``name`` field should be a short, pithy, cryptic +name for the pool class. It should typically start with ``"A"`` if +memory is managed by the garbage collector, and ``"M"`` if memory is +managed by alloc/free. Examples are "AMC", "MV". -.. note:: +_`.field.attr`: The ``attr`` field must be a bitset of pool class +attributes. See design.mps.type.attr_. - There are also some ``PoolTriv*`` methods. David Jones, 1997-08-19. - -_`.method.name`: The name field should be a short, pithy, cryptic name -for the pool class. Examples are "AMC", "MV". - -The ``size`` field is the size of the pool instance structure. For the -``Foo`` ``PoolClass`` this can reasonably be expected to be -``sizeof(FooStruct)``. - -The ``offset`` field is the offset into the pool instance structure of -the generic ``PoolStruct``. Typically this field is called -``poolStruct``, so something like ``offsetof(FooStruct, poolStruct)`` -is typical. If possible, arrange for this to be zero. - -The ``init`` field is the class's init method. This method is called -via the generic function ``PoolInit()``, which is in turn called by -``PoolCreate()``. The generic function allocates the pool's structure -(using the size and offset information), initializes the -``PoolStruct`` (generic part) then calls the ``init`` method to do any -class-specific initialization. Typically this means initializing the -fields in the class instance structure. If ``init`` returns a non-OK -result code the instance structure will be deallocated and the code -returned to the caller of ``PoolInit()``` or ``PoolCreate()``. Note that -the ``PoolStruct`` isn't made fully valid until ``PoolInit()`` returns. - -The ``finish`` field is the class's finish method. This method is -called via the generic function ``PoolFinish()``, which is in turn -called by ``PoolDestroy()``. It is expected to finalise the pool -instance structure and release any resources allocated to the pool, it -is expected to release the memory associated with the pool instance -structure. Note that the pool is valid when it is passed to -``finish``. The ``PoolStruct`` (generic part) is finished off when the -class's ``finish`` method returns. - -The ``alloc`` field is the class's allocation method. This method is -called via the generic function ``PoolAlloc()``. It is expected to -return a pointer to a fresh (that is, not overlapping with any other -live object) object of the required size. Failure to allocate should -be indicated by returning an appropriate Error code, and in such a -case, ``*pReturn`` should not be updated. Classes are not required to -provide this method, but they should provide at least one of ``alloc`` -and ``bufferCreate``. +.. _design.mps.type.attr: type -.. note:: +_`.field.size`: The ``size`` field is the size of the pool instance +structure. For the ``PoolFoo`` class this can reasonably be expected +to be ``sizeof(PoolFooStruct)``. - There is no ``bufferCreate``. Gareth Rees, 2013-04-14. +_`.field.offset`: The ``offset`` field is the offset into the pool +instance structure of the generic ``PoolStruct``. Typically this field +is called ``poolStruct``, so something like ``offsetof(PoolFooStruct, +poolStruct)`` is typical. If possible, arrange for this to be zero. -The ``free_`` field is the class's free method. This is intended -primarily for manual style pools. this method is called via the -generic function ``PoolFree()``. The parameters to this method are + +Methods +------- + +_`.method`: These methods are provided by pool classes as part of the +``PoolClass`` object (see impl.h.mpmst.class). They form part of the +interface which allows the MPM to treat pools in a uniform manner. + +_`.method.unused`: If a pool class is not required to provide a +certain method, the class should assign the appropriate ``PoolNo`` +method for that method to ensure that erroneous calls are detected. It +is not acceptable to use ``NULL``. + +_`.method.trivial`: If a pool class if required to provide a certain +method, but the class provides no special behaviour in this case, it +should assign the appropriate ``PoolTriv`` method. + +_`.method.init`: The ``init`` field is the pool class's init method. +This method is called via the generic function ``PoolInit()``, which +is in turn called by ``PoolCreate()``. The generic function allocates +the pool's structure (using the ``size`` and ``offset`` fields), +initializes the ``PoolStruct`` (generic part), then calls the ``init`` +method to do any class-specific initialization. Typically this means +initializing the fields in the pool instance structure. If ``init`` +returns a non-OK result code the instance structure will be +deallocated and the code returned to the caller of ``PoolInit()`` or +``PoolCreate()``. Note that the ``PoolStruct`` isn't made fully valid +until ``PoolInit()`` returns, so the ``init`` method must not call +``PoolCheck()``. + +_`.method.finish`: The ``finish`` field is the pool class's finish +method. This method is called via the generic function +``PoolFinish()``, which is in turn called by ``PoolDestroy()``. It is +expected to finalise the pool instance structure, release any +resources allocated to the pool, and release the memory associated +with the pool instance structure. Note that the pool is valid when it +is passed to ``finish``. The ``PoolStruct`` (generic part) is finished +when the pool class's ``finish`` method returns. + +_`.method.alloc`: The ``alloc`` field is the pool class's allocation +method. This method is called via the generic function +``PoolAlloc()``. It is expected to return a pointer to a fresh (that +is, not overlapping with any other live object) object of the required +size. Failure to allocate should be indicated by returning an +appropriate error code, and in such a case, ``*pReturn`` should not be +updated. Pool classes are not required to provide this method. + +_`.method.free`: The ``free`` method is the pool class's free method. +This is intended primarily for manual style pools. This method is +called via the generic function ``PoolFree()``. The parameters are required to correspond to a previous allocation request (possibly via a buffer). It is an assertion by the client that the indicated object is no longer required and the resources associated with it can be -recycled. Pools are not required to provide this method. - -The ``bufferInit`` field is the class's buffer initialization method. -It is called by the generic function ``BufferCreate()``, which allocates -the buffer descriptor and initializes the generic fields. The pool may -optionally adjust these fields or fill in extra values when -``bufferInit`` is called, but often pools set ``bufferInit`` to -``PoolTrivBufferInit()`` because they don't need to do any. If -``bufferInit`` returns a result code other than ``ResOK``, the buffer -structure is deallocated and the code is returned to the called of -``BufferCreate()``. Note that the ``BufferStruct`` isn't fully valid -until ``BufferCreate()`` returns. - -The ``bufferFinish`` field is the class's buffer finishing method. It -is called by the the generic function ``BufferDestroy()``. The pool is -expected to detach the buffer from any memory and prepare the buffer -for destruction. The class is expected to release the resources -associated with the buffer structure, and any unreserved memory in the -buffer may be recycled. It is illegal for a buffer to be destroyed -when there are pending allocations on it (that is, an allocation has -been reserved, but not committed) and this is checked in the generic -function. This method should be provided if and only if -``bufferCreate`` is provided. [there is no ``bufferCreate`` -- drj -1997-08-19] - -The ``condemn`` field is used to condemn a pool. This method is called -via the generic function ``PoolCondemn()``. The class is expected to -condemn a subset (possible the whole set) of objects it manages and -participate in a global trace to determine liveness. The class should -register the refsig of the condemned set with the trace using -``TraceCondemn()``. The class should expect fix requests (via the fix -method below) during a global trace. Classes are not required to -provide this method, but it is expected that automatic style classes -will. This interface is expected to change in the future. - -.. note:: - - ``condemn`` now takes an action and a segment and should condemn - the segment (turn it white) if it corresponds to the - interpretation of the action. David Jones, 1997-08-19. - - It is now called ``whiten``. David Jones, 1998-02-02. - -The ``mark`` field is used to mark an entire pool. This method is -called via the generic function ``PoolMark()``. The class should -consider all of its objects, except any set that has been condemned in -this trace, to be marked, that is ready for scanning. The class should -arrange that any appropriate invariants are preserved possibly by the -Protection interface. Classes are not required to provide this method, -and not doing so indicates that all instances of this class will have -no fixable or traceable references in them. - -.. note:: - - ``mark`` is no longer present: ``grey`` turns an entire segment - grey. David Jones, 1997-08-19. - -The ``scan`` field is used to perform scanning. This method is called -via the generic function ``PoolScan()``. The class should scan the -segment specified. It should scan all the known live (marked, that is, -those objects on which fix has been called) on the segment and -accumulate a summary of *all* the objects on the segment. This means -that mark and sweep pools may have to jump through hoops a little bit -(see design.mps.poolasm.summary for a pedagogical example). Classes -are not required to provide this method, and not doing so indicates -that all instances of this class will have no fixable or traceable -reference in them. - -.. note:: - - The ``scan`` method now takes an extra return parameter which - classes should use to indicate whether they scanned all objects in - segment or not. Classes should return summary only of object they - scanned. Caller of this method (``TraceScan()``) is responsible - for updating summaries correctly when not a total scan. Hence no - jumping through hoops required. David Jones, 1998-01-30. - -The ``fix`` field is used to perform fixing. This method is called via -the generic function ``TraceFix()``. It indicates that the specified -reference has been found and the class should consider the object -live. There is provision for adjusting the value of the reference (to -allow for classes that move objects). Classes are not required to -provide this method, and not doing so indicates that the class is not -automatic style (ie it does not use global tracing to determine -liveness). - -The ``reclaim`` field is used to reclaim memory. This method is called -via the generic function ``PoolReclaim()``. It indicates that the trace -has fixed all references to reachable objects. - -.. note:: - - Actually it indicates that any remaining white objects have now - been proved unreachable, hence are dead. David Jones, 1997-08-19. - -The class should consider objects that have been condemned and not -fixed in this trace to be dead and may reclaim the resources -associated with them. Classes are not required to provide this method. - -.. note:: - - ``reclaim`` is now called on each segment. David Jones, - 1997-08-19. - -The ``access`` field is used to indicate client access. This method is -called via the generic functions ``SpaceAccess()`` and -``PoolAccess()``. It indicates that the client has attempted to access -the specified region, but has been denied and the request trapped due -to a protection state. The class should perform any work necessary to -remove the protection whilst still preserving appropriate invariants -(typically this will be scanning work). Classes are not required to -provide this method, and not doing so indicates they never protect any -memory managed by the pool. - -.. note:: - - ``access`` is no longer present. David Jones, 1997-08-19. - -_`.method.act`: ``act`` is called when the MPM has decided to execute -an action that the class declared. The Class should arrange execution -of the associated work (usually by beginning an incremental trace). - -_`.method.walk`: ``walk`` is used by the heap walker. ``walk`` is only -required to be implemented by classes which specify the AttrFMT -attribute (formatted pools). The ``walk`` method should apply the -passed in function (along with its closure variables (which are also -passed in) and the object format) to all *black* objects in the -segment. Padding objects may or may not be included in the walk at the -classes discretion, in any case in will be the responsibility of the -client to do something sensible with padding objects. - -.. note:: - - What about broken hearts? David Jones, 1998-01-30. - -The ``describe`` field is used to print out a description of a pool. -This method is called via the generic function ``PoolDescribe()``. The -class should emit an textual description of the pool's contents onto -the specified stream. Each line should begin with two spaces. Classes -are not required to provide this method. +recycled. Pool classes are not required to provide this method. + +_`.method.bufferInit`: The ``bufferInit`` method is the pool class's +buffer initialization method. It is called by the generic function +``BufferCreate()``, which allocates the buffer descriptor and +initializes the generic fields. The pool may optionally adjust these +fields or fill in extra values. If ``bufferInit`` returns a result +code other than ``ResOK``, the buffer structure is deallocated and the +result code is returned to the caller of ``BufferCreate()``. Note that +the ``BufferStruct`` isn't fully valid until ``BufferCreate()`` +returns. Pool classes are not required to provide this method. + +_`.method.bufferFinish`: The ``bufferFinish`` method is the pool +class's buffer finishing method. It is called by the the generic +function ``BufferDestroy()``. The pool is expected to detach the +buffer from any memory and prepare the buffer for destruction. The +pool is expected to release the resources associated with the buffer +structure, and any unreserved memory in the buffer may be recycled. It +is illegal for a buffer to be destroyed when there are pending +allocations on it (that is, an allocation has been reserved, but not +committed) and this is checked in the generic function. This method +must be provided if and only if ``bufferInit`` is provided. + +_`.method.access`: The ``access`` method is used to handle client +access. This method is called via the generic functions +``ArenaAccess()`` and ``PoolAccess()``. It indicates that the client +has attempted to access the specified region, but has been denied and +the request trapped due to a protection state. The pool should perform +any work necessary to remove the protection whilst still preserving +appropriate invariants (typically this will be scanning work). Pool +classes are not required to provide this method, and not doing so +indicates they never protect any memory managed by the pool. + +_`.method.whiten`: The ``whiten`` method is used to condemn a segment +belonging to a pool. This method is called via the generic function +``PoolWhiten()``. The pool is expected to condemn a subset (but +typically all) of the objects in the segment and prepare the segment +for participation in a global trace to determine liveness. The pool +should expect fix requests (via the ``fix`` method below) during a +global trace. Pool classes that automatically reclaim dead objects +must provide this method, and must additionally set the ``AttrGC`` +attribute. + +_`.method.grey`: The ``grey`` method is used to greyen a segment +belonging to a pool. This method is called via the generic function +``PoolGrey()``. The pool should set all of the objects in the segment +(excepting any set that has been condemned in this trace) to be grey, +that is, ready for scanning. The pool should arrange that any +appropriate invariants are preserved, possibly by using the protection +interface (see design.mps.prot_). Pool classes are not required to +provide this method, and not doing so indicates that all instances of +this class will have no fixable or traceable references in them. + +.. _design.mps.prot: prot + +_`.method.blacken`: The ``blacken`` method is used to blacken a +segment belonging to a pool. This method is called via the generic +function ``PoolBlacken()`` when it is known that the segment cannot +refer to the white set. The pool must blacken all grey objects in the +segment. Pool classes are not required to provide this method, and not +doing so indicates that all instances of this class will have no +fixable or traceable references in them. + +_`.method.scan`: The ``scan`` method is used to scan a segment. This +method is called via the generic function ``PoolScan()``. The pool +must scan all the known grey objects on the segment and it may also +accumulate a summary of *all* the objects on the segment. If it +succeeds in accumulating such a summary it must indicate that it has +done so by setting the ``totalReturn`` parameter to ``TRUE``. Pool +classes are not required to provide this method, and not doing so +indicates that all instances of this class will have no fixable or +traceable reference in them. + +_`.method.fix`: The ``fix`` method is used to perform fixing. This +method is called via the generic function ``TraceFix()``. It indicates +that the specified reference has been found and the pool should +consider the object to be live. There is provision for adjusting the +value of the reference (to allow for classes that move objects). not +required to provide this method. Pool classes that automatically +reclaim dead objects must provide this method, and must additionally +set the ``AttrGC`` attribute. Pool classes that may move objects must +also set the ``AttrMOVINGGC`` attribute. + +_`.method.fixEmergency`: The ``fixEmergency`` method is used to +perform fixing in "emergency" situations. It must complete its work +without allocating memory (perhaps by using some approximation, or by +running more slowly). Pool classes must provide this method if they +provide the ``fix`` method. + +_`.method.reclaim`: The ``reclaim`` method is used to reclaim memory +in a segment. This method is called via the generic function +``PoolReclaim()``. It indicates that any remaining white objects in +the segment have now been proved unreachable, hence are dead. The pool +should reclaim the resources associated with the dead objects. Pool +classes are not required to provide this method. If they do, they must +set the ``AttrGC`` attribute. + +_`.method.walk`: The ``walk`` method is used by the heap walker. The +``walk`` method should apply the visitor function (along with its +closure parameters and the object format) to all *black* objects in +the segment. Padding objects may or may not be included in the walk at +the classes discretion, in any case in will be the responsibility of +the client to do something sensible with padding objects. Forwarding +objects are never included in the walk. Pool classes need not provide +this method. If they do, they must set the ``AttrFMT`` attribute. + +_`.method.describe`: The ``describe`` field is used to print out a +description of a pool. This method is called via the generic function +``PoolDescribe()``. The class should emit an textual description of +the pool's contents onto the specified stream. Each line should begin +with two spaces. Classes are not required to provide this method. Events ------ _`.replay`: To work with the allocation replayer (see -design.mps.telemetry.replayer), the pool has to emit an event for each +design.mps.telemetry.replayer_), the pool has to emit an event for each call to an external interface, containing all the parameters passed by the user. If a new event type is required to carry this information, the replayer (impl.c.eventrep) must then be extended to recreate the call. +.. _design.mps.telemetry.replayer: telemetry#replayer + _`.replay.Init`: In particular, the ``init`` method should emit a ``PoolInit`` event with all the pool parameters. @@ -270,6 +255,8 @@ Document history - 2013-03-12 GDR_ Converted to reStructuredText. +- 2014-06-08 GDR_ Bring method descriptions up to date. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/collection.txt b/design/collection.txt index 2b23a578c6..ebf0c05df9 100644 --- a/design/collection.txt +++ b/design/collection.txt @@ -55,7 +55,9 @@ for the big picture. Other notable components that the MPM manages to integrate into a single framework are manually-managed memory and finalization services -(see design.mps.finalize). +(see design.mps.finalize_). + +.. _design.mps.finalize: finalize .. note:: @@ -154,7 +156,9 @@ _`.refsets.scan`: The refset information is collected during scanning. The scan state protocol provides a way for the pool and the format scan methods to cooperate in this, and to pass this information to the tracer module which checks it and updates the segment (see -design.mps.scan). +design.mps.scan_). + +.. _design.mps.scan: scan .. note:: @@ -197,7 +201,9 @@ _`.tracer`: The tracer is an engine for implementing multiple garbage collection processes. Each process (called a "trace") proceeds independently of the others through five phases as described in analysis.tracer. The following sections describe how the action of -each phase fits into the framework. See design.mps.trace for details +each phase fits into the framework. See design.mps.trace_ for details + +.. _design.mps.trace: trace .. note:: @@ -226,9 +232,11 @@ on a given trace. In each slice, it selects a small amount of work to do, based on the state of the trace, and does it, using facilities provided by the -pools. .trace.scan: A typical unit of work is to scan a single -segment. The tracer can choose to do this for multiple traces at once, -provided the segment is grey for more than one trace. +pools. + +_`.trace.scan`: A typical unit of work is to scan a single segment. +The tracer can choose to do this for multiple traces at once, provided +the segment is grey for more than one trace. _`.trace.barrier`: Barrier hits might also cause a need to scan :mps:a segment (see `.hw-barriers.hit`_). Again, the tracer can @@ -258,7 +266,9 @@ is much like allocation) .. note:: - Soon, this will be handled by segment loci, see design.mps.locus. + Soon, this will be handled by segment loci, see design.mps.locus_. + + .. _design.mps.locus: locus _`.phase.condemn.mutator`: At this point, the mutator might reference any objects, that is, it is grey. Allocation can be in any colour, @@ -299,10 +309,12 @@ flip and because it doesn't cause any copying. The flip phase .............. -_`.phase.flip`: The roots (see design.mps.root) are scanned. This has +_`.phase.flip`: The roots (see design.mps.root_) are scanned. This has to be an atomic action as far as the mutator is concerned, so all threads are suspended for the duration. +.. _design.mps.root: root + _`.phase.flip.mutator`: After this, the mutator is black: if we use a strong barrier (analysis.async-gc.strong), this means it cannot refer to white objects. Allocation will be in black (could be grey as well, diff --git a/design/config.txt b/design/config.txt index 03bfec5109..eff7438848 100644 --- a/design/config.txt +++ b/design/config.txt @@ -20,6 +20,9 @@ _`.intro`: This document describes how the `Memory Pool System that it can target different architectures, operating systems, build environments, varieties, and products. +_`.readership`: Any MPS developer; anyone porting the MPS to a new +platform. + Requirements @@ -97,6 +100,10 @@ as a dimension of configuration since `.req.prod`_ has been retired. _`.def.target`: The *target* is the result of the build. +_`.def.option`: An *option* is a feature of the MPS that is not +selected via the *platform* and *variety*. See `.opt`_. + + Overview -------- @@ -150,7 +157,7 @@ _`.build.cc`: A consequence of this approach is that it should always be possible to build a complete target with a single UNIX command line calling the compiler driver (usually "cc" or "gcc"), for example:: - cc -o main -DCONFIG_VAR_DF foo.c bar.c baz.s -lz + cc -o main -DCONFIG_VAR_COOL foo.c bar.c baz.s -lz _`.build.defs`: The "defs" are the set of preprocessor macros which are to be predefined when compiling the module sources:: @@ -180,7 +187,7 @@ _`.var.rash`: ``RASH`` bugs tend to be extremely expensive to deal with. _`.default.hot`: If no ``CONFIG_VAR`` is present, ``HOT`` is assumed in -`config.h`_. +config.h_. _`.build.srcs`: The "srcs" are the set of sources that must be compiled in order to build the target. The set of sources may vary @@ -190,7 +197,7 @@ may be required to build different architectures. .. note:: This is a dependency between the makefile (or whatever) and the - module configuration in `config.h`_. + module configuration in config.h_. _`.build.libs`: The "libs" are the set of libraries to which the compiled sources must be linked in order to build the target. For @@ -284,7 +291,7 @@ Publishing division of Harlequin. Implementation -------------- -_`.impl`: The two implementation files `config.h`_ and `mpstd.h`_ can be +_`.impl`: The two implementation files config.h_ and mpstd.h_ can be seen as preprocessor programs which "accept" build parameters and "emit" configuration parameters (`.fig.impl`_). The build parameters are defined either by the builder (in the case of target detection) or by @@ -299,8 +306,8 @@ Build parameters Source file Configuration parameters ``_WIN32`` ⟶ ``mpstd.h`` ⟶ ``MPS_OS_W3``, etc. =========================== ============== =========================================== -_`.impl.dep`: No source code, other than the directives in `config.h`_ -and `mpstd.h`_, should depend on any build parameters. That is, +_`.impl.dep`: No source code, other than the directives in config.h_ +and mpstd.h_, should depend on any build parameters. That is, identifers beginning "CONFIG\_" should only appear in impl.h.config. Code may depend on configuration parameters in certain, limited ways, as defined below (`.conf`_). @@ -313,18 +320,20 @@ Target platform detection ......................... _`.pf`: The target platform is "detected" by the preprocessor directives in -`mpstd.h`_. +mpstd.h_. _`.pf.form`: This file consists of sets of directives of the form:: #elif #define MPS_PF_ + #define MPS_PF_STRING "" #define MPS_OS_ #define MPS_ARCH_ #define MPS_BUILD_ #define MPS_T_WORD #define MPS_T_ULONGEST - #define MPS_WORD_SHIFT + #define MPS_WORD_WIDTH + #define MPS_WORD_SHIFT #define MPS_PF_ALIGN _`.pf.detect`: The conjunction of builder predefinitions is a constant @@ -351,11 +360,13 @@ the the target detected (`.pf.detect`_). For example:: _`.pf.word`: The declaration of ``MPS_T_WORD`` defines the unsigned integral type which corresponds, on the detected target, to the machine word. It is used to defined the MPS Word type -(design.mps.type.word). For example:: +(design.mps.type.word_). For example:: #define MPS_T_WORD unsigned long -We avoid using ``typedef`` here because `mpstd.h`_ could potentially +.. _design.mps.type.word: type#word + +We avoid using ``typedef`` here because mpstd.h_ could potentially be included in assembly language source code. _`.pf.word-width`: The declaration of ``MPS_WORD_WIDTH`` defines the @@ -513,6 +524,33 @@ For example, this sort of thing:: This violates `.no-spaghetti`_. +Configuration options +--------------------- + +_`.opt`: Options select features of the MPS that are not selected by the *platform* and the *variety*. + +_`.opt.support`: The features selected by options are not supported or +documented in the public interface. This is to keep the complexity of +the MPS manageable: at present the number of supported configuration +is *platforms* × *varieties* (at time of writing, 9 × 3 = 27). Each +supported option would double (or worse) the number of supported +configurations. + +_`.opt.ansi`: ``CONFIG_PF_ANSI`` tells ``mps.c`` to exclude the +sources for the auto-detected platform, and use the generic ("ANSI") +platform instead. + +_`.opt.thread`: ``CONFIG_THREAD_SINGLE`` causes the MPS to be built +for single-threaded execution only, where locks are not needed and so +lock operations can be defined as no-ops by ``lock.h``. + +_`.opt.poll`: ``CONFIG_POLL_NONE`` causes the MPS to be built without +support for polling. This means that garbage collections will only +happen if requested explicitly via ``mps_arena_collect()`` or +``mps_arena_step()``, but it also means that protection is not needed, +and so shield operations can be replaced with no-ops in ``mpm.h``. + + To document ----------- - What about constants in config.h? diff --git a/design/critical-path.txt b/design/critical-path.txt index 9bcac7e17e..6576ca3760 100644 --- a/design/critical-path.txt +++ b/design/critical-path.txt @@ -28,7 +28,7 @@ design, with reference to more detailed documents. What makes the critical path critical ------------------------------------- -In order to determine which object can be recycled, the garbage +In order to determine which objects can be recycled, the garbage collector has to frequently examine a very large number of pointers in the program's objects. It does this by scanning_ memory, both allocated objects and roots (such as the thread stacks). @@ -55,7 +55,7 @@ unnecessary scanning and fixing. Firstly, the MPS must occasionally decide which objects to try to recycle. It does this using various facts it knows about the objects, primarily their age and whether they've survived previous attempts at -recycling them. It then `"condemns"`_ a large number of objects +recycling them. It then "`condemns`_" a large number of objects at once, and each of these objects must be "preserved" by fixing references to them. @@ -67,16 +67,17 @@ the object or any other data structure. The MPS arranges that objects which will probably die at the same time are in the same zones. -The MPS allocates in "segments". Each segment is of the order of one +The MPS allocates in "segments". Each segment is of the order of one "tract" of memory (generally the same as the operating system page -size, usually 4KiB or 8KiB) but may be larger if there are large -objects inside. The MPS maintains a "summary" of the zones pointed to +size, usually 4 KiB or 8 KiB) but may be larger if there are large +objects inside. The MPS maintains a "summary" of the zones pointed to by all the pointers in a segment from previous scans. So, once the MPS has decided what to condemn, it can quickly eliminate -all segments which definitely do not point to anything in those zones. -This avoids a large amount of scanning. It is an implementation of a -`remembered set`_, though it is unlike that in most other GCs. +all segments which definitely do not point to anything in those zones. +This avoids a large amount of scanning. It is an implementation of a +`remembered set`_, though it is unlike that in most other garbage +collectors. In addition, the fix operation can quickly ignore pointers to the wrong zones. This is called the "zone check" and is a BIBOP_ technique. @@ -104,10 +105,10 @@ Where to find the critical path ------------------------------- Very briefly, the critical path consists of five stages: -#. The scanner, which iterates over pointers in objects. The MPS has - several internal scanners, but the most important ones will be format - scanners in client code registered through ``mps_format_create()`` - functions. +#. The scanner, which iterates over pointers in objects. The MPS has + several internal scanners, but the most important ones will be + format scanners in client code registered through + ``mps_fmt_create_k()``. .. note:: @@ -118,17 +119,18 @@ Very briefly, the critical path consists of five stages: scanner. This is implemented in ``MPS_FIX()`` macros in mps.h_. -.. _mps.h: ../code/mps.h + .. _mps.h: ../code/mps.h #. The second-stage fix, which filters out pointers using general - information about segments. This is ``_mps_fix2`` in - `trace.c <../code/trace.c>`_. + information about segments. This is ``_mps_fix2()`` in trace.c_. + + .. _trace.c: ../code/trace.c #. The third-stage fix, which filters out pointers using pool-specific - information. Implemented in pool class functions called ``AMCFix()``, - ``LOFix()``, etc. in pool*.c. + information. Implemented in pool class functions called + ``AMCFix()``, ``LOFix()``, etc. in pool*.c. -#. Preserving the object, which might entail +#. Preserving the object, which might entail: - marking_ it to prevent it being recycled; and/or @@ -139,17 +141,15 @@ Very briefly, the critical path consists of five stages: - adding it to a queue of objects to be scanned later, if it contains pointers. - Found in or near the pool class fix functions. - The format scanner ------------------ -The critical path starts when a format scan method is called. That is a -call from the MPS to a client function of type ``mps_fmt_scan_t`` -registered with one of the ``mps_format_create()`` functions in mps.h_. +The critical path starts when a format scan method is called. That is +a call from the MPS to a client function of type ``mps_fmt_scan_t`` +registered with ``mps_fmt_create_k()``. Here is an example of part of a format scanner for scanning contiguous -runs of pointers, from `fmtdy.c <../code/fmtdy.c>`_, the scanner for the `Open Dylan`_ +runs of pointers, from fmtdy.c_, the scanner for the `Open Dylan`_ runtime:: static mps_res_t dylan_scan_contig(mps_ss_t mps_ss, @@ -175,6 +175,8 @@ runtime:: return MPS_RES_OK; } +.. _fmtdy.c: ../code/fmtdy.c + (To help with understanding optimisation of this code, it's written in a pseudo-assembler style, with one line roughly corresponding to each instruction of an idealized intermediate code.) @@ -188,7 +190,7 @@ remembered set. The macros ``MPS_SCAN_BEGIN()`` and ``MPS_SCAN_END()`` load key data from the scan state into local variables, and hopefully into processor registers. This avoids aliasing values that we know won't change when -calls are made to ``_mps_fix2`` later, and so allows the compiler to +calls are made to ``_mps_fix2()`` later, and so allows the compiler to keep the scan loop small and avoid unnecessary memory references. This scanner knows that words not ending in 0b00 aren't pointers to @@ -213,12 +215,12 @@ away from the tight loop with the zone check. to as "fix stage 1" or "the first stage fix" in other documents and comments. -If these inline checks pass, ``_mps_fix2`` is called. If the MPS has +If these inline checks pass, ``_mps_fix2()`` is called. If the MPS has been built as a separate object file or library, this is where the function call out of the scan loop happens. Since version 1.110 of the MPS, we encourage clients to compile the MPS in the same translation unit as their format code, so that the compiler can be intelligent -about inlining parts of ``_mps_fix2`` in the format scanner. The +about inlining parts of ``_mps_fix2()`` in the format scanner. The instructions for doing this are in `Building the Memory Pool System <../manual/build.txt>`_, part of the manual. @@ -226,32 +228,34 @@ instructions for doing this are in `Building the Memory Pool System The second stage fix in the MPM ------------------------------- If a pointer gets past the first-stage fix filters, it is passed to -``_mps_fix2``, the "second stage fix". The second stage can filter out -yet more pointers using information about segments before it has to -consult the pool class. - -The first test applied is the "tract test". The MPS looks up the tract -containing the address in the tract table, which is a simple linear -table indexed by the address shifted -- a kind of flat page table. - -Note that if the arena has been extended, the tract table becomes less -simple, and this test may involved looking in more than one table. -This will cause a considerable slow-down in garbage collection -scanning. This is the reason that it's important to give a good +``_mps_fix2()``, the "second stage fix". The second stage can filter +out yet more pointers using information about segments before it has +to consult the pool class. + +The first test is to determine if the address points to a *chunk* (a +contiguous region of address space managed by the arena). Addresses +that do not point to any chunk (for example, ambiguous references that +are not in fact pointers) are rejected immediately. See +``ChunkOfAddr()``. + +When there are many chunks (that is, when the arena has been extended +many times), this test can consume the majority of the garbage +collection time. This is the reason that it's important to give a good estimate of the amount of address space you will ever occupy with objects when you initialize the arena. -The pointer might not even be in the arena (and so not in any tract). -The first stage fix doesn't guarantee it. So we eliminate any pointers -not in the arena at this stage. +The second test applied is the "tract test". The MPS looks up the +tract containing the address in the tract table, which is a simple +linear table indexed by the address shifted---a kind of flat page +table. See ``TractOfAddr()``. If the pointer is in an allocated tract, then the table also contains -a cache of the "white set" -- the set of garbage collection traces for +a cache of the "white set"---the set of garbage collection traces for which the tract is "interesting". If a tract isn't interesting, then we know that it contains no condemned objects, and we can filter out the pointer. -If the tract is interesting them it's part of a segment containing +If the tract is interesting, then it's part of a segment containing objects that have been condemned. The MPM can't know anything about the internal layout of the segment, so at this point we dispatch to the third stage fix. @@ -272,9 +276,8 @@ table entry, and that should be in the cache. dispatch to a pool class would indirect through the pool class object. That would be a double indirection from the tract, so instead we have a cache of the pool's fix method in the pool object. This also allows -a pool class to vary its fix method per pool instance, a fact that is -exploited to optimize fixing in the AMC Pool depending on what kind of -object format it is managing. +a pool class to vary its fix method per pool instance if that would +improve performance. The third stage fix in the pool class @@ -301,16 +304,17 @@ according to its policy, and possibly ensure that the object gets scanned at some point in the future, if it contains more pointers. If the object is moved to preserve it (for instance, if the pool class -implements a copying GC), or was already moved when fixing a previous -reference to it, the reference being fixed must be updated (this is -the origin of the term "fix"). - -As a simple example, ``LOFix()`` is the pool fix method for the Leaf -Only pool class. It implements a marking garbage collector, and does -not have to worry about scanning preserved objects because it is used -to store objects that don't contain pointers. (It is used in compiler -run-time systems to store binary data such as character strings, thus -avoiding any scanning, decoding, or remembered set overhead for them.) +implements a copying collector), or was already moved when fixing a +previous reference to it, the reference being fixed must be updated +(this is the origin of the term "fix"). + +As a simple example, ``LOFix()`` is the pool fix method for the LO +(Leaf Object) pool class. It implements a marking garbage collector, +and does not have to worry about scanning preserved objects because it +is used to store objects that don't contain pointers. (It is used in +compiler run-time systems to store binary data such as character +strings, thus avoiding any scanning, decoding, or remembered set +overhead for them.) ``LOFix()`` filters any ambiguous pointers that aren't aligned, since they can't point to objects it allocated. Otherwise it subtracts the @@ -318,8 +322,9 @@ segment base address and shifts the result to get an index into a mark bit table. If the object wasn't marked and the pointer is weak, then it sets the pointer to zero, since the object is about to be recycled. Otherwise, the mark bit is set, which preserves the object from -recycling when `LOReclaim` is called later on. `LOFix` illustrates -about the minimum and most efficient thing a pool fix method can do. +recycling when ``LOReclaim()`` is called later on. ``LOFix()`` +illustrates about the minimum and most efficient thing a pool fix +method can do. Other considerations @@ -354,11 +359,11 @@ References .. _scanning: http://www.memorymanagement.org/glossary/s.html#scan .. _marking: http://www.memorymanagement.org/glossary/m.html#marking .. _copying: http://www.memorymanagement.org/glossary/c.html#copying.garbage.collection -.. _`"condemns"`: http://www.memorymanagement.org/glossary/c.html#condemned.set +.. _condemns: http://www.memorymanagement.org/glossary/c.html#condemned.set .. _BIBOP: http://www.memorymanagement.org/glossary/b.html#bibop -.. _`remembered set`: http://www.memorymanagement.org/glossary/r.html#remembered.set -.. _`reference tag`: http://www.memorymanagement.org/glossary/t.html#tag -.. _`Open Dylan`: http://opendylan.org/ +.. _remembered set: http://www.memorymanagement.org/glossary/r.html#remembered.set +.. _reference tag: http://www.memorymanagement.org/glossary/t.html#tag +.. _Open Dylan: http://opendylan.org/ Document History diff --git a/design/diag.txt b/design/diag.txt index 68fe1301d4..f8e86e2bdd 100644 --- a/design/diag.txt +++ b/design/diag.txt @@ -58,9 +58,11 @@ diagnostics compiled-in. Currently, that means variety.cool. See There are two mechanism for getting diagnostic output: -#. Automatically via the telemetry system. See design.mps.telemetry, +#. Automatically via the telemetry system. See design.mps.telemetry_, and the "Telemetry" chapter in the manual. + .. _design.mps.telemetry: telemetry + #. Manually via the debugger. In the debugger, set break points at the places where you want to inspect data structures (or wait for the debugger to be entered via an ``abort()`` call or unhandled @@ -77,21 +79,23 @@ There are two mechanism for getting diagnostic output: (gdb) frame 12 #12 0x000000010000b1fc in MVTFree (pool=0x103ffe160, base=0x101dfd000, size=5024) at poolmv2.c:711 711 Res res = CBSInsert(MVTCBS(mvt), base, limit); - (gdb) p MVTDescribe(mvt, mps_lib_get_stdout()) - MVT 0000000103FFE160 - { - minSize: 8 - meanSize: 42 - maxSize: 8192 - fragLimit: 30 - reuseSize: 16384 - fillSize: 8192 - availLimit: 1110835 - abqOverflow: FALSE - splinter: TRUE - splinterSeg: 0000000103FEE780 - splinterBase: 0000000101D7ABB8 - splinterLimit: 0000000101D7B000 + (gdb) p MVTDescribe(mvt, mps_lib_get_stdout(), 0) + MVT 0000000103FFE160 { + minSize: 8 + meanSize: 42 + maxSize: 8192 + fragLimit: 30 + reuseSize: 16384 + fillSize: 8192 + availLimit: 90931 + abqOverflow: FALSE + splinter: TRUE + splinterBase: 0000000106192FF0 + splinterLimit: 0000000106193000 + size: 303104 + allocated: 262928 + available: 40176 + unavailable: 0 # ... etc ... } @@ -145,13 +149,15 @@ systems: additional events containing diagnostic information. Additionally, the telemetry-log-events stream might in future be available as a channel for emitting human-readable text diagnostics. See also - design.mps.telemetry. - + design.mps.telemetry_. + - The MPS message system. This is present in all varieties, and manages asynchronous communication from the MPS to the client program). However, the MPS message system might in future also be available as a channel for emitting diagnostics. See also - design.mps.message. + design.mps.message_. + + .. _design.mps.message: message diff --git a/design/exec-env.txt b/design/exec-env.txt new file mode 100644 index 0000000000..65de1dd228 --- /dev/null +++ b/design/exec-env.txt @@ -0,0 +1,189 @@ +.. mode: -*- rst -*- + +Execution environment +===================== + +:Tag: design.mps.exec-env +:Author: Richard Brooksby +:Date: 1996-08-30 +:Status: incomplete design +:Revision: $Id$ +:Copyright: See section `Copyright and License`_. +:Index terms: pair: execution; environment + + +Introduction +------------ + +_`.intro`: This document describes how the MPS is designed to work in +different execution environments (see standard.ansic section 5.1.2). + + +Discussion +---------- + +_`.std`: These are the relevant statements from the International +Standard ISO/IEC 9899:1990 "Programming languages — C", with tags +added: + + 4. Compliance + + […] + + _`.std.com.hosted`: A "conforming hosted implementation" shall + accept any strictly conforming program. _`.std.com.free`: A + "conforming freestanding implementation" shall accept any strictly + conforming program in which the use of the features specified in + the library clause (clause 7) is confined to the contents of the + standard headers ````, ````, ````, + and ````. A conforming implementation may have + extensions (including additional library functions), provided they + do not alter the behaviour of any strictly conforming program. + + […] + + 5.1.2 Execution environments + + _`.std.def`: Two execution environments are defined: + "freestanding" and "hosted". […] + + _`.std.init`: All objects in static storage shall be "initialized" + (set to their initial values) before program startup. The manner + and timing of such initialization are otherwise unspecified. […] + + _`.std.term`: "Program termination" returns control to the execution + environment. […] + + 5.1.2.1 Freestanding environment + + _`.std.free.lib`: Any library facilities available to a + freestanding environment are implementation-defined. + + _`.std.free.term`: The effect of program termination in a + free-standing environment is implementation-defined. + + +Interpretation +-------------- + +_`.int.free`: We interpret the "freestanding environment" as being the +sort of environment you'd expect in an embedded system. The classic +example is a washing machine. There are no library facilities +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 +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 +call :c:func:`abort` anyway, because it's not defined in the headers +listed above (`.int.free.lib`_). + +_`.int.free.term.own`: We can add an interface for asserting, that is, +reporting an error and not returning, for use in debugging builds +only. This is because the environment can implement this in a way that +does not return to the MPS, but doesn't terminate, either. We need +this if debugging builds are to run in a (possibly simulated or +emulated) freestanding environment at all. + + +Requirements +------------ + +_`.req`: It should be possible to make use of the MPS in a +freestanding environment such as an embedded controller. + +_`.req.conf`: There can be configurations of the MPS that are not +freestanding (such as using a VM arena). + + +Architecture +------------ + +_`.arch`: Like Gaul, the MPS is divided into three parts: the *core*, +the *platform*, and the *plinth*. + +_`.arch.core`: The *core* consists of the Memory Pool Manager (the +core data structures and algorithms) and the built-in Pool Classes. +The core must be freestanding. + +_`.arch.platform`: The *platform* provides the core with interfaces to +features of the operating system and processor (locks, memory +protection, protection mutator context, stack probing, stack and +register scanning, thread management, and virtual memory). The +platform is specialized to a particular environment and so can safely +use whatever features are available in that environment. + +_`.arch.plinth`: The *plinth* provides the core with interfaces to +features of the user environment (time, assertions, and logging). See +design.mps.io_ and design.mps.lib_. + +.. _design.mps.io: io +.. _design.mps.lib: lib + +_`.arch.distinction`: The distinction between *plinth* and *platform* +is that end users will need to customize the features provided by the +plinth for most programs that use the MPS (and so the interface needs +to be simple, documented and supported), whereas implementing the +platform interface is a specialized task that will typically be done +once for each platform and then maintained alongside the core. + + +Document History +---------------- + +- 1996-08-30 RB_ Created to clarify concepts needed for + design.mps.io_. + +- 2015-02-06 GDR_ Converted to reStructuredText; bring the + architecture description up to date by describing the platform + interface. + +.. _RB: http://www.ravenbrook.com/consultants/rb/ +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 1996-2015 Ravenbrook Limited. All rights reserved. +. This is an open source license. Contact +Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** diff --git a/design/failover.txt b/design/failover.txt new file mode 100644 index 0000000000..1787da261a --- /dev/null +++ b/design/failover.txt @@ -0,0 +1,150 @@ +.. mode: -*- rst -*- + +Fail-over allocator +=================== + +:Tag: design.mps.failover +:Author: Gareth Rees +:Date: 2014-04-01 +:Status: complete design +:Revision: $Id$ +:Copyright: See section `Copyright and License`_. + + +Introduction +------------ + +_`.intro`: This is the design of the fail-over allocator, a data +structure for the management of address ranges. + +_`.readership`: This document is intended for any MPS developer. + +_`.source`: design.mps.land_, design.mps.poolmvt_, design.mps.poolmvff_. + +_`.overview`: The fail-over allocator combines two *land* instances. +It stores address ranges in one of the lands (the *primary*) unless +insertion fails, in which case it falls back to the other (the +*secondary*). The purpose is to be able to combine two lands with +different properties: with a CBS_ for the primary and a +Freelist_ for the secondary, operations are fast so long as there +is memory to allocate new nodes in the CBS_, but operations can +continue using the Freelist_ when memory is low. + +.. _CBS: cbs +.. _Freelist: freelist +.. _design.mps.land: land +.. _design.mps.poolmvt: poolmvt +.. _design.mps.poolmvff: poolmvff + + +Interface +--------- + +_`.land`: The fail-over allocator is an implementation of the *land* +abstract data type, so the interface consists of the generic functions +for lands. See design.mps.land_. + + +External types +.............. + +``typedef struct FailoverStruct *Failover`` + +_`.type.failover`: The type of fail-over allocator structures. A +``FailoverStruct`` may be embedded in another structure, or you can +create it using ``LandCreate()``. + + +External functions +.................. + +``LandClass FailoverLandClassGet(void)`` + +_`.function.class`: The function ``FailoverLandClassGet()`` returns +the fail-over allocator class, a subclass of ``LandClass`` suitable +for passing to ``LandCreate()`` or ``LandInit()``. + + +Keyword arguments +................. + +When initializing a fail-over allocator, ``LandCreate()`` and +``LandInit()`` require these two keyword arguments: + +* ``FailoverPrimary`` (type ``Land``) is the primary land. + +* ``FailoverSecondary`` (type ``Land``) is the secondary land. + + +Implementation +-------------- + +_`.impl.assume`: The implementation assumes that the primary is fast +but space-hungry (a CBS_) and the secondary is slow but space-frugal +(a Freelist_). This assumption is used in the following places: + +_`.impl.assume.flush`: The fail-over allocator attempts to flush the +secondary to the primary before any operation, in order to benefit +from the speed of the primary wherever possible. In the normal case +where the secondary is empty this is cheap. + +_`.impl.assume.delete`: When deletion of a range on the primary fails +due to lack of memory, we assume that this can only happen when there +are splinters on both sides of the deleted range, one of which needs +to be allocated a new node (this is the case for CBS_), and that +therefore the following procedure will be effective: first, delete the +enclosing range from the primary (leaving no splinters and thus +requiring no allocation), and re-insert the splinters (failing over to +the secondary if necessary). + + + +Document History +---------------- + +- 2014-04-03 GDR_ Created. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 2014 Ravenbrook Limited. All rights reserved. +. This is an open source license. Contact +Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** diff --git a/design/finalize.txt b/design/finalize.txt index 2d70468c94..e3668716df 100644 --- a/design/finalize.txt +++ b/design/finalize.txt @@ -16,11 +16,13 @@ Overview -------- _`.overview`: Finalization is implemented internally using the -Guardian Pool Class (design.mps.poolmrg). Objects can be registered -for finalization using an interface function (called -``mps_finalize()``). Notification of finalization is given to the client -via the messaging interface. ``PoolClassMRG`` (design.mps.poolmrg) -implements a Message Class which implements the finalization messages. +Guardian pool class (design.mps.poolmrg_). Objects can be registered +for finalization using ``mps_finalize()``. Notification of +finalization is given to the client via the messaging interface. The +Guardian pool class implements a ``Message`` subclass which implements +the finalization messages. + +.. _design.mps.poolmrg: poolmrg Requirements @@ -62,7 +64,9 @@ Implementation _`.int.over`: Registering an object for finalization corresponds to allocating a reference of rank FINAL to that object. This reference is allocated in a guardian object in a pool of ``PoolClassMRG`` (see -design.mps.poolmrg). +design.mps.poolmrg_). + +.. _design.mps.poolmrg: poolmrg _`.int.arena.struct`: The MRG pool used for managing final references is kept in the Arena (Space), referred to as the "final pool". diff --git a/design/fix.txt b/design/fix.txt index 9656229e15..dce6b561c8 100644 --- a/design/fix.txt +++ b/design/fix.txt @@ -40,10 +40,12 @@ condemned space). _`.protocol.was-marked.conservative`: It is always okay to set the ``wasMarked`` field to ``TRUE``. -_`.protocol.was-marked.finalizable`: The MRG pool (design.mps.poolmrg) +_`.protocol.was-marked.finalizable`: The MRG pool (design.mps.poolmrg_) uses the value of the ``wasMarked`` field to determine whether an object is finalizable. +.. _design.mps.poolmrg: poolmrg + Implementation --------------- diff --git a/design/freelist.txt b/design/freelist.txt index bc859cd3e3..c89181505c 100644 --- a/design/freelist.txt +++ b/design/freelist.txt @@ -9,13 +9,13 @@ Free list allocator :Status: incomplete design :Revision: $Id$ :Copyright: See section `Copyright and License`_. +:Index terms: pair: free list allocator; design Introduction ------------ -_`.intro`: This document describes the free list allocator for the -Memory Pool System. +_`.intro`: This is the design of the free list allocator. _`.readership`: Any MPS developer. @@ -40,174 +40,53 @@ When memory becomes available again to allocate control structures, the free lists can be "flushed" back into the more efficient data structures. -_`.bg`: The free list allocator was formerly part of the Coalescing -Block Structure module (see design.mps.cbs) but it was split into its -own module because this makes it: - -#. simpler (no need to interact with CBS) and thus more maintainable; -#. possible to test directly (no need to create a CBS and then force - its control pool to run out of memory); and -#. usable as a fallback allocator in other pools (not just in pools - that use CBS). - - -Definitions ------------ - -_`.def.range`: A (contiguous) *range* of addresses is a semi-open -interval on address space. - -_`.def.isolated`: A contiguous range is *isolated* with respect to -some property it has, if adjacent elements do not have that property. - Requirements ------------ -_`.req.set`: Must maintain a set of free address ranges. +In addition to the generic land requirements (see design.mps.land_), +free lists must satisfy: -_`.req.add`: Must be able to add free address ranges to the set. - -_`.req.remove`: Must be able to remove address ranges from the set (in -particular, when memory is allocated). - -_`.req.iterate`: Must support the iteration of all isolated contiguous -ranges. - -_`.req.protocol`: Must detect protocol violations. - -_`.req.align`: Must support an alignment (the alignment of all -addresses specifying ranges) of down to ``sizeof(void *)`` without -losing memory. +.. _design.mps.land: land _`.req.zero-overhead`: Must have zero space overhead for the storage of any set of free blocks, so that it can be used to manage memory when no memory can be allocated for control structures. -_`.req.source`: This set of requirements is derived from those of the -CBS module (see design.mps.cbs.req), except that there is no -equivalent of design.mps.cbs.req.fast, and design.mps.cbs.req.small -has been replaced with `.req.zero-overhead`_. - Interface --------- +_`.land`: Free lists are an implementation of the *land* abstract data +type, so the interface consists of the generic functions for lands. +See design.mps.land_. + Types ..... ``typedef struct FreelistStruct *Freelist`` -_`.type.freelist`: The type of free lists. The structure -``FreelistStruct`` is declared in the header so that it can be inlined -in other structures, but you should not depend on its details. - -``typedef Bool (*FreelistIterateMethod)(Bool *deleteReturn, Freelist fl, Range range, void *closureP, Size closureS)`` - -_`.type.iterate.method`: A callback function that may be passed to -``FreelistIterate()``. It is called for every isolated contiguous -range in address order, and with the closure arguments that were -originally passed to ``FreelistIterate()``. It must update -``*deleteReturn`` to ``TRUE`` if the range must be deleted from the -free lists, or ``FALSE`` if the range must be kept. The function must -return ``TRUE`` if the iteration must continue, and ``FALSE`` if the -iteration must stop (after possibly deleting the current range). - - -Functions -......... - -``Res FreelistInit(Freelist fl, Align alignment)`` - -_`.function.init`: Initialize the ``Freelist`` structure pointed to by -``fl``. The argument ``alignment`` is the alignment of address ranges -to be maintained. An initialised free list contains no address ranges. - -``void FreelistFinish(Freelist fl)`` - -_`.function.finish`: Finish the free list pointed to by ``fl``. - -``Res FreelistInsert(Range rangeReturn, Freelist fl, Range range)`` - -_`.function.insert`: If any part of ``range`` is already in the free -list ``fl``, then leave the free list unchanged and return -``ResFAIL``. Otherwise, insert ``range`` into the free list ``fl``; -update ``rangeReturn`` to describe the contiguous isolated range -containing the inserted range (this may differ from ``range`` if there -was coalescence on either side) and return ``ResOK``. +_`.type.freelist`: The type of free lists. A ``FreelistStruct`` may be +embedded in another structure, or you can create it using +``LandCreate()``. -``Res FreelistDelete(Range rangeReturn, Freelist fl, Range range)`` -_`.function.delete`: If any part of the range is not in the free list, -then leave the free list unchanged and return ``ResFAIL``. Otherwise, -remove ``range`` from the free list and update ``rangeReturn`` to -describe the contiguous isolated range that formerly contained the -deleted range (this may differ from ``range`` if there were fragments -left on either side), and return ``ResOK``. +External functions +.................. -``void FreelistIterate(Freelist fl, FreelistIterateMethod iterate, void *closureP, Size closureS)`` +``LandClass FreelistLandClassGet(void)`` -_`.function.iterate`: Iterate all isolated contiguous ranges in the -free list ``fl`` in address order, calling ``iterate`` for each one. -See ``FreelistIterateMethod`` for details. +_`.function.class`: The function ``FreelistLandClassGet()`` returns +the free list class, a subclass of ``LandClass`` suitable for passing +to ``LandCreate()`` or ``LandInit()``. -``Bool FreelistFindFirst(Range rangeReturn, Range oldRangeReturn, Freelist fl, Size size, FindDelete findDelete)`` -_`.function.find.first`: Locate the first isolated contiguous range in -address order, within the free list ``fl``, of at least ``size`` -bytes, update ``rangeReturn`` to that range, and return ``TRUE``. If -there is no such continuous range, return ``FALSE``. - -In addition, optionally delete the found range from the free list, -depending on the ``findDelete`` argument. This saves a separate call -to ``FreelistDelete()``, and uses the knowledge of exactly where we -found the range. The value of ``findDelete`` must come from this -enumeration:: - - enum { - FindDeleteNONE, /* don't delete after finding */ - FindDeleteLOW, /* delete size bytes from low end of block */ - FindDeleteHIGH, /* delete size bytes from high end of block */ - FindDeleteENTIRE /* delete entire range */ - }; - -The original contiguous isolated range in which the range was found is -returned via the ``oldRangeReturn`` argument. (If ``findDelete`` is -``FindDeleteNONE`` or ``FindDeleteENTIRE``, then this will be -identical to the range returned via the ``rangeReturn`` argument.) - -``Bool FreelistFindLast(Range rangeReturn, Range oldRangeReturn, Freelist fl, Size size, FindDelete findDelete)`` - -_`.function.find.last`: Like ``FreelistFindFirst()``, except that it -finds the last block in address order. - -``Bool FreelistFindLargest(Range rangeReturn, Range oldRangeReturn, Freelist fl, Size, size, FindDelete findDelete)`` - -_`.function.find.largest`: Locate the largest block within the free -list ``fl``, and if that block is at least as big as ``size``, return -its range via the ``rangeReturn`` argument, and return ``TRUE``. If -there are no blocks in the free list at least as large as ``size``, -return ``FALSE``. Pass 0 for ``size`` if you want the largest block -unconditionally. - -Like ``FreelistFindFirst()``, optionally delete the range from the -free list. (Always the whole range: specifying ``FindDeleteLOW`` or -``FindDeleteHIGH`` has the same effect as ``FindDeleteENTIRE``). - -``void FreelistFlushToCBS(Freelist fl, CBS cbs)`` - -Remove free address ranges from the free list ``fl`` and add them to -the Coalescing Block Structure ``cbs``. Continue until a call to -``CBSInsert()`` fails, or until the free list is empty, whichever -happens first. - -``Res FreelistDescribe(Freelist fl, mps_lib_FILE *stream)`` - -_`.function.describe`: Print a textual representation of the free -list ``fl`` to the given stream, indicating the contiguous ranges in -order. It is provided for debugging purposes only. +Keyword arguments +................. +When initializing a free list, ``LandCreate()`` and ``LandInit()`` +take no keyword arguments. Pass ``mps_args_none``. Implementation @@ -220,12 +99,13 @@ an address-ordered singly linked free list. (As in traditional _`.impl.block`: If the free address range is large enough to contain an inline block descriptor consisting of two pointers, then the two pointers stored are to the next free range in address order (or -``NULL`` if there are no more ranges), and to the limit of current -free address range, in that order. +``freelistEND`` if there are no more ranges), and to the limit of the +current free address range, in that order. _`.impl.grain`: Otherwise, the free address range must be large enough to contain a single pointer. The pointer stored is to the next free -range in address order, or ``NULL`` if there are no more ranges. +range in address order, or ``freelistEND`` if there are no more +ranges. _`.impl.tag`: Grains and blocks are distinguished by a one-bit tag in the low bit of the first word (the one containing the pointer to the @@ -238,14 +118,33 @@ _`.impl.merge`: When a free address range is added to the free list, it is merged with adjacent ranges so as to maintain `.impl.invariant`_. -_`.impl.rule.break`: The use of ``NULL`` to mark the end of the list -violates the rule that exceptional values should not be used to +_`.impl.rule.break`: The use of ``freelistEND`` to mark the end of the +list violates the rule that exceptional values should not be used to distinguish exeptional situations. This infraction allows the implementation to meet `.req.zero-overhead`_. (There are other ways to do this, such as using another tag to indicate the last block in the list, but these would be more complicated.) +Testing +------- + +_`.test`: The following testing will be performed on this module: + +_`.test.land`: A generic test for land implementations. See +design.mps.land.test_. + +.. _design.mps.land.test: land#design.mps.land.test + +_`.test.pool`: Two pools (MVT_ and MVFF_) use free lists as a fallback +when low on memory. These are subject to testing in development, QA, +and are heavily exercised by customers. + +.. _MVT: poolmvt +.. _MVFF: poolmvff + + + Opportunities for improvement ----------------------------- @@ -255,7 +154,7 @@ exceed the recorded size of the list. _`.improve.maxsize`: We could maintain the maximum size of any range on the list, and use that to make an early exit from -``FreelistFindLargest()``. It's not clear that this would actually be +``freelistFindLargest()``. It's not clear that this would actually be an improvement. @@ -265,6 +164,8 @@ Document History - 2013-05-18 GDR_ Initial draft based on CBS "emergency block" design. +- 2014-04-01 GDR_ Moved generic material to design.mps.land_. + .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/guide.hex.trans.txt b/design/guide.hex.trans.txt index 06b9dd83d5..8120f6e53e 100644 --- a/design/guide.hex.trans.txt +++ b/design/guide.hex.trans.txt @@ -22,8 +22,11 @@ hexadecimal digits. _`.readership`: This document is intended for anyone devising arbitrary constants which may appear in hex-dumps. -_`.sources`: This transliteration was supplied by RichardK in -mail.richardk.1997-04-07.13-44. +_`.sources`: This transliteration was supplied by Richard Kistruck +[RHSK-1997-04-07]_ based on magic number encodings for object signatures +used by Richard Brooksby [RB-1996-02-12]_, the existence of which was +inspired by the structure marking used in the Multics operating system +[THVV-1995]_. Transliteration @@ -76,13 +79,15 @@ _`.trans.t`: T is an exception to `.numbers`_, but is such a common letter that it deserves it. -4. Notes --------- +Notes +----- _`.change`: This transliteration differs from the old transliteration -used for signatures (see design.mps.sig(0)), as follows: J:6->1; +used for signatures (see design.mps.sig_), as follows: J:6->1; L:1->7; N:9->4; R:4->6; W:8->3; X:5->8; Y:E->I. +.. _design.mps.sig: sig + _`.problem.mw`: There is a known problem that M and W are both common, map to the same digit (3), and are hard to distinguish in context. @@ -102,6 +107,33 @@ selected (by capitalisation), e.g.:: #define SpaceSig ((Sig)0x5195BACE) /* SIGnature SPACE */ +References +---------- + +.. [RB-1996-02-12] + "Signature magic numbers" (e-mail message); + `Richard Brooksby`_; + Harlequin; + 1996-12-02 12:05:30Z. + +.. _`Richard Brooksby`: mailto:rb@ravenbrook.com + +.. [RHSK-1997-04-07] + "Alpha-to-Hex v1.0 beta"; + Richard Kistruck; + Ravenbrook; + 1997-04-07 14:42:02+0100; + . + +.. [THVV-1995] + "Structure Marking"; + Tom Van Vleck; + multicians.org_; + . + +.. _multicians.org: http://www.multicians.org/ + + Document History ---------------- 2013-05-10 RB_ Converted to reStructuredText and imported to MPS design. diff --git a/design/guide.impl.c.naming.txt b/design/guide.impl.c.naming.txt new file mode 100644 index 0000000000..bffc58554f --- /dev/null +++ b/design/guide.impl.c.naming.txt @@ -0,0 +1,144 @@ +.. mode: -*- rst -*- + +C Style -- naming +================= + +:Tag: guide.impl.c.naming +:Author: Gareth Rees +:Date: 2014-10-07 +:Status: incomplete guide +:Format: rst +:Revision: $Id$ +:Copyright: See `Copyright and License`_. +:Index terms: + pair: C language; naming guide + pair: C language naming; guide + + +Introduction +------------ + +_`.scope`: This document describes the conventions for naming in C +source code that's internal in the MPS. See design.mps.interface-c_ +for the corresponding conventions for the public interface. + +.. _design.mps.interface-c: interface-c + +_`.readership`: This document is intended for anyone working on or +with the C source code. + + +Capitalization +-------------- + +_`.capital.macro`: Statement-like macros have names consisting of +uppercase words separated by underscores, for example +``ARG_DEFINE_KEY``. + +_`.capital.constant`: Constants have names consisting of a type (named +according to `.capital.program`_ or `.capital.other`_), concatenated +with an identifier in uppercase with underscores, for example +``BufferFramePOP_PENDING``. + +_`.capital.program`: Other names with program scope consist of +concatenated title-case words, for example ``BufferFramePush``. + +_`.capital.other`: Other names (including function parameters, names +with block scope, and names with file scope) consist of concatenated +words, the first of which is lowercase and the remainder are +uppercase. For example, ``poolReturn``. + + +Prefixes +-------- + +_`.prefix.program`: Any name with program scope must start with the +name of the module to which it belongs. For example, names belonging +to the buffer module must start with ``buffer`` or ``Buffer`` or +``BUFFER``. Justification: the C language lacks a namespace facility +so the only way to avoid name clashes is for each name to be globally +unique. + +_`.prefix.file`: Any name with file scope should start with the name +of the module to which it belongs. Justification: makes it easy to +tell which module a function belongs to; makes it easy to set +breakpoints in the debugger. + + +Suffixes +-------- + +_`.suffix.struct`: The type of a structure must be the same as the +structure tag, and must consist of the type of the pointer to the +structure concatenated with ``Struct``. For example, ``ArenaStruct``. + +_`.suffix.union`: The type of a union must be the same as the union +tag, and must consist of the type of the pointer to the union +concatenated with ``Union``. For example, ``PageUnion``. + +_`.suffix.class`: The type of a class (see design.mps.protocol_) +must end with ``Class``. For example, ``ArenaClass``. + +.. _design.mps.protocol: protocol + +_`.suffix.method`: The type of a method in a class must end with +``Method``. For example, ``PoolFixMethod``. + +_`.suffix.visitor`: The type of a visitor function must end with +``Visitor``. For example, ``TreeVisitor``. + +_`.suffix.function`: The type of other functions must end with +``Function``. For example, ``TreeKeyFunction``. + + +Document History +---------------- + +- 2014-10-07 GDR_ Created based on job003693_. + +.. _job003693: http://www.ravenbrook.com/project/mps/issue/job003693/ +.. _GDR: http://www.ravenbrook.com/consultants/gdr + + +Copyright and License +--------------------- + +This document is copyright © 2002-2012 [Ravenbrook +Limited](http://www.ravenbrook.com/). All rights reserved. This is an +open source license. Contact Ravenbrook for commercial licensing +options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on + how to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more + than the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it + contains. It does not include source code for modules or files that + typically accompany the major components of the operating system on + which the executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** diff --git a/design/vman.txt b/design/guide.review.txt similarity index 64% rename from design/vman.txt rename to design/guide.review.txt index de42015d3c..b1298b26b9 100644 --- a/design/vman.txt +++ b/design/guide.review.txt @@ -1,46 +1,62 @@ .. mode: -*- rst -*- -ANSI fake VM -============ - -:Tag: design.mps.vman -:Author: David Jones -:Date: 1996-11-07 -:Status: incomplete document +Review checklist +================ + +:Tag: guide.review +:Status: incomplete documentation +:Author: Gareth Rees +:Organization: Ravenbrook Limited +:Date: 2015-08-10 :Revision: $Id$ -:Copyright: See `Copyright and License`_. -:Index terms: pair: ANSI fake VM; design +:Copyright: See section `Copyright and License`_. +:Index terms: pair: review; checklist + + +Introduction +------------ + +_`.scope`: This document contains a list of checks to apply when +reviewing code or other documents in the Memory Pool System. + +_`.readership`: This document is intended for reviewers. + +_`.example`: The "example" links are issues caused by a failure to +apply the checklist item. +_`.diff`: Some items in the checklist are particularly susceptible to +being ignored if one reviews only via the version control diff. These +items refer to this tag. -_`.intro`: The ANSI fake VM is an implementation of the MPS VM -interface (see design.mps.vm) using services provided by the ANSI C -Library (standard.ansic.7) -- ``malloc()`` and ``free()``, as it -happens. -_`.align`: The VM is aligned to ``VMAN_ALIGN`` (defined in -impl.h.mpmconf) by adding ``VMAN_ALIGN`` to the requested size, -allocating a block that large using ``malloc()``, then rounding the -pointer to the base of the block. ``vm->base`` is the aligned pointer, -``vm->block`` is the pointer returned by ``malloc()``, which is used -in ``VMDestroy()``. +Checklist +--------- + +_`.test`: If a new feature has been added to the code, is there a test +case? Example: job003923_. + +.. _job003923: http://www.ravenbrook.com/project/mps/issue/job003923/ + +_`.unwind`: If code has been updated in a function that unwinds its +state in failure cases, have the failure cases been updated to +correspond? Example: job003922_. See `.diff`_. + +.. _job003922: http://www.ravenbrook.com/project/mps/issue/job003922/ + Document History ---------------- -- 1996-11-07 David Jones. Incomplete document. - -- 2002-06-07 RB_ Converted from MMInfo database design document. -- 2013-05-23 GDR_ Converted to reStructuredText. +2015-08-10 GDR_ Created. -.. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. +Copyright © 2015 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/design/index.txt b/design/index.txt index b47c04bbcc..7e48b62531 100644 --- a/design/index.txt +++ b/design/index.txt @@ -39,76 +39,91 @@ Designs .. class:: index ====================== ================================================ -alloc-frame_ Design of the MPS allocation frame protocol -arena_ The design of the MPS arena +abq_ Fixed-length queues +alloc-frame_ Allocation frame protocol +an_ Generic modules +arena_ Arena arenavm_ Virtual memory arena +bootstrap_ Bootstrapping bt_ Bit tables buffer_ Allocation buffers and allocation points -cbs_ Design for coalescing block structure -check_ Design of checking in MPS -class-interface_ Design of the pool class interface -collection_ The collection framework -config_ The design of MPS configuration +cbs_ Coalescing block structures +check_ Checking +class-interface_ Pool class interface +collection_ Collection framework +config_ MPS configuration critical-path_ The critical path through the MPS -diag_ The design of MPS diagnostic feedback +diag_ Diagnostic feedback +exec-env_ Execution environment +failover_ Fail-over allocator finalize_ Finalization -fix_ The Design of the Generic Fix Function +fix_ The generic fix function freelist_ Free list allocator -guide.hex.trans_ Guide to transliterating the alphabet into hexadecimal +guide.hex.trans_ Transliterating the alphabet into hexadecimal guide.impl.c.format_ Coding standard: conventions for the general format of C source code in the MPS -interface-c_ The design of the Memory Pool System interface to C -io_ The design of the MPS I/O subsystem -keyword-arguments_ The design of the MPS mechanism for passing arguments by keyword. -lib_ The design of the Memory Pool System library interface -lock_ The design of the lock module -locus_ The design for the locus manager -message_ MPS to client message protocol -message-gc_ Messages sent when garbage collection begins or ends -object-debug_ Debugging Features for Client Objects -pool_ The design of the pool and pool class mechanisms -poolamc_ The design of the automatic mostly-copying memory pool class -poolams_ The design of the automatic mark-and-sweep pool class -poolawl_ Automatic weak linked -poollo_ Leaf object pool class -poolmfs_ The design of the manual fixed small memory pool class -poolmrg_ Guardian poolclass -poolmv_ The design of the manual variable memory pool class -poolmvt_ The design of a new manual-variable memory pool class -poolmvff_ Design of the manually-managed variable-size first-fit pool -prot_ Generic design of the protection module -protan_ ANSI implementation of protection module +guide.impl.c.naming_ Coding standard: conventions for internal names +guide.review_ Review checklist +interface-c_ C interface +io_ I/O subsystem +keyword-arguments_ Keyword arguments +land_ Lands (collections of address ranges) +lib_ Library interface +lock_ Lock module +locus_ Locus manager +message_ Client message protocol +message-gc_ GC messages +nailboard_ Nailboards for ambiguously referenced segments +object-debug_ Debugging features for client objects +pool_ Pool and pool class mechanisms +poolamc_ Automatic Mostly-Copying pool class +poolams_ Automatic Mark-and-Sweep pool class +poolawl_ Automatic Weak Linked pool class +poollo_ Leaf Object pool class +poolmfs_ Manual Fixed Small pool class +poolmrg_ Manual Rank Guardian pool class +poolmv_ Manual Variable pool class +poolmvt_ Manual Variable Temporal pool class +poolmvff_ Manual Variable First-Fit pool class +prmc_ Protection mutator context +prot_ Memory protection protli_ Linux implementation of protection module -protocol_ The design for protocol inheritance in MPS +protocol_ Protocol inheritance protsu_ SunOS 4 implementation of protection module -pthreadext_ Design of the Posix thread extensions for MPS -reservoir_ The design of the low-memory reservoir -ring_ The design of the ring data structure -root_ The design of the root manager -scan_ The design of the generic scanner -seg_ The design of the MPS segment data structure -shield_ Shield abstraction: separate control of collector access and mutator (client) access to memory +pthreadext_ POSIX thread extensions +range_ Ranges of addresses +reservoir_ The low-memory reservoir +ring_ Ring data structure +root_ Root manager +scan_ The generic scanner +seg_ Segment data structure +shield_ Shield sig_ Signatures in the MPS -splay_ Design of splay trees +sp_ Stack probe +splay_ Splay trees +ss_ Stack and register scanning sso1al_ Stack scanner for Digital Unix / Alpha systems strategy_ Collection strategy -telemetry_ The design of the MPS telemetry mechanism -tests_ The design of MPS internal tests -thread-manager_ The design of the MPS thread manager -thread-safety_ Thread Safety in the MPS +telemetry_ Telemetry +tests_ Tests +testthr_ Multi-threaded testing +thread-manager_ Thread manager +thread-safety_ Thread safety in the MPS trace_ Tracer -type_ The design of the general MPS types -version-library_ Design of the MPS library version mechanism -version_ Design of MPS software versions -vm_ The design of the virtual mapping interface -vman_ ANSI fake VM +type_ General MPS types +version-library_ Library version mechanism +version_ Software versions +vm_ Virtual mapping vmo1_ VM Module on DEC Unix vmso_ VM Design for Solaris -writef_ The design of the MPS writef function +writef_ The WriteF function ====================== ================================================ +.. _abq: abq .. _alloc-frame: alloc-frame +.. _an: an .. _arena: arena .. _arenavm: arenavm +.. _bootstrap: bootstrap .. _bt: bt .. _buffer: buffer .. _cbs: cbs @@ -118,19 +133,25 @@ writef_ The design of the MPS writef function .. _config: config .. _critical-path: critical-path .. _diag: diag +.. _exec-env: exec-env +.. _failover: failover .. _finalize: finalize .. _fix: fix .. _freelist: freelist .. _guide.hex.trans: guide.hex.trans .. _guide.impl.c.format: guide.impl.c.format +.. _guide.impl.c.naming: guide.impl.c.naming +.. _guide.review: guide.review .. _interface-c: interface-c .. _io: io .. _keyword-arguments: keyword-arguments +.. _land: land .. _lib: lib .. _lock: lock .. _locus: locus .. _message: message .. _message-gc: message-gc +.. _nailboard: nailboard .. _object-debug: object-debug .. _pool: pool .. _poolamc: poolamc @@ -142,12 +163,13 @@ writef_ The design of the MPS writef function .. _poolmv: poolmv .. _poolmvt: poolmvt .. _poolmvff: poolmvff +.. _prmc: prmc .. _prot: prot -.. _protan: protan .. _protli: protli .. _protocol: protocol .. _protsu: protsu .. _pthreadext: pthreadext +.. _range: range .. _reservoir: reservoir .. _ring: ring .. _root: root @@ -155,11 +177,14 @@ writef_ The design of the MPS writef function .. _seg: seg .. _shield: shield .. _sig: sig +.. _sp: sp .. _splay: splay +.. _ss: ss .. _sso1al: sso1al .. _strategy: strategy .. _telemetry: telemetry .. _tests: tests +.. _testthr: testthr .. _thread-manager: thread-manager .. _thread-safety: thread-safety .. _trace: trace @@ -167,7 +192,6 @@ writef_ The design of the MPS writef function .. _version-library: version-library .. _version: version .. _vm: vm -.. _vman: vman .. _vmo1: vmo1 .. _vmso: vmso .. _writef: writef @@ -202,6 +226,7 @@ Document History - 2013-05-25 RB_ Replacing "cstyle" with reworked "guide.impl.c.format". - 2013-06-07 RB_ Converting to reST_. Linking to [RB_2002-06-18]_. - 2014-01-29 RB_ The arena no longer manages generation zonesets. +- 2014-01-17 GDR_ Add abq, nailboard, range. .. _RB: http://www.ravenbrook.com/consultants/rb .. _NB: http://www.ravenbrook.com/consultants/nb @@ -212,7 +237,7 @@ Document History Copyright and License --------------------- -Copyright © 2002-2014 Ravenbrook Limited. All rights reserved. +Copyright © 2002-2015 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/design/interface-c.txt b/design/interface-c.txt index fabf45f634..1e5424105b 100644 --- a/design/interface-c.txt +++ b/design/interface-c.txt @@ -6,7 +6,7 @@ C interface design :Tag: design.mps.interface.c :Author: Richard Brooksby :Date: 1996-07-29 -:Status: incomplete document +:Status: complete design :Revision: $Id$ :Copyright: See `Copyright and License`_. :Index terms: pair: C interface; design @@ -18,7 +18,9 @@ Introduction _`.scope`: This document is the design for the Memory Pool System (MPS) interface to the C Language, impl.h.mps. -_`.bg`: See mail.richard.1996-07-24.10-57. +_`.bg`: See `mail.richard.1996-07-24.10-57`_. + +.. _mail.richard.1996-07-24.10-57: https://info.ravenbrook.com/project/mps/mail/1996/07/24/10-57/0.txt Analysis @@ -29,7 +31,9 @@ Goals _`.goal.c`: The file impl.h.mps is the C external interface to the MPS. It is the default interface between client code written in C and -the MPS. _`.goal.cpp`: impl.h.mps is not specifically designed to be +the MPS. + +_`.goal.cpp`: impl.h.mps is not specifically designed to be an interface to C++, but should be usable from C++. @@ -38,36 +42,32 @@ Requirements _`.req`: The interface must provide an interface from client code written in C to the functionality of the MPS required by the product -(see req.product), Dylan (req.dylan), and the Core RIP (req.epcore). +(see req.product), and Open Dylan (req.dylan). -``mps.h`` may not include internal MPS header files (such as -``pool.h``). +_`.req.separation`: The external interface may not include internal +MPS header files (such as ``pool.h``). -It is essential that the interface cope well with change, in order to -avoid restricting possible future MPS developments. This means that -the interface must be "open ended" in its definitions. This accounts -for some of the apparently tortuous methods of doing things -(``mps_fmt_A_t``, for example). The requirement is that the MPS should +_`.req.flexibility`: It is essential that the interface cope well with +change, in order to avoid restricting possible future MPS +developments. This means that the interface must be "open ended" in +its definitions. This accounts for some of the apparently tortuous +methods of doing things (such as the keyword argument mechanism; see +design.mps.keyword-arguments_). The requirement is that the MPS should be able to add new functionality, or alter the implementation of existing functionality, without affecting existing client code. A stronger requirement is that the MPS should be able to change without *recompiling* client code. This is not always possible. -.. note:: - - `.naming.global` was presumably done in response to unwritten - requirements regarding the use of the name spaces in C, perhaps - these: +.. _design.mps.keyword-arguments: keyword-arguments - - _`.req.name.iso`: The interface shall not conflict in terms of - naming with any interfaces specified by ISO C and all reasonable - future versions. +_`.req.name.iso`: The interface shall not conflict in terms of +naming with any interfaces specified by ISO C and all reasonable +future versions. - - _`.req.name.general`: The interface shall use a documented and - reasonably small portion of the namespace so that clients can - interoperate easily. - - David Jones, 1998-10-01. +_`.req.name.general`: The interface shall use a documented and +reasonably small portion of the namespace so that clients can use the +MPS C interface in combination with other interfaces without name +conflicts. Architecture @@ -82,13 +82,18 @@ layer" which does the job of converting types and checking parameters before calling through to the MPS proper, using internal MPS methods. -General conventions -------------------- +Naming conventions +------------------ _`.naming`: The external interface names should adhere to the -documented interface conventions; these are found in -doc.mps.ref-man.if-conv(0).naming. They are paraphrased/recreated -here. +documented interface conventions; these are found in the “`Interface +conventions`_” chapter of the Reference Manual. They are +paraphrased/recreated here. + +.. _Interface conventions: ../topic/interface.html + +_`.naming.file`: All files in the external interface have names +starting with ``mps``. _`.naming.unixy`: The external interface does not follow the same naming conventions as the internal code. The interface is designed to @@ -97,13 +102,13 @@ resemble a more conventional C, Unix, or Posix naming convention. _`.naming.case`: Identifiers are in lower case, except non-function-like macros, which are in upper case. -_`.naming.global`: All publicised identifiers are -prefixed ``mps_`` or ``MPS_``. +_`.naming.global`: All documented identifiers begin ``mps_`` or +``MPS_``. -_`.naming.all`: All identifiers defined by the MPS -should begin ``mps_`` or ``MPS_`` or ``_mps_``. +_`.naming.all`: All identifiers defined by the MPS begin ``mps_`` or +``MPS_`` or ``_mps_``. -_`.naming.type`: Types are suffixed ``_t``. +_`.naming.type`: Types are suffixed ``_t``, except for structure and union types. _`.naming.struct`: Structure types and tags are suffixed ``_s``. @@ -117,15 +122,14 @@ structure and union members, macros, macro parameters, labels. _`.naming.scope.labels`: labels (for ``goto`` statements) should be rare, only in special block macros and probably not even then. -.. note:: - - This principle is not adhered to in the source code, which uses - ``goto`` for handling error cases. Gareth Rees, 2013-05-27. - _`.naming.scope.other`: The naming convention would also extend to enumeration types and parameters in functions prototypes but both of those are prohibited from having names in an interface file. + +Type conventions +---------------- + _`.type.gen`: The interface defines memory addresses as ``void *`` and sizes as ``size_t`` for compatibility with standard C (in particular, with ``malloc()``). These types must be binary compatible with the @@ -139,13 +143,13 @@ which are never defined. These types are cast to the corresponding internal types in ``mpsi.c``. _`.type.trans`: Some transparent structures are defined. The client is -expected to read these, or poke about in them, under restrictions -which should be documented. The most important is probably the -allocation point (``mps_ap_s``) which is part of allocation buffers. -The transparent structures must be binary compatible with -corresponding internal structures. For example, the fields of -``mps_ap_s`` must correspond with ``APStruct`` internally. This is -checked by ``mpsi.c`` in ``mps_check()``. +expected to read these, or poke about in them, under documented +restrictions. The most important is the allocation point structure +(``mps_ap_s``) which is part of allocation buffers. The transparent +structures must be binary compatible with corresponding internal +structures. For example, the fields of ``mps_ap_s`` must correspond +with ``APStruct`` internally. This is checked by ``mpsi.c`` in +``mps_check()``. _`.type.pseudo`: Some pseudo-opaque structures are defined. These only exist so that code can be inlined using macros. The client code @@ -153,9 +157,9 @@ shouldn't mess with them. The most important case of this is the scan state (``mps_ss_s``) which is accessed by the in-line scanning macros, ``MPS_SCAN_*`` and ``MPS_FIX*``. -_`.type.enum`: There should be no enumeration types in the interface. -Note that enum specifiers (to declare integer constants) are fine as -long as no type is declared. See guide.impl.c.misc.enum.type. +_`.type.enum`: There are no enumeration types in the interface. Note +that enum specifiers (to declare integer constants) are fine as long +as no type is declared. See guide.impl.c.misc.enum.type. _`.type.fun`: Whenever function types or derived function types (such as pointer to function) are declared a prototype should be used and @@ -184,39 +188,44 @@ See guide.impl.c.misc.prototype.parameters. Checking -------- -_`.check.space`: When the arena needs to be recovered from a parameter -it is check using ``AVERT(Foo, foo)`` before any attempt to call -``FooArena(foo)``. The macro ``AVERT()`` in impl.h.assert performs -simple thread-safe checking of ``foo``, so it can be called outside of -``ArenaEnter()`` and ``ArenaLeave()``. +_`.check.testt`: Before any use of a parameter ``foo`` belonging to a +pointer type ``Foo``, it is checked using ``TESTT(Foo, foo)``. The +macro ``TESTT()`` in impl.h.check performs simple thread-safe checking +of ``foo``, so it can be called outside of ``ArenaEnter()`` and +``ArenaLeave()``. + +_`.check.avert`: With the arena lock held, ``foo`` is checked using +``AVERT(Foo, foo)``. This macro has different definitions depending on +how the MPS is compiled (see design.mps.config.def.var_). It may +expand to ``TESTT()``, or it may call the full checking function for +the type. + +.. _design.mps.config.def.var: config#def-var _`.check.types`: We use definitions of types in both our external interface and our internal code, and we want to make sure that they are compatible. (The external interface changes less often and hides -more information.) At first, we were just checking their sizes, which -wasn't very good, but I've come up with some macros which check the -assignment compatibility of the types too. This is a sufficiently -useful trick that I thought I'd send it round. It may be useful in -other places where types and structures need to be checked for -compatibility at compile time. +more information.) This checking uses the following macros, originally +from `mail.richard.1996-08-07.09-49`_. -These macros don't generate warnings on the compilers I've tried. +.. _mail.richard.1996-08-07.09-49: https://info.ravenbrook.com/project/mps/mail/1996/08/07/09-49/0.txt ``COMPATLVALUE(lvalue1, lvalue2)`` -This macro checks the assignment compatibility of two lvalues. It uses -``sizeof()`` to ensure that the assignments have no effect. :: +_`.check.types.compat.lvalue`: This macro checks the assignment +compatibility of two lvalues. It uses ``sizeof()`` to ensure that the +assignments have no effect. :: #define COMPATLVALUE(lv1, lv2) \ ((void)sizeof((lv1) = (lv2)), (void)sizeof((lv2) = (lv1)), TRUE) ``COMPATTYPE(type1, type2)`` -This macro checks that two types are assignment-compatible and equal -in size. The hack here is that it generates an lvalue for each type by -casting zero to a pointer to the type. The use of ``sizeof()`` avoids -the undefined behaviour that would otherwise result from dereferencing -a null pointer. :: +_`.check.types.compat.type`: This macro checks that two types are +assignment-compatible and equal in size. The hack here is that it +generates an lvalue for each type by casting zero to a pointer to the +type. The use of ``sizeof()`` avoids the undefined behaviour that +would otherwise result from dereferencing a null pointer. :: #define COMPATTYPE(t1, t2) \ (sizeof(t1) == sizeof(t2) && \ @@ -224,8 +233,8 @@ a null pointer. :: ``COMPATFIELDAPPROX(structure1, field1, structure2, field2)`` -This macro checks that the offset and size of two fields in two -structure types are the same. :: +_`.check.types.compat.field.approx`: This macro checks that the offset +and size of two fields in two structure types are the same. :: #define COMPATFIELDAPPROX(s1, f1, s2, f2) \ (sizeof(((s1 *)0)->f1) == sizeof(((s2 *)0)->f2) && \ @@ -233,8 +242,8 @@ structure types are the same. :: ``COMPATFIELD(structure1, field1, structure2, field2)`` -This macro checks the offset, size, and assignment-compatibility of -two fields in two structure types. :: +_`.check.types.compat.field`: This macro checks the offset, size, and +assignment-compatibility of two fields in two structure types. :: #define COMPATFIELD(s1, f1, s2, f2) \ (COMPATFIELDAPPROX(s1, f1, s2, f2) && \ @@ -245,23 +254,38 @@ Binary compatibility issues --------------------------- As in, "Enumeration types are not allowed" (see -mail.richard.1995-09-08.09-28). +`mail.richard.1995-09-08.09-28`_). -There are two main aspects to run-time compatibility: binary interface -and protocol. The binary interface is all the information needed to -correctly use the library, and includes external symbol linkage, +.. _mail.richard.1995-09-08.09-28: https://info.ravenbrook.com/project/mps/mail/1995/09/08/09-28/0.txt + +_`.compat`: There are two main aspects to run-time compatibility: +binary interface and protocol. + +_`.compat.binary`: The binary interface is all the information needed +to correctly use the library, and includes external symbol linkage, calling conventions, type representation compatibility, structure -layouts, etc. The protocol is how the library is actually used by the -client code -- whether this is called before that -- and determines -the semantic correctness of the client with respect to the library. +layouts, etc. + +_`.compat.binary.unneeded`: Binary compatibility is not required by +the open source MPS: we expect (and indeed, recommend) that a client +program is compiled against the MPS sources. Nonetheless we try to +maintain binary compatibility in case the capability is required in +future. + +_`.compat.binary.dependencies`: The binary interface is determined +completely by the header file and the target. The header file +specifies the external names and the types, and the target platform +specifies calling conventions and type representation. There is +therefore a many-to-one mapping between the header file version and +the binary interface. -The binary interface is determined completely by the header file and -the target. The header file specifies the external names and the -types, and the target platform specifies calling conventions and type -representation. There is therefore a many-to-one mapping between the -header file version and the binary interface. +_`.compat.protocol`: The protocol is how the library is actually used +by the client code -- whether this is called before that -- and +determines the semantic correctness of the client with respect to the +library. -The protocol is determined by the implementation of the library. +_`.compat.protocol.dependencies`: The protocol is determined by the +implementation of the library. Constraints @@ -271,54 +295,85 @@ _`.cons`: The MPS C Interface constrains the MPS in order to provide useful memory management services to a C or C++ program. _`.cons.addr`: The interface constrains the MPS address type, Addr -(design.mps.type.addr), to being the same as C's generic pointer type, +(design.mps.type.addr_), to being the same as C's generic pointer type, ``void *``, so that the MPS can manage C objects in the natural way. +.. _design.mps.type.addr: type#addr + _`.pun.addr`: We pun the type of ``mps_addr_t`` (which is ``void *``) -into ``Addr`` (an incomplete type, see design.mps.type.addr). This +into ``Addr`` (an incomplete type, see design.mps.type.addr_). This happens in the call to the scan state's fix function, for example. _`.cons.size`: The interface constrains the MPS size type, ``Size`` -(design.mps.type.size), to being the same as C's size type, +(design.mps.type.size_), to being the same as C's size type, ``size_t``, so that the MPS can manage C objects in the natural way. + +.. _design.mps.type.size: type#size _`.pun.size`: We pun the type of ``size_t`` in mps.h into ``Size`` in the MPM, as an argument to the format methods. We assume this works. -_`.cons.word`: The MPS assumes that ``Word`` (design.mps.type.word) -and ``Addr`` (design.mps.type.addr) are the same size, and the +_`.cons.word`: The MPS assumes that ``Word`` (design.mps.type.word_) +and ``Addr`` (design.mps.type.addr_) are the same size, and the interface constrains ``Word`` to being the same size as C's generic pointer type, ``void *``. +.. _design.mps.type.word: type#word -Notes ------ -The file ``mpstd.h`` is the MPS target detection header. It decodes -preprocessor symbols which are predefined by build environments in -order to determine the target platform, and then defines uniform -symbols, such as ``MPS_ARCH_I3``, for use internally by the MPS. +Implementation +-------------- + +_`.impl`: The external interface consists of the following header +files: + +_`.impl.mps`: ``mps.h`` is the main external interface, containing of +type and function declarations needed by all clients of the MPS. + +_`.impl.mpstd`: ``mpstd.h`` is the MPS target detection header. It +decodes preprocessor symbols which are predefined by build +environments in order to determine the target platform (see +design.mps.config_), and then defines uniform symbols, such as +``MPS_ARCH_I3``, for use externally and internally by the MPS. +``mpstd.h`` is not included by any of the other external headers, as +it relies on exact set of preprocessor constants defined by compilers. + +.. _design.mps.config: config -There is a design document for the mps interface, -design.mps.interface, but it was written before we had the idea of -having a C interface layer. It is quite relevant, though, and could be -updated. We should use it during the review. +_`.impl.mpsio`: ``mpsio.h`` is the interface to the MPS I/O subsystem, +part of the plinth. See design.mps.io_. -All exported identifiers and file names should begin with ``mps_`` or -``MPS_`` so that they don't clash with other systems. +.. _design.mps.io: io -We should probably have a specialized set of rules and a special -checklist for this interface. +_`.impl.mpslib`: ``mpslib.h`` is the interface to the MPS Library +Interface, part of the plinth. See design.mps.lib_. -_`.fmt.extend`: This paragraph should be an explanation of why -``mps_fmt_A_t`` is so called. The underlying reason is future -extensibility. +.. _design.mps.lib: lib -_`.thread-safety`: Most calls through this interface lock the space +_`.impl.mpsa`: Interfaces to arena classes are in files with names +starting ``mpsa``: for example, the interface to the Virtual Memory +arena class is in ``mpsavm.h``. + +_`.impl.mpsc`: Interfaces to pool classes are in files with names +starting ``mpsc``: for example, the interface to the MVFF pool class +is in ``mpscmvff.h``. + + +Notes +----- + +_`.fmt.extend`: ``mps_fmt_A_t`` is so called because new pool classes +might require new format methods, but these methods cannot be added to +the format structure without breaking binary compatibility. Therefore +these new pool classes would use new format structures named +``mps_fmt_B_t`` and so on. + +_`.thread-safety`: Most calls through this interface lock the arena and therefore make the MPM single-threaded. In order to do this they -must recover the space from their parameters. Methods such as -``ThreadSpace()`` must therefore be callable when the space is *not* -locked. These methods are tagged with the tag of this note. +must recover the arena from their parameters. Methods such as +``FormatArena()`` and ``ThreadArena()`` must therefore be callable +when the arena is *not* locked. These methods are tagged with the tag +of this note. _`.lock-free`: Certain functions inside the MPM are thread-safe and do not need to be serialized by using locks. They are marked with the tag diff --git a/design/io.txt b/design/io.txt index 0e78d19d18..27ab9d7802 100644 --- a/design/io.txt +++ b/design/io.txt @@ -95,17 +95,19 @@ which the MPM sends and receives "messages" to and from the hosted I/O module. _`.arch.module`: The modules are part of the MPS but not part of the -freestanding core system (see design.mps.exec-env). The I/O module is +freestanding core system (see design.mps.exec-env_). The I/O module is responsible for transmitting those messages to the external tools, and for receiving messages from external tools and passing them to the MPM. +.. _design.mps.exec-env: exec-env + _`.arch.module.example`: For example, the "file implementation" might just send/write telemetry messages into a file so that they can be received/read later by an off-line measurement tool. _`.arch.external`: The I/O Interface is part of interface to the -freestanding core system (see design.mps.exec-env). This is so that +freestanding core system (see design.mps.exec-env_). This is so that the MPS can be deployed in a freestanding environment, with a special I/O module. For example, if the MPS is used in a washing machine the I/O module could communicate by writing output to the seven-segment @@ -411,9 +413,12 @@ Document History - 1996-08-30 RB_ Document created from paper notes. -- 1997-06-10 RB_ Updated with mail.richard.1997-05-30.16-13 and +- 1997-06-10 RB_ Updated with `mail.richard.1997-05-30.16-13`_ and subsequent discussion in the Pool Hall at Longstanton. (See also - mail.drj.1997-06-05.15-20.) + `mail.drj.1997-06-05.15-20`_.) + + .. _mail.richard.1997-05-30.16-13: https://info.ravenbrook.com/project/mps/mail/1997/05/30/16-13/0.txt + .. _mail.drj.1997-06-05.15-20: https://info.ravenbrook.com/project/mps/mail/1997/06/05/15-20/0.txt - 2002-06-07 RB_ Converted from MMInfo database design document. @@ -426,7 +431,7 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. +Copyright © 2013-2015 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/design/keyword-arguments.txt b/design/keyword-arguments.txt index 0c69803687..af6db232a5 100644 --- a/design/keyword-arguments.txt +++ b/design/keyword-arguments.txt @@ -103,10 +103,10 @@ but ``arg.h`` provides a macro for this:: We define keys as static structures (rather than, say, an enum) because: - The set of keys can be extended indefinitely. -- The set of keys can be extended by indepdently linked modules. +- The set of keys can be extended by independently linked modules. - The structure contents allow strong checking of argument lists. -In the MPS Interface, we declare keys like this:: +In the MPS C Interface, we declare keys like this:: extern const struct mps_key_s _mps_key_extend_by; #define MPS_KEY_EXTEND_BY (&_mps_key_extend_by) @@ -137,6 +137,9 @@ arena or pool class. For example:: This leaves the main body of code, and any future code, free to just handle keyword arguments only. +Varargs methods must be thread-safe as they are called without taking +the arena lock. + The use of varargs is deprecated in the manual and the interface and these methods can be deleted at some point in the future. diff --git a/design/land.txt b/design/land.txt new file mode 100644 index 0000000000..df16fc922b --- /dev/null +++ b/design/land.txt @@ -0,0 +1,361 @@ +.. mode: -*- rst -*- + +Lands +===== + +:Tag: design.mps.land +:Author: Gareth Rees +:Date: 2014-04-01 +:Status: complete design +:Revision: $Id$ +:Copyright: See section `Copyright and License`_. + + +Introduction +------------ + +_`.intro`: This is the design of the *land* abstract data type, which +represents a collection of contiguous address ranges. + +_`.readership`: This document is intended for any MPS developer. + +_`.source`: design.mps.cbs_, design.mps.freelist_. + +_`.overview`: Collections of address ranges are used in several places +in the MPS: the arena stores a set of mapped address ranges; pools +store sets of address ranges which have been acquired from the arena +and sets of address ranges that are available for allocation. The +*land* abstract data type makes it easy to try out different +implementations with different performance characteristics and other +attributes. + +_`.name`: The name is inspired by *rangeland* meaning *group of +ranges* (where *ranges* is used in the sense *grazing areas*). + + +Definitions +----------- + +_`.def.range`: A (contiguous) *range* of addresses is a semi-open +interval on address space. + +_`.def.isolated`: A contiguous range is *isolated* with respect to +some property it has, if adjacent elements do not have that property. + + +Requirements +------------ + +_`.req.set`: Must maintain a set of addresses. + +_`.req.add`: Must be able to add address ranges to the set. + +_`.req.remove`: Must be able to remove address ranges from the set. + +_`.req.size`: Must report concisely to the client when isolated +contiguous ranges of at least a certain size appear and disappear. + +_`.req.iterate`: Must support the iteration of all isolated +contiguous ranges. + +_`.req.protocol`: Must detect protocol violations. + +_`.req.debug`: Must support debugging of client code. + +_`.req.align`: Must support an alignment (the alignment of all +addresses specifying ranges) of down to ``sizeof(void *)`` without +losing memory. + + +Interface +--------- + +Types +..... + +``typedef LandStruct *Land`` + +_`.type.land`: The type of a generic land instance. + +``typedef Bool (*LandVisitor)(Land land, Range range, void *closureP, Size closureS)`` + +_`.type.visitor`: Type ``LandVisitor`` is a callback function that may +be passed to ``LandIterate()``. It is called for every isolated +contiguous range in address order. The function must return a ``Bool`` +indicating whether to continue with the iteration. + +``typedef Bool (*LandDeleteVisitor)(Bool *deleteReturn, Land land, Range range, void *closureP, Size closureS)`` + +_`.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 +update ``*deleteReturn`` to ``TRUE`` if the range must be deleted from +the land, or ``FALSE`` if the range must be kept. (The default is to +keep the range.) + + +Generic functions +................. + +``Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args)`` + +_`.function.init`: ``LandInit()`` initializes the land structure for +the given class. The land will perform allocation (if necessary -- not +all land classes need to allocate) in the supplied arena. The +``alignment`` parameter is the alignment of the address ranges that +will be stored and retrieved from the land. The parameter ``owner`` is +output as a parameter to the ``LandInit`` event. The newly initialized +land contains no ranges. + +``Res LandCreate(Land *landReturn, Arena arena, LandClass class, Align alignment, void *owner, ArgList args)`` + +_`.function.create`: ``LandCreate()`` allocates memory for a land +structure of the given class in ``arena``, and then passes all +parameters to ``LandInit()``. + +``void LandDestroy(Land land)`` + +_`.function.destroy`: ``LandDestroy()`` calls ``LandFinish()`` to +finish the land structure, and then frees its memory. + +``void LandFinish(Land land)`` + +_`.function.finish`: ``LandFinish()`` finishes the land structure and +discards any other resources associated with the land. + +``void LandSize(Land land)`` + +_`.function.size`: ``LandSize()`` returns the total size of the ranges +stored in the land. + +``Res LandInsert(Range rangeReturn, Land land, Range range)`` + +_`.function.insert`: If any part of ``range`` is already in the +land, then leave it unchanged and return ``ResFAIL``. Otherwise, +attempt to insert ``range`` into the land. If the insertion succeeds, +then update ``rangeReturn`` to describe the contiguous isolated range +containing the inserted range (this may differ from ``range`` if there +was coalescence on either side) and return ``ResOK``. If the insertion +fails, return a result code indicating allocation failure. + +_`.function.insert.fail`: Insertion of a valid range (that is, one +that does not overlap with any range in the land) can only fail if the +new range is isolated and the allocation of the necessary data +structure to represent it failed. + +_`.function.insert.alias`: It is acceptable for ``rangeReturn`` and +``range`` to share storage. + +``Res LandDelete(Range rangeReturn, Land land, Range range)`` + +_`.function.delete`: If any part of the range is not in the land, +then leave the land unchanged and return ``ResFAIL``. Otherwise, update +``rangeReturn`` to describe the contiguous isolated range that +contains ``range`` (this may differ from ``range`` if there are +fragments on either side) and attempt to delete the range from the +land. If the deletion succeeds, return ``ResOK``. If the deletion +fails, return a result code indicating allocation failure. + +_`.function.delete.fail`: Deletion of a valid range (that is, one +that is wholly contained in the land) can only fail if there are +fragments on both sides and the allocation of the necessary data +structures to represent them fails. + +_`.function.delete.return`: ``LandDelete()`` returns the contiguous +isolated range that contains ``range`` even if the deletion fails. +This is so that the caller can try deleting the whole block (which is +guaranteed to succeed) and managing the fragments using a fallback +strategy. + +_`.function.delete.alias`: It is acceptable for ``rangeReturn`` and +``range`` to share storage. + +``Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS)`` + +_`.function.iterate`: ``LandIterate()`` is the function used to +iterate all isolated contiguous ranges in a land. It receives a +visitor function to invoke on every range, and a pointer, ``Size`` +closure pair to pass on to the visitor function. If the visitor +function returns ``FALSE``, then iteration is terminated and +``LandIterate()`` returns ``FALSE``. If all iterator method calls +return ``TRUE``, then ``LandIterate()`` returns ``TRUE`` + +``Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS)`` + +_`.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) +within the land of at least the specified size, update ``rangeReturn`` +to describe that range, and return ``TRUE``. If there is no such +block, it returns ``FALSE``. + +In addition, optionally delete the top, bottom, or all of the found +range, depending on the ``findDelete`` argument. This saves a separate +call to ``LandDelete()``, and uses the knowledge of exactly where we +found the range. The value of ``findDelete`` must come from this +enumeration:: + + enum { + FindDeleteNONE, /* don't delete after finding */ + FindDeleteLOW, /* delete size bytes from low end of block */ + FindDeleteHIGH, /* delete size bytes from high end of block */ + FindDeleteENTIRE /* delete entire range */ + }; + +The original contiguous isolated range in which the range was found is +returned via the ``oldRangeReturn`` argument. (If ``findDelete`` is +``FindDeleteNONE`` or ``FindDeleteENTIRE``, then this will be +identical to the range returned via the ``rangeReturn`` argument.) + +``Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)`` + +_`.function.find.last`: Like ``LandFindFirst()``, except that it +finds the last block in address order. + +``Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)`` + +_`.function.find.largest`: Locate the largest block within the +land, and if that block is at least as big as ``size``, return its +range via the ``rangeReturn`` argument, and return ``TRUE``. If there +are no blocks in the land at least as large as ``size``, return +``FALSE``. Pass 0 for ``size`` if you want the largest block +unconditionally. + +Like ``LandFindFirst()``, optionally delete the range (specifying +``FindDeleteLOW`` or ``FindDeleteHIGH`` has the same effect as +``FindDeleteENTIRE``), and return the original contiguous isolated +range in which the range was found via the ``oldRangeReturn`` +argument. + +``Res LandFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high)`` + +_`.function.find.zones`: Locate a block at least as big as ``size`` +that lies entirely within the ``zoneSet``, return its range via the +``rangeReturn`` argument, set ``*foundReturn`` to ``TRUE``, and return +``ResOK``. (The first such block, if ``high`` is ``FALSE``, or the +last, if ``high`` is ``TRUE``.) If there is no such block, set +``*foundReturn`` to ``FALSE``, and return ``ResOK``. + +Delete the range as for ``LandFindFirst()`` and ``LastFindLast()`` +(with the effect of ``FindDeleteLOW`` if ``high`` is ``FALSE`` and the +effect of ``FindDeleteHIGH`` if ``high`` is ``TRUE``), and return the +original contiguous isolated range in which the range was found via +the ``oldRangeReturn`` argument. + +_`.function.find.zones.fail`: It's possible that the range can't be +deleted from the land because that would require allocation, in which +case the result code indicates the cause of the failure. + +``Res LandDescribe(Land land, mps_lib_FILE *stream)`` + +_`.function.describe`: ``LandDescribe()`` prints a textual +representation of the land to the given stream. It is provided for +debugging purposes only. + +``void LandFlush(Land dest, Land src)`` + +_`.function.flush`: Delete ranges of addresses from ``src`` and insert +them into ``dest``, so long as ``LandInsert()`` remains successful. + + +Implementations +--------------- + +There are three land implementations: + +#. CBS (Coalescing Block Structure) stores ranges in a splay tree. It + has fast (logarithmic in the number of ranges) insertion, deletion + and searching, but has substantial space overhead. See + design.mps.cbs_. + +#. Freelist stores ranges in an address-ordered free list, as in + traditional ``malloc()`` implementations. Insertion, deletion, and + searching are slow (proportional to the number of ranges) but it + does not need to allocate. See design.mps.freelist_. + +#. Failover combines two lands, using one (the *primary*) until it + fails, and then falls back to the other (the *secondary*). See + design.mps.failover_. + +.. _design.mps.cbs: cbs +.. _design.mps.freelist: freelist +.. _design.mps.failover: failover + + +Testing +------- + +_`.test`: There is a stress test for implementations of this interface +in impl.c.landtest. This allocates a large block of memory and then +simulates the allocation and deallocation of ranges within this block +using both a ``Land`` and a ``BT``. It makes both valid and invalid +requests, and compares the ``Land`` response to the correct behaviour +as determined by the ``BT``. It iterates the ranges in the ``Land``, +comparing them to the ``BT``. It invokes the ``LandDescribe()`` +generic function, but makes no automatic test of the resulting output. + + +Document History +---------------- + +- 2014-04-01 GDR_ Created based on design.mps.cbs_. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 2014-2015 Ravenbrook Limited. All rights reserved. +. This is an open source license. Contact +Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** diff --git a/design/lib.txt b/design/lib.txt index fa3973f0df..1dd7efe696 100644 --- a/design/lib.txt +++ b/design/lib.txt @@ -29,11 +29,13 @@ _`.goal`: The goals of the MPS library interface are: _`.goal.host`: To control the dependency of the MPS on the hosted ISO C library so that the core MPS remains freestanding (see -design.mps.exec-env). +design.mps.exec-env_). + +.. _design.mps.exec-env: exec-env _`.goal.free`: To allow the core MPS convenient access to ISO C functionality that is provided on freestanding platforms (see -design.mps.exec-env.std.com.free). +design.mps.exec-env_). Description @@ -46,7 +48,7 @@ _`.overview.access`: The core MPS needs to access functionality that could be provided by an ISO C hosted environment. _`.overview.hosted`: The core MPS must not make direct use of any -facilities in the hosted environment (design.mps.exec-env). However, +facilities in the hosted environment (design.mps.exec-env_). However, it is sensible to make use of them when the MPS is deployed in a hosted environment. @@ -54,16 +56,6 @@ _`.overview.hosted.indirect`: The core MPS does not make any direct use of hosted ISO C library facilities. Instead, it indirects through the MPS Library Interface, impl.h.mpslib. -_`.overview.free`: The core MPS can make direct use of freestanding -ISO C library facilities and does not need to include any of the -header files ````, ````, and ```` -directly. - -_`.overview.complete`: The MPS Library Interface can be considered as -the complete "interface to ISO" (in that it provides direct access to -facilities that we get in a freestanding environment and equivalents -of any functionality we require from the hosted environment). - _`.overview.provision.client`: In a freestanding environment the client is expected to provide functions meeting this interface to the MPS. @@ -72,19 +64,6 @@ _`.overview.provision.hosted`: In a hosted environment, impl.c.mpsliban may be used. It just maps impl.h.mpslib directly onto the ISO C library equivalents. -[missing diagram] - - -Outside the interface -..................... - -We provide impl.c.mpsliban to the client, for two reasons: - -#. the client can use it to connect the MPS to the ISO C library if it - exists; - -#. as an example implementation of the MPS Library Interface. - Implementation -------------- @@ -97,8 +76,7 @@ which parallel those parts of the non-freestanding ISO headers which are used by the MPS. _`.impl.include`: The header file also includes the freestanding -headers ````, ````, and ```` (and not -````, though perhaps it should). +header ````. Document History @@ -117,7 +95,7 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. +Copyright © 2013-2015 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/design/lock.txt b/design/lock.txt index 5663bc41b4..069474ae42 100644 --- a/design/lock.txt +++ b/design/lock.txt @@ -1,50 +1,36 @@ .. mode: -*- rst -*- -The lock module -=============== +Lock module +=========== :Tag: design.mps.lock :Author: David Moore :Date: 1995-11-21 -:Status: incomplete design +:Status: complete design :Revision: $Id$ :Copyright: See `Copyright and License`_. :Index terms: pair: locking; design -Purpose -------- - -_`.purpose`: Support the locking needs of the thread-safe design. In -particular: +Introduction +------------ -- recursive locks; -- binary locks; -- recursive "global" lock that need not be allocated or initialized by - the client; -- binary "global" lock that need not be allocated or initialized by - the client. +_`.intro`: This is the design of the lock module. -_`.context`: The MPS has to be able to operate in a multi-threaded -environment. The thread-safe design (design.mps.thread-safety) -requires client-allocatable binary locks, a global binary lock and a -global recursive lock. An interface to client-allocatable recursive -locks is also present to support any potential use, because of -historic requirements, and because the implementation will presumably -be necessary anyway for the global recursive lock. +_`.readership`: Any MPS developer; anyone porting the MPS to a new +platform. Background ---------- _`.need`: In an environment where multiple threads are accessing -shared data. The threads which access data which is shared with other -threads need to cooperate with those threads to maintain consistency. -Locks provide a simple mechanism for doing this. +shared data, threads need to cooperate to maintain consistency. Locks +provide a simple mechanism for doing this. _`.ownership`: A lock is an object which may be "owned" by a single thread at a time. By claiming ownership of a lock before executing -some piece of code a thread can guarantee that no other thread owns +some piece of code, a thread can guarantee that no other thread owns the lock during execution of that code. If some other thread holds a claim on a lock, the thread trying to claim the lock will suspend until the lock is released by the owning thread. @@ -56,44 +42,55 @@ accessing thread. More generally any set of operations which are required to be mutually exclusive may be performed so by using locks. -Overview --------- +Requirements +------------ + +_`.req.thread-safety`: Support the locking needs of +design.mps.thread-safety_. + +.. _design.mps.thread-safety: thread-safety + +_`.req.binary`: Provide *binary* locks: that is, locks that can be +claimed, and until released, other attempts to claim them block. (This +is needed to implement the arena lock.) + +_`.req.recursive`: Provide *recursive* locks: that is, locks that can +be claimed again by the thread currently holding them, without +blocking or deadlocking. (This is needed to implement the global +recursive lock.) + +_`.req.global`: Provide *global* locks: that is locks that need not be +allocated or initialized by the user. -_`.adt`: There is an abstract datatype ``Lock`` which points to a -locking structure ``LockStruct``. This structure is opaque to any -client, although an interface is provided to supply the size of the -structure for any client wishing to make a new lock. The lock is not -allocated by the module as allocation itself may require locking. -``LockStruct`` is implementation specific. +_`.req.global.binary`: Provide a global binary lock. (This is required +to protect the data structure allowing multiple arenas to coordinate +handling of protection faults: see +design.mps.thread-safety.arch.global.binary_.) -_`.simple-lock`: There are facilities for claiming and releasing -locks. ``Lock`` is used for both binary and recursive locking. +.. _design.mps.thread-safety.arch.global.binary: thread-safety#arch.global.binary -_`.global-locks`: "Global" locks are so called because they are used -to protect data in a global location (such as a global variable). The -lock module provides two global locks; one recursive and one binary. -There are facilities for claiming and releasing both of these locks. -These global locks have the advantage that they need not be allocated -or atomically initialized by the client, so they may be used for -locking the initialization of the allocator itself. The binary global -lock is intended to protect mutable data, possibly in conjunction with -other local locking strategies. The recursive global lock is intended -to protect static read-only data during one-off initialization. See -design.mps.thread-safety. +_`.req.global.recursive`: Provide a global recursive lock. (This is +required to protect pool class initialization: see +design.mps.thread-safety.arch.global.recursive_.) -_`.deadlock`: This module does not provide any deadlock protection. -Clients are responsible for avoiding deadlock by using traditional -strategies such as ordering of locks. (See -design.mps.thread-safety.deadlock.) +.. _design.mps.thread-safety.arch.global.recursive: thread-safety#arch.global.recursive -_`.single-thread`: In the single-threaded configuration, locks are not -needed and the claim/release interfaces defined to be no-ops. +_`.req.deadlock.not`: There is no requirement to provide protection +against deadlock. (Clients are able to avoid deadlock using +traditional strategies such as ordering of locks; see +design.mps.thread-safety.deadlock_.) +.. _design.mps.thread-safety.deadlock: thread-safety#deadlock -Detailed design ---------------- -_`.interface`: The interface comprises the following functions: +Interface +--------- + +``typedef LockStruct *Lock`` + +An opaque type representing a lock. Clients that needs to allocate +space for a lock should dynamically allocate space for the structure, +calling ``LockSize()`` to determine the size. ``size_t LockSize(void)`` @@ -101,18 +98,19 @@ Return the size of a ``LockStruct`` for allocation purposes. ``void LockInit(Lock lock)`` -After initialisation the lock is not owned by any thread. +Initialize the lock. This must be called before any use of the lock. +After initialization, the lock is not owned by any thread. ``void LockFinish(Lock lock)`` -Before finalisation the lock must not beowned by any thread. +Finish the lock. The lock must not be owned by any thread. ``void LockClaim(Lock lock)`` -Claims ownership of a lock that was previously not held by current -thread. +Wait, if necessary, until the lock is not owned by any thread. Then +claim ownership of the lock by the current thread. -``void LockReleaseMPM(Lock lock)`` +``void LockRelease(Lock lock)`` Releases ownership of a lock that is currently owned. @@ -123,8 +121,8 @@ thread and claims the lock (if not already held). ``void LockReleaseRecursive(Lock lock)`` -Testores the previous state of the lock stored by corresponding -``LockClaimRecursive()`` call. +Restores the previous state of the lock remembered by the +corresponding ``LockClaimRecursive()`` call. ``void LockClaimGlobal(void)`` @@ -142,47 +140,135 @@ to the current thread and claims the lock (if not already held). ``void LockReleaseGlobalRecursive(void)`` -Restores the previous state of the recursive global lock stored by -corresponding ``LockClaimGlobalRecursive()`` call. +Restores the previous state of the recursive global lock remembered by +the corresponding ``LockClaimGlobalRecursive()`` call. + + +Implementation +-------------- _`.impl.recursive`: For recursive claims, the list of previous states -can be simply implemented by keeping a count of the number of claims -made by the current thread so far. In multi-threaded implementation -below this is handled by the operating system. A count is still kept -and used to check correctness. +can be implemented by keeping a count of the number of claims made by +the current thread so far. In the multi-threaded implementations this +is handled by the operating system interface, but a count is still +kept and used to check correctness. + +_`.impl.recursive.limit`: The implementation imposes a limit on the +number of recursive claims (see issue.lock-claim-limit_). On Windows, +the critical section object contains the field ``LONG +RecursionCount``. In typical POSIX Threads implementations, +``pthread_mutex_t`` uses an ``int`` for the count of recursive claims. + +.. _issue.lock-claim-limit: https://info.ravenbrook.com/project/mps/import/2001-09-27/mminfo/issue/lock-claim-limit -_`.impl.global`: The binary and recursive global locks may actually be -implemented using the same mechanism as normal locks. +_`.impl.global`: The binary and recursive global locks are typically +implemented using the same mechanism as normal locks. (But an +operating system-specific mechanism is used, if possible, to ensure +that the global locks are initialized just once.) -_`.impl.ansi`: Single-Threaded Generic Implementation: +_`.impl.an`: Single-threaded generic implementation ``lockan.c``: -- single-thread; +- single-threaded; - no need for locking; - locking structure contains count; - provides checking in debug version; - otherwise does nothing except keep count of claims. -_`.impl.win32`: Win32 Implementation: +_`.impl.w3`: Windows implementation ``lockw3.c``: -- supports Win32's threads; -- uses Critical Sections [ref?]; -- locking structure contains a Critical Section; -- both recursive and non-recursive calls use same Windows function; +- supports Windows threads; +- uses critical section objects [cso]_; +- locking structure contains a critical section object; +- recursive and non-recursive calls use the same Windows function; - also performs checking. -_`.impl.linux`: LinuxThreads Implementation (possibly suitable for all -PThreads implementations): +_`.impl.ix`: POSIX implementation ``lockix.c``: -- supports LinuxThreads threads, which are an implementation of - PThreads (see ``_); +- supports [POSIXThreads]_; - locking structure contains a mutex, initialized to check for recursive locking; - locking structure contains a count of the number of active claims; -- non-recursive locking calls ``pthread_mutex_lock()`` and expects success; +- non-recursive locking calls ``pthread_mutex_lock()`` and expects + success; - recursive locking calls ``pthread_mutex_lock()`` and expects either success or ``EDEADLK`` (indicating a recursive claim); - also performs checking. +_`.impl.li`: Linux implementation ``lockli.c``: + +- supports [POSIXThreads]_; +- also supports [LinuxThreads]_, a partial implementation of POSIX Threads + that was used in Linux 2.4 and 2.5; +- almost identical to `.impl.posix`_, except that on LinuxThreads + ``pthread_mutexattr_setkind_np`` is used where POSIX has + ``pthread_mutexattr_settype``. + + +Example +------- + +_`.example.init`: An example of allocating and initializing a lock:: + + #include "lock.h" + + static Lock lock; + + void init() + { + mps_addr_t p; + if (mps_alloc(&p, pool, LockSize()) != MPS_RES_OK) + exit(1); + lock = p; + LockInit(lock); + } + +_`.example.binary`: An example of using a binary lock:: + + void binaryUse() + { + /* lock must not be owned by this thread, or else this deadlocks. */ + LockClaim(lock); + /* lock is now owned by this thread. */ + /* cannot call binaryUse() at this point. */ + /* only one thread at a time may be at this point. */ + LockRelease(lock); + /* lock not owned by this thread. */ + } + +_`.example.recursive`: An example of using a recursive lock:: + + void recursiveUse() + { + /* lock may or may not already be owned by this thread. */ + LockClaimRecursive(lock); + /* lock is now owned by this thread. */ + /* cannot call binaryUse() at this point. */ + /* can call recursiveUse() at this point. */ + /* only one thread at a time may be at this point. */ + LockReleaseRecursive(lock); + /* lock is still owned by this thread if it was before. */ + } + + +References +---------- + +.. [cso] + Microsoft Developer Network; + "Critical Section Objects"; + + +.. [LinuxThreads] + Xavier Leroy; + "The LinuxThreads library"; + + +.. [POSIXThreads] + The Open Group; + "The Single UNIX Specification, Version 2---Threads"; + + + Document History ---------------- @@ -192,6 +278,8 @@ Document History - 2013-04-14 GDR_ Converted to reStructuredText. +- 2014-10-21 GDR_ Brought up to date. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/locus.txt b/design/locus.txt index 1e70de035b..4777857c9d 100644 --- a/design/locus.txt +++ b/design/locus.txt @@ -1,7 +1,7 @@ .. mode: -*- rst -*- -MPS Configuration -================= +Locus manager +============= :Tag: design.mps.locus :Author: Gavin Matthews @@ -20,8 +20,14 @@ the burden of having to be clever about tract/group placement away from the pools, preserving trace differentiability and contiguity where appropriate. -_`.source`: mail.gavinm.1998-02-05.17-52(0), mail.ptw.1998-02-05.19-53(0), -mail.pekka.1998-02-09.13-58(0), and mail.gavinm.1998-02-09.14-05(0). +_`.source`: `mail.gavinm.1998-02-05.17-52`_, +`mail.ptw.1998-02-05.19-53`_, `mail.pekka.1998-02-09.13-58`_, and +`mail.gavinm.1998-02-09.14-05`_. + +.. _mail.gavinm.1998-02-05.17-52: https://info.ravenbrook.com/project/mps/mail/1998/02/05/17-52/0.txt +.. _mail.ptw.1998-02-05.19-53: https://info.ravenbrook.com/project/mps/mail/1998/02/05/19-53/0.txt +.. _mail.pekka.1998-02-09.13-58: https://info.ravenbrook.com/project/mps/mail/1998/02/09/13-58/0.txt +.. _mail.gavinm.1998-02-09.14-05: https://info.ravenbrook.com/project/mps/mail/1998/02/09/14-05/0.txt _`.readership`: Any MPS developer. @@ -37,17 +43,19 @@ The MPS manages three main resources: The locus manager manages address space at the arena level. -.. note: +.. note:: + + Tucker was right: see `mail.ptw.1998-11-02.14-25`_. Richard + Kistruck, 2007-04-24. - Tucker was right: see mail.ptw.1998-11-02.14-25. Richard Kistruck, - 2007-04-24. + .. _mail.ptw.1998-11-02.14-25: https://info.ravenbrook.com/project/mps/mail/1998/11/02/14-25/0.txt When a pool wants some address space, it expresses some preferences to the locus manager. The locus manager and the arena (working together) try to honour these preferences, and decide what address space the pool gets. -Preferences are expressed by the ``SegPref`` argument to +Preferences are expressed by the ``LocusPref`` argument to ``SegAlloc()``. Note that, when they call ``SegAlloc()``, pools are asking for address space and writeable storage simultaneously, in a single call. There is currently no way for pools to reserve address @@ -76,13 +84,8 @@ Why is it important to manage address space? The locus manager manages the mapping from clumps to zones. - To specify a clump, pools use the ``SegPrefGen`` argument to - ``SegPrefExpress()``. - - .. note:: - - The name is misleading: generations are only one sort of clump. - Richard Kistruck, 2007-04-24. + To specify a clump, pools can pass ``LocusPrefZONESET`` and a set + of zones to ``LocusPrefExpress()``. #. Prevent address space fragmentation (within the arena) @@ -138,7 +141,7 @@ Why is it important to manage address space? Pools can specify a preference for High and Low ends of address space, which implies a search-order. Pools could also specify - clumping, using either ``SegPrefGen`` or ``SegPrefZoneSet``. + clumping, using ``LocusPrefZONESET``. Discovering the layout @@ -511,7 +514,10 @@ requested (to allow for large objects). _`.arch.chunk`: Arenas may allocate more address space in additional chunks, which may be disjoint from the existing chunks. Inter-chunk space will be represented by dummy regions. There are also sentinel -regions at both ends of the address space. +regions at both ends of the address space. See +design.mps.arena.chunk_. + +.. _design.mps.arena.chunk: arena#chunk Overview of strategy diff --git a/design/message-gc.txt b/design/message-gc.txt index 004e80d4bc..f69a3d2c3f 100644 --- a/design/message-gc.txt +++ b/design/message-gc.txt @@ -75,11 +75,14 @@ be extra content that is only meaningful to MPS staff, to help us diagnose client problems. While there is some overlap with the Diagnostic Feedback system -(design.mps.diag) and the Telemetry system (design.mps.telemetry), the +(design.mps.diag_) and the Telemetry system (design.mps.telemetry_), the main contrasts are that these GC messages are present in release builds, are stable from release to release, and are designed to be parsed by the client program. +.. _design.mps.telemetry: telemetry +.. _design.mps.diag: diag + Names and parts --------------- diff --git a/design/message.txt b/design/message.txt index c81cb75e4f..807f9cc6cd 100644 --- a/design/message.txt +++ b/design/message.txt @@ -35,7 +35,7 @@ message should be critical. _`.contents`: This document describes the design of the external and internal interfaces and concludes with a sketch of an example design of an internal client. The example is that of implementing -finalization using PoolMRG. +finalization using ``PoolMRG``. _`.readership`: Any MPS developer. @@ -44,10 +44,12 @@ Requirements ------------ _`.req`: The client message protocol will be used for implementing -finalization (see design.mps.finalize and req.dylan.fun.final). It +finalization (see design.mps.finalize_ and req.dylan.fun.final). It will also be used for implementing the notification of various conditions (possibly req.dylan.prot.consult is relevant here). +.. _design.mps.finalize: finalize + External interface ------------------ @@ -139,7 +141,9 @@ abstractly: ``FinalizationMessage(Ref)``. _`.type.finalization.semantics`: A finalization message indicates that an object has been discovered to be finalizable (see -design.mps.poolmrg.def.final.object for a definition of finalizable). +design.mps.poolmrg.def.final.object_ for a definition of finalizable). + +.. _design.mps.poolmrg.def.final.object: poolmrg#def.final.object _`.type.finalization.ref`: There is an accessor to get the reference of the finalization message (i.e. a reference to the object which is @@ -184,11 +188,13 @@ _`.message.instance`: Messages are instances of Message Classes. _`.message.concrete`: Concretely a message is represented by a ``MessageStruct``. A ``MessageStruct`` has the usual signature field -(see design.mps.sig). A ``MessageStruct`` has a type field which +(see design.mps.sig_). A ``MessageStruct`` has a type field which defines its type, a ring node, which is used to attach the message to the queue of pending messages, a class field, which identifies a ``MessageClass`` object. +.. _design.mps.sig: sig + _`.message.intent`: The intention is that a ``MessageStruct`` will be embedded in some richer object which contains information relevant to that specific type of message. @@ -347,16 +353,21 @@ Finalization .. note:: - Possibly out of date, see design.mps.finalize and - design.mps.poolmrg instead. David Jones, 1997-08-28. + Possibly out of date, see design.mps.finalize_ and + design.mps.poolmrg_ instead. David Jones, 1997-08-28. + + .. _design.mps.poolmrg: poolmrg + .. _design.mps.finalize: finalize This subsection is a sketch of how PoolMRG will use Messages for -finalization (see design.mps.poolmrg). +finalization (see design.mps.poolmrg_). -PoolMRG has guardians (see design.mps.poolmrg.guardian). Guardians are -used to manage final references and detect when an object is +PoolMRG has guardians (see design.mps.poolmrg.guardian_). Guardians +are used to manage final references and detect when an object is finalizable. +.. _design.mps.poolmrg.guardian: poolmrg#guardian + The link part of a guardian will include a ``MessageStruct``. The ``MessageStruct`` is allocated when the final reference is created diff --git a/design/nailboard-1.svg b/design/nailboard-1.svg new file mode 100644 index 0000000000..cb260e2f61 --- /dev/null +++ b/design/nailboard-1.svg @@ -0,0 +1,567 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + + + + + base + limit + + + + ibase + ilimit + + diff --git a/design/nailboard-2.svg b/design/nailboard-2.svg new file mode 100644 index 0000000000..feb69c25cd --- /dev/null +++ b/design/nailboard-2.svg @@ -0,0 +1,564 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + + + + base + limit + + + + ibase + ilimit + + diff --git a/design/nailboard-3.svg b/design/nailboard-3.svg new file mode 100644 index 0000000000..6d53a80507 --- /dev/null +++ b/design/nailboard-3.svg @@ -0,0 +1,628 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + + + + + + ibase + ilimit + + + + + leftsplinter + rightsplinter + + + + base + limit + + diff --git a/design/nailboard.txt b/design/nailboard.txt new file mode 100644 index 0000000000..2f88b5b57b --- /dev/null +++ b/design/nailboard.txt @@ -0,0 +1,254 @@ +.. mode: -*- rst -*- + +Nailboards for ambiguously referenced segments +============================================== + +:Tag: design.mps.nailboard +:Author: Gareth Rees +:Date: 2014-01-15 +:Status: complete design +:Revision: $Id$ +:Copyright: See section `Copyright and License`_. +:Index terms: pair: nailboard; design + + +Introduction +------------ + +_`.intro`: This is the design of the nailboard module. + +_`.readership`: Any MPS developer. + +_`.overview`: A nailboard represents a set of addresses to which +ambiguous references have been found. It is implemented as a +specialized bit table that maps addresses within a range to *nails*. +The mapping has granularity, so that all addresses within a word, say, +will map to the same nail. + +_`.purpose`: Nailboards are used by the AMC pool class to record +ambiguous references to grains within a segment. See +design.mps.poolamc.nailboard_. + +.. _design.mps.poolamc.nailboard: poolamc#nailboard + + +Requirements +------------ + +_`.req.granularity`: A nailboard must be able to set nails for +addresses down to the grain size of the segment. (Because individual +objects may be this small, and we must be able to preserve or reclaim +individual objects.) + +_`.req.set`: A nailboard must be able to set a nail corresponding to +any aligned address in the range covered. (Because ambiguous +references may have arbitrary values.) + +_`.req.reset.not`: A nailboard is *not* required to be able to reset a +nail. (Because resetting a nail would correspond to proving that there +is *no* ambiguous reference to that address, but that can only be +established when the trace is complete.) + +_`.req.range`: A nailboard must be able to determine if any nail is +set in a continguous range. (Because we must preserve the whole object +if there is any ambiguous reference to it.) + +_`.req.range.cost`: Determining if any nail is set in a continuous +range must be cheap. That is, it must take time that is no more than +logarithmic in the size of the range. (Because scanning overhead must +be proportional to the number of objects, not to their size.) + + +Implementation +-------------- + +_`.impl.table`: The nailboard consists of a header structure and one +or more bit tables. Each bit table covers the whole range of +addresses, but at a different level of detail. + +_`.impl.table.level0`: The level 0 bit table has one bit for each +aligned address in the range. + +_`.impl.align`: The alignment of the nailboard need not be the same as +the pool alignment. This is because nailboards are per-segment, and +the pool may know the minimum size of an object in a particular +segment. + +_`.impl.table.k`: The level *k* bit table has one bit for each ``scale`` +bits in the level *k*\−1 bit table (this bit is set if any bit in the +corresponding word in the level *k*\−1 table is set). + +_`.impl.scale`: Here ``scale`` is an arbitrary scale factor that must +be a power of 2. It could in future be supplied as a parameter when +creating a nailboard, but in the current implementation it is always +``MPS_WORD_WIDTH``. + +_`.impl.table.last`: The last bit table is always shorter than one +word. This is slightly wasteful in some cases (for example, a +nailboard with 64 nails and ``scale`` 64 will have two levels, the +second level having just one bit), but allows the code to support +small nailboards without special cases in the code (consider the case +of a nailboard with just one nail). + +_`.impl.size`: The size of the level *i* bit table is the ceiling of + + (``limit`` − ``base``) / (``align`` × ``scale``\ :superscript:`i`\ ) + +where ``base`` and ``limit`` are the bounds of the address range being +represented in the nailboard and ``align`` is the alignment. + +_`.impl.address`: The address *a* may be looked up in the level *i* +bit table at the bit + + (*a* − ``base``) / (``align`` × ``scale``\ :superscript:`i`\ ) + +and since ``align`` and ``scale`` are powers of 2, that's + + (*a* − ``base``) >> (log\ :subscript:`2`\ ``align`` + *i* log\ :subscript:`2`\ ``scale``) + +_`.impl.set`: Setting a nail for an address *a* in a nailboard is on +the critical path: it is called for every fix of an ambiguous +reference to an address in an AMC pool. When setting a nail, we set +the corresponding bit in every level of the nailboard. + +_`.impl.isresrange`: Testing a range of addresses to see if any nails +are set is also on the critical path: it is called for every object in +any AMC segment with a nailboard when the segment is scanned and when +it is reclaimed. + +_`.impl.isresrange.strategy`: The strategy for testing to see if any +nails are set in a range is to handle the cases that are expected to +be common first. In particular, we expect that there will only be few +nails in a nailboard, so most calls to ``NailboardIsResRange()`` will +return ``TRUE``. + +_`.impl.isresrange.alignment`: When testing a range against a level of +a nailboard, the base and limit of the range will typically not align +exactly to the bits of that level. Therefore we test against a +slightly larger range, as shown in the diagram: + +.. figure:: nailboard-1.svg + :align: center + :alt: Diagram: Testing a range against a level of a nailboard. + + Testing a range against a level of a nailboard. + +_`.impl.isresrange.empty`: If all bits in the range [``ibase``, +``ilimit``) are reset, as shown above, then there are no nails in the +range of addresses [``base``, ``limit``). This provides an early exit +with result ``TRUE``. + +_`.impl.isresrange.level0`: If the "empty" early exit is not taken, +and we are looking at the level 0 bit table, then the range is not +empty. This provides an early exit with result ``FALSE``. + +_`.impl.isresrange.inner`: If any bit in the range [``ibase``\+1, +``ilimit``\−1) is set, as shown below, then there is a nail in the +range of addresses [``base``, ``limit``). This provides an early exit +with result ``FALSE``. + +.. figure:: nailboard-2.svg + :align: center + :alt: Diagram: a nail is set in this range. + + A nail is set in this range. + +_`.impl.isresrange.splinter`: If none of the three early exits is +taken, then we are in a situation like the one shown below, with one +or two *splinters*. In this situation we know that there is a nail, +but it is not clear whether the nail is inside the splinter or not. We +handle this situation by moving up to the previous level and looking +at the range of addresses covered by the splinter. + +.. figure:: nailboard-3.svg + :align: center + :alt: Diagram: it is not clear if a nail is set in this range. + + It is not clear if a nail is set in this range. + +_`.impl.isresrange.splinter.recurse`: When looking at a splinter, we +might reach the same situation: namely, that the interior of the +splinter is empty, but the edge of the splinter is set. We handle this +by reducing the size of the splinter and moving up to the previous +level. + +_`.impl.isresrange.splinter.one-sided`: This splinter-of-a-splinter is +one-sided: that is, we don't need to look at the right splinter of a +left splinter or vice versa, because we know that it is empty. + + +Future +------ + +_`.future.tune`: The implementation makes heavy use of +``BTIsResRange()``, but this function is not well tuned for scanning +small arrays (which we expect to be the common case for nailboards). +Performance might be improved by special-casing the small levels. + +_`.future.limit`: In C and C++, a pointer to "one past the last +element of an array object" (the limit of the object in our +terminology) is a valid pointer and can be used in pointer arithmetic. +See §6.5.6.8–9 of [C1999]_. So in theory a programmer could have such +a pointer as the only reference keeping an object alive, and still +expect to be able to subtract from it to get back to the object. The +current nailboard implementation does not support this use case. + + +References +---------- + +.. [C1999] + International Standard ISO/IEC 9899:1999; + "Programming languages — C"; + + + +Document History +---------------- + +- 2014-01-15 GDR_ Initial draft. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 2014 Ravenbrook Limited. All rights reserved. +. This is an open source license. Contact +Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** diff --git a/design/object-debug.txt b/design/object-debug.txt index 300a4e261a..df89b5cf21 100644 --- a/design/object-debug.txt +++ b/design/object-debug.txt @@ -70,6 +70,11 @@ an ``AVER()`` has fired. Naturally, if the information required for the dump has been corrupted, it will fail, as softly as possible (source @@@@). +_`.req.portable`: Client code that uses these features must be easily +portable to all the supported platforms. (Source: job003749_.) + +.. _job003749: http://www.ravenbrook.com/project/mps/issue/job003749/ + .. note:: There are more requirements, especially about memory dumps and @@ -90,6 +95,11 @@ specified as a byte/word which used repeatedly to fill the fencepost. _`.fence.content.template`: The content could be given as a template which is of the right size and is simply copied onto the fencepost. +_`.fence.content.template.repeat`: The content could be given as a +template which is copied repeatedly until the fencepost is full. (This +would avoid the need to specify different templates on different +architectures, and so help meet `.req.portable`_.) + _`.fence.walk`: `.req.fencepost.check`_ requires the ability to find all the allocated objects. In formatted pools, this is not a problem. In unformatted pools, we could use the walker. It's a feasible @@ -188,11 +198,14 @@ adjust the pointer and the size for alloc and free, to put down the fenceposts during alloc, and to check them; to avoid slowing down all allocation, this would require some MOPping to make the format class affect the choice of the alloc and free methods (see -mail.pekka.1998-06-11.18-18). +`mail.pekka.1998-06-11.18-18`_). + +.. _mail.pekka.1998-06-11.18-18: https://info.ravenbrook.com/project/mps/mail/1998/06/11/18-18/0.txt _`.fence.wrapper.alloc.size`: We could just communicate the size of the fenceposts between the format and the allocation routines, but -then you couldn't use variable fenceposts (.fence.wrapper.variable). +then you couldn't use variable fenceposts +(`.fence.wrapper.variable`_). .. note:: @@ -233,14 +246,14 @@ to pools. In particular, clients will be able to use tagging and fenceposting separately on each pool. _`.fence.size`: Having fenceposts of adjustable size and pattern is -quite useful. We feel that restricting the size to an integral -multiple of the [pool or format?] alignment is harmless and simplifies -the implementation enormously. +useful. Restricting the size to an integral multiple of the [pool or +format?] alignment would simplify the implementation but breaks +`.req.portable`_. _`.fence.template`: We use templates (`.fence.content.template`_) to fill in the fenceposts, but we do not give any guarantees about the -location of the fenceposts, only that they're properly aligned. This -leaves us the opportunity to do tail-only fenceposting, if we choose. +location of the fenceposts. This leaves us the opportunity to do +tail-only fenceposting, if we choose. _`.fence.slop`: [see impl.c.dbgpool.FenceAlloc @@@@] @@ -314,32 +327,26 @@ fenceposts in a pool (``AVER()`` if a problem is found) _`.interface.fenceposting.format`: A function to wrap a format (class) to provide fenceposting. -``void (*mps_fmt_adjust_fencepost_t)(size_t *size_io)`` +``typedef void (*mps_fmt_adjust_fencepost_t)(size_t *size_io)`` _`.interface.fenceposting.adjust`: A format method to adjust size of a block about to be allocted to allow for fenceposts. -``void (*mps_fmt_put_fencepost_t)(mps_addr_t * addr_io, size_t size)`` +``typedef void (*mps_fmt_put_fencepost_t)(mps_addr_t * addr_io, size_t size)`` _`.interface.fenceposting.add`: A format method to add a fencepost around a block about to be allocated. The ``NULL`` method adds a tail fencepost. -``mps_bool_t (*mps_fmt_check_fenceposts_t)(mps_addr_t)`` +``typedef mps_bool_t (*mps_fmt_check_fenceposts_t)(mps_addr_t)`` _`.interface.fenceposting.checker`: A format method to check the fenceposts around an object. The ``NULL`` method checks tails. -``mps_class_t mps_debug_class(mps_class_t class)`` +``mps_res_t mps_alloc_dbg(mps_addr_t *, mps_pool_t, size_t, ...)`` +``mps_res_t mps_alloc_dbg_v(mps_addr_t *, mps_pool_t, size_t, va_list)`` -_`.interface.fenceposting.pool`: A function to wrap a pool class -to provide fenceposting (note absence of arena parameter). - -``mps_res_t mps_alloc(mps_addr_t *, mps_pool_t, size_t);`` -``mps_res_t mps_alloc_dbg(mps_addr_t *, mps_pool_t, size_t, ...);`` -``mps_res_t mps_alloc_dbg_v(mps_addr_t *, mps_pool_t, size_t, va_list);`` - -_`.interface.tags.alloc`: Three functions to replace existing +_`.interface.tags.alloc`: Two functions to extend the existing ``mps_alloc()`` (request.???.??? proposes to remove the varargs) ``void (*mps_objects_step_t)(mps_addr_t addr, size_t size, mps_fmt_t format, mps_pool_t pool, void *tag_data, void *p)`` @@ -410,12 +417,16 @@ Document History - 1998-09-10 Pekka Pirinen The first draft merely records all the various ideas about fenceposting that came up in discussions in June, July and September 1998. This includes the format wrapping - idea from mail.ptw.1998-06-19.21-13(0). + idea from `mail.ptw.1998-06-19.21-13`_. + + .. _mail.ptw.1998-06-19.21-13: https://info.ravenbrook.com/project/mps/mail/1998/06/19/21-13/0.txt - 2002-06-07 RB_ Converted from MMInfo database design document. - 2013-04-14 GDR_ Converted to reStructuredText. +- 2014-04-09 GDR_ Added newly discovered requirement `.req.portable`_. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/poolamc.txt b/design/poolamc.txt index d7d698a766..fca51e38da 100644 --- a/design/poolamc.txt +++ b/design/poolamc.txt @@ -128,8 +128,8 @@ Placement pads are the BEF and LSP pads created in "to-space" when placing objects into segments. This wasted space is an expected space-cost of AMC's naive (but time-efficient) approach to placement of objects into segments. This is normally not a severe problem. (The -worst case is a client that always requests ``ArenaAlign() + 1`` byte -objects: this has a nearly 100% overhead). +worst case is a client that always requests ``amc->extendBy + 1`` byte +objects: this has an overhead of nearly ``ArenaGrainSize() / amc->extendBy``). Retained pads could be a problem @@ -156,20 +156,23 @@ specially. Small, medium, and large segments --------------------------------- -AMC categorises segments as **small** (one page), **medium** -(several pages), or **large** (``AMCLargeSegPAGES`` or more):: +AMC categorises segments as **small** (up to ``amc->extendBy``), **medium** +(larger than small but smaller than large), or **large** (``amc->largeSize`` or +more):: - pages = SegSize(seg) / ArenaAlign(arena); - if(pages == 1) { + size = SegSize(seg); + if(size < amc->extendBy) { /* small */ - } else if(pages < AMCLargeSegPAGES) { + } else if(size < amc->largeSize) { /* medium */ } else { /* large */ } -``AMCLargeSegPAGES`` is currently 8 -- see `The LSP payoff -calculation`_ below. +``amc->extendBy`` defaults to 4096 (rounded up to the arena alignment), and is +settable by using :c:macro:`MPS_KEY_EXTEND_BY` keyword argument. +``amc->largeSize`` is currently 32768 -- see `The LSP payoff calculation`_ +below. AMC might treat "Large" segments specially, in two ways: @@ -206,8 +209,8 @@ and ignore all others; this would be extremely simple to implement. But AMC cannot guarantee this, because in the MPS Allocation Point Protocol the client is permitted to make a large buffer reserve and then fill it with many small objects. In such a case, AMC must honour -all nails (if the buffer reserve request was an exact multiple of -``ArenaAlign()``), or all nails except to the last object (if there +all nails (if the buffer reserve request was an exact multiple of the +arena grain size), or all nails except to the last object (if there was a remainder filled with an LSP pad). Because an LSP pad cannot be distinguished from a client object, and the requested allocation size is not recorded, AMC cannot distinguish these two conditions at @@ -253,15 +256,17 @@ segments smaller than eight pages long do not need to be treated as large: the insurance cost to "play safe" would be considerable (wasting up to one page of remainder per seven pages of allocation), and the fragmentation overhead risk is not that great (at most eight -times worse than the unavoidable minimum). So ``AMCLargeSegPAGES`` is -defined as 8 in config.h. As long as the assumption that most segments +times worse than the unavoidable minimum). So ``AMC_LARGE_SIZE_DEFAULT`` is +defined as 32768 in config.h. As long as the assumption that most segments are not ambiguously referenced remains correct, I expect this policy will be satisfactory. To verify that this threshold is acceptable for a given client, poolamc.c calculates metrics; see `Feedback about retained pages`_ below. If this one-size-fits-all approach is not satisfactory, -``AMCLargeSegPAGES`` could be made a client-tunable parameter. +``amc->largeSize`` is a client-tunable parameter which defaults to +``AMC_LARGE_SIZE_DEFAULT``. It can be tuned by passing an +:c:macro:`MPS_KEY_LARGE_SIZE` keyword argument to :c:func:`mps_pool_create_k`. Retained pages @@ -345,26 +350,18 @@ Segments -------- _`.seg.class`: AMC allocates segments of class ``AMCSegClass``, which -is a subclass of ``GCSegClass``. Instances contain a ``segTypeP`` -field, which is of type ``int*``. +is a subclass of ``GCSegClass``. -_`.seg.gen`: AMC organizes the segments it manages into generations. +_`.seg.gen`: AMC organizes the segments it manages into generations. _`.seg.gen.map`: Every segment is in exactly one generation. -_`.seg.gen.ind`: The segment's ``segTypeP`` field indicates which +_`.seg.gen.ind`: The segment's ``gen`` field indicates which generation (that the segment is in) (an ``AMCGenStruct`` see blah below). -_`.seg.typep`: The ``segTypeP`` field actually points to either the -type field of a generation or to the type field of a nail board. - -_`.seg.typep.distinguish`: The ``type`` field (which can be accessed -in either case) determines whether the ``segTypeP`` field is pointing -to a generation or to a nail board. - _`.seg.gen.get`: The map from segment to generation is implemented by -``AMCSegGen()`` which deals with all this. +``amcSegGen()`` which deals with all this. Fixing and nailing @@ -375,42 +372,42 @@ Fixing and nailing This section contains placeholders for design rather than design really. David Jones, 1998-02-04. -_`.nailboard`: AMC uses a nail board structure for recording ambiguous -references to segments. A nail board is a bit table with one bit per -grain in the segment. +_`.nailboard`: AMC uses a nailboard structure for recording ambiguous +references to segments. See design.mps.nailboard_. -_`.nailboard.create`: Nail boards are allocated dynamically whenever a -segment becomes newly ambiguously referenced. +.. _design.mps.nailboard: nailboard -_`.nailboard.destroy`: They are deallocated during reclaim. Ambiguous -fixes simply set the appropriate bit in this table. This table is used -by subsequent scans and reclaims in order to work out what objects -were marked. +_`.nailboard.create`: A nailboard is allocated dynamically whenever a +segment becomes newly ambiguously referenced. This table is used by +subsequent scans and reclaims in order to work out which objects were +ambiguously referenced. + +_`.nailboard.destroy`: The nailboatrd is deallocated during reclaim. _`.nailboard.emergency`: During emergency tracing two things relating -to nail boards happen that don't normally: +to nailboards happen that don't normally: -#. _`.nailboard.emergency.nonew`: Nail boards aren't allocated when we +#. _`.nailboard.emergency.nonew`: Nailboards aren't allocated when we have new ambiguous references to segments. _`.nailboard.emergency.nonew.justify`: We could try and allocate a - nail board, but we're in emergency mode so short of memory so it's + nailboard, but we're in emergency mode so short of memory so it's unlikely to succeed, and there would be additional code for yet another error path which complicates things. -#. _`.nailboard.emergency.exact`: nail boards are used to record exact +#. _`.nailboard.emergency.exact`: nailboards are used to record exact references in order to avoid copying the objects. - _`.nailboard.hyper-conservative`: Not creating new nail boards + _`.nailboard.hyper-conservative`: Not creating new nailboards (`.nailboard.emergency.nonew`_ above) means that when we have a new reference to a segment during emergency tracing then we nail the entire segment and preserve everything in place. _`.fix.nail.states`: Partition the segment states into four sets: -#. white segment and not nailed (and has no nail board); -#. white segment and nailed and has no nail board; -#. white segment and nailed and has nail board; +#. white segment and not nailed (and has no nailboard); +#. white segment and nailed and has no nailboard; +#. white segment and nailed and has nailboard; #. the rest. _`.fix.nail.why`: A segment is recorded as being nailed when either @@ -420,26 +417,23 @@ wasn't enough memory to allocate the copy). In either of these cases reclaim cannot simply destroy the segment (usually the segment will not be destroyed because it will have live objects on it, though see `.nailboard.limitations.middle`_ below). If the segment is nailed then -we might be using a nail board to mark objects on the segment. -However, we cannot guarantee that being nailed implies a nail board, -because we might not be able to allocate the nail board. Hence all +we might be using a nailboard to mark objects on the segment. +However, we cannot guarantee that being nailed implies a nailboard, +because we might not be able to allocate the nailboard. Hence all these states actually occur in practice. _`.fix.nail.distinguish`: The nailed bits in the segment descriptor -(``SegStruct``) are used to record whether a segment is nailed or not. -The ``segTypeP`` field of the segment either points to (the "type" -field of) an ``AMCGen`` or to an ``AMCNailBoard``, the type field can -be used to determine which of these is the case. (see `.seg.typep`_ -above). +(``SegStruct``) are used to record the set of traces for which a +segment has nailed objects. -_`.nailboard.limitations.single`: Just having a single nail board per +_`.nailboard.limitations.single`: Just having a single nailboard per segment prevents traces from improving on the findings of each other: a later trace could find that a nailed object is no longer nailed or -even dead. Until the nail board is discarded, that is. +even dead. Until the nailboard is discarded, that is. -_`.nailboard.limitations.middle`: An ambiguous reference into the -middle of an object will cause the segment to survive, even if there -are no surviving objects on it. +_`.nailboard.limitations.middle`: An ambiguous reference to a segment +that does not point into any object in that segment will cause that +segment to survive even though there are no surviving objects on it. _`.nailboard.limitations.reclaim`: ``AMCReclaimNailed()`` could cover each block of reclaimed objects between two nailed objects with a @@ -453,13 +447,13 @@ _`.emergency.fix`: ``AMCFixEmergency()`` is at the core of AMC's emergency tracing policy (unsurprisingly). ``AMCFixEmergency()`` chooses exactly one of three options: -#. use the existing nail board structure to record the fix; +#. use the existing nailboard structure to record the fix; #. preserve and nail the segment in its entirety; #. snapout an exact (or high rank) pointer to a broken heart to the broken heart's forwarding pointer. If the rank of the reference is ``RankAMBIG`` then it either does (1) -or (2) depending on wether there is an existing nail board or not. +or (2) depending on wether there is an existing nailboard or not. Otherwise (the rank is exact or higher) if there is a broken heart it is used to snapout the pointer. Otherwise it is as for an ``RankAMBIG`` reference: we either do (1) or (2). @@ -487,9 +481,11 @@ which is how segments get allocated in that generation. _`.buffer.condemn`: We condemn buffered segments, but not the contents of the buffers themselves, because we can't reclaim uncommitted -buffers (see design.mps.buffer for details). If the segment has a +buffers (see design.mps.buffer_ for details). If the segment has a forwarding buffer on it, we detach it. +.. _design.mps.buffer: buffer + .. note:: Why? Forwarding buffers are detached because they used to cause @@ -548,8 +544,8 @@ one generation. _`.gen.rep`: Generations are represented using an ``AMCGenStruct`` structure. -_`.gen.create`: All the generation are create when the pool is created -(during ``AMCInitComm()``). +_`.gen.create`: All the generations are created when the pool is +created (during ``AMCInitComm()``). _`.gen.manage.ring`: An AMC's generations are kept on a ring attached to the ``AMCStruct`` (the ``genRing`` field). @@ -702,15 +698,8 @@ memory block. See format documentation for details of the interface. _`.header.client`: The code mostly deals in client pointers, only computing the base and limit of a block when these are needed (such as when an object is copied). In several places, the code gets a block of -some sort, a segment or a buffer, and creates a client pointer by -adding the header length (``pool->format->headerLength``). - -_`.header.fix`: There are two versions of the fix method, due to its -criticality, with (``AMCHeaderFix()``) and without (``AMCFix()``) -headers. The correct one is selected in ``AMCInitComm()``, and placed -in the pool's fix field. This is the main reason why fix methods -dispatch through the instance, rather than the class like all other -methods. +some sort (a segment or a buffer) and creates a client pointer by +adding the header size (``pool->format->headerSize``). Old and aging notes below here diff --git a/design/poolams.txt b/design/poolams.txt index bf602b5470..ef59e46724 100644 --- a/design/poolams.txt +++ b/design/poolams.txt @@ -21,21 +21,25 @@ _`.intro`: This is the design of the AMS pool class. _`.readership`: MM developers. -_`.source`: design.mps.buffer, design.mps.trace, design.mps.scan, -design.mps.action and design.mps.class-interface [none of these were -actually used -- pekka 1998-04-21]. No requirements doc [we need a +_`.source`: design.mps.buffer_, design.mps.trace_, design.mps.scan_, +design.mps.action and design.mps.class-interface_ [none of these were +actually used -- pekka 1998-04-21]. No requirements doc [we need a req.mps that captures the commonalities between the products -- pekka 1998-01-27]. +.. _design.mps.class-interface: class-interface +.. _design.mps.scan: scan +.. _design.mps.trace: trace +.. _design.mps.buffer: buffer + Overview -------- -_`.overview`: This document describes the design of the AMS (Automatic -Mark-and-Sweep) pool class. The AMS pool is a proof-of-concept design -for a mark-sweep pool in the MPS. It's not meant to be efficient, but -it could serve as a model for an implementation of a more advanced -pool (such as EPVM). +_`.overview`: This is the design of the AMS (Automatic Mark-and-Sweep) +pool class. The AMS pool is a proof-of-concept design for a mark-sweep +pool in the MPS. It's not meant to be efficient, but it could serve as +a model for an implementation of a more advanced pool (such as EPVM). Requirements @@ -101,7 +105,7 @@ new segment. _`.no-alloc`: Do not support ``PoolAlloc()``, because we can't support one-phase allocation for a scannable pool (unless we disallow -incremental collection). For exact details, see design.mps.buffer. +incremental collection). For exact details, see design.mps.buffer_. _`.no-free`: Do not support ``PoolFree()``, because automatic pools don't need explicit free and having it encourages clients to use it @@ -183,7 +187,7 @@ buffer (see `.iteration.buffer`_), as usual. .. note:: - design.mps.buffer should explain why this works, but doesn't. + design.mps.buffer_ should explain why this works, but doesn't. Pekka P. Pirinen, 1998-02-11. _`.fix.to-black`: When fixing a reference to a white object, if the @@ -235,7 +239,7 @@ the appropriate action, if any, on it. .. note:: ScanLimit is used for reasons which are not documented in - design.mps.buffer. + design.mps.buffer_. Scanning Algorithm @@ -405,7 +409,7 @@ what segments may be split or merged: - _`.split-merge.constrain.align`: Segments may only be split or merged at an address which is aligned to the pool alignment as well - as to the arena alignment. + as to the arena grain size. _`.split-merge.constrain.align.justify`: This constraint is implied by the design of allocation and colour tables, which cannot @@ -425,13 +429,15 @@ what segments may be split or merged: _`.split-merge.fail`: The split and merge methods are not proper anti-methods for each other (see -design.mps.seg.split-merge.fail.anti.no). Methods will not reverse the +design.mps.seg.split-merge.fail.anti.no_). Methods will not reverse the side-effects of their counterparts if the allocation of the colour and allocation bit tables should fail. Client methods which over-ride split and merge should not be written in such a way that they might detect failure after calling the next method, unless they have reason to know that the bit table allocations will not fail. +.. _design.mps.seg.split-merge.fail.anti.no: seg#split-merge.fail.anti.no + Testing ------- diff --git a/design/poolawl.txt b/design/poolawl.txt index 790956b28f..b8b7482064 100644 --- a/design/poolawl.txt +++ b/design/poolawl.txt @@ -40,7 +40,9 @@ Must satisfy request.dylan.170123_. _`.req.obj-format`: Only objects of a certain format need be supported. This format is a subset of the Dylan Object Format. The pool uses the first slot in the fixed part of an object to store an -association. See mail.drj.1997-03-11.12-05 +association. See `mail.drj.1997-03-11.12-05`_. + +.. _mail.drj.1997-03-11.12-05: https://info.ravenbrook.com/project/mps/mail/1997/03/11/12-05/0.txt Definitions @@ -157,7 +159,7 @@ AWL pools to be 1. _`.awlseg`: The pool defines a segment class ``AWLSegClass``, which is a subclass of ``GCSegClass`` (see -design.mps.seg.over.hierarchy.gcseg). All segments allocated by the +design.mps.seg.over.hierarchy.gcseg_). All segments allocated by the pool are instances of this class, and are of type ``AWLSeg``, for which the structure is:: @@ -173,10 +175,14 @@ which the structure is:: Sig sig; } +.. _design.mps.seg.over.hierarchy.gcseg: seg#over.hierarchy.gcseg + _`.awlseg.bt`: The mark, alloc, and scanned fields are bit-tables (see -design.mps.bt). Each bit in the table corresponds to a a single +design.mps.bt_). Each bit in the table corresponds to a a single alignment grain in the pool. +.. _design.mps.bt: bt + _`.awlseg.mark`: The mark bit table is used to record mark bits during a trace. ``AWLCondemn()`` (see `.fun.condemn`_ below) sets all the bits of this table to zero. Fix will read and set bits in this table. @@ -409,7 +415,9 @@ perform another pass (see `.fun.scan.pass`_ above). ``Res AWLFix(Pool pool, ScanState ss, Seg seg, Ref *refIO)`` _`.fun.fix`: ``ss->wasMarked`` is set to ``TRUE`` (clear compliance -with design.mps.fix.protocol.was-marked.conservative). +with design.mps.fix.protocol.was-marked.conservative_). + +.. _design.mps.fix.protocol.was-marked.conservative: fix#protocol.was-marked.conservative If the rank (``ss->rank``) is ``RankAMBIG`` then fix returns immediately unless the reference is aligned to the pool alignment. @@ -451,7 +459,7 @@ objects. Now reclaim doesn't need to check that the objects are allocated before skipping them. There may be a corresponding change for scan as well. -``Res AWLDescribe(Pool pool, mps_lib_FILE *stream)`` +``Res AWLDescribe(Pool pool, mps_lib_FILE *stream, Count depth)`` _`.fun.describe`: @@ -463,10 +471,11 @@ Internal _`.fun.awlsegcreate`: Creates a segment of class ``AWLSegClass`` of size at least ``size``. -_`.fun.awlsegcreate.size.round`: ``size`` is rounded up to an -``ArenaAlign`` before requesting the segment. +_`.fun.awlsegcreate.size.round`: ``size`` is rounded up to the arena +grain size before requesting the segment. + _`.fun.awlsegcreate.size.round.justify`: The arena requires that all -segment sizes are aligned to the ``ArenaAlign``. +segment sizes are rounded up to the arena grain size. _`.fun.awlsegcreate.where`: The segment is allocated using a generation preference, using the generation number stored in the diff --git a/design/poollo.txt b/design/poollo.txt index f5bc78e7e3..ebb402c316 100644 --- a/design/poollo.txt +++ b/design/poollo.txt @@ -152,10 +152,12 @@ _`.loseg.sig`: The signature for a loseg is 0x519705E9 (SIGLOSEG). _`.loseg.lo`: The lo field points to the LO structure that owns this segment. -_`.loseg.bit`: Bit Tables (see design.mps.bt) are used to record +_`.loseg.bit`: Bit Tables (see design.mps.bt_) are used to record allocation and mark information. This is relatively straightforward, but might be inefficient in terms of space in some circumstances. +.. _design.mps.bt: bt + _`.loseg.mark`: This is a Bit Table that is used to mark objects during a trace. Each grain in the segment is associated with 1 bit in this table. When ``LOFix()`` (see `.fun.fix`_ below) is called the diff --git a/design/poolmrg.txt b/design/poolmrg.txt index 26e401ff9a..148e016fae 100644 --- a/design/poolmrg.txt +++ b/design/poolmrg.txt @@ -29,7 +29,10 @@ Generation-Based Garbage Collector") were used in this design. Some analysis of this design (including various improvements and some more in-depth justification) is in analysis.mps.poolmrg. That document should be understood before changing this document. It is also helpful -to look at design.mps.finalize and design.mps.message. +to look at design.mps.finalize_ and design.mps.message_. + +.. _design.mps.message: message +.. _design.mps.finalize: finalize Goals @@ -98,7 +101,9 @@ intended to provide the functionality of "finalization". _`.over.internal`: The MRG pool class is internal to the MPM: it is not intended to have a client interface. Clients are expected to access the functionality provided by this pool (finalization) using a -separate MPS finalization interface (design.mps.finalize). +separate MPS finalization interface (design.mps.finalize_). + +.. _design.mps.finalize: finalize _`.over.one-size`: The MRG pool class manages objects of a single size, each object containing a single reference of rank final. @@ -142,11 +147,13 @@ necessary for implementing finalization. It is the means by which the MPS detects that objects are finalizable. _`.over.message`: ``PoolClassMRG`` implements a ``MessageClass`` (see -design.mps.message). All the messages are of one ``MessageType``. This +design.mps.message_). All the messages are of one ``MessageType``. This type is ``MessageTypeFinalization``. Messages are created when objects are discovered to be finalizable and destroyed when the MPS client has received the message. +.. _design.mps.message: message + _`.over.message.justify`: Messages provide a means for the MPS to communicate with its client. Notification of finalization is just such a communication. Messages allow the MPS to inform the client of @@ -174,7 +181,9 @@ Object Registration _`.protocol.register`: There is a protocol by which objects can be registered for finalization. This protocol is handled by the arena module on behalf of finalization. see -design.mps.finalize.int.finalize. +design.mps.finalize.int.finalize_. + +.. _design.mps.finalize.int.finalize: finalize#int.finalize Finalizer execution @@ -193,7 +202,9 @@ Setup / destroy _`.protocol.life`: An instance of PoolClassMRG is needed in order to support finalization, it is called the "final" pool and is attached to -the arena (see design.mps.finalize.int.arena.struct). +the arena (see design.mps.finalize.int.arena.struct_). + +.. _design.mps.finalize.int.arena.struct: finalize#int.arena.struct _`.protocol.life.birth`: The final pool is created lazily by ``ArenaFinalize()``. @@ -410,8 +421,8 @@ used to be in the object is not accidentally fixed. _`.init`: Initializes the entry list, the free ring, the ref ring, and the ``extendBy`` field. -_`.init.extend`: The ``extendBy`` field is initialized to one -``ArenaAlign()`` (usually a page). +_`.init.extend`: The ``extendBy`` field is initialized to the arena +grain size. _`.init.extend.justify`: This is adequate as the pool is not expected to grow very quickly. @@ -471,11 +482,12 @@ required. See analysis.mps.poolmrg.improve.scan.nomove for a suggested improvement that avoids redundant unlinking and relinking. -``Res MRGDescribe(Pool pool, mps_lib_FILE *stream)`` +``Res MRGDescribe(Pool pool, mps_lib_FILE *stream, Count depth)`` _`.describe`: Describes an MRG pool. Iterates along each of the entry and exit lists and prints the guardians in each. The location of the guardian and the value of the reference in it will be printed out. +Provided for debugging only. _`.functions.unused`: All of these will be unused: ``BufferInit()``, ``BufferFill()``, ``BufferEmpty()``, ``BufferFinish()``, @@ -517,7 +529,9 @@ Future ------ _`.future.array`: In future, for speed or simplicity, this pool could -be rewritten to use an array. See mail.gavinm.1997-09-04.13-08(0). +be rewritten to use an array. See `mail.gavinm.1997-09-04.13-08`_. + +.. _mail.gavinm.1997-09-04.13-08: https://info.ravenbrook.com/project/mps/mail/1997/09/04/13-08/0.txt Tests @@ -616,10 +630,6 @@ All objects from the MRG pool will then be freed (thus dropping all references to the AMC objects). This will test `.promise.faithful`_ and `.promise.live`_. -_`.test.promise.ut.not`: The following part of the test has not -implemented. This is because the messaging system has not yet been -implemented. - _`.test.promise.ut.alloc`: A number of objects will be allocated in the AMC pool. diff --git a/design/poolmvff.txt b/design/poolmvff.txt index 2c16b80c4a..d29efc6ba8 100644 --- a/design/poolmvff.txt +++ b/design/poolmvff.txt @@ -40,102 +40,44 @@ allocation can be used at the same time, but in that case, the first ap must be created before any allocations. _`.over.buffer.class`: The pool uses the simplest buffer class, -BufferClass. This is appropriate since these buffers don't attach to -segments, and hence don't constrain buffered regions to lie within +``BufferClass``. This is appropriate since these buffers don't attach +to segments, and hence don't constrain buffered regions to lie within segment boundaries. -_`.over.segments`: The pool uses the simplest segment class -(SegClass). There's no need for anything more complex. - Methods ------- -_`.method`: The MVFF pool supports the following methods: - -``Res MVFFInit(Pool pool, Args arg)`` - -_`.method.init`: This takes six `keyword arguments`_: - -.. _`keyword arguments`: keyword-arguments - -================================== ============================================ -Keyword argument Description -================================== ============================================ -``MPS_KEY_EXTEND_BY`` The segment size. -``MPS_KEY_MEAN_SIZE`` The average object size. -``MPS_KEY_ALIGN`` The alignment of allocations and frees. - Must be at least ``sizeof(void *)``. -``MPS_KEY_MVFF_SLOT_HIGH`` Whether to allocate objects at the end of - free blocks found, as opposed to at - the start (for unbuffered - allocation). -``MPS_KEY_MVFF_ARENA_HIGH`` Whether to express ``SegPrefHIGH`` - to the arena, as opposed to - ``SegPrefLOW``. -``MPS_KEY_MVFF_FIRST_FIT`` whether to use the suitable block of lowest - address, as opposed to the highest - (for unbuffered allocation) -================================== ============================================ - -_`.method.init.epdl`: To simulate the EPDL pool, specify ``extendBy``, -``avgSize``, and ``maxSize`` as normal, and use ``slotHigh=FALSE``, -``arenaHigh=FALSE``, ``firstFit=TRUE``. - -_`.method.init.epdr`: To simulate the EPDR pool, specify ``extendBy``, -``avgSize``, and ``maxSize`` as normal, and use ``slotHigh=TRUE``, -``arenaHigh=TRUE``, ``firstFit=TRUE``. - -_`.method.init.other`: The performance characteristics of other -combinations are unknown. - -_`.method.finish`: The ``PoolFinish()`` method, - -_`.method.alloc`: ``PoolAlloc()`` and ``PoolFree()`` methods are -supported, implementing the policy set by the pool params (see -`.method.init`_). - -_`.method.describe`: The usual describe method. - _`.method.buffer`: The buffer methods implement a worst-fit fill strategy. -External Functions ------------------- - -_`.function`: MVFF supports the following external functions: - -_`.function.free-size`: ``mps_mvff_free_size()`` returns the total -size of free space in segments allocated to the MVFF pool instance. - -_`.function.size`: ``mps_mvff_size()`` returns the total memory used -by pool segments, whether free or allocated. - -_`.function.class`: ``mps_class_mvff()`` returns the class object for -the pool class, to be used in pool creation. - - Implementation -------------- +_`.impl.alloc_list`: The pool stores the address ranges that it has +acquired from the arena in a CBS (see design.mps.cbs_). + _`.impl.free-list`: The pool stores its free list in a CBS (see -//gdr-peewit/info.ravenbrook.com/project/mps/branch/2013-05-17/emergency/design/poolmvff.txt -`design.mps.cbs `_), failing over in emergencies to a Freelist -(see design.mps.freelist) when the CBS cannot allocate new control +design.mps.cbs_), failing over in emergencies to a Freelist (see +design.mps.freelist_) when the CBS cannot allocate new control structures. This is the reason for the alignment restriction above. +.. _design.mps.cbs: cbs +.. _design.mps.freelist: freelist + Details ------- -_`.design.seg-size`: When adding a segment, we use extendBy as the -segment size unless the object won't fit, in which case we use the -object size (in both cases we align up). +_`.design.acquire-size`: When acquiring memory from the arena, we use +``extendBy`` as the unit of allocation unless the object won't fit, in +which case we use the object size (in both cases we align up to the +arena alignment). -_`.design.seg-fail`: If allocating a segment fails, we try again with -a segment size just large enough for the object we're allocating. This -is in response to request.mps.170186_. +_`.design.acquire-fail`: If allocating ``extendBy``, we try again with +an aligned size just large enough for the object we're allocating. +This is in response to request.mps.170186_. .. _request.mps.170186: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/mps/170186 @@ -160,6 +102,12 @@ Document History - 2013-06-04 GDR_ The CBS module no longer maintains its own emergency list, so MVFF handles the fail-over from its CBS to a Freelist. +- 2014-04-15 GDR_ The address ranges acquired from the arena are now + stored in a CBS; segments are no longer used for this purpose. + +- 2014-06-12 GDR_ Remove public interface documentation (this is in + the reference manual). + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/poolmvt.txt b/design/poolmvt.txt index e6f998855d..73f289017b 100644 --- a/design/poolmvt.txt +++ b/design/poolmvt.txt @@ -25,9 +25,13 @@ _`.readership`: MM developers _`.source`: req.dylan(6), req.epcore(16), req.product(2) -_`.background`: design.mps.poolmv(0), design.mps.poolepdl(0), +_`.background`: design.mps.poolmv_, design.mps.poolepdl(0), design.product.soft.drop(0), paper.wil95(1), paper.vo96(0), -paper.grun92(1), paper.beck82(0), mail.ptw.1998-02-25.22-18(0) +paper.grun92(1), paper.beck82(0), `mail.ptw.1998-02-25.22-18`_. + +.. _design.mps.poolmv: poolmv + +.. _mail.ptw.1998-02-25.22-18: https://info.ravenbrook.com/project/mps/mail/1998/02/25/22-18/0.txt Definitions @@ -76,9 +80,11 @@ object (see also, glossary.reference.count ). _`.def.segment`: A segment is a contiguous extent of memory. In this document, segment is used to mean a contiguous extent of memory -managed by the MPS arena (design.mps.arena(1)) and subdivided by the +managed by the MPS arena (design.mps.arena_) and subdivided by the pool to provide blocks (see `.def.block`_) to its clients. +.. _design.mps.arena: arena + _`.def.splay-tree`: A splay tree is a self-adjusting binary tree (paper.st85(0), paper.sleator96(0)). @@ -474,7 +480,7 @@ and space. _`.anal.strategy.risk`: The current MPS segment substrate can cause internal fragmentation which an individual pool can do nothing about. -We expect that `request.epcore.170193.sugg.loci`_ will be implemented to +We expect that request.epcore.170193.sugg.loci_ will be implemented to remove this risk. .. _`request.epcore.170193.sugg.loci`: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/epcore/170193/ @@ -775,8 +781,8 @@ Fail-over to address-ordered free list ...................................... _`.impl.c.freelist`: Because the CBS uses out-of-band storage, it may -be unable to handle insert (design.mps.cbs.function.cbs.insert.fail) -and delete (design.mps.cbs.function.cbs.delete.fail) operations. When +be unable to handle insert (design.mps.cbs.function.cbs.insert.fail_) +and delete (design.mps.cbs.function.cbs.delete.fail_) operations. When this happen MVT fails over to an address-ordered singly linked free list. This uses in-band storage and so cannot run out of memory, but it has much worse performance than the CBS. Therefore MVT eagerly @@ -784,7 +790,9 @@ attempts to flush blocks from the free list back to the CBS. See design.mps.freelist_ for the design and implementation of the free list. -.. _design.mps.freelist: freelist.txt +.. _design.mps.cbs.function.cbs.delete.fail: cbs#function.cbs.delete.fail +.. _design.mps.cbs.function.cbs.insert.fail: cbs#function.cbs.insert.fail +.. _design.mps.freelist: freelist Available Block Queue @@ -817,15 +825,17 @@ freeing. Standard checking and description should be provided. collected pools, but no manual pool currently does.] _`.impl.c.future`: The implementation should not preclude "buffered -free" (mail.ptw.1997-12-05.19-07(0), ff.) being added in the future. +free" (`mail.ptw.1997-12-05.19-07`_) being added in the future. + +.. _mail.ptw.1997-12-05.19-07: https://info.ravenbrook.com/project/mps/mail/1997/12/05/19-07/0.txt _`.impl.c.parameters`: The pool parameters are calculated as follows from the input parameters: minimum, mean, and maximum size are taked directly from the parameters. _`.impl.c.parameter.fill-size`: The fill size is set to the maximum -size times the reciprocal of the fragmentation limit, aligned to the -arena alignment. +size times the reciprocal of the fragmentation limit, rounded up to +the arena grain size. _`.imple.c.parameter.reuse-size`: The reuse size is set to twice the fill size (see `.arch.abq.return.segment`_, @@ -943,10 +953,11 @@ _`.test.component`: Components `.impl.c.splay`_, `.impl.c.cbs`_, and `.impl.c.abq`_ will be subjected to individual component tests to verify their functionality. -_`.test.regression`: All tests applied to poolmv -(design.mps.poolmv(0)) and poolepdl (design.mps.poolepdl(0)) will be -applied to poolmvt to ensure that mvt is at least as functional as the -pools it is replacing. +_`.test.regression`: All tests applied to MV (design.mps.poolmv_) and +EPDL (design.mps.poolepdl(0)) will be applied to poolmvt to ensure +that mvt is at least as functional as the pools it is replacing. + +.. _design.mps.poolmv: poolmv _`.test.qa`: Once poolmvt is integrated into the MPS, the standard MPS QA tests will be applied to poolmvt prior to each release. @@ -959,7 +970,9 @@ proc.release.epcore(2).test) Text ---- -Possible tweaks (from mail.pekka.1998-04-15.13-10(0)): +Possible tweaks (from `mail.pekka.1998-04-15.13-10`_): + +.. _mail.pekka.1998-04-15.13-10: https://info.ravenbrook.com/project/mps/mail/1998/04/15/13-10/0.txt - Try to coalesce splinters returned from AP's with the front (or any) block on the ABQ. @@ -973,19 +986,29 @@ B. Document History ------------------- - 1998-02-04 PTW Initial e-mail discussion. See thread starting with - mail.ptw.1998-02-04.21-27(0). + `mail.ptw.1998-02-04.21-27`_. + + .. _mail.ptw.1998-02-04.21-27: https://info.ravenbrook.com/project/mps/mail/1998/02/04/21-27/0.txt - 1998-02-13 PTW Initial draft based on e-mail request for comments. - See thread starting with mail.ptw.1998-02-12.03-36. + See thread starting with `mail.ptw.1998-02-12.03-36`_. + + .. _mail.ptw.1998-02-12.03-36: https://info.ravenbrook.com/project/mps/mail/1998/02/12/03-36/0.txt - 1998-04-01 PTW Revised in response to e-mail request for comments. - See thread starting with mail.ptw.1998-03-23.20-43(0). + See thread starting with `mail.ptw.1998-03-23.20-43`_. + + .. _mail.ptw.1998-03-23.20-43: https://info.ravenbrook.com/project/mps/mail/1998/03/23/20-43/0.txt - 1998-04-15 PTW Revised in response to e-mail request for comments. - See thread starting with mail.ptw.1998-04-13.21-40(0). + See thread starting with `mail.ptw.1998-04-13.21-40`_. + + .. _mail.ptw.1998-04-13.21-40: https://info.ravenbrook.com/project/mps/mail/1998/04/13/21-40/0.txt - 1998-05-06 PTW Revised in response to review - review.design.mps.poolmv2.2(0). + review.design.mps.poolmv2.2_(0). + +.. _design.mps.poolmv2.2: poolmv2#2 - 2002-06-07 RB_ Converted from MMInfo database design document. diff --git a/design/prmc.txt b/design/prmc.txt new file mode 100644 index 0000000000..0822e70952 --- /dev/null +++ b/design/prmc.txt @@ -0,0 +1,311 @@ +.. mode: -*- rst -*- + +Protection mutator context +========================== + +:Tag: design.mps.prmc +:Author: Gareth Rees +:Date: 2014-10-23 +:Status: complete design +:Revision: $Id$ +:Copyright: See `Copyright and License`_. +:Index terms: pair: protection mutator context; design + + +Introduction +------------ + +_`.intro`: This is the design of the protection mutator context +module. + +_`.readership`: Any MPS developer; anyone porting the MPS to a new +platform. + +_`.overview`: The protection mutator context module decodes the +*context* of a mutator thread at the point when it caused a protection +fault, so that access to a protected region of memory can be handled, +or when it was suspended by the thread manager, so that its registers +and control stack can be scanned. + +_`.def.context`: The *context* of a thread (also called its +*continuation*) is an abstract representation of the control state of +the thread at a point in time, including enough information to +continue the thread from that point. + +_`.status`: The protection mutator context module does not currently +present a clean interface to the rest of the MPS: source files are +inconsistently named, and the implementation is (necessarily) mixed up +with the implementation of the memory protection module +(design.mps.prot_) and the thread manager +(design.mps.thread-manager_). + +.. _design.mps.prot: prot +.. _design.mps.thread-manager: thread-manager + + +Requirements +------------ + +_`.req.fault.addr`: Must determine the address that the mutator was +trying to access when it caused a protection fault. (Without this +address the MPS can't handle the fault. See ``ArenaAccess()``.) + +_`.req.fault.access`: Should determine whether the mutator was trying +to read or write the address when it caused a protection fault. (This +enables a performance improvement in the case of a write fault. A read +fault must be handled by ensuring the address pointed to has been +fixed, which may require scanning the segment, whereas a write fault +merely requires that the segment's summary be discarded. See +``TraceSegAccess()``.) + +_`.req.fault.step`: Should be able to emulate the access that caused +the fault. (This enables a significant performance improvement for +weak hash tables. See request.dylan.160044_.) + +.. _request.dylan.160044: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/160044/ + +_`.req.suspend.scan`: Must capature enough information to ambiguously +scan all roots in the context of a thread that has been suspended by +the thread manager. (This is necessary for conservative garbage +collection to work. See design.mps.thread-manager.if.scan_.) + +.. _design.mps.thread-manager.if.scan: thread-manager#if.scan + + +Interface +--------- + +``typedef MutatorFaultContextStruct *MutatorFaultContext`` + +_`.if.context`: A structure representing the context of the mutator at +the point when a protection fault occurred, or when it was suspended +by the thread manager. This structure should be declared in a header +so that it can be inlined in the ``Thread`` structure if necessary. +See design.mps.thread-manager.if.thread_. + +.. _design.mps.thread-manager.if.thread: thread-manager#if.thread + +``Bool ProtCanStepInstruction(MutatorFaultContext context)`` + +_`.if.canstep`: Examine the context to determine whether the +protection module can single-step the instruction which is causing the +fault. Return ``TRUE`` if ``ProtStepInstruction()`` is capable of +single-stepping the instruction, or ``FALSE`` if not. + +``Bool Res ProtStepInstruction(MutatorFaultContext context)`` + +_`.if.step`: Single-step the instruction which is causing the fault. +Update the mutator context according to the emulation or execution of +the instruction, so that resuming the mutator will not cause the +instruction which was caused the fault to be re-executed. Return +``ResOK`` if the instruction was single-stepped successfully, or +``ResUNIMPL`` if the instruction cannot be single-stepped. + +This function is only called if ``ProtCanStepInstruction(context)`` +returned ``TRUE``. + +``Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext context)`` + +_`.if.context.scan`: Scan all roots found in ``context`` using the +given scan state (typically by calling ``TraceScanAreaTagged()``), and +return the result code from the scanner. + +``Addr MutatorFaultContextSP(MutatorFaultContext context)`` + +_`.if.context.sp`: Return the pointer to the "top" of the thread's +stack at the point given by ``context``. In the common case, where the +stack grows downwards, this is actually the lowest stack address. + + +Implementations +--------------- + +Generic implementation +...................... + +_`.impl.an`: In ``prmcan.c``. + +_`.impl.an.context`: There is no definition of +``MutatorFaultContextStruct`` and so the mutator context cannot be +decoded. + +_`.impl.an.fault`: Compatible only with the generic memory protection +module (design.mps.prot.impl.an_) where there are no protection +faults. + +.. _design.mps.prot.impl.an: prot#impl.an + +_`.impl.an.suspend`: Compatible only with the generic thread manager +module (design.mps.thread-manager.impl.an_) where there is only one +thread, and so no threads are suspended. + +.. _design.mps.thread-manager.impl.an: thread-manager#impl.an + + +Unix implementation +................... + +_`.impl.ix`: In ``protsgix.c``, with processor-specific parts in +``proti3.c`` and ``proti6.c``, and other platform-specific parts in +``prmci3fr.c``, ``prmci3li.c``, ``prmci6fr.c``, and ``prmci6li.c``. + +_`.impl.ix.context`: The context consists of the |siginfo_t|_ and +|ucontext_t|_ structures. POSIX specifies some of the fields in +``siginfo_t``, but says nothing about the contents of ``ucontext_t``. +This is decoded on a platform-by-platform basis. + +.. |siginfo_t| replace:: ``siginfo_t`` +.. _siginfo_t: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html +.. |ucontext_t| replace:: ``ucontext_t`` +.. _ucontext_t: http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html + +_`.impl.ix.fault.addr`: POSIX specifies that ``siginfo_t.si_addr`` is +the address that the faulting instruction was attempting to access. + +_`.impl.ix.fault.mode`: This implementation does not attempt to +determine whether the fault was a read or write. + +_`.impl.ix.fault.step`: This is implemented only on IA-32, and only +for "simple MOV" instructions. + +_`.impl.ix.suspend`: ``PThreadextSuspend()`` records the context of +each suspended thread, and ``ThreadRingSuspend()`` stores this in the +``Thread`` structure. + +_`.impl.ix.context.scan`: The context's root registers are found in +the ``ucontext_t.uc_mcontext`` structure. + +_`.impl.ix.context.sp`: The stack pointer is obtained from +``ucontext_t.uc_mcontext.mc_esp`` (FreeBSD on IA-32), +``uc_mcontext.gregs[REG_ESP]`` (Linux on IA-32), +``ucontext_t.uc_mcontext.mc_rsp`` (FreeBSD on x86-64), or +``uc_mcontext.gregs[REG_RSP]`` (Linux on x86-64). + + +Windows implementation +...................... + +_`.impl.w3`: In ``proti3.c``, ``proti6.c``, ``prmci3w3.c``, and +``prmci6w3.c``. + +_`.impl.w3.context`: The context of a thread that hit a protection +fault is given by the |EXCEPTION_POINTERS|_ structure passed to a +vectored exception handler, which points to |EXCEPTION_RECORD|_ and +|CONTEXT|_ structures. + +.. |EXCEPTION_POINTERS| replace:: ``EXCEPTION_POINTERS`` +.. _EXCEPTION_POINTERS: http://msdn.microsoft.com/en-us/library/windows/desktop/ms679331.aspx +.. |EXCEPTION_RECORD| replace:: ``EXCEPTION_RECORD`` +.. _EXCEPTION_RECORD: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363082.aspx +.. |CONTEXT| replace:: ``CONTEXT`` +.. _CONTEXT: http://msdn.microsoft.com/en-gb/library/windows/desktop/ms679284.aspx + +_`.impl.w3.fault.addr`: ``EXCEPTION_RECORD.ExceptionAddress`` is the +address that the faulting instruction was trying to access. + +_`.impl.w3.fault.mode`: ``EXCEPTION_RECORD.ExceptionInformation[0]`` +is 0 for a read fault, 1 for a write fault, and 8 for an execute +fault (which we handle as a read fault). + +_`.impl.w3.fault.step`: This is implemented only on IA-32, and only +for "simple MOV" instructions. + +_`.impl.w3.suspend`: The context of a suspended thread is returned by +|GetThreadContext|_. + +.. |GetThreadContext| replace:: ``GetThreadContext()`` +.. _GetThreadContext: http://msdn.microsoft.com/en-gb/library/windows/desktop/ms679362.aspx + +_`.impl.w3.context.scan`: The context's root registers are found in +the |CONTEXT|_ structure. + +_`.impl.w3.context.sp`: The stack pointer is obtained from +``CONTEXT.Esp`` (on IA-32) or ``CONTEXT.Rsp`` (on x86-64). + + +OS X implementation +................... + +_`.impl.xc`: In ``protxc.c``, with processor-specific parts in +``proti3.c`` and ``proti6.c``, and other platform-specific parts in +``prmci3xc.c`` and ``prmci6xc.c``. + +_`.impl.xc.context`: The context consists of the +``__Request__mach_exception_raise_state_identity_t`` and +``x86_thread_state32_t`` or ``x86_thread_state64_t`` structures. There +doesn't seem to be any documentation for these structures, but they +are defined in the Mach headers. + +_`.impl.xc.fault.addr`: ``__Request__mach_exception_raise_state_identity_t.code[1]`` is the +address that the faulting instruction was trying to access. + +_`.impl.xc.fault.mode`: This implementation does not attempt to +determine whether the fault was a read or write. + +_`.impl.xc.fault.step`: This is implemented only on IA-32, and only +for "simple MOV" instructions. + +_`.impl.xc.suspend`: The context of a suspended thread is obtained by +calling |thread_get_state|_. + +.. |thread_get_state| replace:: ``thread_get_state()`` +.. _thread_get_state: http://www.gnu.org/software/hurd/gnumach-doc/Thread-Execution.html + +_`.impl.xc.context.scan`: The thread's registers are found in the +``x86_thread_state32_t`` or ``x86_thread_state64_t`` structure. + +_`.impl.xc.context.sp`: The stack pointer is obtained from +``x86_thread_state32_t.__esp`` (on IA-32) or +``x86_thread_state64_t.__rsp`` (on x86-64). + + +Document History +---------------- + +- 2014-10-23 GDR_ Initial draft based on design.mps.thread-manager_ + and design.mps.prot_. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 2014 Ravenbrook Limited. All rights reserved. +. This is an open source license. Contact +Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** diff --git a/design/prot.txt b/design/prot.txt index a56e53db09..fb06b78b32 100644 --- a/design/prot.txt +++ b/design/prot.txt @@ -1,26 +1,73 @@ .. mode: -*- rst -*- -The protection module -===================== +Memory protection +================= :Tag: design.mps.prot :Author: David Jones :Date: 1997-04-02 -:Status: incomplete document +:Status: complete design :Revision: $Id$ :Copyright: See `Copyright and License`_. -:Index terms: pair: protection interface; design +:Index terms: pair: memory protection; design Introduction ------------ -_`.intro`: This is the generic design of the Protection Module. The -protection module provides protection services to other parts of the -MPS. It is expected that different operating systems will have -different implementations of this module. +_`.intro`: This is the design of the memory protection module. -_`.readership`: Any MPS developer. +_`.readership`: Any MPS developer; anyone porting the MPS to a new +platform. + +_`.overview`: The memory protection module ensures that the mutator +sees a consistent view of memory during incremental collection, by +applying protection to areas of memory, ensuring that attempts to read +or write from those areas cause protection faults, and implementing +the means for the MPS to handle these faults. + + +Requirements +------------ + +_`.req.consistent`: Must ensure that the mutator sees a consistent +view of memory during incremental collection: in particular, the +mutator must never see objects in oldspace. (Otherwise there's no way +for the MPS to interface with uncooperative code.) + +_`.req.prot.read`: Should allow collections to proceed incrementally, +by read-protecting pages that are not consistent from the mutator's +point of view. (This is the only way for the MPS to meet real-time +requirements on pause times.) + +_`.req.prot.write`: Should allow the MPS to maintain remembered sets +for segments that it has scanned, by write-protecting pages in these +segments. (This improves performance by allowing the MPS to avoid +scanning these segments again.) + +_`.req.fault.handle`: If the module implements protection, it must +also provide a mechanism for handling protection faults. (Otherwise +the MPS cannot take the correct action: that is, fixing references in +a read-protected segment, and discarding the remembered set from a +write-protected segment. See ``TraceSegAccess()``.) + + +Design +------ + +_`.sol.sync`: If memory protection is not available, only way to meet +`.req.consistent`_, is ensure that no protection is required, +essentially by running the collector until it has no more incremental +work to do. (This makes it impossible to meet real-time requirements +on pause times, but may be the best that can be done.) + +_`.sol.fault.handle`: The protection module handles protection faults +by decoding the context of the fault (see +design.mps.prmc.req.fault.addr_ and design.mps.prmc.req.fault.access_) +and calling ``ArenaAccess()``. + +.. _design.mps.prmc.req.fault.addr: prmc#req.fault.addr +.. _design.mps.prmc.req.fault.access: prmc#req.fault.access Interface @@ -28,62 +75,73 @@ Interface ``void ProtSetup(void)`` -_`.if.setup`: ``ProtSetup()`` will be called exactly once (per -process). It will be called as part of the initialization of the first -space that is created. It should arrange for the setup and -initialization of any datastructures or services that are necessary in -order to implement the protection module. (On UNIX it expected that it -will install a signal handler, on Windows it will do nothing) +_`.if.setup`: Called exactly once (per process) as part of the +initialization of the first arena that is created. It must arrange for +the setup and initialization of any data structures or services that +are necessary in order to implement the memory protection module. + +``Size ProtGranularity(void)`` + +_`.if.granularity`: Return the granularity of protection. The ``base`` +and ``limit`` arguments to ``ProtSet()`` must be multiples of the +protection granularity. ``void ProtSet(Addr base, Addr limit, AccessSet mode)`` -_`.if.set`: ``ProtSet()`` should set the protection of the memory -between base and limit, including base, but not including limit (ie -the half-open interval [base,limit)) to that specified by mode. The -mode parameter should have the ``AccessWRITE`` bit set if write -accesses to the page are to be forbidden, and should have the -``AccessREAD`` bit set if read accesses to the page are to be -forbidden. A request to forbid read accesses (that is, ``AccessREAD`` -is set) may also forbid write accesses, but read accesses will not be -forbidden unless ``AccessREAD`` is set. +_`.if.set`: Set the protection of the range of memory between ``base`` +(inclusive) and ``limit`` (exclusive) to *forbid* the specified modes. +The addresses ``base`` and ``limit`` are multiples of the protection +granularity. The ``mode`` parameter contains the ``AccessWRITE`` bit +if write accesses to the range are to be forbidden, and contains the +``AccessREAD`` bit if read accesses to the range are to be forbidden. + +_`.if.set.read`: If the request is to forbid read accesses (that is, +``AccessREAD`` is set) then the implemntation may also forbid write +accesses, but read accesses must not be forbidden unless +``AccessREAD`` is set. + +_`.if.set.noop`: ``ProtSet()`` is permitted to be a no-op if +``ProtSync()`` is implemented. + +``void ProtSync(Arena arena)`` -``void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), void *p, size_t s)`` +_`.if.sync`: Ensure that the actual protection (as determined by the +operating system) of every segment in the arena matches the segment's +protection mode (``seg->pm``). -_`.if.tramp`: [undocumented] +_`.if.sync.noop`: ``ProtSync()`` is permitted to be a no-op if +``ProtSet()`` is implemented. -``void ProtSync(Space space)`` -_`.if.sync`: ``ProtSync()`` is called to ensure that the actual -protection of each segment (as determined by the OS) is in accordance -with the segments's ``pm`` field. +Implementations +--------------- -``typedef struct MutatorFaultContextStruct *MutatorFaultContext`` +_`.impl.an`: Generic implementation in ``protan.c``. -_`.if.context-type`: This abstract type is implemented by the -protection module (impl.c.prot*). It represents the continuation of -the mutator which is restored after a mutator fault has been handled. -The functions ``ProtCanStepInstruction()`` (`.if.canstep`_ below) and -``ProtStepInstruction()`` (`.if.step`_ below) inspect and manipulate -the context. +_`.impl.an.set`: ``ProtSet()`` does nothing. -``Bool ProtCanStepInstruction(MutatorFaultContext context)`` +_`.impl.an.sync`: ``ProtSync()`` has no way of changing the protection +of a segment, so it simulates faults on all segments that are supposed +to be protected, by calling ``TraceSegAccess()``, until it determines +that no segments require protection any more. This forces the trace to +proceed until it is completed, preventing incremental collection. -_`.if.canstep`: Examines the context to determine whether the -protection module can single-step the instruction which is causing the -fault. Should return ``TRUE`` if and only if the instruction can be -single-stepped (that is, ``ProtStepInstruction()`` can be called). +_`.impl.an.sync.issue`: This relies on the pool actually removing the +protection, otherwise there is an infinite loop here. This is +therefore not compatible with implementations of the protection +mutator context module that support single-stepping of accesses (see design.mps.prmc.req.fault.step_). -``Bool Res ProtStepInstruction(MutatorFaultContext context)`` +.. _design.mps.prmc.req.fault.step: prmc#req.fault.step -_`.if.step`: Single-steps the instruction which is causing the fault. -This function should only be called if ``ProtCanStepInstruction()`` -applied to the context returned ``TRUE``. It should return -``ResUNIMPL`` if the instruction cannot be single-stepped. It should -return ``ResOK`` if the instruction is single-stepped. +_`.impl.ix`: POSIX implementation. -The mutator context will be updated by the emulation/execution of the -instruction such that resuming the mutator will not cause the -instruction which was causing the fault to be executed. +_`.impl.li`: Linux implementation. See design.mps.protli_. + +.. _design.mps.protli: protli + +_`.impl.w3`: Windows implementation. + +_`.impl.xc`: OS X implementation. Document History @@ -95,6 +153,11 @@ Document History - 2013-05-23 GDR_ Converted to reStructuredText. +- 2014-10-23 GDR_ Move protection mutator context interface to + design.mps.prmc_. Bring design up to date. + + .. _design.mps.prmc: prmc + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/protan.txt b/design/protan.txt deleted file mode 100644 index 4ab046d7e4..0000000000 --- a/design/protan.txt +++ /dev/null @@ -1,135 +0,0 @@ -.. mode: -*- rst -*- - -ANSI implementation of protection module -======================================== - -:Tag: design.mps.protan -:Author: David Jones -:Date: 1997-03-19 -:Status: incomplete document -:Revision: $Id$ -:Copyright: See `Copyright and License`_. -:Index terms: - pair: ANSI; protection interface design - pair: ANSI protection interface; design - - -Introduction ------------- - -_`.readership`: Any MPS developer. - -_`.intro`: This is the design for the ANSI implementation of the -protection module. - - -Requirements ------------- - -_`.req.test`: This module is required for testing. Particularly on -platforms where no real implementation of the protection module -exists. - -_`.req.rapid-port`: This module is required for rapid porting. It -should enable a developer to port a minimally useful configuration of -the MPS to new platforms very quickly. - - -Overview --------- - -_`.overview`: Most of the functions in the module do nothing. The -exception is ``ProtSync()`` which traverses over all segments in the -arena and simulates an access to each segment that has any protection -on it. This means that this module depends on certain fields in the -segment structure. - -_`.overview.noos`: No operating system specific (or even ANSI hosted -specific) code is in this module. It can therefore be used on any -platform, particularly where no real implementation of the module -exists. It satisfies `.req.test`_ and `.req.rapid-port`_ in this way. - - -Functions ---------- - -_`.fun.protsetup`: ``ProtSetup()`` does nothing as there is nothing to -do (under UNIX we might expect the protection module to install one or -more signal handlers at this pointer, but that is not appropriate for -the ANSI implementation). Of course, we can't have an empty function -body, so there is a ``NOOP;`` here. - -_`.fun.sync`: ``ProtSync()`` is called to ensure that the actual -protection of each segment (as determined by the OS) is in accordance -with the segments's pm field. In the ANSI implementation we have no -way of changing the protection of a segment, so instead we generate -faults on all protected segments in the assumption that that will -remove the protection on segments. - -_`.fun.sync.how`: Continually loops over all the segments until it -finds that all segments have no protection. - -_`.fun.sync.seg`: If it finds a segment that is protected then -``PoolAccess()`` is called on that segment's pool and with that -segment. The call to ``PoolAccess()`` is wrapped with a -``ShieldEnter()`` and ``ShieldLeave()`` thereby giving the pool the -illusion that the fault was generated outside the MM. This depends on -being able to determine the protection of a segment (using the ``pm`` -field), on being able to call ``ShieldEnter()`` and ``ShieldLeave()``, -and on being able to call ``PoolAccess()``. - - -Document History ----------------- - -- 1997-03-19 David Jones. Incomplete document. - -- 2002-06-07 RB_ Converted from MMInfo database design document. - -- 2013-05-23 GDR_ Converted to reStructuredText. - -.. _RB: http://www.ravenbrook.com/consultants/rb/ -.. _GDR: http://www.ravenbrook.com/consultants/gdr/ - - -Copyright and License ---------------------- - -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact -Ravenbrook for commercial licensing options. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -#. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -#. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -#. Redistributions in any form must be accompanied by information on how - to obtain complete source code for this software and any - accompanying software that uses this software. The source code must - either be included in the distribution or be available for no more than - the cost of distribution plus a nominal fee, and must be freely - redistributable under reasonable conditions. For an executable file, - complete source code means the source code for all modules it contains. - It does not include source code for modules or files that typically - accompany the major components of the operating system on which the - executable file runs. - -**This software is provided by the copyright holders and contributors -"as is" and any express or implied warranties, including, but not -limited to, the implied warranties of merchantability, fitness for a -particular purpose, or non-infringement, are disclaimed. In no event -shall the copyright holders and contributors be liable for any direct, -indirect, incidental, special, exemplary, or consequential damages -(including, but not limited to, procurement of substitute goods or -services; loss of use, data, or profits; or business interruption) -however caused and on any theory of liability, whether in contract, -strict liability, or tort (including negligence or otherwise) arising in -any way out of the use of this software, even if advised of the -possibility of such damage.** diff --git a/design/protli.txt b/design/protli.txt index a5cc1d57a1..8d4a2f7b39 100644 --- a/design/protli.txt +++ b/design/protli.txt @@ -28,7 +28,9 @@ Requirements ------------ _`.req.general`: Required to implement the general protection -interface defined in design.mps.prot.if. +interface defined in design.mps.prot.if_. + +.. _design.mps.prot.if: prot#if Data structures @@ -68,7 +70,7 @@ _`.fun.set`: ``ProtSet()`` uses ``mprotect()`` to adjust the protection for pages. _`.fun.set.convert`: The requested protection (which is expressed in -the ``mode`` parameter, see design.mps.prot.if.set) is translated into +the ``mode`` parameter, see design.mps.prot.if.set_) is translated into an operating system protection. If read accesses are to be forbidden then all accesses are forbidden, this is done by setting the protection of the page to ``PROT_NONE``. If write accesses are to be @@ -77,6 +79,8 @@ and read accesses are allowed, this is done by setting the protection of the page to ``PROT_READ|PROT_EXEC``. Otherwise (all access are okay), the protection is set to ``PROT_READ|PROT_WRITE|PROT_EXEC``. +.. _design.mps.prot.if.set: prot#if.set + _`.fun.set.assume.mprotect`: We assume that the call to ``mprotect()`` always succeeds. @@ -88,35 +92,37 @@ underlying object). _`.fun.sync`: ``ProtSync()`` does nothing in this implementation as ``ProtSet()`` sets the protection without any delay. -_`.fun.tramp`: The protection trampoline, ``ProtTramp()``, is trivial -under Linux, as there is nothing that needs to be done in the dynamic -context of the mutator in order to catch faults. (Contrast this with -Win32 Structured Exception Handling.) - Threads ------- _`.threads`: The design must operate in a multi-threaded environment (with LinuxThreads) and cooperate with the Linux support for locks -(see design.mps.lock) and the thread suspension mechanism (see -design.mps.pthreadext ). +(see design.mps.lock_) and the thread suspension mechanism (see +design.mps.pthreadext_ ). + +.. _design.mps.pthreadext: pthreadext +.. _design.mps.lock: lock _`.threads.suspend`: The ``SIGSEGV`` signal handler does not mask out any signals, so a thread may be suspended while the handler is active, as required by the design (see -design.mps.pthreadext.req.suspend.protection). The signal handlers +design.mps.pthreadext.req.suspend.protection_). The signal handlers simply nest at top of stack. +.. _design.mps.pthreadext.req.suspend.protection: pthreadext#req.suspend.protection + _`.threads.async`: POSIX (and hence Linux) imposes some restrictions on signal handler functions (see -design.mps.pthreadext.anal.signal.safety). Basically the rules say the +design.mps.pthreadext.anal.signal.safety_). Basically the rules say the behaviour of almost all POSIX functions inside a signal handler is undefined, except for a handful of functions which are known to be "async-signal safe". However, if it's known that the signal didn't happen inside a POSIX function, then it is safe to call arbitrary POSIX functions inside a handler. +.. _design.mps.pthreadext.anal.signal.safety: pthreadext#anal.signal.safety + _`.threads.async.protection`: If the signal handler is invoked because of an MPS access, then we know the access must have been caused by client code, because the client is not allowed to permit access to diff --git a/design/protocol.txt b/design/protocol.txt index 025c3742c0..bddebf3f2a 100644 --- a/design/protocol.txt +++ b/design/protocol.txt @@ -33,8 +33,12 @@ benefits of object-oriented class inheritance to maximize code reuse, minimize code maintenance and minimize the use of boilerplate code. _`.purpose.related`: For related discussion, see -mail.tony.1998-08-28.16-26(0), mail.tony.1998-09-01.11-38(0), -mail.tony.1998-10-06.11-03(0) and other messages in the same threads. +`mail.tony.1998-08-28.16-26`_, `mail.tony.1998-09-01.11-38`_, +`mail.tony.1998-10-06.11-03`_ and other messages in the same threads. + +.. _mail.tony.1998-10-06.11-03: https://info.ravenbrook.com/project/mps/mail/1998/10/06/11-03/0.txt +.. _mail.tony.1998-09-01.11-38: https://info.ravenbrook.com/project/mps/mail/1998/09/01/11-38/0.txt +.. _mail.tony.1998-08-28.16-26: https://info.ravenbrook.com/project/mps/mail/1998/08/28/16-26/0.txt Requirements @@ -55,7 +59,7 @@ replace) the behaviour of protocol functions, such as a mechanism for invoking the "next-method". _`.req.ideal.extend`: The object system must provide a standard way -for classes to implement the protocol supported by they superclass and +for classes to implement the protocol supported by their superclass and additionally add new methods of their own which can be specialized by subclasses. @@ -330,7 +334,9 @@ specialized methods for ``coerceClass`` and ``coerceInst`` to be written (see `.overview.coerce-class`_ and `.overview.coerce-inst`_). Documentation on support for multiple inheritance is under construction. This facility is not currently used. The basic idea is -described in mail.tony.1998-10-06.11-03(0). +described in `mail.tony.1998-10-06.11-03`_. + +.. _mail.tony.1998-10-06.11-03: https://info.ravenbrook.com/project/mps/mail/1998/10/06/11-03/0.txt Protocol guidelines @@ -513,7 +519,9 @@ macro uses a global recursive lock instead. The lock is only claimed after an initial unlocked access of the guard variable shows that the class is not initialized. This avoids any locking overhead for the common case where the class is already initialized. This lock is -provided by the lock module -- see design.mps.lock(0). +provided by the lock module -- see design.mps.lock_. + +.. _design.mps.lock: lock Document History diff --git a/design/protsu.txt b/design/protsu.txt index 616d9134c8..e31edf3fe7 100644 --- a/design/protsu.txt +++ b/design/protsu.txt @@ -34,7 +34,9 @@ Requirements ------------ _`.req.general`: Required to implement the general protection -interface defined in design.mps.prot.if.*. +interface defined in design.mps.prot.if_. + +.. _design.mps.prot.if: prot#if Overview @@ -93,7 +95,7 @@ necessary to chain the handler, so we don't. _`.fun.set`: ``ProtSet()`` _`.fun.set.convert`: The requested protection (which is expressed in -the mode parameter, see design.mps.prot.if.set) is translated into an +the mode parameter, see design.mps.prot.if.set_) is translated into an operating system protection. If read accesses are to be forbidden then all accesses are forbidden, this is done by setting the protection of the page to ``PROT_NONE``. If write access are to be forbidden (and @@ -102,6 +104,8 @@ are allowed, this is done by setting the protection of the page to ``PROT_READ | PROT_EXEC``. Otherwise (all access are okay), the protection is set to ``PROT_READ | PROT_WRITE | PROT_EXEC``. +.. _design.mps.prot.if.set: prot#if.set + _`.fun.set.assume.mprotect`: We assume that the call to ``mprotect()`` always succeeds. This is because we should always call the function with valid arguments (aligned, references to mapped pages, and with an @@ -110,11 +114,6 @@ access that is compatible with the access of the underlying object). _`.fun.sync`: ``ProtSync()``. This does nothing in this implementation as ProtSet sets the protection without any delay. -_`.fun.tramp`: ``ProtTramp()``. The protection trampoline is trivial -under SunOS, as there is nothing that needs to be done in the dynamic -context of the mutator in order to catch faults. (Contrast this with -Win32 Structured Exception Handling.) - Document History ---------------- diff --git a/design/pthreadext.txt b/design/pthreadext.txt index b72e2926d0..18b7b2d9d9 100644 --- a/design/pthreadext.txt +++ b/design/pthreadext.txt @@ -42,7 +42,9 @@ any progress. _`.req.suspend.why`: Needed by the thread manager so that other threads registered with an arena can be suspended (see -design.mps.thread-manager). Not directly provided by Pthreads. +design.mps.thread-manager_). Not directly provided by Pthreads. + +.. _design.mps.thread-manager: thread-manager _`.req.resume`: A means to resume suspended threads, so that they are able to make progress again. _`.req.resume.why`: Needed by the thread @@ -90,9 +92,10 @@ summary, a small number of POSIX functions are defined to be restriction in signal handlers. All other POSIX functions are considered to be unsafe. Behaviour is undefined if an unsafe function is interrupted by a signal and the signal handler then proceeds to -call another unsafe function. See mail.tony.1999-08-24.15-40(0)and +call another unsafe function. See `mail.tony.1999-08-24.15-40`_ and followups for some further analysis. +.. _mail.tony.1999-08-24.15-40: https://info.ravenbrook.com/project/mps/mail/1999/08/24/15-40/0.txt .. _sigaction: http://www.opengroup.org/onlinepubs/007908799/xsh/sigaction.html _`.anal.signal.safety.implication`: Since we can't assume that we diff --git a/design/range.txt b/design/range.txt index 86fcab775f..9e6d4ce34a 100644 --- a/design/range.txt +++ b/design/range.txt @@ -1,7 +1,7 @@ .. mode: -*- rst -*- -Ranges -====== +Ranges of addresses +=================== :Tag: design.mps.range :Author: Gareth Rees @@ -9,6 +9,7 @@ Ranges :Status: complete design :Revision: $Id$ :Copyright: See section `Copyright and License`_. +:Index terms: pair: address range; design Introduction @@ -24,8 +25,8 @@ Requirements ------------ _`.req.range`: A range object must be able to represent an arbitrary -range of addresses that does not include the top grain of the address -space. +range of addresses that neither starts at ``NULL`` nor includes the +top grain of the address space. _`.req.empty`: A range object must be able to represent the empty range. @@ -50,6 +51,16 @@ between ``base`` (inclusive) and ``limit`` (exclusive). It must be the case that ``base <= limit``. If ``base == limit`` then the range is empty. +``void RangeCopy(Range dest, Range src)`` + +Initialize ``dest`` to be a copy of ``src``. + +``void RangeInitSize(Range range, Addr base, Size size)`` + +Initialize a range object to represent the half-open address range +between ``base`` (inclusive) and ``base + size`` (exclusive). If +``size == 0`` then the range is empty. + ``void RangeFinish(Range range)`` Finish a range object. Because a range object uses no heap resources @@ -72,6 +83,18 @@ there is a function too.) Return the size of the range. (This is implemented as a macro, but there is a function too. The macro evaluates its argument twice.) +``Bool RangeContains(Range range, Addr addr)`` + +Return ``TRUE`` if ``addr`` belongs to the range, or ``FALSE`` if it +does not. (This is implemented as a macro, but there is a function +too. The macro evaluates its arguments twice.) + +``Bool RangeIsEmpty(Range range)`` + +Return ``TRUE`` if the range is empty (contains no addresses), +``FALSE`` otherwise. (This is implemented as a macro, but there is a +function too. The macro evaluates its argument twice.) + ``Bool RangeIsAligned(Range range, Align alignment)`` Return ``TRUE`` if the base and limit of the range are both aligned to @@ -93,6 +116,7 @@ Document history ---------------- - 2013-05-21 GDR_ Created. +- 2014-01-15 GDR_ Added ``RangeContains``. .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/reservoir.txt b/design/reservoir.txt index 15b9c8b557..1c9e8c6fe1 100644 --- a/design/reservoir.txt +++ b/design/reservoir.txt @@ -38,8 +38,8 @@ maintained in the reservoir. The limit is the maximum amount that it is desired to maintain. _`.wastage`: When the reservoir limit is set by the client, the actual -limit should be increased by an arena alignment amount for every -active mutator buffer. +limit should be increased by one arena grain for every active mutator +buffer. _`.really-empty`: When the reservoir limit is set to 0, assume that the client really doesn't have a need for a reservoir at all. In this @@ -103,9 +103,9 @@ memory of the specified size to the specified pool to the reservoir. If no suitable memory can be found it returns ``ResMEMORY``. _`.interface.withdraw.align`: Currently, ``ReservoirWithdraw()`` can -only withdraw memory in chunks of the size of the arena alignment. -This is because the reservoir doesn't attempt to coalesce adjacent -memory blocks. This deficiency should be fixed in the future. +only withdraw a single arena grain at a time. This is because the +reservoir doesn't attempt to coalesce adjacent memory blocks. This +deficiency should be fixed in the future. _`.pool`: The memory managed by the reservoir is owned by the reservoir pool. This memory is never sub-allocated. Each tract diff --git a/design/ring.txt b/design/ring.txt index 47c945cdcf..c0f4d9adfd 100644 --- a/design/ring.txt +++ b/design/ring.txt @@ -15,8 +15,12 @@ Ring data structure Introduction ------------ -_`.source`: rings are derived from the earlier use of Deques. See -design.mps.deque. +_`.source`: rings are derived from the earlier use of double-ended +queues (deques). RB found that most of the deque features were unused +(see item 6 of `mail.richard.1996-03-25.16-02`_) and so the simple +doubly-linked list structure of rings suffices. + +.. _mail.richard.1996-03-25.16-02: https://info.ravenbrook.com/project/mps/mail/1996/03/25/16-02/0.txt Description @@ -45,14 +49,14 @@ and deletion because the entire ring can be found from any node. In the MPS, rings are used to connect a "parent" structure (such as a ``Arena``) to a number of "child" structures (such as ``Pool``), as -shown in `.fig.ring`_. Note the slight abuse of naming convention, in -that ``barRing`` is not called ``barRingStruct``. +shown in `.fig.ring`_. -_`.fig.ring`: A ring of ``Bar`` objects owned by a ``Foo`` object. +_`.fig.ring`: A ring of ``Child`` objects owned by a ``Parent`` +object. [missing figure] -_`.fig.empty`: An empty ring of ``Bar`` objects owned by a ``Foo`` +_`.fig.empty`: An empty ring of ``Child`` objects owned by a ``Parent`` object. [missing figure] @@ -60,7 +64,7 @@ object. _`.def.singleton`: A "singleton" ring is a ring containing one node, whose previous and next nodes are itself (see `.fig.single`_). -_`.fig.single`: A singleton ``Bar`` object not on any ring. +_`.fig.single`: A singleton ``Child`` object not on any ring. [missing figure] @@ -69,8 +73,11 @@ _`.fig.elt`: How ``RING_ELT()`` gets a parent pointer from a node pointer. [missing figure] +Interface +--------- + Init / Finish -------------- +............. ``void RingInit(Ring ring)`` @@ -84,8 +91,36 @@ ring must be a singleton ring before it can be finished (it is an error to attempt to finish a non-singleton ring). +Checking +........ + +``Bool RingCheck(Ring ring)`` + +_`.check`: ``RingCheck()`` is the check function for rings. See +design.mps.check_). + +.. _design.mps.check: check + +``Bool RingCheckSingle(Ring ring)`` + +_`.check.single`: ``RingCheckSingle()`` is a check function that +additionally checks that ``ring`` is a singleton (see +`.def.singleton`_). + +``Bool RingIsSingle(Ring ring)`` + +_`.is.single`: Return ``TRUE`` if ``ring`` is a singleton (see +`.def.singleton`_). + +``Size RingLength(Ring ring)`` + +_`.length`: Return the number of elements in the ring, not counting +``ring`` itself. This therefore returns 0 for singleton rings, and for +parent-children rings it returns the number of children. + + Iteration ---------- +......... ``RING_FOR(Ring node, Ring ring, Ring next)`` @@ -112,17 +147,25 @@ iteration. _`.for.ex`: An example:: Ring node, nextNode; - RING_FOR(node, &foo->barRing, nextNode) { - Bar bar = RING_ELT(Bar, FooRing, node); - frob(bar); + RING_FOR(node, &parent->childRing, nextNode) { + Child child = RING_ELT(Child, ParentRing, node); + foo(child); } _`.for.ex.elt`: Notice the idiomatic use of ``RING_ELT()`` which is almost universal when using ``RING_FOR()``. -Subclass --------- +Element access +.............. + +``Ring RingNext(Ring ring)`` + +_`.next`: ``RingNext()`` returns the next node in the ring. + +``Ring (RingPrev)(Ring ring)`` + +_`.prev`: ``RingPrev()`` returns the previous node in the ring. ``RING_ELT(type, field, Ring node)`` @@ -137,7 +180,7 @@ result is a pointer to the enclosing structure. Append / Remove ---------------- +............... ``void RingAppend(Ring ring, Ring new)`` @@ -146,7 +189,7 @@ that the newly added element will be last in the iteration sequence). ``void (RingInsert)(Ring ring, Ring new)`` -_`.insert`: ``RingInsert()`` adds a singleton rung to a ring (such that +_`.insert`: ``RingInsert()`` adds a singleton ring to a ring (such that the newly added element will be first in the iteration sequence). ``void (RingRemove)(Ring old)`` @@ -162,11 +205,16 @@ that joined two rings. This is not done as it is not required. Naming ------ -_`.naming`: By convention, when one structure ``Foo`` contains one -ring of ``Bar`` structures, the field in ``Foo`` is usually known as -``barRing``, and the field in ``Bar`` is known as ``fooRing``. If the -``Foo`` structure contains more than one ring of ``Bar`` structures, -then they will have names such as ``spongRing`` and ``frobRing``. +_`.naming`: By convention, when one structure ``Parent`` contains one +ring of ``Child`` structures, the field in ``Parent`` is usually known +as ``childRing``, and the field in ``Child`` is known as +``parentRing``. If the ``Parent`` structure contains more than one +ring of ``Child`` structures, then they should have names like +``allocatedChildRing`` and ``freeChildRing``. + +_`.naming.rule.break`: Note the slight abuse of naming convention, in +that the ring members have names ending in ``Ring`` rather than +``RingStruct``. Deques @@ -186,7 +234,7 @@ This section documents known defects with the current design. _`.app_for.misuse`: It is easy to pass ``RingAppend()`` and ``RING_FOR()`` the arguments in the wrong order as all the arguments -have the same type. see `.head`_. +have the same type. _`.check.improve`: There is no method for performing a full integrity check. This could be added. diff --git a/design/scan.txt b/design/scan.txt index 1683fecc36..2b4c992d18 100644 --- a/design/scan.txt +++ b/design/scan.txt @@ -19,8 +19,8 @@ Scanned summary ............... _`.summary.subset`: The summary of reference seens by scan -(ss.unfixedSummary) is a subset of the summary previously computed -(SegSummary). +(``ss.unfixedSummary``) is a subset of the summary previously computed +(``SegSummary()``). There are two reasons that it is not an equality relation: @@ -34,9 +34,11 @@ There are two reasons that it is not an equality relation: #. A write barrier hit will set the summary to ``RefSetUNIV``. -The reason that ss.unfixedSummary is always a subset of the previous -summary is due to an "optimization" which has not been made in -``TraceFix``. See impl.c.trace.fix.fixed.all. +The reason that ``ss.unfixedSummary`` is always a subset of the +previous summary is due to an "optimization" which has not been made +in ``TraceFix``. See design.mps.trace.fix.fixed.all_. + +.. _design.mps.trace.fix.fixed.all: trace#fix.fixed.all Partial scans @@ -54,8 +56,8 @@ partial scans of condemned segments contribute to the segment summary. _`.clever-summary.acc`: Each time we partially scan a segment, we accumulate the post-scan summary of the scanned objects into a field -in the group, called 'summarySoFar'. The post-scan summary is (summary -\ white) U fixed. +in the group, called ``summarySoFar``. The post-scan summary is +(summary \ white) ∪ fixed. _`.clever-summary.acc.condemn`: The cumulative summary is only meaningful while the segment is condemned. Otherwise it is set to diff --git a/design/seg.txt b/design/seg.txt index 87ff87575f..db797f233c 100644 --- a/design/seg.txt +++ b/design/seg.txt @@ -15,7 +15,7 @@ Segment data structure Introduction ------------ -_`.intro`: This document describes the MPS Segment data structure. +_`.intro`: This is the design of the segment data structure. Overview @@ -43,12 +43,14 @@ methods SegBase, SegLimit, and SegSize which map a segment onto the addresses of the memory block it represents. _`.over.hierarchy`: The Segment datastructure is designed to be -subclassable (see design.mps.protocol). The basic segment class +subclassable (see design.mps.protocol_). The basic segment class (``Seg``) supports colour and protection for use by the tracer, as well as support for a pool ring, and all generic segment functions. Clients may use ``Seg`` directly, but will most probably want to use a subclass with additional properties. +.. _design.mps.protocol: protocol + _`.over.hierarchy.gcseg`: The segment module provides ``GCSeg`` - a subclass of ``Seg`` which has full support for GC including buffering and the ability to be linked onto the grey ring. @@ -63,21 +65,20 @@ Data Structure The implementations are as follows:: typedef struct SegStruct { /* segment structure */ - Sig sig; /* impl.h.misc.sig */ + Sig sig; /* */ SegClass class; /* segment class structure */ Tract firstTract; /* first tract of segment */ RingStruct poolRing; /* link in list of segs in pool */ Addr limit; /* limit of segment */ - unsigned depth : SHIELD_DEPTH_WIDTH; /* see impl.c.shield.def.depth */ - AccessSet pm : AccessMAX; /* protection mode, impl.c.shield */ - AccessSet sm : AccessMAX; /* shield mode, impl.c.shield */ - TraceSet grey : TRACE_MAX; /* traces for which seg is grey */ - TraceSet white : TRACE_MAX; /* traces for which seg is white */ - TraceSet nailed : TRACE_MAX; /* traces for which seg has nailed objects */ - RankSet rankSet : RankMAX; /* ranks of references in this seg */ + unsigned depth : ShieldDepthWIDTH; /* see */ + AccessSet pm : AccessLIMIT; /* protection mode, */ + AccessSet sm : AccessLIMIT; /* shield mode, */ + TraceSet grey : TraceLIMIT; /* traces for which seg is grey */ + TraceSet white : TraceLIMIT; /* traces for which seg is white */ + TraceSet nailed : TraceLIMIT; /* traces for which seg has nailed objects */ + RankSet rankSet : RankLIMIT; /* ranks of references in this seg */ } SegStruct; - typedef struct GCSegStruct { /* GC segment structure */ SegStruct segStruct; /* superclass fields must come first */ RingStruct greyRing; /* link in list of grey segs */ @@ -175,10 +176,9 @@ use of the optional arguments; the built-in classes do not. _`.split.invariants`: The client must ensure some invariants are met before calling ``SegSplit()``: -- _`.split.inv.align`: ``at`` must be appropriately aligned to the - arena alignment, and lie between the base and limit of ``seg``. - Justification: the split segments cannot be represented if this is - not so. +- _`.split.inv.align`: ``at`` must be a multiple of the arena grain + size, and lie between the base and limit of ``seg``. Justification: + the split segments cannot be represented if this is not so. - _`.split.inv.buffer`: If ``seg`` is attached to a buffer, the buffered region must not include address ``at``. Justification: the @@ -248,7 +248,9 @@ allocation, which may use the reservoir if ``withReservoirPermit`` is _`.method.split.next`: A split method should always call the next method, either before or after any class-specific code (see -design.mps.protocol.overview.next-method). +design.mps.protocol.overview.next-method_). + +.. _design.mps.protocol.overview.next-method: protocol#overview.next-method ``typedef Res (*SegMergeMethod)(Seg seg, Seg segHi, Addr base, Addr mid, Addr limit, Bool withReservoirPermit)`` @@ -264,7 +266,9 @@ reservoir if ``withReservoirPermit`` is ``TRUE``. _`.method.merge.next`: A merge method should always call the next method, either before or after any class-specific code (see -design.mps.protocol.overview.next-method). +design.mps.protocol.overview.next-method_). + +.. _design.mps.protocol.overview.next-method: protocol#overview.next-method _`.split-merge.shield`: Split and merge methods may assume that the segments they are manipulating are not in the shield cache. @@ -285,9 +289,11 @@ method (for example, by allocating any objects early in the method). _`.split-merge.fail.anti`: If it's not possible to detect failure before calling the next method, the appropriate anti-method must be -used (see design.mps.protocol.guide.fail.after-next). Split methods +used (see design.mps.protocol.guide.fail.after-next_). Split methods are anti-methods for merge methods, and vice-versa. +.. _design.mps.protocol.guide.fail.after-next: protocol#guide.fail.after-next + _`.split-merge.fail.anti.constrain`: In general, care should be taken when writing split and merge methods to ensure that they really are anti-methods for each other. The anti-method must not fail if the @@ -321,7 +327,9 @@ Document History and 1) was as part of editing MMsrc!seg.c(MMdevel_action2.1). - 1999-04-16 Tony Mann. Rewritten to separate segments and tracts, - following mail.tony.1998-11-02.10-26 + following `mail.tony.1998-11-02.10-26`_. + + .. _mail.tony.1998-11-02.10-26: https://info.ravenbrook.com/project/mps/mail/1998/11/02/10-26/0.txt - 2002-06-07 RB_ Converted from MMInfo database design document. diff --git a/design/sig.txt b/design/sig.txt index 4b4cfbbe83..992d0ba52d 100644 --- a/design/sig.txt +++ b/design/sig.txt @@ -101,18 +101,24 @@ Do not do anything else with signatures. See `.rule.purpose`_. Checking -------- -The signature is checked in various ways. Every function that takes a -(pointer to) a signed structure should check its argument using the -``AVERT`` macro. This macro has different definitions depending on how -the MPS is compiled (see design.mps.config.def.var_). It may simply check -the signature directly, or call the full checking function for the -structure. +_`.check.arg`: Every function that takes a pointer to a signed +structure should check its argument. + +_`.check.arg.unlocked`: A function that does not hold the arena lock +should check the argument using ``AVER(TESTT(type, val))``, which +checks that ``val->sig`` is the correct signature for ``type``. + +_`.check.arg.locked`: A function that holds the arena lock should +check the argument using the ``AVERT`` macro. This macro has different +definitions depending on how the MPS is compiled (see +design.mps.config.def.var_). It may simply check the signature, or +call the full checking function for the structure. .. _design.mps.config.def.var: config#def-var -The checking function for the structure should also validate the -signature as its first step using the ``CHECKS()`` macro (see -check.h_). For example:: +_`.check.sig`: The checking function for the structure should also +validate the signature as its first step using the ``CHECKS()`` macro +(see check.h_). For example:: Bool MessageCheck(Message message) { @@ -127,6 +133,7 @@ pointer into a function. .. _check.h: ../code/check.h + Rules ----- _`.rule.purpose`: **Do not** use signatures for any other purpose. For diff --git a/design/sp.txt b/design/sp.txt new file mode 100644 index 0000000000..47dab436b4 --- /dev/null +++ b/design/sp.txt @@ -0,0 +1,226 @@ +.. mode: -*- rst -*- + +Stack probe +=========== + +:Tag: design.mps.sp +:Author: Gareth Rees +:Date: 2014-10-23 +:Status: complete design +:Revision: $Id$ +:Copyright: See `Copyright and License`_. +:Index terms: pair: stack probe; design + + +Introduction +------------ + +_`.intro`: This is the design of the stack probe module. + +_`.readership`: Any MPS developer; anyone porting the MPS to a new +platform. + +_`.overview`: This module ensures that the stack cannot overflow while +the MPS is holding a lock, so that a mutator can handle stack overflow +faults and call into the MPS from the handler. + + +Requirements +------------ + +_`.req.overflow`: The mutator should be able to call into the MPS from +a stack overflow fault handler. (This is a convenient way to handle +stack overflows in dynamic language implementations: if the stack +overflow exception and associated backtrace are to be represented as +objects, this may require allocation, and hence a call into the MPS.) + +_`.req.complete`: In an application where the mutator might call into +the MPS from a stack overflow fault handler, then whenever the MPS +takes a lock, it must complete the operation and release the lock +without running out of stack. (This is because running out of stack +would cause a stack overflow fault, causing the mutator would enter +the MPS recursively, which would fail because the lock is held.) + + +Design +------ + +_`.sol.probe`: Before taking the arena lock in ``ArenaEnterLock()``, +the MPS *probes* the stack: that is, it checks whether there are at +least ``StackProbeDEPTH`` words available, and provokes a stack +overflow fault if there are not. (This ensures that the fault occurs +outside of the arena lock where it can be handled safely.) + +_`.sol.depth`: The configuration parameter ``StackProbeDEPTH`` +specifies the maximum number of words of stack that the MPS might use. +(It is simpler, faster, and more reliable, to determine this globally +than to try to figure it out dynamically.) + +_`.sol.depth.constraint`: Operating systems typically use a single +"guard page" to detect stack overflow and grow the stack. (See for +example the documentation for Windows_.) This means that the probe +will be ineffective if it skips over the guard page into the memory +beyond. If ``StackProbeDEPTH`` is greater than or equal to the number +of words per page, the implementation might need to carry out multiple +probes. (This constraint is checked in ``MPMCheck()``.) + +.. _Windows: http://support.microsoft.com/kb/100775 + +_`.sol.depth.no-recursion`: In order to implement this design, the MPS +must have constant bounded stack depth, and therefore, no recursion. + +_`.sol.depth.analysis`: Here's a table showing a deep call into the +MPS (in the master sources at changelevel 187378), starting in +``ArenaAccess()`` at the point where the arena ring lock is taken. The +access forces a scan of a segment in an AMC pool, which fixes a +reference to an object in an AMC pool's oldspace, which has to be +forwarded, and this overflows the forwarding buffer, which requires +the arena to allocate a new buffer in an appropriate zone, by +searching the splay tree representing free memory. + +The "Args" column gives the number of arguments to the function (all +arguments to functions in the MPS are word-sized or smaller, since we +prohibit passing structures by value), and the "Locals" column gives +the number of words in local variables. The value "≤64" for the stack +usage of the object format's scan method is the limit that's +documented in the manual. + +==== ====== ======================== +Args Locals Function +==== ====== ======================== + 5 0 ``PoolAccess()`` + 5 0 ``PoolSegAccess()`` + 3 5 ``TraceSegAccess()`` + 4 1 ``traceScanSeg()`` + 4 8 ``traceScanSegRes()`` + 4 0 ``PoolScan()`` + 4 5 ``AMCScan()`` + 3 ≤64 ``format->scan()`` + 4 15 ``AMCFix()`` + 4 5 ``BufferFill()`` + 6 10 ``AMCBufferFill()`` + 6 9 ``PoolGenAlloc()`` + 7 5 ``SegAlloc()`` + 5 5 ``ArenaAlloc()`` + 5 5 ``arenaAllocPolicy()`` + 5 11 ``arenaAllocFromLand()`` + 7 1 ``LandFindInZones()`` + 7 16 ``cbsFindInZones()`` + 5 3 ``cbsFindFirst()`` + 6 7 ``SplayFindFirst()`` + 3 7 ``SplaySplay()`` + 4 8 ``SplaySplitDown()`` + 3 0 ``SplayZig()`` + 109 ≤190 **Total** +==== ====== ======================== + +We expect that a compiler will often be able to share stack space +between function arguments and local variables, but in the worst case +where it cannot, this call requires no more than 299 words of stack +space. + +This isn't necessarily the deepest call into the MPS (the MPS's +modular design and class system makes it hard to do a complete +analysis using call graph tools), but it's probably close. The value +for ``StackProbeDEPTH`` is thus chosen to be a round number that's +comfortably larger than this. + + +Interface +--------- + +``void StackProbe(Size depth)`` + +_`.if.probe`: If there are at least ``depth`` words of stack +available, return. If not, provoke a stack overflow fault. + + +Issues +------ + +_`.issue.an`: The generic implementation is non-functional. This means +that it is only suitable for use with programs that do not handle +stack overflow faults, or do not call into the MPS from the handler. +This is because our customers have only required `.req.overflow`_ on +Windows so far. If this becomes a requirement on other platforms, the +following Standard C implementation might work:: + + void StackProbe(Size depth) { + volatile Word w; + Word *p = &w - depth; + w = *p; + } + +The use of ``volatile`` here is to prevent compilers from warning +about the variable ``w`` being written but never read, or worse, +optimizing away the whole statement under the "as if" rule. + + +Implementations +--------------- + +_`.impl.an`: Generic implementation in ``span.c``. This implementation +does nothing. See `.issue.an`_. + +_`.impl.w3i3`: Implementation for Windows on IA-32 in ``spw3i3.c``. +This uses assembly to get the stack pointer (from the ESP register) +and to read the location ``depth`` words below the stack pointer. + +_`.impl.w3i6`: Implementation for Windows on x86-64 in ``spw3i6.c``. +This passes the argument ``depth*sizeof(Word)`` to the Windows +function |alloca|_, for which the documentation says, "A stack +overflow exception is generated if the space cannot be allocated." + +.. |alloca| replace:: ``_alloca()`` +.. _alloca: http://msdn.microsoft.com/en-us/library/wb1s57t5.aspx + + +Document History +---------------- + +- 2014-10-23 GDR_ Initial draft. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 2014 Ravenbrook Limited. All rights reserved. +. This is an open source license. Contact +Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** diff --git a/design/splay-assemble.svg b/design/splay-assemble.svg new file mode 100644 index 0000000000..45956b496e --- /dev/null +++ b/design/splay-assemble.svg @@ -0,0 +1,427 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + A + + B + + + R + + assemble + + x + y + + L + + + A + + B + + + R + x + + L + + + + diff --git a/design/splay-link-left.svg b/design/splay-link-left.svg new file mode 100644 index 0000000000..f94d145743 --- /dev/null +++ b/design/splay-link-left.svg @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + B + + A + + + L + + linkleft + + x + y + + R + + + B + + A + + + L + + x + y + + R + + diff --git a/design/splay-link-right.svg b/design/splay-link-right.svg new file mode 100644 index 0000000000..95c8c31e0a --- /dev/null +++ b/design/splay-link-right.svg @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + A + + B + + + R + + linkright + + x + y + + L + + + A + + B + + + R + + x + y + + L + + diff --git a/design/splay-rotate-left.svg b/design/splay-rotate-left.svg new file mode 100644 index 0000000000..00ae646a73 --- /dev/null +++ b/design/splay-rotate-left.svg @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + A + + B + + + + C + + + + C + + B + + + + A + + rotateleft + + x + y + y + x + + diff --git a/design/splay-rotate-right.svg b/design/splay-rotate-right.svg new file mode 100644 index 0000000000..d2a02bc48b --- /dev/null +++ b/design/splay-rotate-right.svg @@ -0,0 +1,391 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + A + + B + + + + C + + + + C + + B + + + + A + + rotateright + + x + y + y + x + + diff --git a/design/splay.txt b/design/splay.txt index 8f91115a09..0e77835f73 100644 --- a/design/splay.txt +++ b/design/splay.txt @@ -6,7 +6,7 @@ Splay trees :Tag: design.mps.splay :Author: Gavin Matthews :Date: 1998-05-01 -:Status: draft document +:Status: complete design :Revision: $Id$ :Copyright: See `Copyright and License`_. :Index terms: pair: splay trees; design @@ -21,10 +21,14 @@ implementation. _`.readership`: This document is intended for any MM developer. -_`.source`: The primary sources for this design are paper.st85(0) and -paper.sleator96(0). Also as CBS is a client, design.mps.cbs. As -PoolMVFF is an indirect client, design.mps.poolmvff(1). Also, as -PoolMV2 is an (obsolescent?) indirect client, design.mps.poolmv2. +_`.source`: The primary sources for this design are [ST85]_ and +[Sleator96]_. As CBS is a client, design.mps.cbs_. As PoolMVFF is an +indirect client, design.mps.poolmvff_. Also, as PoolMVT is an indirect +client, design.mps.poolmvt_. + +.. _design.mps.cbs: cbs +.. _design.mps.poolmvt: poolmvt +.. _design.mps.poolmvff: poolmvff _`.background`: The following background documents influence the design: guide.impl.c.adt(0). @@ -43,42 +47,46 @@ usage patterns. Unused nodes have essentially no time overhead. Definitions ----------- -_`.def.splay-tree`: A "Splay Tree" is a self-adjusting binary tree as -described in paper.st85(0), paper.sleator96(0). +_`.def.splay-tree`: A *splay tree* is a self-adjusting binary tree as +described in [ST85]_ and [Sleator96]_. -_`.def.node`: A "node" is used in the typical datastructure sense to -mean an element of a tree (see also `.type.splay.node`_). +_`.def.node`: A *node* is used in the typical data structure sense to +mean an element of a tree (see also `.type.tree`_). -_`.def.key`: A "key" is a value associated with each node; the keys +_`.def.key`: A *key* is a value associated with each node; the keys are totally ordered by a client provided comparator. -_`.def.comparator`: A "comparator" is a function that compares keys to -determine their ordering (see also `.type.splay.compare.method`_). +_`.def.comparator`: A *comparator* is a function that compares keys to +determine their ordering (see also `.type.tree.compare.function`_). -_`.def.successor`: Node *N1* is the "successor" of node *N2* if *N1* -and *N2* are both in the same tree, and the key of *N1* immediately -follows the key of *N2* in the ordering of all keys for the tree. +_`.def.successor`: Node *N*\ :subscript:`2` is the *successor* of node +*N*\ :subscript:`1` if *N*\ :subscript:`1` and *N*\ :subscript:`2` are +both in the same tree, and the key of *N*\ :subscript:`2` immediately +follows the key of *N*\ :subscript:`1` in the ordering of all keys for +the tree. -_`.def.left-child`: Each node *N* contains a "left child", which is a +_`.def.left-child`: Each node *N* contains a *left child*, which is a (possibly empty) sub-tree of nodes. The key of *N* is ordered after the keys of all nodes in this sub-tree. -_`.def.right-child`: Each node *N* contains a "right child", which is +_`.def.right-child`: Each node *N* contains a *right child*, which is a (possibly empty) sub-tree of nodes. The key of *N* is ordered before the keys of all nodes in this sub-tree. -_`.def.neighbour`: A node *N* which has key *Kn* is a "neighbour" of a -key *K* if either *Kn* is the first key in the total order which -compares greater than *K* or if *Kn* is the last key in the total -order which compares less than *K*. +_`.def.neighbour`: The *left neighbour* of a key *K* is the node *N* +with the largest key that compares less than *K* in the total order. +The *right neighbour* of a key *K* is the node *N* with the smaller +key that compares greater than *K* in the total order. A node is a +*neighbour* of a key if it is either the left or right neighbour of +the key. -_`.def.first`: A node is the "first" node in a set of nodes if its key +_`.def.first`: A node is the *first* node in a set of nodes if its key compares less than the keys of all other nodes in the set. -_`.def.last`: A node is the "last" node in a set of nodes if its key +_`.def.last`: A node is the *last* node in a set of nodes if its key compares greater than the keys of all other nodes in the set. -_`.def.client-property`: A "client property" is a value that the +_`.def.client-property`: A *client property* is a value that the client may associate with each node in addition to the key (a block size, for example). This splay tree implementation provides support for efficiently finding the first or last nodes with suitably large @@ -89,32 +97,27 @@ Requirements ------------ _`.req`: These requirements are drawn from those implied by -design.mps.poolmv2, design.mps.poolmvff(1), design.mps.cbs(2) and +design.mps.poolmvt_, design.mps.poolmvff_, design.mps.cbs_, and general inferred MPS requirements. _`.req.order`: Must maintain a set of abstract keys which is totally ordered for a comparator. -_`.req.tree`: The keys must be associated with nodes arranged in a -Splay Tree. - -_`.req.splay`: Common operations must balance the tree by splaying it, -to achieve low amortized cost (see paper.st85(0)). +_`.req.fast`: Common operations must have low amortized cost. -_`.req.add`: Must be able to add new members. This is a common +_`.req.add`: Must be able to add new nodes. This is a common operation. -_`.req.remove`: Must be able to remove members. This is a common +_`.req.remove`: Must be able to remove nodes. This is a common operation. -_`.req.locate`: Must be able to locate a member, given a key. This is +_`.req.locate`: Must be able to locate a node, given a key. This is a common operation. -_`.req.neighbours`: Must be able to locate the neighbouring members -(in order) of a non-member, given a key (see `.def.neighbour`_). This -is a common operation. +_`.req.neighbours`: Must be able to locate the neighbouring nodes of a +key (see `.def.neighbour`_). This is a common operation. -_`.req.iterate`: Must be able to iterate over all members in order +_`.req.iterate`: Must be able to iterate over all nodes in key order with reasonable efficiency. _`.req.protocol`: Must support detection of protocol violations. @@ -141,243 +144,240 @@ _`.req.root`: Must be able to find the root of a splay tree (if one exists). -External types --------------- +Generic binary tree interface +----------------------------- -``typedef struct SplayTreeStruct SplayTreeStruct`` -``typedef struct SplayTreeStruct *SplayTree`` +Types +..... -_`.type.splay.tree`: ``SplayTree`` is the type of the main object at -the root of the splay tree. It is intended that the -``SplayTreeStruct`` can be embedded in another structure (see -`.usage.client-tree`_ for an example). No convenience functions are -provided for allocation or deallocation. +``typedef struct TreeStruct *Tree`` -``typedef struct SplayNodeStruct SplayNodeStruct`` -``typedef struct SplayNodeStruct *SplayNode`` +_`.type.tree`: ``Tree`` is the type of a node in a binary tree. +``Tree`` contains no fields to store the key associated with the node, +or the client property. Again, it is intended that the ``TreeStruct`` +can be embedded in another structure, and that this is how the +association will be made (see `.usage.client-node`_ for an example). +No convenience functions are provided for allocation or deallocation. -_`.type.splay.node`: ``SplayNode`` is the type of a node of the splay -tree. ``SplayNodeStruct`` contains no fields to store the key -associated with the node, or the client property. Again, it is -intended that the ``SplayNodeStruct`` can be embedded in another -structure, and that this is how the association will be made (see -`.usage.client-node`_ for an example). No convenience functions are -provided for allocation or deallocation. +``typedef void *TreeKey`` + +_`.type.treekey`: ``TreeKey`` is the type of a key associated with a +node in a binary tree. It is an alias for ``void *`` but expresses the +intention. + +``typedef TreeKey (*TreeKeyFunction)(Tree tree)`` -``typedef Compare (*SplayCompareMethod)(void *key, SplayNode node)`` +_`.type.tree.key.function`: A function of type ``TreeKey`` returns the +key associated with a node in a binary tree. (Since there is no space +in a ``TreeStruct`` to store a key, it is expected that the +``TreeStruct`` is embedded in another structure from which the key can +be extracted.) -_`.type.splay.compare.method`: A function of type -``SplayCompareMethod`` is required to compare ``key`` with the key the -client associates with that splay tree node ``node``, and return the -appropriate Compare value (see `.usage.compare`_ for an example). The -function compares a key with a node, rather than a pair of keys or -nodes as might seem more obvious. This is because the details of the -mapping between nodes and keys is left to the client (see -`.type.splay.node`_), and the splaying operations compare keys with -nodes (see `.impl.splay`_). +``typedef Compare (*TreeCompareFunction)(Tree tree, TreeKey key)`` -``typedef Res (*SplayNodeDescribeMethod)(SplayNode node, mps_lib_FILE *stream)`` +_`.type.tree.compare.function`: A function of type ``TreeCompareFunction`` is +required to compare ``key`` with the key the client associates with +that splay tree node ``tree``, and return the appropriate Compare +value (see `.usage.compare`_ for an example). The function compares a +key with a node, rather than a pair of keys or nodes as might seem +more obvious. This is because the details of the mapping between nodes +and keys is left to the client (see `.type.tree`_), and the splaying +operations compare keys with nodes (see `.impl.splay`_). -_`.type.splay.node.describe.method`: A function of type -``SplayNodeDescribeMethod`` is required to write (via ``WriteF()``) a +``typedef Res (*TreeDescribeFunction)(Tree tree, mps_lib_FILE *stream)`` + +_`.type.tree.describe.function`: A function of type +``TreeDescribeFunction`` is required to write (via ``WriteF()``) a client-oriented representation of the splay node. The output should be -non-empty, short, and without return characters. This is provided for -debugging purposes only. +non-empty, short, and without newline characters. This is provided for +debugging only. + + +Functions +......... + +``Bool TreeCheck(Tree tree)`` -``typedef Bool (*SplayTestNodeMethod)(SplayTree tree, SplayNode node, void *closureP, unsigned long closureS)`` +_`.function.tree.check`: This is a check function for the +``Tree`` type (see guide.impl.c.adt.method.check and +design.mps.check_). -_`.type.splay.test.node.method`: A function of type -``SplayTestNodeMethod`` required to determine whether the node itself +.. _design.mps.check: check + + +Splay tree interface +-------------------- + +Types +..... + +``typedef struct SplayTreeStruct *SplayTree`` + +_`.type.splay.tree`: ``SplayTree`` is the type of the main object at +the root of the splay tree. It is intended that the +``SplayTreeStruct`` can be embedded in another structure (see +`.usage.client-tree`_ for an example). No convenience functions are +provided for allocation or deallocation. + +``typedef Bool (*SplayTestNodeFunction)(SplayTree splay, Tree tree, void *closureP, Size closureS)`` + +_`.type.splay.test.node.function`: A function of type +``SplayTestNodeFunction`` required to determine whether the node itself meets some client determined property (see `.prop`_ and `.usage.test.node`_ for an example). Parameters ``closureP`` and ``closureS`` describe the environment for the function (see `.function.splay.find.first`_ and `.function.splay.find.last`_). -``typedef Bool (*SplayTestTreeMethod)(SplayTree tree, SplayNode node, void *closureP, unsigned long closureS)`` +``typedef Bool (*SplayTestTreeFunction)(SplayTree splay, Tree tree, void *closureP, Size closureS)`` -_`.type.splay.test.tree.method`: A function of type -``SplayTestTreeMethod`` is required to determine whether any of the +_`.type.splay.test.tree.function`: A function of type +``SplayTestTreeFunction`` is required to determine whether any of the nodes in the sub-tree rooted at the given node meet some client determined property (see `.prop`_ and `.usage.test.tree`_ for an example). In particular, it must be a precise (not conservative) indication of whether there are any nodes in the sub-tree for which -the ``testNode`` method (see `.type.splay.test.node.method`_) would +the ``testNode`` function (see `.type.splay.test.node.function`_) would return ``TRUE``. Parameters ``closureP`` and ``closureS`` describe the environment for the function (see `.function.splay.find.first`_ and `.function.splay.find.last`_). -``typedef void (*SplayUpdateNodeMethod)(SplayTree tree, SplayNode node, SplayNode leftChild, SplayNode rightChild)`` +``typedef void (*SplayUpdateNodeFunction)(SplayTree splay, Tree tree)`` -_`.type.splay.update.node.method`: A function of type -``SplayUpdateNodeMethod`` is required to update any client -datastructures associated with a node to maintain some client -determined property (see `.prop`_) given that the children of the node -have changed. If the node does not have one or both children, then -``NULL`` will be passed as the relevant parameter. (See -`.usage.callback`_ for an example) +_`.type.splay.update.node.function`: A function of type +``SplayUpdateNodeFunction`` is required to update any client data +structures associated with a node to maintain some client determined +property (see `.prop`_) given that the children of the node have +changed. (See `.usage.callback`_ for an example) -External functions ------------------- +Functions +......... _`.function.no-thread`: The interface functions are not designed to be either thread-safe or re-entrant. Clients of the interface are responsible for synchronization, and for ensuring that client-provided -methods invoked by the splay module (`.type.splay.compare.method`_, -`.type.splay.test.node.method`_, `.type.splay.test.tree.method`_, -`.type.splay.update.node.method`_) do not call functions of the splay -module. +functions invoked by the splay module (`.type.tree.compare.function`_, +`.type.tree.key.function`_, `.type.splay.test.node.function`_, +`.type.splay.test.tree.function`_, `.type.splay.update.node.function`_) do +not call functions of the splay module. -``Bool SplayTreeCheck(SplayTree tree)`` +``Bool SplayTreeCheck(SplayTree splay)`` _`.function.splay.tree.check`: This is a check function for the -SplayTree type (see guide.impl.c.adt.method.check & -design.mps.check(0)). - -``Bool SplayNodeCheck(SplayNode node)`` - -_`.function.splay.node.check`: This is a check function for the -``SplayNode`` type (see guide.impl.c.adt.method.check & -design.mps.check(0)). +``SplayTree`` type (see guide.impl.c.adt.method.check and +design.mps.check_). -``void SplayTreeInit(SplayTree tree, SplayCompareMethod compare, SplayUpdateNodeMethod updateNode)`` +``void SplayTreeInit(SplayTree splay, TreeCompareFunction compare, TreeKeyFunction nodeKey, SplayUpdateNodeFunction updateNode)`` _`.function.splay.tree.init`: This function initialises a -``SplayTree`` (see guide.impl.c.adt.method.init). It requires a -``compare`` method that defines a total ordering on nodes (see -`.req.order`_); the effect of supplying a compare method that does not -implement a total ordering is undefined. It also requires an -``updateNode`` method, which will be used to keep client properties up -to date when the tree structure changes; the value -``SplayTrivUpdateNode`` may be used for this method if there is no +``SplayTree`` (see guide.impl.c.adt.method.init). The ``nodeKey`` +function extracts a key from a tree node, and the ``compare`` function +defines a total ordering on keys of nodes (see `.req.order`_). The +effect of supplying a compare function that does not implement a total +ordering is undefined. The ``updateNode`` function is used to keep +client properties up to date when the tree structure changes; the +value ``SplayTrivUpdate`` may be used for this function if there is no need to maintain client properties. (See `.usage.initialization`_ for an example use). -``void SplayTreeFinish(SplayTree tree)`` +``void SplayTreeFinish(SplayTree splay)`` _`.function.splay.tree.finish`: This function clears the fields of a ``SplayTree`` (see guide.impl.c.adt.method.finish). Note that it does -not attempt to finish or deallocate any associated ``SplayNode`` +not attempt to finish or deallocate any associated ``Tree`` objects; clients wishing to destroy a non-empty ``SplayTree`` must -first explicitly descend the tree and call ``SplayNodeFinish()`` on +first explicitly descend the tree and call ``TreeFinish()`` on each node from the bottom up. -``void SplayNodeInit(SplayNode node)`` - -_`.function.splay.node.init`: This function initialises a -``SplayNode`` (see guide.impl.c.adt.method.init). - -``void SplayNodeFinish(SplayNode node)`` - -_`.function.splay.node.finish`: This function clears the fields of a -``SplayNode`` (see guide.impl.c.adt.method.finish). Note that it does -not attempt to finish or deallocate any referenced ``SplayNode`` -objects (see.function.splay.tree.finish). - -``Bool SplayRoot(SplayNode *nodeReturn, SplayTree tree)`` - -_`.function.splay.root`: This function returns the root node of the -tree, if any (see `.req.root`_). If the tree is empty, ``FALSE`` is -returned and ``*nodeReturn`` is not changed. Otherwise, ``TRUE`` is -returned and ``*nodeReturn`` is set to the root. - -``Res SplayTreeInsert(SplayTree tree, SplayNode node, void *key)`` +``Bool SplayTreeInsert(SplayTree splay, Tree tree, void *key)`` _`.function.splay.tree.insert`: This function is used to insert into a splay tree a new node which is associated with the supplied key (see `.req.add`_). It first splays the tree at the key. If an attempt is made to insert a node that compares ``CompareEQUAL`` to an existing -node in the tree, then ``ResFAIL`` will be returned and the node will +node in the tree, then ``FALSE`` will be returned and the node will not be inserted. (See `.usage.insert`_ for an example use). -``Res SplayTreeDelete(SplayTree tree, SplayNode node, void *key)`` +``Bool SplayTreeDelete(SplayTree splay, Tree tree, void *key)`` _`.function.splay.tree.delete`: This function is used to delete from a splay tree a node which is associated with the supplied key (see `.req.remove`_). If the tree does not contain the given node, or the given node does not compare ``CompareEQUAL`` with the given key, then -``ResFAIL`` will be returned, and the node will not be deleted. The +``FALSE`` will be returned, and the node will not be deleted. The function first splays the tree at the given key. (See `.usage.delete`_ for an example use). -``Res SplayTreeSearch(SplayNode *nodeReturn, SplayTree tree, void *key)`` - -_`.function.splay.tree.search`: This function searches the splay tree -for a node that compares ``CompareEQUAL`` to the given key (see -`.req.locate`_). It splays the tree at the key. It returns ``ResFAIL`` -if there is no such node in the tree, otherwise ``*nodeReturn`` will -be set to the node. - -``Res SplayTreeNeighbours(SplayNode *leftReturn, SplayNode *rightReturn, SplayTree tree, void *key)`` - -_`.function.splay.tree.neighbours`: This function searches a splay -tree for the two nodes that are the neighbours of the given key (see -`.req.neighbours`_). It splays the tree at the key. ``*leftReturn`` -will be the neighbour which compares less than the key if such a -neighbour exists; otherwise it will be ``NULL``. ``*rightReturn`` will -be the neighbour which compares greater than the key if such a -neighbour exists; otherwise it will be ``NULL``. The function returns -``ResFAIL`` if any node in the tree compares ``CompareEQUAL`` with the -given key. (See `.usage.insert`_ for an example use). - -``SplayNode SplayTreeFirst(SplayTree tree, void *zeroKey)`` - -_`.function.splay.tree.first`: This function splays the tree at the -first node, and returns that node (see `.req.iterate`_). The supplied -key should compare ``CompareLESS`` with all nodes in the tree. It will -return ``NULL`` if the tree has no nodes. - -``SplayNode SplayTreeNext(SplayTree tree, SplayNode oldNode, void *oldKey)`` - -_`.function.splay.tree.next`: This function receives a node and key -and returns the successor node to that node (see `.req.iterate`_). -This function is intended for use in iteration when the received node -will be the current root of the tree, but is robust against being -interspersed with other splay operations (provided the old node still -exists). The supplied key must compare ``CompareEQUAL`` to the -supplied node. Note that use of this function rebalances the tree for -each node accessed. If many nodes are accessed as a result of multiple -uses, the resultant tree will be generally well balanced. But if the -tree was previously beneficially balanced for a small working set of -accesses, then this local optimization will be lost. (see -`.future.parent`_). - -``Res SplayTreeDescribe(SplayTree tree, mps_lib_FILE *stream, SplayNodeDescribeMethod nodeDescribe)`` +``Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key)`` + +_`.function.splay.tree.find`: Search the splay tree for a node that +compares ``CompareEQUAL`` to the given key (see `.req.locate`_), and +splay the tree at the key. Return ``FALSE`` if there is no such node +in the tree, otherwise set ``*nodeReturn`` to the node and return +``TRUE``. + +``Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, SplayTree splay, TreeKey key)`` + +_`.function.splay.tree.neighbours`: Search a splay tree for the two +nodes that are the neighbours of the given key (see +`.req.neighbours`_). Splay the tree at the key. If any node in the +tree compares ``CompareEQUAL`` with the given key, return ``FALSE``. +Otherwise return ``TRUE``, set ``*leftReturn`` to the left neighbour +of the key (or ``TreeEMPTY`` if the key has no left neighbour), and +set ``*rightReturn`` to the right neighbour of the key (or +``TreeEMPTY`` if the key has no right neighbour). See `.usage.insert`_ +for an example of use. + +``Tree SplayTreeFirst(SplayTree splay)`` + +_`.function.splay.tree.first`: If the tree has no nodes, return +``TreeEMPTY``. Otherwise, splay the tree at the first node, and return +that node (see `.req.iterate`_). + +``Tree SplayTreeNext(SplayTree splay, TreeKey key)`` + +_`.function.splay.tree.next`: If the tree contains a right neighbour +for ``key``, splay the tree at that node and return it. Otherwise +return ``TreeEMPTY``. See `.req.iterate`_. + +``Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, Count depth, TreeDescribeFunction nodeDescribe)`` _`.function.splay.tree.describe`: This function prints (using -``WriteF``) to the stream a textual representation of the given splay -tree, using ``nodeDescribe`` to print client-oriented representations -of the nodes (see `.req.debug`_). - -``Bool SplayFindFirst(SplayNode *nodeReturn, SplayTree tree, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, unsigned long closureS)`` - -_`.function.splay.find.first`: ``SplayFindFirst()`` finds the first node -in the tree that satisfies some client property (as determined by the -``testNode`` and ``testTree`` methods) (see `.req.property.find`_). -``closureP`` and ``closureS`` are arbitrary values, and are passed to -the ``testNode`` and ``testTree`` methods which may use the values as -closure environments. If there is no satisfactory node, then ``FALSE`` -is returned, otherwise ``*nodeReturn`` is set to the node. (See -`.usage.delete`_ for an example use). - -``Bool SplayFindFirst(SplayNode *nodeReturn, SplayTree tree, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, unsigned long closureS)`` - -_`.function.splay.find.last`: ``SplayFindLast()`` finds the last node -in the tree that satisfies some client property (as determined by the -``testNode`` and ``testTree`` methods) (see `.req.property.find`_). -``closureP`` and ``closureS`` are arbitrary values, and are passed to -the ``testNode`` and ``testTree`` methods which may use the values as -closure environments. If there is no satisfactory node, then ``FALSE`` -is returned, otherwise ``*nodeReturn`` is set to the node. - -``void SplayNodeRefresh(SplayTree tree, SplayNode node, void *key)`` - -_`.function.splay.node.refresh`: ``SplayNodeRefresh()`` must be called -whenever the client property (see `.prop`_) at a node changes (see -`.req.property.change`_). It will call the ``updateNode`` method on -the given node, and any other nodes that may require update. The +``WriteF()``) to the stream a textual representation of the given +splay tree, using ``nodeDescribe`` to print client-oriented +representations of the nodes (see `.req.debug`_). Provided for +debugging only. + +``Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, SplayTestNodeFunction testNode, SplayTestTreeFunction testTree, void *closureP, Size closureS)`` + +_`.function.splay.find.first`: Find the first node in the tree that +satisfies some client property, as determined by the ``testNode`` and +``testTree`` functions (see `.req.property.find`_). ``closureP`` and +``closureS`` are arbitrary values, and are passed to the ``testNode`` +and ``testTree`` functions which may use the values as closure +environments. If there is no satisfactory node, return ``FALSE``; +otherwise set ``*nodeReturn`` to the node and return ``TRUE``. See +`.usage.delete`_ for an example. + +``Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, SplayTestNodeFunction testNode, SplayTestTreeFunction testTree, void *closureP, Size closureS)`` + +_`.function.splay.find.last`: As ``SplayFindFirst()``, but find the +last node in the tree that satisfies the client property. + +``void SplayNodeRefresh(SplayTree splay, Tree tree, TreeKey key)`` + +_`.function.splay.node.refresh`: Call the ``updateNode`` function on the +given node, and on any other nodes that may require updating. The client key for the node must also be supplied; the function splays the -tree at this key. (See `.usage.insert`_ for an example use). +tree at this key. (See `.usage.insert`_ for an example use). This +function must be called whenever the client property (see `.prop`_) at +a node changes (see `.req.property.change`_). + +``void SplayNodeUpdate(SplayTree splay, Tree node)`` + +_`.function.splay.node.update`: Call the ``updateNode`` function on the +given node, but leave other nodes unchanged. This may be called when a +new node is created, to get the client property off the ground. Client-determined properties @@ -387,35 +387,35 @@ _`.prop`: To support `.req.property.find`_, this splay tree implementation provides additional features to permit clients to cache maximum (or minimum) values of client properties for all the nodes in a subtree. The splay tree implementation uses the cached values as -part of ``SplayFindFirst`` and ``SplayFindLast`` via the ``testNode`` -and ``testTree`` methods. The client is free to choose how to -represent the client property, and how to compute and store the cached -value. +part of ``SplayFindFirst()`` and ``SplayFindLast()`` via the +``testNode`` and ``testTree`` functions. The client is free to choose +how to represent the client property, and how to compute and store the +cached value. _`.prop.update`: The cached values depend upon the topology of the tree, which may vary as a result of operations on the tree. The client is given the opportunity to compute new cache values whenever -necessary, via the ``updateNode`` method (see +necessary, via the ``updateNode`` function (see `.function.splay.tree.init`_). This happens whenever the tree is -restructured. The client may use the ``SplayNodeRefresh`` method to +restructured. The client may use the ``SplayNodeRefresh()`` function to indicate that the client attributes at a node have changed (see -`.req.property.change`_). A call to ``SplayNodeRefresh`` splays the +`.req.property.change`_). A call to ``SplayNodeRefresh()`` splays the tree at the specified node, which may provoke calls to the -``updateNode`` method as a result of the tree restructuring. The -``updateNode`` method will also be called whenever a new splay node is +``updateNode`` function as a result of the tree restructuring. The +``updateNode`` function will also be called whenever a new splay node is inserted into the tree. -_`.prop.example`: For example, if implementing an address ordered tree +_`.prop.example`: For example, if implementing an address-ordered tree of free blocks using a splay tree, a client might choose to use the base address of each block as the key for each node, and the size of each block as the client property. The client can then maintain as a cached value in each node the size of the largest block in the subtree rooted at that node. This will permit a fast search for the first or last block of at least a given size. See `.usage.callback`_ for an -example ``updateNode`` method for such a client. +example ``updateNode`` function for such a client. _`.prop.ops`: The splay operations must cause client properties for -nodes to be updated in the following circumstances:- (see `.impl`_ for +nodes to be updated in the following circumstances (see `.impl`_ for details): _`.prop.ops.rotate`: rotate left, rotate right -- We need to update @@ -443,8 +443,7 @@ right trees. For the left tree, we traverse the right child line, reversing pointers, until we reach the node that was the last node prior to the transplantation of the root's children. Then we update from that node back to the left tree's root, restoring pointers. -Updating the right tree is the same, mutatis mutandis. (See -`.future.reverse`_ for an alternative approach). +Updating the right tree is the same, mutatis mutandis. Usage @@ -462,38 +461,37 @@ _`.usage.client-tree`: Tree structure to embed a ``SplayTree`` (see /* no obvious client fields for this simple example */ } FreeTreeStruct; -_`.usage.client-node`: Node structure to embed a SplayNode (see `.type.splay.node`_):: +_`.usage.client-node`: Node structure to embed a ``Tree`` (see `.type.tree`_):: typedef struct FreeBlockStruct { - SplayNodeStruct splayNode; /* embedded splay node */ - Addr base; /* base address of block is also the key */ - Size size; /* size of block is also the client property */ - Size maxSize; /* cached value for maximum size in subtree */ + TreeStruct treeStruct; /* embedded splay node */ + Addr base; /* base address of block is also the key */ + Size size; /* size of block is also the client property */ + Size maxSize; /* cached value for maximum size in subtree */ } FreeBlockStruct; -_`.usage.callback`: updateNode callback method (see -`.type.splay.update.node.method`_):: +_`.usage.callback`: ``updateNode`` callback function (see +`.type.splay.update.node.function`_):: - void FreeBlockUpdateNode(SplayTree tree, SplayNode node, - SplayNode leftChild, SplayNode rightChild) + void FreeBlockUpdateNode(SplayTree splay, Tree tree) { /* Compute the maximum size of any block in this subtree. */ /* The value to cache is the maximum of the size of this block, */ /* the cached value for the left subtree (if any) and the cached */ /* value of the right subtree (if any) */ - FreeBlock freeNode = FreeBlockOfSplayNode(node); + FreeBlock freeNode = FreeBlockOfTree(tree); Size maxSize = freeNode.size; - if(leftChild != NULL) { - FreeBlock leftNode = FreeBlockOfSplayNode(leftChild); + if (TreeHasLeft(tree)) { + FreeBlock leftNode = FreeBlockOfTree(TreeLeft(tree)); if(leftNode.maxSize > maxSize) maxSize = leftNode->maxSize; } - if(rightChild != NULL) { - FreeBlock rightNode = FreeBlockOfSplayNode(rightChild); + if (TreeHasRight(tree)) { + FreeBlock rightNode = FreeBlockOfTree(TreeRight(tree)); if(rightNode.maxSize > maxSize) maxSize = rightNode->maxSize; } @@ -501,13 +499,13 @@ _`.usage.callback`: updateNode callback method (see freeNode->maxSize = maxSize; } -_`.usage.compare`: Comparison function (see `.type.splay.compare.method`_):: +_`.usage.compare`: Comparison function (see `.type.tree.compare.function`_):: - Compare FreeBlockCompare(void *key, SplayNode node) { + Compare FreeBlockCompare(Tree tree, TreeKey key) { Addr base1, base2, limit2; - FreeBlock freeNode = FreeBlockOfSplayNode(node); + FreeBlock freeNode = FreeBlockOfTree(tree); - base1 = (Addr *)key; + base1 = (Addr)key; base2 = freeNode->base; limit2 = AddrAdd(base2, freeNode->size); @@ -520,47 +518,47 @@ _`.usage.compare`: Comparison function (see `.type.splay.compare.method`_):: } _`.usage.test.tree`: Test tree function (see -`.type.splay.test.tree.method`_):: +`.type.splay.test.tree.function`_):: - Bool FreeBlockTestTree(SplayTree tree, SplayNode node - void *closureP, unsigned long closureS) { + Bool FreeBlockTestTree(SplayTree splay, Tree tree + void *closureP, Size closureS) { /* Closure environment has wanted size as value of closureS. */ /* Look at the cached value for the node to see if any */ /* blocks in the subtree are big enough. */ - Size size = (Size)closureS; - FreeBlock freeNode = FreeBlockOfSplayNode(node); + Size size = closureS; + FreeBlock freeNode = FreeBlockOfTree(tree); return freeNode->maxSize >= size; } _`.usage.test.node`: Test node function (see -`.type.splay.test.node.method`_):: +`.type.splay.test.node.function`_):: - Bool FreeBlockTestNode(SplayTree tree, SplayNode node - void *closureP, unsigned long closureS) { + Bool FreeBlockTestNode(SplayTree splay, Tree tree + void *closureP, Size closureS) { /* Closure environment has wanted size as value of closureS. */ /* Look at the size of the node to see if is big enough. */ - Size size = (Size)closureS; - FreeBlock freeNode = FreeBlockOfSplayNode(node); + Size size = closureS; + FreeBlock freeNode = FreeBlockOfTree(tree); return freeNode->size >= size; } _`.usage.initialization`: Client's initialization function (see `.function.splay.tree.init`_):: - void FreeTreeInit(FreeTree tree) { + void FreeTreeInit(FreeTree freeTree) { /* Initialize the embedded splay tree. */ - SplayTreeInit(&tree->splayTree, FreeBlockCompare, FreeBlockUpdateNode); + SplayTreeInit(&freeTree->splayTree, FreeBlockCompare, FreeBlockUpdateNode); } _`.usage.insert`: Client function to add a new free block into the tree, merging it with an existing block if possible:: - void FreeTreeInsert(FreeTree tree, Addr base, Addr limit) { - SplayTree splayTree = &tree->splayTree; - SplayNode leftNeighbour, rightNeighbour; - void *key = (void *)base; /* use the base of the block as the key */ + void FreeTreeInsert(FreeTree freeTree, Addr base, Addr limit) { + SplayTree splayTree = &freeTree->splayTree; + Tree leftNeighbour, rightNeighbour; + TreeKey key = base; /* use the base of the block as the key */ Res res; /* Look for any neighbouring blocks. (.function.splay.tree.neighbours) */ @@ -570,32 +568,33 @@ tree, merging it with an existing block if possible:: /* Look to see if the neighbours are contiguous. */ - if (leftNeighbour != NULL && + if (leftNeighbour != TreeEMPTY && FreeBlockLimitOfSplayNode(leftNeighbour) == base) { /* Inserted block is contiguous with left neighbour, so merge it. */ /* The client housekeeping is left as an exercise to the reader. */ /* This changes the size of a block, which is the client */ /* property of the splay node. See `.function.splay.node.refresh`_ */ - SplayNodeRefresh(tree, leftNeighbour, key); + SplayNodeRefresh(splayTree, leftNeighbour, key); - } else if (rightNeighbour != NULL && + } else if (rightNeighbour != TreeEMPTY && FreeBlockBaseOfSplayNode(rightNeighbour) == limit) { /* Inserted block is contiguous with right neighbour, so merge it. */ /* The client housekeeping is left as an exercise to the reader. */ /* This changes the size of a block, which is the client */ /* property of the splay node. See `.function.splay.node.refresh`_ */ - SplayNodeRefresh(tree, rightNeighbour, key); + SplayNodeRefresh(splayTree, rightNeighbour, key); } else { /* Not contiguous - so insert a new node */ FreeBlock newBlock = (FreeBlock)allocate(sizeof(FreeBlockStruct)); - splayNode = &newBlock->splayNode; + Tree newTree = &newBlock->treeStruct; newBlock->base = base; newBlock->size = AddrOffset(base, limit); - SplayNodeInit(splayNode); /* `.function.splay.node.init`_ */ + TreeInit(newTree); /* `.function.tree.init`_ */ + SplayNodeUpdate(splayTree, newTree); /* `.function.splay.node.update`_ */ /* `.function.splay.tree.insert`_ */ - res = SplayTreeInsert(splayTree, splayNode, key); + res = SplayTreeInsert(splayTree, newTree, key); AVER(res == ResOK); /* this client doesn't duplicate free blocks */ } } @@ -605,19 +604,19 @@ given size in address order. For simplicity, this allocates the entire block:: Bool FreeTreeAllocate(Addr *baseReturn, Size *sizeReturn, - FreeTree tree, Size size) { - SplayTree splayTree = &tree->splayTree; - SplayNode splayNode; + FreeTree freeTree, Size size) { + SplayTree splayTree = &freeTree->splayTree; + Tree splayNode; Bool found; /* look for the first node of at least the given size. */ /* closureP parameter is not used. See `.function.splay.find.first.`_ */ found = SplayFindFirst(&splayNode, splayTree, FreeBlockTestNode, FreeBlockTestTree, - NULL, (unsigned long)size); + NULL, size); if (found) { - FreeBlock freeNode = FreeBlockOfSplayNode(splayNode); + FreeBlock freeNode = FreeBlockOfTree(splayNode); Void *key = (void *)freeNode->base; /* use base of block as the key */ Res res; @@ -625,7 +624,7 @@ block:: *baseReturn = freeNode->base; *sizeReturn = freeNode->size; - /* remove the node from the splay tree - `.function.splay.tree.delete`_ */ + /* `.function.splay.tree.delete`_ */ res = SplayTreeDelete(splayTree, splayNode, key); AVER(res == ResOK); /* Must be possible to delete node */ @@ -644,9 +643,9 @@ block:: Implementation -------------- -_`.impl`: For more details of how splay trees work, see paper.st85(0). +_`.impl`: For more details of how splay trees work, see [ST85]_. For more details of how to implement operations on splay trees, see -paper.sleator96(0). Here we describe the operations involved. +[Sleator96]_. Here we describe the operations involved. Top-down splaying @@ -654,22 +653,21 @@ Top-down splaying _`.impl.top-down`: The method chosen to implement the splaying operation is called "top-down splay". This is described as "procedure -top-down splay" in paper.st85(0) - although the implementation here -additionally permits attempts to access items which are not known to -be in the tree. Top-down splaying is particularly efficient for the -common case where the location of the node in a tree is not known at -the start of an operation. Tree restructuring happens as the tree is -descended, whilst looking for the node. +top-down splay" in [ST85]_, but the implementation here additionally +permits attempts to access items which are not known to be in the +tree. Top-down splaying is particularly efficient for the common case +where the location of the node in a tree is not known at the start of +an operation. Tree restructuring happens as the tree is descended, +whilst looking for the node. _`.impl.splay`: The key to the operation of the splay tree is the internal function ``SplaySplay()``. It searches the tree for a node -with a given key and returns whether it suceeded. In the process, it -brings the found node, or an arbitrary neighbour if not found, to the -root of the tree. This "bring-to-root" operation is performed top-down -during the search, and it is not the simplest possible bring-to-root -operation, but the resulting tree is well-balanced, and will give good -amortised cost for future calls to ``SplaySplay()``. (See -paper.st85(0)) +with a given key. In the process, it brings the found node, or an +arbitrary neighbour if not found, to the root of the tree. This +"bring-to-root" operation is performed top-down during the search, and +it is not the simplest possible bring-to-root operation, but the +resulting tree is well-balanced, and will give good amortised cost for +future calls to ``SplaySplay()``. See [ST85]_. _`.impl.splay.how`: To perform this top-down splay, the tree is broken into three parts, a left tree, a middle tree and a right tree. We @@ -683,25 +681,28 @@ they form a partition with the ordering left, middle, right. The splay is then performed by comparing the middle tree with the following six cases, and performing the indicated operations, until none apply. -_`.impl.splay.cases`: Note that paper.st85(0)(Fig. 3) describes only 3 -cases: zig, zig-zig and zig-zag. The additional cases described here -are the symmetric variants which are respectively called zag, zag-zag -and zag-zig. In the descriptions of these cases, ``root`` is the root -of the middle tree; ``node->left`` is the left child of ``node``; -``node->right`` is the right child of ``node``. The comparison -operators (``<``, ``>``, ``==``) are defined to compare a key and a -node in the obvious way by comparing the supplied key with the node's -associated key. +_`.impl.splay.cases`: Note that figure 3 of [ST85]_ describes only 3 +cases: *zig*, *zig-zig* and *zig-zag*. The additional cases described +here are the symmetric variants which are respectively called *zag*, +*zag-zag* and *zag-zig*. In the descriptions of these cases, ``root`` +is the root of the middle tree; ``node->left`` is the left child of +``node``; ``node->right`` is the right child of ``node``. The +comparison operators (``<``, ``>``, ``==``) are defined to compare a +key and a node in the obvious way by comparing the supplied key with +the node's associated key. -_`.impl.splay.zig`: The "zig" case is where ``key < root``, and either: +_`.impl.splay.zig`: The "zig" case is where ``key < root``, and +either: - ``key == root->left``; - ``key < root->left && root->left->left == NULL``; or - ``key > root->left && root->left->right == NULL``. -The operation for the zig case is: link right (see `.impl.link.right`_). +The operation for the zig case is: link right (see +`.impl.link.right`_). -_`.impl.splay.zag`: The "zag" case is where ``key > root``, and either: +_`.impl.splay.zag`: The "zag" case is where ``key > root``, and +either: - ``key == root->right``; - ``key < root->right && root->right->left == NULL``; or @@ -760,48 +761,58 @@ _`.impl.splay.terminal.not-found`: The other typical terminal cases are: - ``key < root && root->left == NULL``; and - ``key > root && root->right == NULL``. -In these cases, the splay operation is complete, the three trees are assembled -(see `.impl.assemble`_), and "not found" is returned. +In these cases, the splay operation is complete, the three trees are +assembled (see `.impl.assemble`_), and "not found" is returned. -_`.impl.rotate.left`: The "rotate left" operation (see paper.st85(0) -Fig. 1) rearranges the middle tree as follows (where any of sub-trees +_`.impl.rotate.left`: The "rotate left" operation (see [ST85]_ +figure 1) rearranges the middle tree as follows (where any of sub-trees A, B and C may be empty): -[missing diagram] +.. figure:: splay-rotate-left.svg + :align: center + :alt: Diagram: the rotate left operation. -_`.impl.rotate.right`: The "rotate right" operation (see paper.st85(0) -Fig. 1) rearranges the middle tree as follows (where any of sub-trees +_`.impl.rotate.right`: The "rotate right" operation (see [ST85]_ +figure 1) rearranges the middle tree as follows (where any of sub-trees A, B and C may be empty): -[missing diagram] +.. figure:: splay-rotate-right.svg + :align: center + :alt: Diagram: the rotate right operation. -_`.impl.link.left`: The "link left" operation (see paper.st85(0) Fig. +_`.impl.link.left`: The "link left" operation (see [ST85]_ figure 11a for symmetric variant) rearranges the left and middle trees as follows (where any of sub-trees A, B, L and R may be empty): -[missing diagram] +.. figure:: splay-link-left.svg + :align: center + :alt: Diagram: the link left operation. The last node of the left tree is now x. -_`.impl.link.right`: The "link right" operation (see paper.st85(0) -Fig. 11a) rearranges the middle and right trees as follows (where any -of sub-trees A, B, L and R may be empty): +_`.impl.link.right`: The "link right" operation (see [ST85]_ figure +11a) rearranges the middle and right trees as follows (where any of +sub-trees A, B, L and R may be empty): -[missing diagram] +.. figure:: splay-link-right.svg + :align: center + :alt: Diagram: the link left operation. The first node of the right tree is now x. -_`.impl.assemble`: The "assemble" operation (see paper.st85(0) -Fig. 12) merges the left and right trees with the middle tree as -follows (where any of sub-trees A, B, L and R may be empty): +_`.impl.assemble`: The "assemble" operation (see [ST85]_ figure 12) +merges the left and right trees with the middle tree as follows (where +any of sub-trees A, B, L and R may be empty): -[missing diagram] +.. figure:: splay-assemble.svg + :align: center + :alt: Diagram: the assemble operation. Top-level operations .................... -_`.impl.insert`: ``SplayTreeInsert()``: (See paper.sleator96(0), chapter +_`.impl.insert`: ``SplayTreeInsert()``: (See [Sleator96]_, chapter 4, function insert). If the tree has no nodes, [how does it smell?] add the inserted node and we're done; otherwise splay the tree around the supplied key. If the splay successfully found a matching node, @@ -810,7 +821,7 @@ the old (newly splayed, but non-matching) root as its left or right child as appropriate, and the opposite child of the old root as the other child of the new root. -_`.impl.delete`: ``SplayTreeDelete()``: (See paper.sleator96(0), chapter +_`.impl.delete`: ``SplayTreeDelete()``: (See [Sleator96]_, chapter 4, function delete). Splay the tree around the supplied key. Check that the newly splayed root is the same node as given by the caller, and that it matches the key; return failure if not. If the given node @@ -821,7 +832,7 @@ splay around the same key. The new root will be the last node in the sub-tree and will have a null right child; this is set to be the right child of the node to be deleted. -_`.impl.search`: ``SplayTreeSearch()``: Splay the node around the +_`.impl.find`: ``SplayTreeFind()``: Splay the node around the supplied key. If the splay found a matching node, return it; otherwise return failure. @@ -866,9 +877,11 @@ Testing ------- _`.test`: There is no plan to test splay trees directly. It is -believed that the testing described in design.mps.cbs.test will be +believed that the testing described in design.mps.cbs.test_ will be sufficient to test this implementation. +.. _design.mps.cbs.test: cbs#test + Error Handling -------------- @@ -878,13 +891,13 @@ protocol error. The cases it doesn't handle will result in undefined behaviour and probably cause an ``AVER`` to fire. These are: _`.error.bad-pointer`: Passing an invalid pointer in place of a -``SplayTree`` or ``SplayNode``. +``SplayTree`` or ``Tree``. _`.error.bad-compare`: Initialising a ``SplayTree`` with a compare function that is not a valid compare function, or which doesn't implement a total ordering on splay nodes. -_`.error.bad-describe`: Passing an invalid describe method to +_`.error.bad-describe`: Passing an invalid describe function to ``SplayTreeDescribe()``. _`.error.out-of-stack`: Stack exhaustion under ``SplayTreeDescribe()``. @@ -893,25 +906,28 @@ _`.error.out-of-stack`: Stack exhaustion under ``SplayTreeDescribe()``. Future ------ -_`.future.tree`: It would be possible to split the splay tree module -into two: one that implements binary trees; and one that implements -splay trees on top of a binary tree. - _`.future.parent`: The iterator could be made more efficient (in an amortized sense) if it didn't splay at each node. To implement this (whilst meeting `.req.stack`_) we really need parent pointers from the nodes. We could use the (first-child, right-sibling/parent) trick -described in paper.st85 to implement this, at a slight cost to all -other tree operations, and an increase in code complexity. paper.st85 +described in [ST85]_ to implement this, at a slight cost to all +other tree operations, and an increase in code complexity. [ST85]_ doesn't describe how to distinguish the first-child between left-child and right-child, and the right-sibling/parent between right-sibling and parent. One could either use the comparator to make these distinctions, or steal some bits from the pointers. -_`.future.reverse`: The assembly phase could be made more efficient if -the link left and link right operations were modified to add to the -left and right trees with pointers reversed. This would remove the -need for the assembly phase to reverse them. + +References +---------- + +.. [ST85] "Self-Adjusting Binary Search Trees"; Daniel Dominic Sleator, + Robert Endre Tarjan; AT&T Bell Laboratories, Murray Hill, NJ; 1985-07; + Journal of the ACM, Vol. 32, Num. 3, pp. 652-686, July 1985; + . + +.. [Sleator96] "Splay Trees"; Daniel Dominic Sleator; CMU, 22/02/96; + CMU 15-211; . Document History @@ -928,6 +944,11 @@ Document History - 2002-06-07 RB_ Converted from MMInfo database design document. +- 2014-02-22 RB_ Fixing abuses of Res and ResFAIL. + +- 2014-03-11 RB_ Updating in response to code review. Removing + .future.tree and .future.reverse, both now implemented. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/ss.txt b/design/ss.txt new file mode 100644 index 0000000000..60b5c2817f --- /dev/null +++ b/design/ss.txt @@ -0,0 +1,170 @@ +.. mode: -*- rst -*- + +Stack and register scanning +=========================== + +:Tag: design.mps.ss +:Author: Gareth Rees +:Date: 2014-10-22 +:Status: complete design +:Revision: $Id$ +:Copyright: See `Copyright and License`_. +:Index terms: pair: stack and register scanning; design + + +Introduction +------------ + +_`.intro`: This is the design of the stack and register scanning +module. + +_`.readership`: Any MPS developer; anyone porting the MPS to a new +platform. + +_`.overview`: This module locates and scans references in the control +stack and registers of the *current* thread. + +_`.other`: The thread manager module is responsible for scanning the +control stack and registers of *other* threads. See +design.mps.thread-manager.if.scan_. + +.. _design.mps.thread-manager.if.scan: thread-manager#if.scan + + +Requirements +------------ + +_`.req.stack.top`: Must locate the top of the mutator's stack. (This +is needed to support conservative garbage collection of uncooperative +code, where references might be stored by mutator on its stack.) + +_`.req.stack.bottom.not`: There is no requirement to locate the bottom +of the stack. (The mutator supplies this as an argument to +``mps_root_create_reg()``.) + +_`.req.registers`: Must locate and scan all references in the *root +registers*, the subset of registers which might contain references +that do not also appear on the stack. (This is needed to support +conservative garbage collection of uncooperative code, where +references might appear in registers.) + + +Design +------ + +_`.sol.stack.top`: Implementations find the top of the stack by +taking the address of a local variable. + +_`.sol.registers`: Implementations spill the root registers onto the +stack so that they can be scanned there. + +_`.sol.registers.root`: The *root registers* are the subset of the +callee-save registers that may contain pointers. + +_`.sol.registers.root.justify`: The caller-save registers will have +been spilled onto the stack by the time the MPS is entered, so will be +scanned by the stack scan. Floating-point registers and debugging +registers do not, as far as we are aware, contain pointers. + +_`.sol.inner`: Having located the top of the stack (``stackTop``), and +spilled the root registers into the next ``n`` words, implementations +call the generic function ``StackScanInner(ss, stackBot, stackTop, +n)`` to actually do the scanning. + + +Interface +--------- + +``Res StackScan(ScanState ss, Addr *stackBot)`` + +_`.if.scan`: Scan the root registers of the current thread, and the +control stack between ``stackBot`` and the top of the stack, in the +context of the given scan state. Return ``ResOK`` if successful, or +another result code if not. + + +Issue +----- + +_`.issue.overscan`: This design leads to over-scanning, because by the +time ``StackScan()`` is called, there are several MPS functions on the +stack. The scan thus ends up scanning references that belong the MPS, +not to the mutator. See job003525_. + +.. _job003525: http://www.ravenbrook.com/project/mps/issue/job003525/ + + +Implementations +--------------- + +_`.impl.an`: Generic implementation in ``ssan.c``. This calls +``setjmp()`` with a stack-allocated ``jmp_buf`` to spill the registers +onto the stack. The C standard specifies that ``jmp_buf`` "is an array +type suitable for holding the information needed to restore a calling +environment. The environment of a call to the ``setjmp`` macro +consists of information sufficient for a call to the ``longjmp`` +function to return execution to the correct block and invocation of +that block, were it called recursively." Note that the C standard does +not specify where the callee-save registers appear in the ``jmp_buf``, +so the whole buffer must be scanned. + +_`.impl.ix`: Unix implementation in ``ssixi3.c`` and ``ssixi6.c``. +Assembler instructions are used to spill exactly the callee-save +registers. (Clang and GCC support a common assembler syntax.) + +_`.impl.w3`: Windows implementation in ``ssw3i3mv.c`` and +``ssw3i6mv.c``. Like `.impl.an`_, this implementation uses +``setjmp()`` with a stack-allocated ``jmp_buf`` to spill the registers +onto the stack. However, we know the layout of the ``jmp_buf`` used by +the compiler, and so can scan exactly the subset of registers we need. + + +Document History +---------------- + +- 2014-10-22 GDR_ Initial draft. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 2014 Ravenbrook Limited. All rights reserved. +. This is an open source license. Contact +Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** diff --git a/design/strategy.txt b/design/strategy.txt index 4b09c3cc00..695813bed8 100644 --- a/design/strategy.txt +++ b/design/strategy.txt @@ -81,140 +81,206 @@ the client to specify preferred relative object locations ("this object should be kept in the same cache line as that one"), to improve cache locality. + Generations ----------- The largest part of the current MPS strategy implementation is the -support for generational GC. Generations are only fully supported for -AMC (and AMCZ) pools. See under "Non-AMC Pools", below, for more -information. - -Data Structures -............... - -The fundamental structure of generational GC is the ``Chain``, -which describes a set of generations. A chain is created by client -code calling ``mps_chain_create()``, specifying the "size" and -"mortality" for each generation. When creating an AMC pool, the -client code must specify the chain which will control collections for -that pool. The same chain may be used for multiple pools. - -Each generation in a chain has a ``GenDesc`` structure, -allocated in an array pointed to from the chain. Each AMC pool has a -set of ``PoolGen`` structures, one per generation. The PoolGens -for each generation point to the GenDesc and are linked together in a -ring on the GenDesc. These structures are (solely?) used to gather -information for strategy decisions. +support for generational garbage collections. + + +General data structures +....................... + +The fundamental structure of generational garbage collection is the +``Chain``, which describes a sequence of generations. -The arena has a unique ``GenDesc`` structure, named +A chain specifies the "capacity" and "mortality" for each generation. +When creating an automatically collected pool, the client code may +specify the chain which will control collections for that pool. The +same chain may be used for multiple pools. If no chain is specified, +the pool uses the arena's default generation chain. + +Each generation in a chain has a ``GenDesc`` structure, allocated in +an array pointed to from the chain. In addition to the generations in +the chains, the arena has a unique ``GenDesc`` structure, named ``topGen`` and described in comments as "the dynamic generation" -(although in fact it is the *least* dynamic generation). Each AMC -pool has one more PoolGen than there are GenDescs in the chain. The -extra PoolGen refers to this topGen. - -AMC segments have a segment descriptor ``amcSegStruct`` which is -a ``GCSegStruct`` with two additional fields. One field -``segTypeP`` is a pointer either to the per-generation per-pool -``amcGen`` structure (a subclass of ``PoolGen``), or to a -nailboard (which then points to an amcGen). The other field -``new`` is a boolean used for keeping track of memory usage for -strategy reasons (see below under 'Accounting'). The ``amcGen`` -is used for statistics (``->segs``) and forwarding buffers -(``->forward``). - -The AMC pool class only ever allocates a segment in order to fill a -buffer: either the buffer for a client Allocation Point, or a -forwarding buffer. In order to support generational collection, there -is a subclass ``amcBuf`` of ``SegBuf``, with a -``gen`` field (pointing to a ``amcGen``). So in -``AMCBufferFill()`` the generation of the new segment can be -determined. - -When an AMC pool is created, these ``amcGen`` and -``amcBuf`` structures are all created, and the -``amcBuf->gen`` fields initialized so that the forwarding buffer -of each amcGen knows that it belongs to the next "older" amcGen (apart -from the "oldest" amcGen - that which refers to the topGen - whose -forwarding buffer belongs to itself). - -When copying an object in ``AMCFix()``, the object's current -generation is determined (``amcSegGen()``), and the object is -copied to that amcGen's forwarding buffer, using the buffer protocol. -Thus, objects are "promoted" up the chain of generations until they -end up in the topGen, which is shared between all chains and all -pools. - -For statistics and reporting purposes, when ``STATISTICS`` is -on, each AMC pool has an array of ``PageRetStruct``s, one per -trace. This structure has many ``Count`` fields, and is -intended to help to assess AMC page retention code. See job001811. +(misleadingly: in fact it is the *least* dynamic generation). + +Each automatically collected pool has a set of ``PoolGen`` structures, +one for each generation that it can allocate or promote into. The +``PoolGen`` structures for each generation point to the ``GenDesc`` +for that generation, and are linked together in a ring on the +``GenDesc``. These structures are used to gather accounting +information for strategy decisions. + +The non-moving automatic pool classes (AMS, AWL and LO) do not support +generational collection, so they allocate into a single generation. +The moving automatic pool classes (AMC and AMCZ) have one pool +generations for each generation in the chain, plus one pool generation +for the arena's "top generation". + + +AMC data structures +................... + +An AMC pool creates an array of pool generation structures of type +``amcGen`` (a subclass of ``PoolGen``). Each pool generation points to +the *forwarding buffer* for that generation: this is the buffer that +surviving objects are copied into. + +AMC segments point to the AMC pool generation that the segment belongs +to, and AMC buffers point to the AMC pool generation that the buffer +will be allocating into. + +The forwarding buffers are set up during AMC pool creation. Each +generation forwards into the next higher generation in the chain, +except for the top generation, which forwards to itself. Thus, objects +are "promoted" up the chain of generations until they end up in the +top generations, which is shared between all generational pools. + + +Collections +........... + +Collections in the MPS start in one of two ways: + +1. A collection of the world starts via ``TraceStartCollectAll()``. + This simply condemns all segments in all automatic pools. + +2. A collection of some set of generations starts via + ``PolicyStartTrace()``. See `.policy.start`_. + Zones ..... -All collections in the MPS start with condemnation of a complete -``ZoneSet``. Each generation in each chain has a zoneset -associated with it (``chain->gen[N].zones``); the condemned -zoneset is the union of some number of generation's zonesets. It is -condemned by code in the chain system calling -``TraceCondemnZones()``. This is either for all chains -(``ChainCondemnAll()`` called for every chain from -``traceCondemnAll()``) or for some number of generations in a -single chain (``ChainCondemnAuto()`` called from -``TracePoll()``). Note that the condemnation is of every -automatic-pool segment in any zone in the zoneset. It is not limited -to the segments actually associated with the condemned generation(s). +Each generation in each chain has a zoneset associated with it +(``gen->zones``); the condemned zoneset is the union of some number of +generation's zonesets. An attempt is made to use distinct zonesets for different generations. -Segments are allocated from ``AMCBufferFill()`` using ``ChainAlloc()`` -which creates a ``SegPref`` using the zoneset from the generation's -``GenDesc``. The zoneset for each generation number starts out -empty. If the zoneset is empty, an attempt is made to allocate from a -free zone. The ``GenDesc`` zoneset is augmented with whichever zones the -new segment occupies. +Segments in automatic pools are allocated using ``PoolGenAlloc()`` +which creates a ``LocusPref`` using the zoneset from the generation's +``GenDesc``. The zoneset for each generation starts out empty. If the +zoneset is empty, an attempt is made to allocate from a free zone. The +``GenDesc`` zoneset is augmented with whichever zones the new segment +occupies. Note that this zoneset can never shrink. + +Parameters +.......... + +_`.param.intro`: A generation has two parameters, *capacity* and +*mortality*, specified by the client program. + +_`.param.capacity`: The *capacity* of a generation is the amount of +*new* allocation in that generation (that is, allocation since the +last time the generation was condemned) that will cause the generation +to be collected by ``TracePoll()``. + +_`.param.capacity.misnamed`: The name *capacity* is unfortunate since +it suggests that the total amount of memory in the generation will not +exceed this value. But that will only be the case for pool classes +that always promote survivors to another generation. When there is +*old* allocation in the generation (that is, prior to the last time +the generation was condemned), as there is in the case of non-moving +pool classes, the size of a generation is unrelated to its capacity. + +_`.param.mortality`: The *mortality* of a generation is the proportion +(between 0 and 1) of memory in the generation that is expected to be +dead when the generation is collected. It is used in ``TraceStart()`` +to estimate the amount of data that will have to be scanned in order +to complete the trace. + + Accounting .......... -- ``gen[N].mortality`` +_`.accounting.intro`: Pool generations maintain the sizes of various +categories of data allocated in that generation for that pool. This +accounting information is reported via the event system, but also used +in two places: + +_`.accounting.poll`: ``ChainDeferral()`` uses the *new size* of +each generation to determine which generations in the chain are over +capacity and so might need to be collected by ``PolicyStartTrace()``. + +_`.accounting.condemn`: ``PolicyStartTrace()`` uses the *new size* of +each generation to determine which generations in the chain will be +collected; it also uses the *total size* of the generation to compute +the mortality. + +_`.accounting.check`: Computing the new size for a pool generation is +far from straightforward: see job003772_ for some (former) errors in +this code. In order to assist with checking that this has been +computed correctly, the locus module uses a double-entry book-keeping +system to account for every byte in each pool generation. This uses +six accounts: + +.. _job003772: http://www.ravenbrook.com/project/mps/issue/job003772/ + +_`.account.total`: Memory acquired from the arena. + +_`.account.total.negated`: From the point of view of the double-entry +system, the *total* should be negative as it is owing to the arena, +but it is inconvenient to represent negative sizes, and so the +positive value is stored instead. - - Specified by the client. - - TODO: fill in how this is used. +_`.account.total.negated.justification`: We don't have a type for +signed sizes; but if we represented it in two's complement using the +unsigned ``Size`` type then Clang's unsigned integer overflow detector +would complain. -- ``gen[N].capacity`` +_`.account.free`: Memory that is not in use (free or lost to +fragmentation). - - Specified by the client. - - TODO: fill in how this is used. +_`.account.new`: Memory in use by the client program, allocated +since the last time the generation was condemned. -- ``amcSeg->new`` +_`.account.old`: Memory in use by the client program, allocated +prior to the last time the generation was condemned. - - TODO: fill this in +_`.account.newDeferred`: Memory in use by the client program, +allocated since the last time the generation was condemned, but which +should not cause collections via ``TracePoll()``. (Due to ramping; see +below.) -- ``pgen->totalSize``: +_`.account.oldDeferred`: Memory in use by the client program, +allocated prior to the last time the generation was condemned, but +which should not cause collections via ``TracePoll()``. (Due to +ramping; see below.) - - incremented by ``AMCBufferFill()``; - - decremented by ``amcReclaimNailed()`` and ``AMCReclaim()``; - - added up by ``GenDescTotalSize(gen)``. +_`.accounting.op`: The following operations are provided: -- ``pgen->newSize``: +_`.accounting.op.alloc`: Allocate a segment in a pool generation. +Debit *total*, credit *free*. (But see `.account.total.negated`_.) - - incremented by ``AMCBufferFill()`` (*when not ramping*) and ``AMCRampEnd()``; - - decremented by ``AMCWhiten()``, - - added up by ``GenDescNewSize(gen)``. +_`.accounting.op.free`: Free a segment. First, ensure that the +contents of the segment are accounted as free, by artificially ageing +any memory accounted as *new* or *newDeferred* (see +`.accounting.op.age`_) and then artifically reclaiming any memory +accounted as *old* or *oldDeferred* (see `.accounting.op.reclaim`_). +Finally, debit *free*, credit *total*. (But see +`.account.total.negated`_.) -- ``gen[N].proflow``: +_`.accounting.op.fill`: Allocate memory, for example by filling a +buffer. Debit *free*, credit *new* or *newDeferred*. - - set to 1.0 by ``ChainCreate()``; - - ``arena->topGen.proflow`` set to 0.0 by ``LocusInit(arena)``; - - *The value of this field is never used*. +_`.accounting.op.empty`: Deallocate memory, for example by emptying +the unused portion of a buffer. Debit *new* or *newDeferred*, credit +*free*. +_`.accounting.op.age`: Condemn memory. Debit *new* or *newDeferred*, +credit *old* or *oldDeferred*. -- ``pgen->newSizeAtCreate``: +_`.accounting.op.reclaim`: Reclaim dead memory. Debit *old* or +*oldDeferred*, credit *free*. + +_`.accounting.op.undefer`: Stop deferring the accounting of memory. Debit *oldDeferred*, credit *old*. Debit *newDeferred*, credit *new*. - - set by ``traceCopySizes()`` (that is its purpose); - - output in the ``TraceStartPoolGen`` telemetry event. Ramps ..... @@ -289,29 +355,31 @@ Reclaiming any AMC segment: Now, some deductions: #. When OUTSIDE, the count is always zero, because (a) it starts that -way, and the only ways to go OUTSIDE are (b) by leaving an outermost -ramp (count goes to zero) or (c) by reclaiming when the count is zero. + way, and the only ways to go OUTSIDE are (b) by leaving an + outermost ramp (count goes to zero) or (c) by reclaiming when the + count is zero. #. When BEGIN, the count is never zero (consider the transitions to -BEGIN and the transition to zero). + BEGIN and the transition to zero). -#. When RAMPING, the count is never zero (again consider transitions to -RAMPING and the transition to zero). +#. When RAMPING, the count is never zero (again consider transitions + to RAMPING and the transition to zero). -#. When FINISH, the count can be anything (the transition to FINISH has -zero count, but the Enter transition when FINISH can change that and -then it can increment to any value). +#. When FINISH, the count can be anything (the transition to FINISH + has zero count, but the Enter transition when FINISH can change + that and then it can increment to any value). #. When COLLECTING, the count can be anything (from the previous fact, -and the transition to COLLECTING). + and the transition to COLLECTING). -#. *This is a bug!!* The ramp generation is not always reset (to forward -to the after-ramp generation). If we get into FINISH and then see -another ramp before the next condemnation of the ramp generation, we -will Enter followed by Leave. The Enter will keep us in FINISH, and -the Leave will take us back to OUTSIDE, skipping the transition to the -COLLECTING state which is what resets the ramp generation forwarding -buffer. [TODO: check whether I made an issue and/or fixed it; NB 2013-06-04] +#. *This is a bug!!* The ramp generation is not always reset (to + forward to the after-ramp generation). If we get into FINISH and + then see another ramp before the next condemnation of the ramp + generation, we will Enter followed by Leave. The Enter will keep us + in FINISH, and the Leave will take us back to OUTSIDE, skipping the + transition to the COLLECTING state which is what resets the ramp + generation forwarding buffer. [TODO: check whether I made an issue + and/or fixed it; NB 2013-06-04] The simplest change to fix this is to change the behaviour of the Leave transition, which should only take us OUTSIDE if we are in BEGIN or @@ -349,44 +417,139 @@ other uses of that: - in ``AMCWhiten()``, if new is TRUE, the segment size is deducted from ``poolGen.newSize`` and new is set to FALSE. -Non-AMC Pools -............. -The implementations of AMS, AWL, and LO pool classes are all aware of -generations (this is necessary because all tracing is driven by the -generational data structures described above), but do not make use of -them. For LO and AWL, when a pool is created, a chain with a single -generation is also created, with size and mortality parameters -hard-wired into the pool-creation function (LOInit, AWLInit). For -AMS, a chain is passed as a pool creation parameter into -``mps_pool_create()``, but this chain must also have only a -single generation (otherwise ``ResPARAM`` is returned). +Policy +------ + +_`.policy`: Functions that make decisions about what action to take +are collected into the policy module (policy.c). The purpose of doing +so is to make it easier to understand this set of decisions and how +they interact, and to make it easier to maintain and update the policy. + + +Assignment of zones +................... + +``Res PolicyAlloc(Tract *tractReturn, Arena arena, LocusPref pref, Size size, Pool pool)`` + +_`.policy.alloc`: Allocate ``size`` bytes of memory on behalf of +``pool``, based on the preferences described by ``pref``. If +successful, update ``*tractReturn`` to point to the first tract in the +allocated memory and return ``ResOK``. Otherwise, return a result code +describing the problem, for example ``ResCOMMIT_LIMIT``. + +_`.policy.alloc.impl`: This tries various methods in succession until +one succeeds. First, it tries to allocate from the arena's free land +in the requested zones. Second, it tries allocating from free zones. +Third, it tries extending the arena and then trying the first two +methods again. Fourth, it tries allocating from any zone that is not +blacklisted. Fifth, it tries allocating from any zone at all. + +_`.policy.alloc.issue`: This plan performs poorly under stress. See +for example job003898_. + +.. _job003898: http://www.ravenbrook.com/project/mps/issue/job003898/ -Note that these chains are separate from any chain used by an AMC pool -(except in the trivial case when a single-generation chain is used for -both AMC and AMS). Note also that these pools do not use or point to -the ``arena->topGen``, which applies only to AMC. -Non-AMC pools have no support for ramps. -Starting a Trace +Deciding whether to collect the world +..................................... + +``Bool PolicyShouldCollectWorld(Arena arena, double interval, double multiplier, Clock now, Clock clocks_per_sec)`` + +_`.policy.world`: Determine whether now is a good time for +``mps_arena_step()`` to start a collection of the world. Return +``TRUE`` if so, ``FALSE`` if not. The ``interval`` and ``multiplier`` +arguments are the ones the client program passed to +``mps_arena_step()``, ``now`` is the current time as returned by +``ClockNow()``, and ``clocks_per_sec`` is the result of calling +``ClocksPerSec()``. + +_`.policy.world.impl`: There are two conditions: the client program's +estimate of the available time must be enough to complete the +collection, and the last collection of the world must be long enough +in the past that the ``mps_arena_step()`` won't be spending more than +a certain fraction of runtime in collections. (This fraction is given +by the ``ARENA_MAX_COLLECT_FRACTION`` configuration parameter.) + + + +Starting a trace ................ -TODO: Why do we start a trace? How do we choose what to condemn? +``Bool PolicyStartTrace(Trace *traceReturn, Arena arena)`` + +_`.policy.start`: Consider starting a trace. If a trace was started, +update ``*traceReturn`` to point to the trace and return TRUE. +Otherwise, leave ``*traceReturn`` unchanged and return FALSE. + +_`.policy.start.impl`: This uses the "Lisp Machine" strategy, which +tries to schedule collections of the world so that the collector just +keeps pace with the mutator: that is, it starts a collection when the +predicted completion time of the collection is around the time when +the mutator is predicted to reach the current memory limit. See +[Pirinen]_. -Trace Progress +_`.policy.start.chain`: If it is not yet time to schedule a collection +of the world, ``PolicyStartTrace()`` considers collecting a set of +zones corresponding to a set of generations on a chain. + +It picks these generations by calling ``ChainDeferral()`` for each +chain; this function indicates if the chain needs collecting, and if +so, how urgent it is to collect that chain. The most urgent chain in +need of collection (if any) is then condemned by calling +``policyCondemnChain()``, which chooses the set of generations to +condemn, computes the zoneset corresponding to the union those +generations, and condemns those zones by calling +``TraceCondemnZones()``. + +Note that the resulting condemned set includes every segment in an +automatic pool in any zone in the zoneset. It is not limited to the +segments actually associated with the condemned generations. + + + +Trace progress .............. -TODO: When do we do some tracing work? How much tracing work do we do? + +``Bool PolicyPoll(Arena arena)`` + +_`.policy.poll`: Return TRUE if the MPS should do some tracing work; +FALSE if it should return to the mutator. + +``Bool PolicyPollAgain(Arena arena, Clock start, Size tracedSize)`` + +_`.policy.poll.again`: Return TRUE if the MPS should do another unit +of work; FALSE if it should return to the mutator. ``start`` is the +clock time when the MPS was entered; ``tracedSize`` is the amount of +work done by the last call to ``TracePoll()``. + +_`.policy.poll.impl`: The implementation balances collection work +against mutator allocation so that there is approximately one call to +``TracePoll()`` for every ``ArenaPollALLOCTIME`` bytes of allocation. + + +References +---------- + +.. [Pirinen] + "The Lisp Machine Strategy"; + Pekka Pirinin; + 1998-04-27; + + Document History ---------------- - 2013-06-04 NB_ Checked this in although it's far from complete. Pasted in my 'ramping notes' from email, which mention some bugs which I may have fixed (TODO: check this). -- 2014-01-29 RB_ The arena no longer manages generation zonesets. +- 2014-01-29 RB_ The arena no longer manages generation zonesets. +- 2014-05-17 GDR_ Bring data structures and condemn logic up to date. -.. _RB: http://www.ravenbrook.com/consultants/rb +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ .. _NB: http://www.ravenbrook.com/consultants/nb/ +.. _RB: http://www.ravenbrook.com/consultants/rb Copyright and License diff --git a/design/telemetry.txt b/design/telemetry.txt index 2bc359ed35..c19bbab005 100644 --- a/design/telemetry.txt +++ b/design/telemetry.txt @@ -21,8 +21,11 @@ the MPS. _`.readership`: This document is intended for any MPS developer. _`.source`: Various meetings and brainstorms, including -meeting.general.1997-03-04(0), mail.richard.1997-07-03.17-01(0), -mail.gavinm.1997-05-01.12-40(0). +meeting.general.1997-03-04(0), `mail.richard.1997-07-03.17-01`_, +`mail.gavinm.1997-05-01.12-40`_. + +.. _mail.gavinm.1997-05-01.12-40: https://info.ravenbrook.com/project/mps/mail/1997/05/01/12-40/0.txt +.. _mail.richard.1997-07-03.17-01: https://info.ravenbrook.com/project/mps/mail/1997/07/03/17-01/0.txt Overview @@ -409,7 +412,7 @@ _`.debug.dump`: The contents of all buffers can be dumped with the _`.debug.describe`: Individual events can be described with the EventDescribe function, for example:: - gdb> print EventDescribe(EventLast[3], mps_lib_get_stdout()) + gdb> print EventDescribe(EventLast[3], mps_lib_get_stdout(), 0) _`.debug.core`: The event buffers are preserved in core dumps and can be used to work out what the MPS was doing before a crash. Since the @@ -429,8 +432,7 @@ Allocation replayer tool ........................ _`.replayer`: A tool for replaying an allocation sequence from a log -is available in impl.c.replay. For details, see -design.mps.telemetry.replayer. +is available in impl.c.replay. Document History diff --git a/design/template-with-guide.html b/design/template-with-guide.html deleted file mode 100644 index 56ed9a8e5d..0000000000 --- a/design/template-with-guide.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - - -MODULE_LONG_NAME - - - - - - - -

This document contains a guide to the MODULE_LONG_NAME, followed by the historical initial design. References, History, Copyright and License are at the end.

- -
- -

Guide

- -

Readership: any MPS developer. Not confidential.

- -

Introduction

-

- ....etc.... -

- - -
- -

Initial Design

- - -
-                           MODULE_LONG_NAME
-                           design.mps.MODULE
-                            incomplete doc
-                             ....etc....
-
-
-
- -
- - -

A. References

- - - - -

B. Document History

- -
- - - - - - - -
2007-03-22RHSKCreate template for design docs with a prefixed Guide.
- - -

C. Copyright and License

- -

This document is copyright © 2007 Ravenbrook Limited. All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options.

- -

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

- -
    - -
  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. - -
  3. 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.
  4. - -
  5. Redistributions in any form must be accompanied by information on how to obtain complete source code for the this software and any accompanying software that uses this software. The source code must either be included in the distribution or be available for no more than the cost of distribution plus a nominal fee, and must be freely redistributable under reasonable conditions. For an executable file, complete source code means the source code for all modules it contains. It does not include source code for modules or files that typically accompany the major components of the operating system on which the executable file runs.
  6. - -
- -

This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose, or non-infringement, are disclaimed. In no event shall the copyright holders and contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.

- - -
- - - - - - diff --git a/design/testthr.txt b/design/testthr.txt new file mode 100644 index 0000000000..a1bf99bb24 --- /dev/null +++ b/design/testthr.txt @@ -0,0 +1,157 @@ +.. mode: -*- rst -*- + +Multi-threaded testing +====================== + +:Tag: design.mps.testthr +:Author: Gareth Rees +:Date: 2014-10-21 +:Status: complete design +:Revision: $Id$ +:Copyright: See section `Copyright and License`_. +:Index terms: pair: threads; testing + + +Introduction +------------ + +_`.intro`: This is the design of the multi-threaded testing module +in the Memory Pool System. + +_`.readership`: Any MPS developer. + +_`.overview`: The MPS is designed to work in a multi-threaded +environment (see design.mps.thread-safety_) and this needs to be +tested on all supported platforms. The multi-threaded testing module +provides an interface for creating and joining threads, so that +multi-threaded test cases are portable to all platforms on which the +MPS runs. + +.. _design.mps.thread-safety: thread-safety + + +Requirements +------------ + +_`.req.create`: The module must provide an interface for creating +threads and running code in them. (Because there is no such interface +in the Standard C Library.) + +_`.req.join`: The module must provide an interface for joining a +running thread: that is, waiting for the thread to finish and +collecting a result. (Because we want to be able to test that the MPS +behaves correctly when interacting with a finished thread.) + +_`.req.portable`: The module must be easily portable to all the +platforms on which the MPS runs. + +_`.req.usable`: The module must be simple to use, not requiring +elaborate setup or tear-down or error handling. (Because we want test +cases to be easy to write.) + + +Implementation +-------------- + +_`.impl.posix`: To meet `.req.portable`_ and `.req.usable`_, the +module presents an interface that is essentially identical to the +POSIX Threads interface [pthreads]_, except for the names. On POSIX +platforms the implementation is trivial; on Windows it is +necessary to translate the concepts back and forth. + +_`.impl.storage`: To meet `.req.usable`_, the module defines the +``testthr_t`` type in the header ``testthr.h`` (even though this +requires an ``#if``), so that test cases can easily declare variables +and allocate storage for thread identifiers. + +_`.impl.error`: To meet `.req.usable`_, the module does not propagate +error codes, but calls ``error()`` from the test library if anything +goes wrong. There is thus no need for the test cases to check result +codes. + + +Interface +--------- + +``typedef testthr_t`` + +The type of thread identifiers. + +``typedef void *(*testthr_routine_t)(void *)`` + +The type of a function that can be called when a thread is created. + +``void testthr_create(testthr_t *thread_o, testthr_routine_t start, void *arg)`` + +Create a thread. Store the identifier of the newly created thread in +``*thread_o``, and call ``start()``, passing ``arg`` as the single +parameter. + +``void testthr_join(testthr_t *thread, void **result_o)`` + +Wait for a thread to complete. Suspend execution of the calling thread +until the target thread terminates (if necessary), and if ``result_o`` +is non-NULL, update ``*result_o`` with the return value of the +thread's ``start()`` function. + + +References +---------- + +.. [pthreads] + The Open Group; + "The Single UNIX Specification, Version 2---Threads"; + + + +Document History +---------------- + +- 2014-10-21 GDR_ Initial draft. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +Copyright and License +--------------------- + +Copyright © 2014 Ravenbrook Limited. All rights reserved. +. This is an open source license. Contact +Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +#. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +#. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +#. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.** + + diff --git a/design/thread-manager.txt b/design/thread-manager.txt index c80a818c33..48cbb91253 100644 --- a/design/thread-manager.txt +++ b/design/thread-manager.txt @@ -1,6 +1,6 @@ .. mode: -*- rst -*- -Thread Manager +Thread manager ============== :Tag: design.mps.thread-manager @@ -9,149 +9,312 @@ Thread Manager :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. -:Index terms: pair: thread manager; design +:Index terms: pair: thread manager; design -Purpose -------- +Introduction +------------ -The Thread Manager handles various thread-related functions required -by the MPS. These are: +_`.intro`: This is the design of the thread manager module. -- stack scanning; -- suspension and resumption of the mutator threads. +_`.readership`: Any MPS developer; anyone porting the MPS to a new +platform. +_`.overview`: The thread manager implements two features that allow +the MPS to work in a multi-threaded environment: exclusive access to +memory, and scanning of roots in a thread's registers and control +stack. -Context -------- -The barrier requires suspension and resumption of threads in order to -ensure that the collector has exclusive access to part of memory. -[design.mps.barrier.@@@@] +Requirements +------------ -Stack scanning is provided as a service to the client. [Link?@@@@] +_`.req.exclusive`: The thread manager must provide the MPS with +exclusive access to the memory it manages in critical sections of the +code. (This is necessary to avoid for the MPS to be able to flip +atomically from the point of view of the mutator.) +_`.req.scan`: The thread manager must be able to locate references in +the registers and control stack of the current thread, or of a +suspended thread. (This is necessary in order to implement +conservative collection, in environments where the registers and +control stack contain ambiguous roots. Scanning of roots is carried +out during the flip, hence while other threads are suspended.) -Overview --------- +_`.req.register.multi`: It must be possible to register the same +thread multiple times. (This is needed to support the situation where +a program that does not use the MPS is calling into MPS-using code +from multiple threads. On entry to the MPS-using code, the thread can +be registered, but it may not be possible to ensure that the thread is +deregistered on exit, because control may be transferred by some +non-local mechanism such as an exception or ``longjmp()``. We don't +want to insist that the client program keep a table of threads it has +registered, because maintaining the table might require allocation, +which might provoke a collection. See request.dylan.160252_.) + +.. _request.dylan.160252: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/160252/ + +_`.req.thread.die`: It would be nice if the MPS coped with threads +that die while registered. (This makes it easier for a client program +to interface with foreign code that terminates threads without the +client program being given an opportunity to deregister them. See +request.dylan.160022_ and request.mps.160093_.) + +.. _request.dylan.160022: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/160022 +.. _request.mps.160093: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/mps/160093/ + + +Design +------ + +_`.sol.exclusive`: In order to meet `.req.exclusive`_, the arena +maintains a ring of threads (in ``arena->threadRing``) that have been +registered by the client program. When the MPS needs exclusive access +to memory, it suspends all the threads in the ring except for the +currently running thread. When the MPS no longer needs exclusive +access to memory, it resumes all threads in the ring. + +_`.sol.exclusive.assumption`: This relies on the assumption that any +thread that might refer to, read from, or write to memory in +automatically managed pool classes is registered with the MPS. This is +documented in the manual under ``mps_thread_reg()``. + +_`.sol.thread.term`: The thread manager cannot reliably detect that a +thread has terminated. The reason is that threading systems do not +guarantee behaviour in this case. For example, POSIX_ says, "A +conforming implementation is free to reuse a thread ID after its +lifetime has ended. If an application attempts to use a thread ID +whose lifetime has ended, the behavior is undefined." For this reason, +the documentation for ``mps_thread_dereg()`` specifies that it is an +error if a thread dies while registered. + +.. _POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_02 + +_`.sol.thread.term.attempt`: Nonetheless, the thread manager makes a +"best effort" to continue running after detecting a terminated thread, +by moving the thread to a ring of dead threads, and avoiding scanning +it. This might allow a malfunctioning client program to limp along. + + +Interface +--------- ``typedef struct mps_thr_s *Thread`` -Each thread is represented by an object of type ``Thread``. The -``Thread`` type is implemented as an ADT. A list of ``Thread`` objects -is maintained in the arena (as the ``Ring`` structure -``arena->threadRing``). The ``Thread`` object contains -operating-system-dependent information about the thread -- information -necessary for manipulating the thread and for scanning the thread -context. Thread "registration" adds or removes the current thread to -the ``Thread`` list in the arena. +_`.if.thread`: The type of threads. It is a pointer to an opaque +structure, which must be defined by the implementation. +``Bool ThreadCheck(Thread thread)`` -Detailed Design ---------------- +_`.if.check`: The check function for threads. See design.mps.check_. -Stack scan -.......... +.. _design.mps.check: check -This is a module providing a stack scanning function. The scanning is -architecture and operating system dependent. Typically the function -will push the subste of the save registers (those preserved across -function calls) which may contain pointers (that is, neither -floating-point or debugging registers) and call ``TraceScanStack()`` -on the appropriate range. +``Bool ThreadCheckSimple(Thread thread)`` +_`.if.check.simple`: A thread-safe check function for threads, for use +by ``mps_thread_dereg()``. It can't use ``AVER(TESTT(Thread, +thread))``, as recommended by design.mps.sig.check.arg.unlocked_, +since ``Thread`` is an opaque type. -Thread interface -................ +.. _design.mps.sig.check.arg.unlocked: sig#check.arg.unlocked + +``Arena ThreadArena(Thread thread)`` + +_`.if.arena`: Return the arena that the thread is registered with. +Must be thread-safe as it needs to be called by ``mps_thread_dereg()`` +before taking the arena lock. ``Res ThreadRegister(Thread *threadReturn, Arena arena)`` -Register the current thread with the arena, allocating a new -``*threadReturn`` to point to it. +_`.if.register`: Register the current thread with the arena, +allocating a new ``Thread`` object. If successful, update +``*threadReturn`` to point to the new thread and return ``ResOK``. +Otherwise, return a result code indicating the cause of the error. ``void ThreadDeregister(Thread thread, Arena arena)`` -Remove ``thread`` from the list of threads managed by the arena and -free it. +_`.if.deregister`: Remove ``thread`` from the list of threads managed +by the arena and free it. + +``void ThreadRingSuspend(Ring threadRing, Ring deadRing)`` + +_`.if.ring.suspend`: Suspend all the threads on ``threadRing``, except +for the current thread. If any threads are discovered to have +terminated, move them to ``deadRing``. -``void ThreadRingSuspend(Ring threadRing)`` +``void ThreadRingResume(Ring threadRing, Ring deadRing)`` -Suspend all the threads on the list ``threadRing``, except for the -current thread. +_`.if.ring.resume`: Resume all the threads on ``threadRing``. If any +threads are discovered to have terminated, move them to ``deadRing``. -``void ThreadRingResume(Ring threadRing)`` +``Thread ThreadRingThread(Ring threadRing)`` -Resume all the threads on the list ``threadRing``. +_`.if.ring.thread`: Return the thread that owns the given element of +the thread ring. ``Res ThreadScan(ScanState ss, Thread thread, void *stackBot)`` -Scan the stacks and root registers of ``thread``, treating each value -found as an ambiguous reference. The exact definition is operating -system and architecture dependent +_`.if.scan`: Scan the stacks and root registers of ``thread``, +treating each value found as an ambiguous reference. ``stackBot`` +points to the "bottom" of the thread's stack---this is the value that +was supplied by the client program when it called +``mps_root_create_reg()``. In the common case, where the stack grows +downwards, ``stackBot`` is actually the highest stack address. Return +``ResOK`` if successful, another result code otherwise. + + +Implementations +--------------- +Generic implementation +...................... -Single-threaded generic implementation -...................................... +_`.impl.an`: In ``than.c``. -In ``than.c``. +_`.impl.an.single`: Supports a single thread. (This cannot be enforced +because of `.req.register.multi`_.) -- Single threaded (calling ``ThreadRegister()`` on a second thread - causes an assertion failure). -- ``ThreadRingSuspend()`` and ``ThreadRingResume()`` do nothing - because there are no other threads. -- ``ThreadScan()`` calls ``StackScan()``. +_`.impl.an.register.multi`: There is no need for any special treatment +of multiple threads, because ``ThreadRingSuspend()`` and +``ThreadRingResume()`` do nothing. + +_`.impl.an.suspend`: ``ThreadRingSuspend()`` does nothing because +there are no other threads. + +_`.impl.an.resume`: ``ThreadRingResume()`` does nothing because no +threads are ever suspended. + +_`.impl.an.scan`: Just calls ``StackScan()`` since there are no +suspended threads. POSIX threads implementation ............................ -In ``thix.c``. See design.mps.pthreadext. +_`.impl.ix`: In ``thix.c`` and ``pthrdext.c``. See +design.mps.pthreadext_. +.. _design.mps.pthreadext: pthreadext -Win32 implementation -.................... +_`.impl.ix.multi`: Supports multiple threads. -In ``thw3.c``. +_`.impl.ix.register`: ``ThreadRegister()`` records the thread id +the current thread by calling |pthread_self|_. -- Supports multiple threads. -- Structured exception style faults are expected. -- ``ThreadRingSuspend()`` and ``ThreadRingResume()`` loop over threads - and call Win32 API functions ``SuspendThread()`` and - ``ResumeThread()``. -- ``ThreadRegister()`` records information for the current thread: +.. |pthread_self| replace:: ``pthread_self()`` +.. _pthread_self: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_self.html - - A Win32 "handle" with access flags ``THREAD_SUSPEND_RESUME`` and - ``THREAD_GET_CONTEXT``. This handle is needed as parameter to - ``SuspendThread()`` and ``ResumeThread()``. - - The Win32 ``GetCurrentThreadId()`` so that the current thread may - be identified. +_`.impl.ix.register.multi`: Multiply-registered threads are handled +specially by the POSIX thread extensions. See +design.mps.pthreadext.req.suspend.multiple_ and +design.mps.pthreadext.req.resume.multiple_. -- Stack scanning is Win32 specific: +.. _design.mps.pthreadext.req.suspend.multiple: pthreadext#req.suspend.multiple +.. _design.mps.pthreadext.req.resume.multiple: pthreadext#req.resume.multiple - - ``ThreadScan()`` calls ``StackScan()`` if the thread is current. - ``GetThreadContext()`` doesn't work on the current thread (the - context would not necessarily have the values which were in the - saved registers on entry to the MM). - - Otherwise it calls ``GetThreadContext()`` to get the root - registers and the stack pointer. - - The thread's registers are dumped into a ``CONTEXT`` structure and - fixed in memory. - - Scan the stack (getting the stack pointer from ``CONTEXT.Rsp``). +_`.impl.ix.suspend`: ``ThreadRingSuspend()`` calls +``PThreadextSuspend()``. See design.mps.pthreadext.if.suspend_. +.. _design.mps.pthreadext.if.suspend: pthreadext#if.suspend -Issues ------- +_`.impl.ix.resume`: ``ThreadRingResume()`` calls +``PThreadextResume()``. See design.mps.pthreadext.if.resume_. + +.. _design.mps.pthreadext.if.resume: pthreadext#if.resume + +_`.impl.ix.scan.current`: ``ThreadScan()`` calls ``StackScan()`` if +the thread is current. -#. Scanning after exceptions. ``StackScan()`` relies on the - non-preserved registers having been pushed on the stack. If we want - to scan after a fault we must make sure that these registers are - either already stored on the stack, or, have an extra function to - do this explicitly. +_`.impl.ix.scan.suspended`: ``PThreadextSuspend()`` records the +context of each suspended thread, and ``ThreadRingSuspend()`` stores +this in the ``Thread`` structure, so that is available by the time +``ThreadScan()`` is called. -#. Multiple registration. It is not clear whether a thread should be - allowed to be registered multiple times. We do not provide a - mechanism for knowing whether a thread is already registered with - an arena. + +Windows implementation +...................... + +_`.impl.w3`: In ``thw3.c``. + +_`.impl.w3.multi`: Supports multiple threads. + +_`.impl.w3.register`: ``ThreadRegister()`` records the following +information for the current thread: + + - A ``HANDLE`` to the process, with access flags + ``THREAD_SUSPEND_RESUME`` and ``THREAD_GET_CONTEXT``. This handle + is needed as parameter to |SuspendThread|_ and + |ResumeThread|_. + + - The result of |GetCurrentThreadId|_, so that the current thread + may be identified in the ring of threads. + +.. |SuspendThread| replace:: ``SuspendThread()`` +.. _SuspendThread: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686345.aspx +.. |ResumeThread| replace:: ``ResumeThread()`` +.. _ResumeThread: http://msdn.microsoft.com/en-us/library/windows/desktop/ms685086.aspx +.. |GetCurrentThreadId| replace:: ``GetCurrentThreadId()`` +.. _GetCurrentThreadId: http://msdn.microsoft.com/en-us/library/windows/desktop/ms683183.aspx + +_`.impl.w3.register.multi`: There is no need for any special treatment +of multiple threads, because Windows maintains a suspend count that is +incremented on |SuspendThread|_ and decremented on +|ResumeThread|_. + +_`.impl.w3.suspend`: ``ThreadRingSuspend()`` calls |SuspendThread|_. + +_`.impl.w3.resume`: ``ThreadRingResume()`` calls |ResumeThread|_. + +_`.impl.w3.scan.current`: ``ThreadScan()`` calls ``StackScan()`` if +the thread is current. This is because |GetThreadContext|_ doesn't +work on the current thread: the context would not necessarily have the +values which were in the saved registers on entry to the MPS. + +.. |GetThreadContext| replace:: ``GetThreadContext()`` +.. _GetThreadContext: http://msdn.microsoft.com/en-us/library/windows/desktop/ms679362.aspx + +_`.impl.w3.scan.suspended`: Otherwise, ``ThreadScan()`` calls +|GetThreadContext|_ to get the root registers and the stack +pointer. + + +OS X implementation +................... + +_`.impl.xc`: In ``thxc.c``. + +_`.impl.xc.multi`: Supports multiple threads. + +_`.impl.xc.register`: ``ThreadRegister()`` records the Mach port of +the current thread by calling |mach_thread_self|_. + +.. |mach_thread_self| replace:: ``mach_thread_self()`` +.. _mach_thread_self: http://www.gnu.org/software/hurd/gnumach-doc/Thread-Information.html + +_`.impl.xc.register.multi`: There is no need for any special treatment +of multiple threads, because Mach maintains a suspend count that is +incremented on |thread_suspend|_ and decremented on +|thread_resume|_. + +.. |thread_suspend| replace:: ``thread_suspend()`` +.. _thread_suspend: http://www.gnu.org/software/hurd/gnumach-doc/Thread-Execution.html +.. |thread_resume| replace:: ``thread_resume()`` +.. _thread_resume: http://www.gnu.org/software/hurd/gnumach-doc/Thread-Execution.html + +_`.impl.xc.suspend`: ``ThreadRingSuspend()`` calls +|thread_suspend|_. + +_`.impl.xc.resume`: ``ThreadRingResume()`` calls |thread_resume|_. + +_`.impl.xc.scan.current`: ``ThreadScan()`` calls ``StackScan()`` if +the thread is current. + +_`.impl.xc.scan.suspended`: Otherwise, ``ThreadScan()`` calls +|thread_get_state|_ to get the root registers and the stack pointer. + +.. |thread_get_state| replace:: ``thread_get_state()`` +.. _thread_get_state: http://www.gnu.org/software/hurd/gnumach-doc/Thread-Execution.html Document History @@ -163,6 +326,8 @@ Document History - 2013-05-26 GDR_ Converted to reStructuredText. +- 2014-10-22 GDR_ Complete design. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ diff --git a/design/thread-safety.txt b/design/thread-safety.txt index b091ac984a..826d008a59 100644 --- a/design/thread-safety.txt +++ b/design/thread-safety.txt @@ -26,10 +26,12 @@ threads calling into the MPS. The initial approach is very simple. Some of the code is known to operate with exclusive access to the data it manipulates, so this code is safe. For the rest of the code, shared data structures are locked by the use of a single binary lock -(design.mps.lock(0)) per arena. This lock is claimed on entry to the +(design.mps.lock_) per arena. This lock is claimed on entry to the MPS and released on exit from it. So there is at most a single thread (per arena) running "inside" the MPS at a time. +.. _design.mps.lock: lock + Requirements ------------ @@ -47,11 +49,15 @@ _`.arch.arena`: Arena Lock: no shared data between arenas. _`.arch.global.binary`: Global binary lock: protects mutable data shared between arenas -- that is, the arena ring, see -design.mps.arena.static.ring.lock. +design.mps.arena.static.ring.lock_. + +.. _design.mps.arena.static.ring.lock: arena#static.ring.lock _`.arch.global.recursive`: Global recursive lock: protects static data which must be initialized once -- for example, pool classes, see -design.mps.protocol.impl.init-lock. +design.mps.protocol.impl.init-lock_. + +.. _design.mps.protocol.impl.init-lock: protocol#impl.init-lock _`.arch.other`: Other: data not shared. @@ -118,12 +124,14 @@ running on genuine multiprocessor `.lock-cost.wait`_ is irrelevant. _`.anal.perf.alloc`: During typical use we expect that it is allocation that is the most frequent activity. Allocation buffers -(design.mps.buffer) are designed to allow allocation in concurrent +(design.mps.buffer_) are designed to allow allocation in concurrent threads without needing a lock. So the most significant time a thread spends in the MPS will be on a buffer-fill or during a collection. The next most significant use is likely to be buffer create and deletion, as a separate buffer will be required for each thread. +.. _design.mps.buffer: buffer + _`.anal.perf.lock`: So overall the performance cost of locking is, I estimate, most significantly the overhead of calling the locking functions. Hence it would be undesirable from a performance point of @@ -184,7 +192,7 @@ would avoid deadlock. Implementation -------------- -Use MPS locks (design.mps.lock) to do locking. +Use MPS locks (design.mps.lock_) to do locking. Locking Functions ................. diff --git a/design/trace.txt b/design/trace.txt index 4186a90215..236a0cae5d 100644 --- a/design/trace.txt +++ b/design/trace.txt @@ -25,19 +25,22 @@ Introduction Architecture ------------ -_`.instance.limit`: There will be a limit on the number of traces that -can be created at any one time. This effectively limits the number of -concurrent traces. This limitation is expressed in the symbol -``TRACE_MAX``. +_`.instance.limit`: There is a limit on the number of traces that can +be created at any one time. This limits the number of concurrent +traces. This limitation is expressed in the symbol ``TraceLIMIT``. .. note:: - ``TRACE_MAX`` is currently set to 1, see request.mps.160020_ - "Multiple traces would not work". David Jones, 1998-06-15. + ``TraceLIMIT`` is currently set to 1 as the MPS assumes in various + places that only a single trace is active at a time. See + request.mps.160020_ "Multiple traces would not work". David Jones, + 1998-06-15. .. _request.mps.160020: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/mps/160020 -_`.rate`: See `mail.nickb.1997-07-31.14-37 `_. +_`.rate`: See `mail.nickb.1997-07-31.14-37`_. + +.. _mail.nickb.1997-07-31.14-37: https://info.ravenbrook.com/project/mps/mail/1997/07/31/14-37/0.txt .. note:: @@ -46,26 +49,32 @@ _`.rate`: See `mail.nickb.1997-07-31.14-37 fixedSummary`` is accumulated (in the fixer) -for all the pointers whether or not they are genuine references. We -could accumulate fewer pointers here; if a pointer fails the -``TractOfAddr()`` test then we know it isn't a reference, so we needn't -accumulate it into the fixed summary. The design allows this, but it -breaks a useful post-condition on scanning (if the accumulation of -``ss->fixedSummary`` was moved the accuracy of ``ss->fixedSummary`` -would vary according to the "width" of the white summary). See -mail.pekka.1998-02-04.16-48 for improvement suggestions. +_`.exact.legal`: Exact references must either point outside the arena +(to non-managed address space) or to a tract allocated to a pool. +Exact references that are to addresses which the arena has reserved +but hasn't allocated memory to are illegal (such a reference cannot +possibly refer to a real object, and so cannot be exact). We check +that this is the case in ``TraceFix()``. + +.. note:: + + Depending on the future semantics of ``PoolDestroy()`` we might + need to adjust our strategy here. See `mail.dsm.1996-02-14.18-18`_ + for a strategy of coping gracefully with ``PoolDestroy()``. + + .. _mail.dsm.1996-02-14.18-18: https://info.ravenbrook.com/project/mps/mail/1996/02/14/18-18/0.txt + +_`.fix.fixed.all`: ``ss->fixedSummary`` is accumulated (in +``TraceFix()``) for all pointers, whether or not they are genuine +references. We could accumulate fewer pointers here; if a pointer +fails the ``TractOfAddr()`` test then we know it isn't a reference, so +we needn't accumulate it into the fixed summary. The design allows +this, but it breaks a useful post-condition on scanning (if the +accumulation of ``ss->fixedSummary`` was moved the accuracy of +``ss->fixedSummary`` would vary according to the "width" of the white +summary). See `mail.pekka.1998-02-04.16-48`_ for improvement suggestions. + +.. _mail.pekka.1998-02-04.16-48: https://info.ravenbrook.com/project/mps/mail/1998/02/04/16-48/0.txt Analysis @@ -81,6 +90,7 @@ memory for copying. .. _request.dylan.170560: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/170560 + Ideas ----- @@ -96,30 +106,56 @@ Implementation Speed ..... -_`.fix`: The fix path is critical to garbage collection speed. -Abstractly fix is applied to all the references in the non-white heap -and all the references in the copied heap. Remembered sets cut down -the number of segments we have to scan. The zone test cuts down the -number of references we call fix on. The speed of the remainder of the -fix path is still critical to system performance. Various -modifications to and aspects of the system are concerned with -maintaining the speed along this path. - -_`.fix.tractofaddr`: ``TractOfAddr()`` is called on every reference that -passes the zone test and is on the critical path, to determine whether -the segment is white. There is no need to examine the segment to -perform this test, since whiteness information is duplicated in -tracts, specifically to optimize this test. ``TractOfAddr()`` itself is -a simple class dispatch function (which dispatches to the arena -class's ``TractOfAddr()`` method). Inlining the dispatch and inlining -the functions called by ``VMTractOfAddr()`` makes a small but noticable -difference to the speed of the dylan compiler. +_`.fix`: The function implementing the fix operation should be called +``TraceFix()`` and this name is pervasive in the MPS and its documents +to describe this function. Nonethless, optimisation and strict +aliasing rules have meant that we need to use the external name for +it, ``_mps_fix2()``. + +_`.fix.speed`: The fix path is critical to garbage collection speed. +Abstractly, the fix operation is applied to all references in the +non-white heap and all references in the copied heap. Remembered sets +cut down the number of segments we have to scan. The zone test cuts +down the number of references we call fix on. The speed of the +remainder of the fix path is still critical to system performance. +Various modifications to and aspects of the system are concerned with +maintaining the speed along this path. See +`design.mps.critical_path`_. + +.. _design.mps.critical_path: critical_path + +_`.fix.tractofaddr`: A reference that passes the zone test is then +looked up to find the tract it points to, an operation equivalent to +calling ``TractOfAddr()``. + +_`.fix.tractofaddr.inline`: ``TraceFix()`` doesn't actually call +``TractOfAddr()``. Instead, it expands this operation inline (calling +``ChunkOfAddr()``, then ``INDEX_OF_ADDR()``, checking the appropriate +bit in the chunk's ``allocTable``, and finally looking up the tract in +the chunk's page table). The reason for inlining this code is that we +need to know whether the reference points to a chunk (and not just +whether it points to a tract) in order to check the `.exact.legal`_ +condition. + +_`.fix.whiteseg`: The reason for looking up the tract is to determine +whether the segment is white. There is no need to examine the segment +to perform this test, since whiteness information is duplicated in +tracts, specifically to optimize this test. + +.. note:: + + Nonetheless, it is likely to be more efficient to maintain a + separate lookup table from address to white segment, rather than + indirecting through the chunk and the tract. See job003796_. + +.. _job003796: http://www.ravenbrook.com/project/mps/issue/job003796/ _`.fix.noaver`: ``AVER()`` statements in the code add bulk to the code (reducing I-cache efficacy) and add branches to the path (polluting -the branch pedictors) resulting in a slow down. Removing all the -``AVER()`` statements from the fix path improves the overall speed of -the Dylan compiler by as much as 9%. +the branch pedictors) resulting in a slow down. Replacing the +``AVER()`` statements with ``AVER_CRITICAL()`` on the critical path +improves the overall speed of the Dylan compiler by as much as 9%. See +`design.mps.critical_path`_. _`.fix.nocopy`: ``AMCFix()`` used to copy objects by using the format's copy method. This involved a function call (through an indirection) @@ -131,19 +167,15 @@ inlined by the C compiler. This change results in a 4–5% speed-up in the Dylan compiler. _`.reclaim`: Because the reclaim phase of the trace (implemented by -``TraceReclaim()``) examines every segment it is fairly time intensive. -rit's profiles presented in request.dylan.170551_ show a gap between -the two varieties variety.hi and variety.wi. +``TraceReclaim()``) examines every segment it is fairly time +intensive. Richard Tucker's profiles presented in +request.dylan.170551_ show a gap between the two varieties variety.hi +and variety.wi. .. _request.dylan.170551: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/170551 -_`.reclaim.noaver`: Converting ``AVER()`` statements in the loops of -``TraceReclaim()``, ``PoolReclaim()``, ``AMCReclaim()`` (``LOReclaim()``? -``AWLReclaim()``?) will result in a noticeable speed improvement. - -.. note:: - - Insert actual speed improvement here, if any. +_`.reclaim.noaver`: Accordingly, reclaim methods use +``AVER_CRITICAL()`` instead of ``AVER()``. Life cycle of a trace object @@ -169,9 +201,9 @@ Some segments get condemned (made white). - Immediately calls ``traceFlip`` which flips the trace and moves it into state ``TraceFLIPPED``. -Whilst a trace is alive every so often its ``traceQuantum`` method -gets invoked (via ``TracePoll()``) in order to do a quantum of tracing -work. ``traceQuantum`` is responsible for ticking through the trace's +Whilst a trace is alive every so often its ``TraceAdvance()`` method +gets invoked (via ``TracePoll()``) in order to do a step of tracing +work. ``TraceAdvance()`` is responsible for ticking through the trace's top-level state machine. Most of the interesting work, the tracing, happens in the ``TraceFLIPPED`` state. @@ -185,7 +217,7 @@ in this state; all traces are immediately flipped to be in the Once the trace is in the ``TraceFINISHED`` state it performs no more work and it can be safely destroyed. Generally the callers of -``traceQuantum`` will destroy the trace. +``TraceAdvance()`` will destroy the trace. Making progress: scanning grey segments @@ -217,13 +249,12 @@ can reach by following references of the third, second, and first ranks. And so on. The description of the tracer working like this originated in [RHSK_2007-06-25]_. -A trace keep track of which band it is tracing. This is returned by +A trace keeps track of which band it is tracing. This is returned by the ``TraceBand()`` method. Keeping this band information helps it implement the semantics of finalization and weakness. The band used to not be explicitly stored, but this hindered the implementation of good -finalization semantics (essentially in some circumstances finalization -messages were delayed by at least one collection cycle, see -job001658_. +finalization semantics (in some circumstances finalization messages +were delayed by at least one collection cycle: see job001658_). .. _job001658: https://info.ravenbrook.com/project/mps/issue/job001658/ diff --git a/design/type.txt b/design/type.txt index 6a7efc9352..25943f41a4 100644 --- a/design/type.txt +++ b/design/type.txt @@ -6,7 +6,7 @@ General MPS types :Tag: design.mps.type :Author: Richard Brooksby :Date: 1996-10-23 -:Status: incomplete document +:Status: complete document :Revision: $Id$ :Copyright: See `Copyright and License`_. :Index terms: pair: general types; design @@ -32,6 +32,87 @@ say ``Byte`` in your code if it's what you mean. Concrete types -------------- +``typedef unsigned AccessSet`` + +_`.access-set`: An ``AccessSet`` is a bitset of ``Access`` modes, +which are ``AccessREAD`` and ``AccessWRITE``. ``AccessSetEMPTY`` is +the empty ``AccessSet``. + + +``typedef struct AddrStruct *Addr`` + +_`.addr`: ``Addr`` is the type used for "managed addresses", that is, +addresses of objects managed by the MPS. + +_`.addr.def`: ``Addr`` is defined as ``struct AddrStruct *``, but +``AddrStruct`` is never defined. This means that ``Addr`` is always an +incomplete type, which prevents accidental dereferencing, arithmetic, +or assignment to other pointer types. + +_`.addr.use`: ``Addr`` should be used whenever the code needs to deal +with addresses. It should not be used for the addresses of memory +manager data structures themselves, so that the memory manager remains +amenable to working in a separate address space. Be careful not to +confuse ``Addr`` with ``void *``. + +_`.addr.ops`: Limited arithmetic is allowed on addresses using +``AddrAdd()`` and ``AddrOffset()`` (impl.c.mpm). Addresses may also be +compared using the relational operators ``==``, ``!=``, ``<``, ``<=``, +``>``, and ``>=``. + +_`.addr.ops.mem`: We need efficient operators similar to ``memset()``, +``memcpy()``, and ``memcmp()`` on ``Addr``; these are called ``AddrSet()``, +``AddrCopy()``, and ``AddrComp()``. When ``Addr`` is compatible with +``void *``, these are implemented through the functions +``mps_lib_memset()``, ``mps_lib_memcpy()``, and ``mps_lib_memcmp()`` +functions in the plinth (impl.h.mpm). + +_`.addr.conv.c`: ``Addr`` is converted to ``mps_addr_t`` in the MPS C +Interface. ``mps_addr_t`` is defined to be the same as ``void *``, so +using the MPS C Interface confines the memory manager to the same +address space as the client data. + +_`.addr.readonly`: For read-only addresses, see `.readonlyaddr`_. + + +``typedef Word Align`` + +_`.align`: ``Align`` is an unsigned integral type which is used to +represent the alignment of managed addresses. All alignments are +positive powers of two. ``Align`` is large enough to hold the maximum +possible alignment. + +_`.align.use`: ``Align`` should be used whenever the code needs to +deal with the alignment of a managed address. + +_`.align.conv.c`: ``Align`` is converted to ``mps_align_t`` in the MPS +C Interface. + + +``typedef unsigned Attr`` + +_`.attr`: Pool attributes. A bitset of pool class attributes, which +are: + +=================== =================================================== +Attribute Description +=================== =================================================== +``AttrFMT`` Contains formatted objects. + Used to decide which pools to walk. +``AttrGC`` Is garbage collecting, that is, parts may be + reclaimed. Used to decide which segments are + condemned. +``AttrMOVINGGC`` Is moving, that is, objects may move in memory. + Used to update the set of zones that might have + moved and so implement location dependency. +=================== =================================================== + +There is an attribute field in the pool class (``PoolClassStruct``) +which declares the attributes of that class. See design.mps.class-interface.field.attr_. + +.. _design.mps.class-interface.field.attr: class-interface + + ``typedef int Bool`` _`.bool`: The ``Bool`` type is mostly defined so that the intention of @@ -70,96 +151,39 @@ compared ``w3i3mv\hi\amcss.exe`` running with and without the macro for ``BoolCheck`` on the PC Aaron. "With" ran in 97.7% of the time (averaged over 3 runs). +_`.bool.bitfield`: When a Boolean needs to be stored in a bitfield, +the type of the bitfield must be ``unsigned:1``, not ``Bool:1``. +(That's because the two values of the type ``Bool:1`` are ``0`` and +``-1``, which means that assigning ``TRUE`` would require a sign +conversion.) To make it clear why this is done, ``misc.h`` provides +the ``BOOLFIELD`` macro. -``typedef int Res`` - -_`.res`: ``Res`` is the type of result codes. A result code indicates -the success or failure of an operation, along with the reason for -failure. Like Unix error codes, the meaning of the code depends on the -call that returned it. These codes are just broad categories with -mnemonic names for various sorts of problems. - -=================== ======================================================= -Result code Description -=================== ======================================================= -``ResOK`` The operation succeeded. Return parameters may only be - updated if OK is returned, otherwise they must be left - untouched. -------------------- ------------------------------------------------------- -``ResFAIL`` Something went wrong which doesn't fall into any of the - other categories. The exact meaning depends on the - call. See documentation. -------------------- ------------------------------------------------------- -``ResRESOURCE`` A needed resource could not be obtained. Which resource - depends on the call. See also ``ResMEMORY``, which is a - special case of this. -------------------- ------------------------------------------------------- -``ResMEMORY`` Needed memory (committed memory, not address space) - could not be obtained. -------------------- ------------------------------------------------------- -``ResLIMIT`` An internal limitation was reached. For example, the - maximum number of somethings was reached. We should - avoid returning this by not including static - limitations in our code, as far as possible. (See - rule.impl.constrain and - rule.impl.limits.) -------------------- ------------------------------------------------------- -``ResUNIMPL`` The operation, or some vital part of it, is - unimplemented. This might be returned by functions - which are no longer supported, or by operations which - are included for future expansion, but not yet - supported. -------------------- ------------------------------------------------------- -``ResIO`` An I/O error occurred. Exactly what depends on the - function. -------------------- ------------------------------------------------------- -``ResCOMMIT_LIMIT`` The arena's commit limit would have been exceeded - as a result of allocation. -------------------- ------------------------------------------------------- -``ResPARAM`` An invalid parameter was passed. Normally reserved for - parameters passed from the client. -=================== ======================================================= - -_`.res.use`: ``Res`` should be returned from any function which might -fail. Any other results of the function should be passed back in -"return" parameters (pointers to locations to fill in with the -results). - -.. note:: This is documented elsewhere, I think -- richard - -_`.res.use.spec`: The most specific code should be returned. - - -``typedef void (*Fun)(void)`` +_`.bool.bitfield.assign`: To avoid warnings about loss of data from +GCC with the ``-Wconversion`` option, ``misc.h`` provides the +``BOOLOF`` macro for coercing a value to an unsigned single-bit field. -_`.fun`: ``Fun`` is the type of a pointer to a function about which -nothing more is known. +_`.bool.bitfield.check`: A Boolean bitfield cannot have an incorrect +value, and if you call ``BoolCheck()`` on such a bitfield then GCC 4.2 +issues the warning "comparison is always true due to limited range of +data type". When avoiding such a warning, reference this tag. -_`.fun.use`: ``Fun`` should be used where it's necessary to handle a -function in a polymorphic way without calling it. For example, if you -need to write a function ``g`` which passes another function ``f`` -through to a third function ``h``, where ``h`` knows the real type of -``f`` but ``g`` doesn't. +``typedef unsigned BufferMode`` -``typedef MPS_T_WORD Word`` +_`.buffermode`: ``BufferMode`` is a bitset of buffer attributes. See +design.mps.buffer_. It is a sum of the following: -_`.word`: ``Word`` is an unsigned integral type which matches the size -of the machine word, that is, the natural size of the machine -registers and addresses. +.. _design.mps.buffer: buffer -_`.word.use`: ``Word`` should be used where an unsigned integer is -required that might range as large as the machine word. - -_`.word.source`: ``Word`` is derived from the macro ``MPS_T_WORD`` -which is declared in impl.h.mpstd according to the target platform -(design.mps.config.pf.word). - -_`.word.conv.c`: ``Word`` is converted to ``mps_word_t`` in the MPS C -Interface. - -_`.word.ops`: ``WordIsAligned()``, ``WordAlignUp()``, -``WordAlignDown()`` and ``WordRoundUp()``. +======================== ============================================== +Mode Description +======================== ============================================== +``BufferModeATTACHED`` Buffer is attached to a region of memory. +``BufferModeFLIPPED`` Buffer has been flipped. +``BufferModeLOGGED`` Buffer remains permanently trapped, so that + all reserve and commit events can be logged. +``BufferModeTRANSITION`` Buffer is in the process of being detached. +======================== ============================================== ``typedef unsigned char Byte`` @@ -176,15 +200,34 @@ _`.byte.source`: ``Byte`` is a just pedagogic version of ``unsigned char``, since ``char`` is the unit of ``sizeof``. -``typedef Word Index`` +``typedef Word Clock`` -_`.index`: ``Index`` is an unsigned integral type which is large -enough to hold any array index. +_`.clock`: ``Clock`` is an unsigned integral type representing clock +time since some epoch. + +_`.clock.use`: A ``Clock`` value is returned by the plinth function +``mps_clock``. It is used to make collection scheduling decisions and +to calibrate the time stamps on events in the telemetry stream. + +_`.clock.units`: The plinth function ``mps_clocks_per_sec`` defines +the units of a ``Clock`` value. + +_`.clock.conv.c`: ``Clock`` is converted to ``mps_clock_t`` in the MPS +C Interface. -_`.index.use`: ``Index`` should be used where the maximum size of the -array cannot be statically determined. If the maximum size can be -determined then the smallest unsigned integer with a large enough -range may be used instead. + +``typedef unsigned Compare`` + +_`.compare`: ``Compare`` is the type of tri-state comparison +values. + +================== ==================================================== +Value Description +================== ==================================================== +``CompareLESS`` A value compares less than another value. +``CompareEQUAL`` Two values compare the same. +``CompareGREATER`` A value compares greater than another value. +================== ==================================================== ``typedef Word Count`` @@ -199,148 +242,116 @@ determined then the smallest unsigned integer with a large enough range may be used instead (although ``Count`` may be preferable for clarity). -.. note:: - - Should ``Count`` be used to count things that aren't represented - by objects (for example, a level)? I would say yes. gavinm - 1998-07-21 +_`.count.use.other`: ``Count`` may also be used to count things that +aren't represented by objects (for example, levels), but only where it +can be determined that the maximum count is less than the number of +objects. -.. note:: - - Only where it can be determined that the maximum count is less - than the number of objects. pekka 1998-07-21 - - -``typedef Word Accumulation`` - -_`.accumulation`: ``Accumulation`` is an arithmetic type which is -large enough to hold accumulated totals of objects of bytes (for -example, total number of objects allocated, total number of bytes -allocated). - -_`.accumulation.type`: Currently it is ``double``, but the reason for -the interface is so that we can more easily change it if we want to -(if we decide we need more accuracy for example). - -_`.accumulation.use`: Currently the only way to use an -``Accumulation`` is to reset it (by calling ``AccumulatorReset``) and -accumulate amounts into it (by calling ``Accumulate``). There is no -way to read it at the moment, but that's okay, because no one seems to -want to. - -_`.accumulation.future`: Probably we should have methods which return -the accumulation into an ``unsigned long``, and also a ``double``; -these functions should return ``Bool`` to indicate whether the -accumulation can fit in the requested type. Possibly we could have -functions which returned scaled accumulations. For example, -``AccumulatorScale(a, d)`` would divide the ``Accumulation a`` by -``double d`` and return the ``double`` result if it fitted into a -``double``. - - -``typedef struct AddrStruct *Addr`` - -_`.addr`: ``Addr`` is the type used for "managed addresses", that is, -addresses of objects managed by the MPS. - -_`.addr.def`: ``Addr`` is defined as ``struct AddrStruct *``, but -``AddrStruct`` is never defined. This means that ``Addr`` is always an -incomplete type, which prevents accidental dereferencing, arithmetic, -or assignment to other pointer types. - -_`.addr.use`: ``Addr`` should be used whenever the code needs to deal -with addresses. It should not be used for the addresses of memory -manager data structures themselves, so that the memory manager remains -amenable to working in a separate address space. Be careful not to -confuse ``Addr`` with ``void *``. -_`.addr.ops`: Limited arithmetic is allowed on addresses using -``AddrAdd()`` and ``AddrOffset()`` (impl.c.mpm). Addresses may also be -compared using the relational operators ``==``, ``!=``, ``<``, ``<=``, -``>``, and ``>=``. +``typedef Size Epoch`` -_`.addr.ops.mem`: We need efficient operators similar to ``memset()``, -``memcpy()``, and ``memcmp()`` on ``Addr``; these are called ``AddrSet()``, -``AddrCopy()``, and ``AddrComp()``. When ``Addr`` is compatible with -``void *``, these are implemented through the functions -``mps_lib_memset()``, ``mps_lib_memcpy()``, and ``mps_lib_memcmp()`` -functions in the plinth (impl.h.mpm). +_`.epoch`: An ``Epoch`` is a count of the number of flips that have +occurred, in which objects may have moved. It is used in the +implementation of location dependencies. -.. note:: +``Epoch`` is converted to ``mps_word_t`` in the MPS C Interface, as a +field of ``mps_ld_s``. - No other implementation exists at present. pekka 1998-09-07 -_`.addr.conv.c`: ``Addr`` is converted to ``mps_addr_t`` in the MPS C -Interface. ``mps_addr_t`` is defined to be the same as ``void *``, so -using the MPS C Interface confines the memory manager to the same -address space as the client data. +``typedef unsigned FindDelete`` +_`.finddelete`: ``FindDelete`` represents an instruction to one of the +*find* methods of a ``Land`` as to what it should do if it finds a +suitable block. See design.mps.land_. It takes one of the following +values: -``typedef Word Size`` +.. _design.mps.land: land -_`.size`: ``Size`` is an unsigned integral type large enough to -hold the size of any object which the MPS might manage. +==================== ================================================== +Value Description +==================== ================================================== +``FindDeleteNONE`` Don't delete after finding. +``FindDeleteLOW`` Delete from low end of block. +``FindDeleteHIGH`` Delete from high end of block. +``FindDeleteENTIRE`` Delete entire block. +==================== ================================================== -_`.size.byte`: ``Size`` should hold a size calculated in bytes. -.. warning:: This may not be true for all existing code. +``typedef unsigned FrameState`` -_`.size.use`: ``Size`` should be used whenever the code needs to deal -with the size of managed memory or client objects. It should not be -used for the sizes of the memory manager's own data structures, so -that the memory manager is amenable to working in a separate address -space. Be careful not to confuse it with ``size_t``. +_`.framestate`: ``FrameState`` represents the current state in a +buffer frame's lifecycle. See design.mps.alloc-frame_. It takes one of +the following values: -_`.size.ops`: ``SizeIsAligned()``, ``SizeAlignUp()``, -``SizeAlignDown()`` and ``SizeRoundUp()``. +.. _design.mps.alloc-frame: alloc-frame -_`.size.conv.c`: ``Size`` is converted to ``size_t`` in the MPS C -Interface. This constrains the memory manager to the same address -space as the client data. +========================== ============================================ +State Description +========================== ============================================ +``BufferFrameVALID`` Indicates that ``PushFrame()`` can be a + lightweight operation and need not be + synchronized. +``BufferFramePOP_PENDING`` Indicates that there has been a + ``PopFrame()`` operation that the pool + must respond to. +``BufferFrameDISABLED`` Indicates that the pool has disabled + support for lightweight operations for + this buffer. +========================== ============================================ -``typedef Word Align`` +``typedef void (*Fun)(void)`` -_`.align`: ``Align`` is an unsigned integral type which is used to -represent the alignment of managed addresses. All alignments are -positive powers of two. ``Align`` is large enough to hold the maximum -possible alignment. +_`.fun`: ``Fun`` is the type of a pointer to a function about which +nothing more is known. -_`.align.use`: ``Align`` should be used whenever the code needs to -deal with the alignment of a managed address. +_`.fun.use`: ``Fun`` should be used where it's necessary to handle a +function in a polymorphic way without calling it. For example, if you +need to write a function ``g`` which passes another function ``f`` +through to a third function ``h``, where ``h`` knows the real type of +``f`` but ``g`` doesn't. -_`.align.conv.c`: ``Align`` is converted to ``mps_align_t`` in the MPS -C Interface. +``typedef Word Index`` -``typedef unsigned Shift`` +_`.index`: ``Index`` is an unsigned integral type which is large +enough to hold any array index. -_`.shift`: ``Shift`` is an unsigned integral type which can hold the -amount by which a ``Word`` can be shifted. It is therefore large -enough to hold the word width (in bits). +_`.index.use`: ``Index`` should be used where the maximum size of the +array cannot be statically determined. If the maximum size can be +determined then the smallest unsigned integer with a large enough +range may be used instead. -_`.shift.use`: ``Shift`` should be used whenever a shift value (the -right-hand operand of the ``<<`` or ``>>`` operators) is intended, to -make the code clear. It should also be used for structure fields which -have this use. -_`.shift.conv.c`: ``Shift`` is converted to ``mps_shift_t`` in the MPS -C Interface. +``typedef int LocusPrefKind`` +_`.segprefkind`: The type ``LocusPrefKind`` expresses a preference for +addresses within an address space. It takes one of the following +values: -``typedef Addr Ref`` +==================== ==================================== +Kind Description +==================== ==================================== +``LocusPrefHIGH`` Prefer high addresses. +``LocusPrefLOW`` Prefer low addresses. +``LocusPrefZONESET`` Prefer addresses in specified zones. +==================== ==================================== -_`.ref`: ``Ref`` is a reference to a managed object (as opposed to any -old managed address). ``Ref`` should be used where a reference is -intended. -.. note:: This isn't too clear -- richard +``typedef unsigned MessageType`` +_`.messagetype`: ``MessageType`` is the type of a message. See +design.mps.message_. It takes one of the following values: -``typedef Word RefSet`` +.. _design.mps.message: message -_`.refset`: ``RefSet`` is a conservative approximation to a set of -references. See design.mps.refset. +=========================== =========================================== +Message type Description +=========================== =========================================== +``MessageTypeFINALIZATION`` A block is finalizable. +``MessageTypeGC`` A garbage collection finished. +``MessageTypeGCSTART`` A garbage collection started. +=========================== =========================================== ``typedef unsigned Rank`` @@ -348,22 +359,20 @@ references. See design.mps.refset. _`.rank`: ``Rank`` is an enumeration which represents the rank of a reference. The ranks are: -============= ===== ===================================================== +============= ===== ================================================== Rank Index Description -============= ===== ===================================================== +============= ===== ================================================== ``RankAMBIG`` 0 The reference is ambiguous. That is, it must be - assumed to be a reference, but not updated in case it - isn't. -------------- ----- ----------------------------------------------------- + assumed to be a reference, but not updated in + case it isn't. ``RankEXACT`` 1 The reference is exact, and refers to an object. -------------- ----- ----------------------------------------------------- -``RankFINAL`` 2 The reference is exact and final, so special action - is required if only final or weak references remain - to the object. -------------- ----- ----------------------------------------------------- -``RankWEAK`` 3 The reference is exact and weak, so should be deleted - if only weak references remain to the object. -============= ===== ===================================================== +``RankFINAL`` 2 The reference is exact and final, so special + action is required if only final or weak + references remain to the object. +``RankWEAK`` 3 The reference is exact and weak, so should + be deleted if only weak references remain to the + object. +============= ===== ================================================== ``Rank`` is stored with segments and roots, and passed around. @@ -374,7 +383,7 @@ references must be scanned in order to respect the properties of references of the ranks. Therefore they are declared explicitly with their integer values. -.. note:: Could ``Rank`` be a ``short``? +.. note:: Could ``Rank`` be an ``unsigned short`` or ``unsigned char``? .. note:: @@ -382,89 +391,119 @@ their integer values. document, then referenced from the implementation more thoroughly. -``typedef Size Epoch`` +``typedef unsigned RankSet`` -_`.epoch`: An ``Epoch`` is a count of the number of flips that have -occurred. It is used in the implementation of location dependencies. +_`.rankset`: ``RankSet`` is a set of ranks, represented as a bitset. -``Epoch`` is converted to ``mps_word_t`` in the MPS C Interface, as a -field of ``mps_ld_s``. +``typedef const struct AddrStruct *ReadonlyAddr`` -``typedef unsigned TraceId`` +_`.readonlyaddr`: ``ReadonlyAddr`` is the type used for managed +addresses that an interface promises it will only read through, never +write. Otherwise it is identical to ``Addr``. -_`.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. +``typedef Addr Ref`` -``typedef unsigned TraceSet`` +_`.ref`: ``Ref`` is a reference to a managed object (as opposed to any +old managed address). ``Ref`` should be used where a reference is +intended. -_`.traceset`: A ``TraceSet`` is a bitset of ``TraceId``, -represented in the obvious way:: +.. note:: This isn't too clear -- richard - member(ti, ts) ⇔ ((1<>`` operators) is intended, to +make the code clear. It should also be used for structure fields which +have this use. + +.. note:: Could ``Shift`` be an ``unsigned short`` or ``unsigned char``? + + +``typedef unsigned long Sig`` + +_`.sig`: ``Sig`` is the type of signatures, which are written into +structures when they are created, and invalidated when they are +destroyed. They provide a limited form of run-time type checking and +dynamic scope checking. See design.mps.sig_. + +.. _design.mps.sig: sig + + +``typedef Word Size`` + +_`.size`: ``Size`` is an unsigned integral type large enough to +hold the size of any object which the MPS might manage. + +_`.size.byte`: ``Size`` should hold a size calculated in bytes. + +.. warning:: + + This is violated by ``GenParams.capacity`` (which is measured in + kilobytes). + +_`.size.use`: ``Size`` should be used whenever the code needs to deal +with the size of managed memory or client objects. It should not be +used for the sizes of the memory manager's own data structures, so +that the memory manager is amenable to working in a separate address +space. Be careful not to confuse it with ``size_t``. + +_`.size.ops`: ``SizeIsAligned()``, ``SizeAlignUp()``, +``SizeAlignDown()`` and ``SizeRoundUp()``. + +_`.size.conv.c`: ``Size`` is converted to ``size_t`` in the MPS C +Interface. This constrains the memory manager to the same address +space as the client data. + + +``typedef unsigned TraceId`` + +_`.traceid`: A ``TraceId`` is an unsigned integer which is less than +``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`` + +_`.traceset`: A ``TraceSet`` is a bitset of ``TraceId``, +represented in the obvious way:: + + member(ti, ts) ⇔ ((1<serial, - " base $A init $A alloc $A limit $A\n", - (WriteFA)buffer->base, (WriteFA)buffer->ap.init, - (WriteFA)buffer->ap.alloc, (WriteFA)buffer->ap.limit, - " Pool $P\n", (WriteFP)buffer->pool, - " Seg $P\n", (WriteFP)buffer->seg, - " rank $U\n", (WriteFU)buffer->rank, - " alignment $W\n", (WriteFW)buffer->alignment, - " grey $B\n", (WriteFB)buffer->grey, - " shieldMode $B\n", (WriteFB)buffer->shieldMode, - " p $P i $U\n", (WriteFP)buffer->p, (WriteFU)buffer->i, - "} Buffer $P ($U)\n", (WriteFP)buffer, (WriteFU)buffer->serial, - NULL); - -_`.types`: For each format ``$X`` that ``WriteF()`` supports, there is a -type defined in impl.h.mpmtypes ``WriteFX()`` which is the promoted -version of that type. These are provided both to ensure promotion and -to avoid any confusion about what type should be used in a cast. It is -easy to check the casts against the formats to ensure that they -correspond. + res = WriteF(stream, depth, + "Hello: $A\n", (WriteFA)address, + "Spong: $U ($S)\n", (WriteFU)number, (WriteFS)string, + NULL); + if (res != ResOK) + return res; + +This makes ``Describe()`` methods much easier to write. For example, ``BufferDescribe()`` contains the following code:: + + res = WriteF(stream, depth, + "Buffer $P ($U) {\n", + (WriteFP)buffer, (WriteFU)buffer->serial, + " class $P (\"$S\")\n", + (WriteFP)buffer->class, (WriteFS)buffer->class->name, + " Arena $P\n", (WriteFP)buffer->arena, + " Pool $P\n", (WriteFP)buffer->pool, + " ", buffer->isMutator ? "Mutator" : "Internal", " Buffer\n", + " mode $C$C$C$C (TRANSITION, LOGGED, FLIPPED, ATTACHED)\n", + (WriteFC)((buffer->mode & BufferModeTRANSITION) ? 't' : '_'), + (WriteFC)((buffer->mode & BufferModeLOGGED) ? 'l' : '_'), + (WriteFC)((buffer->mode & BufferModeFLIPPED) ? 'f' : '_'), + (WriteFC)((buffer->mode & BufferModeATTACHED) ? 'a' : '_'), + " fillSize $UKb\n", (WriteFU)(buffer->fillSize / 1024), + " emptySize $UKb\n", (WriteFU)(buffer->emptySize / 1024), + " alignment $W\n", (WriteFW)buffer->alignment, + " base $A\n", (WriteFA)buffer->base, + " initAtFlip $A\n", (WriteFA)buffer->initAtFlip, + " init $A\n", (WriteFA)buffer->ap_s.init, + " alloc $A\n", (WriteFA)buffer->ap_s.alloc, + " limit $A\n", (WriteFA)buffer->ap_s.limit, + " poolLimit $A\n", (WriteFA)buffer->poolLimit, + " alignment $W\n", (WriteFW)buffer->alignment, + " rampCount $U\n", (WriteFU)buffer->rampCount, + NULL); + if (res != ResOK) + return res; + +_`.types`: For each format ``$X`` that ``WriteF()`` supports, there is +a type ``WriteFX`` defined in mpmtypes.h, which is the promoted +version of that type. These types are provided both to ensure +promotion and to avoid any confusion about what type should be used in +a cast. It is easy to check the casts against the formats to ensure +that they correspond. + +_`.types.cast`: Every argument to ``WriteF()`` must be cast, because +in variable-length argument lists the "default argument promotion" +rules apply and this could cause an argument to be read incorrectly on +some platforms: for example on a 64-bit platform the ``$W`` format, +which expects a 64-bit argument, is incompatible with a 32-bit +``unsigned`` argument, which will not be promoted to 64 bits by the +default argument promotion rules. (Note that most of these casts are +unnecessary, but requiring them all makes it easy to check that the +necessary ones are all there.) _`.types.future`: It is possibly that this type set or similar may be used in future in some generalisation of varargs in the MPS. _`.formats`: The formats supported are as follows. -======= =========== ================== ====================================== -Code Bame Type Example rendering -======= =========== ================== ====================================== +======= =========== ================== ======================================= +Code Name Type Example rendering +======= =========== ================== ======================================= ``$A`` address ``Addr`` ``000000019EF60010`` ``$P`` pointer ``void *`` ``000000019EF60100`` -``$F`` function ``void *(*)()`` ``0001D69E01000000`` (see `.f`_) +``$F`` function ``void *(*)()`` ``0001D69E01000000`` (see `.function`_) ``$S`` string ``char *`` ``hello`` ``$C`` character ``char`` ``x`` ``$W`` word ``ULongest`` ``0000000000109AE0`` ``$U`` decimal ``ULongest`` ``42`` ``$B`` binary ``ULongest`` ``00000000000000001011011110010001`` ``$$`` dollar -- ``$`` -======= =========== ================== ====================================== +======= =========== ================== ======================================= Note that ``WriteFC`` is an ``int``, because that is the default promotion of a ``char`` (see `.types`_). _`.snazzy`: We should resist the temptation to make ``WriteF()`` an incredible snazzy output engine. We only need it for ``Describe()`` -methods and assertion messages. At the moment it's a very simple bit -of code -- let's keep it that way. +methods. At the moment it's a simple bit of code -- let's keep it that +way. -_`.f`: The ``F`` code is used for function pointers. ISO C forbids casting -function pointers to other types, so the bytes of their representation are -written sequentially, and may have a different endianness to other pointers. -Could be smarter, or even look up function names, but see `.snazzy`_. +_`.function`: The ``F`` code is used for function pointers. ISO C +forbids casting function pointers to other types, so the bytes of +their representation are written sequentially, and may have a +different endianness to other pointers. Could be smarter, or even look +up function names, but see `.snazzy`_. Document History @@ -115,6 +149,8 @@ Document History - 2013-05-22 GDR_ Converted to reStructuredText. +- 2014-04-17 GDR_ ``WriteF()`` now takes a ``depth`` parameter. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ @@ -122,7 +158,7 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. +Copyright © 2013-2015 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/example/scheme/Makefile.in b/example/scheme/Makefile.in index aca3a682ba..77ed1f4f0e 100644 --- a/example/scheme/Makefile.in +++ b/example/scheme/Makefile.in @@ -2,8 +2,7 @@ # # $Id$ -# -fno-inline-functions is a temporary workaround for job003359. -CFLAGS = @CFLAGS@ @CPPFLAGS@ @LDFLAGS@ -std=c99 -fno-inline-functions +CFLAGS = @CFLAGS@ @CPPFLAGS@ @LDFLAGS@ -std=c99 MPS = ../../code diff --git a/example/scheme/scheme-advanced.c b/example/scheme/scheme-advanced.c index 22009cf232..830b5b9da3 100644 --- a/example/scheme/scheme-advanced.c +++ b/example/scheme/scheme-advanced.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -409,6 +410,7 @@ static void error(const char *format, ...) if (error_handler) { longjmp(*error_handler, 1); } else { + fflush(stdout); fprintf(stderr, "Fatal error during initialization: %s\n", error_message); abort(); @@ -422,7 +424,7 @@ static void error(const char *format, ...) * that type. * * These functions illustrate the two-phase MPS Allocation Point - * Protocol with `reserve` and `commmit`. This protocol allows very fast + * Protocol with `reserve` and `commit`. This protocol allows very fast * in-line allocation without locking, but there is a very tiny chance that * the object must be re-initialized. In nearly all cases, however, it's * just a pointer bump. See topic/allocation. @@ -798,13 +800,13 @@ static int string_equalp(obj_t obj1, obj_t obj2) 0 == strcmp(obj1->string.string, obj2->string.string)); } -static int buckets_find(obj_t tbl, buckets_t buckets, obj_t key, mps_ld_t ld, size_t *b) +static int buckets_find(obj_t tbl, buckets_t buckets, obj_t key, int add, size_t *b) { unsigned long i, h, probe; unsigned long l = UNTAG_COUNT(buckets->length) - 1; int result = 0; assert(TYPE(tbl) == TYPE_TABLE); - h = tbl->table.hash(key, ld); + h = tbl->table.hash(key, add ? &tbl->table.ld : NULL); probe = (h >> 8) | 1; h &= l; i = h; @@ -861,7 +863,7 @@ static int table_rehash(obj_t tbl, size_t new_length, obj_t key, size_t *key_buc if (old_key != obj_unused && old_key != obj_deleted) { int found; size_t b; - found = buckets_find(tbl, new_keys, old_key, &tbl->table.ld, &b); + found = buckets_find(tbl, new_keys, old_key, 1, &b); assert(found); /* new table shouldn't be full */ assert(new_keys->bucket[b] == obj_unused); /* shouldn't be in new table */ new_keys->bucket[b] = old_key; @@ -885,18 +887,28 @@ static int table_rehash(obj_t tbl, size_t new_length, obj_t key, size_t *key_buc * moved by the garbage collector: in this case we need to re-hash the * table. See topic/location. */ +static int table_find(obj_t tbl, obj_t key, int add, size_t *b) +{ + if (!buckets_find(tbl, tbl->table.keys, key, add, b)) { + return 0; + } else if ((tbl->table.keys->bucket[*b] == obj_unused + || tbl->table.keys->bucket[*b] == obj_deleted) + && mps_ld_isstale(&tbl->table.ld, arena, key)) { + return table_rehash(tbl, UNTAG_COUNT(tbl->table.keys->length), key, b); + } else { + return 1; + } +} + static obj_t table_ref(obj_t tbl, obj_t key) { size_t b; assert(TYPE(tbl) == TYPE_TABLE); - if (buckets_find(tbl, tbl->table.keys, key, NULL, &b)) { + if (table_find(tbl, key, 0, &b)) { obj_t k = tbl->table.keys->bucket[b]; if (k != obj_unused && k != obj_deleted) return tbl->table.values->bucket[b]; } - if (mps_ld_isstale(&tbl->table.ld, arena, key)) - if (table_rehash(tbl, UNTAG_COUNT(tbl->table.keys->length), key, &b)) - return tbl->table.values->bucket[b]; return NULL; } @@ -904,7 +916,7 @@ static int table_try_set(obj_t tbl, obj_t key, obj_t value) { size_t b; assert(TYPE(tbl) == TYPE_TABLE); - if (!buckets_find(tbl, tbl->table.keys, key, &tbl->table.ld, &b)) + if (!table_find(tbl, key, 1, &b)) return 0; if (tbl->table.keys->bucket[b] == obj_unused) { tbl->table.keys->bucket[b] = key; @@ -940,17 +952,9 @@ static void table_delete(obj_t tbl, obj_t key) { size_t b; assert(TYPE(tbl) == TYPE_TABLE); - if(!buckets_find(tbl, tbl->table.keys, key, NULL, &b) || - tbl->table.keys->bucket[b] == obj_unused || - tbl->table.keys->bucket[b] == obj_deleted) - { - if(!mps_ld_isstale(&tbl->table.ld, arena, key)) - return; - if(!table_rehash(tbl, UNTAG_COUNT(tbl->table.keys->length), key, &b)) - return; - } - if(tbl->table.keys->bucket[b] != obj_unused && - tbl->table.keys->bucket[b] != obj_deleted) + if(table_find(tbl, key, 0, &b) + && tbl->table.keys->bucket[b] != obj_unused + && tbl->table.keys->bucket[b] != obj_deleted) { tbl->table.keys->bucket[b] = obj_deleted; tbl->table.keys->deleted @@ -987,22 +991,12 @@ static char *symbol_name(obj_t symbol) } -/* port_close -- close and definalize a port %%MPS - * - * Ports objects are registered for finalization when they are created - * (see make_port). When closed, we definalize them. This is purely an - * optimization: it would be harmless to finalize them because setting - * 'stream' to NULL prevents the stream from being closed multiple - * times. See topic/finalization. - */ static void port_close(obj_t port) { assert(TYPE(port) == TYPE_PORT); if(port->port.stream != NULL) { - mps_addr_t port_ref = port; fclose(port->port.stream); port->port.stream = NULL; - mps_definalize(arena, &port_ref); } } @@ -2011,8 +2005,6 @@ static obj_t entry_do(obj_t env, obj_t op_env, obj_t operator, obj_t operands) return result; } } - error("%s: unimplemented", operator->operator.name); - return obj_error; } @@ -4005,9 +3997,9 @@ static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) break; default: assert(0); + fflush(stdout); fprintf(stderr, "Unexpected object on the heap\n"); abort(); - return MPS_RES_FAIL; } } } MPS_SCAN_END(ss); @@ -4076,9 +4068,9 @@ static mps_addr_t obj_skip(mps_addr_t base) break; default: assert(0); + fflush(stdout); fprintf(stderr, "Unexpected object on the heap\n"); abort(); - return NULL; } return base; } @@ -4322,7 +4314,7 @@ static int start(int argc, char *argv[]) mps_addr_t ref; mps_res_t res; mps_root_t globals_root; - int exit_code; + int exit_code = EXIT_SUCCESS; total = (size_t)0; error_handler = &jb; @@ -4370,20 +4362,24 @@ static int start(int argc, char *argv[]) make_operator(optab[i].name, optab[i].entry, obj_empty, obj_empty, env, op_env)); } else { + fflush(stdout); fprintf(stderr, "Fatal error during initialization: %s\n", error_message); abort(); } - if(argc >= 2) { + if (argc > 0) { /* Non-interactive file execution */ if(setjmp(*error_handler) != 0) { + fflush(stdout); fprintf(stderr, "%s\n", error_message); + fflush(stderr); exit_code = EXIT_FAILURE; } else { - load(env, op_env, make_string(strlen(argv[1]), argv[1])); - exit_code = EXIT_SUCCESS; + int a; + for (a = 0; a < argc; ++a) + load(env, op_env, make_string(strlen(argv[a]), argv[a])); } } else { /* Ask the MPS to tell us when it's garbage collecting so that we can @@ -4398,12 +4394,15 @@ static int start(int argc, char *argv[]) "If you recurse too much the interpreter may crash from using too much C stack."); for(;;) { if(setjmp(*error_handler) != 0) { + fflush(stdout); fprintf(stderr, "%s\n", error_message); + fflush(stderr); } mps_chat(); printf("%lu, %lu> ", (unsigned long)total, (unsigned long)mps_collections(arena)); + fflush(stdout); obj = read(input); if(obj == obj_eof) break; obj = eval(env, op_env, obj); @@ -4413,7 +4412,6 @@ static int start(int argc, char *argv[]) } } puts("Bye."); - exit_code = EXIT_SUCCESS; } /* See comment at the end of `main` about cleaning up. */ @@ -4446,6 +4444,7 @@ static mps_gen_param_s obj_gen_params[] = { int main(int argc, char *argv[]) { + size_t arenasize = 32ul * 1024 * 1024; mps_res_t res; mps_chain_t obj_chain; mps_fmt_t obj_fmt, buckets_fmt; @@ -4453,11 +4452,41 @@ int main(int argc, char *argv[]) mps_root_t reg_root; int exit_code; void *marker = ▮ + int ch; + while ((ch = getopt(argc, argv, "m:")) != -1) + switch (ch) { + case 'm': { + char *p; + arenasize = (unsigned)strtoul(optarg, &p, 10); + switch(toupper(*p)) { + case 'G': arenasize <<= 30; break; + case 'M': arenasize <<= 20; break; + case 'K': arenasize <<= 10; break; + case '\0': break; + default: + fprintf(stderr, "Bad arena size %s\n", optarg); + return EXIT_FAILURE; + } + } + break; + default: + fprintf(stderr, + "Usage: %s [option...] [file...]\n" + "Options:\n" + " -m n, --arena-size=n[KMG]?\n" + " Initial size of arena (default %lu).\n", + argv[0], + (unsigned long)arenasize); + return EXIT_FAILURE; + } + argc -= optind; + argv += optind; + /* Create an MPS arena. There is usually only one of these in a process. It holds all the MPS "global" state and is where everything happens. */ MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 32 * 1024 * 1024); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arenasize); res = mps_arena_create_k(&arena, mps_arena_class_vm(), args); } MPS_ARGS_END(args); if (res != MPS_RES_OK) error("Couldn't create arena"); diff --git a/example/scheme/scheme-boehm.c b/example/scheme/scheme-boehm.c index 839f1a56fd..43e8967fb2 100644 --- a/example/scheme/scheme-boehm.c +++ b/example/scheme/scheme-boehm.c @@ -1,6 +1,6 @@ /* scheme.c -- SCHEME INTERPRETER EXAMPLE FOR THE MEMORY POOL SYSTEM * - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * TO DO * - unbounded integers, other number types. @@ -281,6 +281,7 @@ static void error(char *format, ...) if (error_handler) { longjmp(*error_handler, 1); } else { + fflush(stdout); fprintf(stderr, "Fatal error during initialization: %s\n", error_message); abort(); @@ -1728,8 +1729,6 @@ static obj_t entry_do(obj_t env, obj_t op_env, obj_t operator, obj_t operands) return result; } } - error("%s: unimplemented", operator->operator.name); - return obj_error; } @@ -3601,6 +3600,7 @@ int main(int argc, char *argv[]) make_operator(optab[i].name, optab[i].entry, obj_empty, obj_empty, env, op_env)); } else { + fflush(stdout); fprintf(stderr, "Fatal error during initialization: %s\n", error_message); @@ -3610,18 +3610,24 @@ int main(int argc, char *argv[]) if(argc >= 2) { /* Non-interactive file execution */ if(setjmp(*error_handler) != 0) { + fflush(stdout); fprintf(stderr, "%s\n", error_message); return EXIT_FAILURE; } - load(env, op_env, argv[1]); + for (i = 1; i < argc; ++i) + load(env, op_env, argv[i]); return EXIT_SUCCESS; } else { /* Interactive read-eval-print loop */ puts("Scheme Test Harness"); for(;;) { - if(setjmp(*error_handler) != 0) + if(setjmp(*error_handler) != 0) { + fflush(stdout); fprintf(stderr, "%s\n", error_message); + fflush(stderr); + } printf("%lu> ", (unsigned long)total); + fflush(stdout); obj = read(input); if(obj == obj_eof) break; obj = eval(env, op_env, obj); @@ -3638,7 +3644,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/example/scheme/scheme-malloc.c b/example/scheme/scheme-malloc.c index 799e7db66c..af619f9c15 100644 --- a/example/scheme/scheme-malloc.c +++ b/example/scheme/scheme-malloc.c @@ -1,6 +1,6 @@ /* scheme.c -- SCHEME INTERPRETER EXAMPLE FOR THE MEMORY POOL SYSTEM * - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * TO DO * - unbounded integers, other number types. @@ -279,6 +279,7 @@ static void error(char *format, ...) if (error_handler) { longjmp(*error_handler, 1); } else { + fflush(stdout); fprintf(stderr, "Fatal error during initialization: %s\n", error_message); abort(); @@ -1728,8 +1729,6 @@ static obj_t entry_do(obj_t env, obj_t op_env, obj_t operator, obj_t operands) return result; } } - error("%s: unimplemented", operator->operator.name); - return obj_error; } @@ -3598,6 +3597,7 @@ int main(int argc, char *argv[]) make_operator(optab[i].name, optab[i].entry, obj_empty, obj_empty, env, op_env)); } else { + fflush(stdout); fprintf(stderr, "Fatal error during initialization: %s\n", error_message); @@ -3607,18 +3607,24 @@ int main(int argc, char *argv[]) if(argc >= 2) { /* Non-interactive file execution */ if(setjmp(*error_handler) != 0) { + fflush(stdout); fprintf(stderr, "%s\n", error_message); return EXIT_FAILURE; } - load(env, op_env, argv[1]); + for (i = 1; i < argc; ++i) + load(env, op_env, argv[i]); return EXIT_SUCCESS; } else { /* Interactive read-eval-print loop */ puts("Scheme Test Harness"); for(;;) { - if(setjmp(*error_handler) != 0) + if(setjmp(*error_handler) != 0) { + fflush(stdout); fprintf(stderr, "%s\n", error_message); + fflush(stderr); + } printf("%lu> ", (unsigned long)total); + fflush(stdout); obj = read(input); if(obj == obj_eof) break; obj = eval(env, op_env, obj); @@ -3635,7 +3641,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/example/scheme/scheme.c b/example/scheme/scheme.c index 4b2b36797c..fb43e62e34 100644 --- a/example/scheme/scheme.c +++ b/example/scheme/scheme.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -401,6 +402,7 @@ static void error(const char *format, ...) if (error_handler) { longjmp(*error_handler, 1); } else { + fflush(stdout); fprintf(stderr, "Fatal error during initialization: %s\n", error_message); abort(); @@ -414,7 +416,7 @@ static void error(const char *format, ...) * that type. * * These functions illustrate the two-phase MPS Allocation Point - * Protocol with `reserve` and `commmit`. This protocol allows very fast + * Protocol with `reserve` and `commit`. This protocol allows very fast * in-line allocation without locking, but there is a very tiny chance that * the object must be re-initialized. In nearly all cases, however, it's * just a pointer bump. See topic/allocation. @@ -872,13 +874,13 @@ static int string_equalp(obj_t obj1, obj_t obj2) 0 == strcmp(obj1->string.string, obj2->string.string)); } -static struct bucket_s *buckets_find(obj_t tbl, obj_t buckets, obj_t key, mps_ld_t ld) +static struct bucket_s *buckets_find(obj_t tbl, obj_t buckets, obj_t key, int add) { unsigned long i, h, probe; struct bucket_s *result = NULL; assert(TYPE(tbl) == TYPE_TABLE); assert(TYPE(buckets) == TYPE_BUCKETS); - h = tbl->table.hash(key, ld); + h = tbl->table.hash(key, add ? &tbl->table.ld : NULL); probe = (h >> 8) | 1; h &= (buckets->buckets.length-1); i = h; @@ -925,7 +927,7 @@ static struct bucket_s *table_rehash(obj_t tbl, size_t new_length, obj_t key) for (i = 0; i < tbl->table.buckets->buckets.length; ++i) { struct bucket_s *old_b = &tbl->table.buckets->buckets.bucket[i]; if (old_b->key != NULL && old_b->key != obj_deleted) { - struct bucket_s *b = buckets_find(tbl, new_buckets, old_b->key, &tbl->table.ld); + struct bucket_s *b = buckets_find(tbl, new_buckets, old_b->key, 1); assert(b != NULL); /* new table shouldn't be full */ assert(b->key == NULL); /* shouldn't be in new table */ *b = *old_b; @@ -944,17 +946,26 @@ static struct bucket_s *table_rehash(obj_t tbl, size_t new_length, obj_t key) * moved by the garbage collector: in this case we need to re-hash the * table. See topic/location. */ +static struct bucket_s *table_find(obj_t tbl, obj_t key, int add) +{ + struct bucket_s *b; + assert(TYPE(tbl) == TYPE_TABLE); + b = buckets_find(tbl, tbl->table.buckets, key, add); + if ((b == NULL || b->key == NULL || b->key == obj_deleted) + && mps_ld_isstale(&tbl->table.ld, arena, key)) + { + b = table_rehash(tbl, tbl->table.buckets->buckets.length, key); + } + return b; +} + static obj_t table_ref(obj_t tbl, obj_t key) { struct bucket_s *b; assert(TYPE(tbl) == TYPE_TABLE); - b = buckets_find(tbl, tbl->table.buckets, key, NULL); + b = table_find(tbl, key, 0); if (b && b->key != NULL && b->key != obj_deleted) return b->value; - if (mps_ld_isstale(&tbl->table.ld, arena, key)) { - b = table_rehash(tbl, tbl->table.buckets->buckets.length, key); - if (b) return b->value; - } return NULL; } @@ -962,7 +973,7 @@ static int table_try_set(obj_t tbl, obj_t key, obj_t value) { struct bucket_s *b; assert(TYPE(tbl) == TYPE_TABLE); - b = buckets_find(tbl, tbl->table.buckets, key, &tbl->table.ld); + b = table_find(tbl, key, 1); if (b == NULL) return 0; if (b->key == NULL) { @@ -998,33 +1009,20 @@ static void table_delete(obj_t tbl, obj_t key) { struct bucket_s *b; assert(TYPE(tbl) == TYPE_TABLE); - b = buckets_find(tbl, tbl->table.buckets, key, NULL); - if ((b == NULL || b->key == NULL) && mps_ld_isstale(&tbl->table.ld, arena, key)) { - b = table_rehash(tbl, tbl->table.buckets->buckets.length, key); - } - if (b != NULL && b->key != NULL) { + b = table_find(tbl, key, 0); + if (b && b->key != NULL && b->key != obj_deleted) { b->key = obj_deleted; ++ tbl->table.buckets->buckets.deleted; } } -/* port_close -- close and definalize a port %%MPS - * - * Ports objects are registered for finalization when they are created - * (see make_port). When closed, we definalize them. This is purely an - * optimization: it would be harmless to finalize them because setting - * 'stream' to NULL prevents the stream from being closed multiple - * times. See topic/finalization. - */ static void port_close(obj_t port) { assert(TYPE(port) == TYPE_PORT); if(port->port.stream != NULL) { - mps_addr_t port_ref = port; fclose(port->port.stream); port->port.stream = NULL; - mps_definalize(arena, &port_ref); } } @@ -2037,8 +2035,6 @@ static obj_t entry_do(obj_t env, obj_t op_env, obj_t operator, obj_t operands) return result; } } - error("%s: unimplemented", operator->operator.name); - return obj_error; } @@ -3992,9 +3988,9 @@ static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) break; default: assert(0); + fflush(stdout); fprintf(stderr, "Unexpected object on the heap\n"); abort(); - return MPS_RES_FAIL; } } } MPS_SCAN_END(ss); @@ -4069,9 +4065,9 @@ static mps_addr_t obj_skip(mps_addr_t base) break; default: assert(0); + fflush(stdout); fprintf(stderr, "Unexpected object on the heap\n"); abort(); - return NULL; } return base; } @@ -4250,7 +4246,7 @@ static int start(int argc, char *argv[]) mps_addr_t ref; mps_res_t res; mps_root_t globals_root; - int exit_code; + int exit_code = EXIT_SUCCESS; total = (size_t)0; @@ -4300,20 +4296,24 @@ static int start(int argc, char *argv[]) make_operator(optab[i].name, optab[i].entry, obj_empty, obj_empty, env, op_env)); } else { + fflush(stdout); fprintf(stderr, "Fatal error during initialization: %s\n", error_message); abort(); } - if(argc >= 2) { + if (argc > 0) { /* Non-interactive file execution */ if(setjmp(*error_handler) != 0) { + fflush(stdout); fprintf(stderr, "%s\n", error_message); + fflush(stderr); exit_code = EXIT_FAILURE; } else { - load(env, op_env, make_string(strlen(argv[1]), argv[1])); - exit_code = EXIT_SUCCESS; + int a; + for (a = 0; a < argc; ++a) + load(env, op_env, make_string(strlen(argv[a]), argv[a])); } } else { /* Ask the MPS to tell us when it's garbage collecting so that we can @@ -4328,12 +4328,15 @@ static int start(int argc, char *argv[]) "If you recurse too much the interpreter may crash from using too much C stack."); for(;;) { if(setjmp(*error_handler) != 0) { + fflush(stdout); fprintf(stderr, "%s\n", error_message); + fflush(stderr); } mps_chat(); printf("%lu, %lu> ", (unsigned long)total, (unsigned long)mps_collections(arena)); + fflush(stdout); obj = read(input); if(obj == obj_eof) break; obj = eval(env, op_env, obj); @@ -4343,7 +4346,6 @@ static int start(int argc, char *argv[]) } } puts("Bye."); - exit_code = EXIT_SUCCESS; } /* See comment at the end of `main` about cleaning up. */ @@ -4380,6 +4382,7 @@ static mps_gen_param_s obj_gen_params[] = { int main(int argc, char *argv[]) { + size_t arenasize = 32ul * 1024 * 1024; mps_res_t res; mps_chain_t obj_chain; mps_fmt_t obj_fmt; @@ -4387,11 +4390,41 @@ int main(int argc, char *argv[]) mps_root_t reg_root; int exit_code; void *marker = ▮ + int ch; + while ((ch = getopt(argc, argv, "m:")) != -1) + switch (ch) { + case 'm': { + char *p; + arenasize = (unsigned)strtoul(optarg, &p, 10); + switch(toupper(*p)) { + case 'G': arenasize <<= 30; break; + case 'M': arenasize <<= 20; break; + case 'K': arenasize <<= 10; break; + case '\0': break; + default: + fprintf(stderr, "Bad arena size %s\n", optarg); + return EXIT_FAILURE; + } + } + break; + default: + fprintf(stderr, + "Usage: %s [option...] [file...]\n" + "Options:\n" + " -m n, --arena-size=n[KMG]?\n" + " Initial size of arena (default %lu).\n", + argv[0], + (unsigned long)arenasize); + return EXIT_FAILURE; + } + argc -= optind; + argv += optind; + /* Create an MPS arena. There is usually only one of these in a process. It holds all the MPS "global" state and is where everything happens. */ MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 32 * 1024 * 1024); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arenasize); res = mps_arena_create_k(&arena, mps_arena_class_vm(), args); } MPS_ARGS_END(args); if (res != MPS_RES_OK) error("Couldn't create arena"); diff --git a/manual/.p4ignore b/manual/.p4ignore index 3902ee8b10..86aa08494c 100644 --- a/manual/.p4ignore +++ b/manual/.p4ignore @@ -1,2 +1,5 @@ doctrees converted +epub +html +mmref \ No newline at end of file diff --git a/manual/build.txt b/manual/build.txt index c10763ec76..7b8e17c51b 100644 --- a/manual/build.txt +++ b/manual/build.txt @@ -96,8 +96,7 @@ Building the MPS for development If you're making modifications to the MPS itself, want to build MPS libraries for linking, or want to build MPS tests and tools, you should -use the MPS build. This uses makefiles or Xcode projects. [Coming -soon, Microsoft Visual Studio solutions.] +use the MPS build. This uses makefiles or Xcode projects. Prerequisites @@ -151,9 +150,9 @@ Platform OS Architecture Compiler Makefile Historically, the MPS worked on a much wider variety of platforms, and still could: IRIX, OSF/1 (Tru64), Solaris, SunOS, Classic Mac OS; MIPS, PowerPC, ALPHA, SPARC v8, SPARC v9; Metrowerks Codewarrior, -SunPro C, Digital C, EGCS. If you are interested in support on any of -these platforms or any new platforms, please contact Ravenbrook at -`mps-questions@ravenbrook.com `_. +SunPro C, Digital C, EGCS, Pelles C. If you are interested in support +on any of these platforms or any new platforms, please contact +Ravenbrook at `mps-questions@ravenbrook.com `_. Running make @@ -190,9 +189,10 @@ You will need to switch your build environment between 32-bit and 64-bit using Microsoft's ``setenv`` command, for example, ``setenv /x86`` or ``setenv /x64``. -To build just one target, run the command:: +To build just one target, run one of these commands:: - nmake /f w3i3mv.nmk + nmake /f w3i3mv.nmk (32-bit) + nmake /f w3i6mv.nmk (64-bit) On Mac OS X, you can build from the command line with:: @@ -264,6 +264,7 @@ this program, you need to install the SQLite3 development resources. it contains files named ``sqlite3.c`` and ``sqlite3.h``. Copy these two files into the ``code`` directory in the MPS Kit. Then in the "Visual Studio Command Prompt", visit the ``code`` directory and run - the command:: + one of these commands:: - nmake /f w3i3mv.nmk mpseventsql.exe + nmake /f w3i3mv.nmk mpseventsql.exe (32-bit) + nmake /f w3i6mv.nmk mpseventsql.exe (64-bit) diff --git a/manual/source/_templates/links.html b/manual/source/_templates/links.html index b62c13d781..b7263205f3 100644 --- a/manual/source/_templates/links.html +++ b/manual/source/_templates/links.html @@ -1,13 +1,11 @@

Downloads

-MPS Kit release {{ release }}
All MPS Kit releases

Issues

-Known issues
-Issues fixed in release {{ release }} +All open issues

diff --git a/manual/source/bib.rst b/manual/source/bib.rst new file mode 100644 index 0000000000..cf92855928 --- /dev/null +++ b/manual/source/bib.rst @@ -0,0 +1,3906 @@ +.. _bibliography: + +Bibliography +************ + +* .. _AD97: + + Ole Agesen, David L. Detlefs. 1997. "`Finding References in Java Stacks `_". Sun Labs. OOPSLA97 Workshop on Garbage Collection and Memory Management. + + .. admonition:: Abstract + + Exact garbage collection for the strongly-typed Java language may + seem straightforward. Unfortunately, a single pair of bytecodes in + the Java Virtual Machine instruction set presents an obstacle that + has thus far not been discussed in the literature. We explain the + problem, outline the space of possible solutions, and present a + solution utilizing bytecode-preprocessing to enable exact garbage + collection while maintaining compatibility with existing compiled + Java class files. + +* .. _ADM98: + + Ole Agesen, David L. Detlefs, J. Eliot B. Moss. 1998. "`Garbage Collection and Local Variable Type-precision and Liveness in Java Virtual Machines `_". ACM. Proceedings of the ACM SIGPLAN '98 conference on Programming language design and implementation, pp. 269--279. + + .. admonition:: Abstract + + Full precision in garbage collection implies retaining only those + heap allocated objects that will actually be used in the future. + Since full precision is not computable in general, garbage + collectors use safe (i.e., conservative) approximations such as + reachability from a set of root references. Ambiguous roots + collectors (commonly called "conservative") can be overly + conservative because they overestimate the root set, and thereby + retain unexpectedly large amounts of garbage. We consider two more + precise collection schemes for Java virtual machines (JVMs). One + uses a type analysis to obtain a type-precise root set (only those + variables that contain references); the other adds a live variable + analysis to reduce the root set to only the live reference + variables. Even with the Java programming language's strong + typing, it turns out that the JVM specification has a feature that + makes type-precise root sets difficult to compute. We explain the + problem and ways in which it can be solved. + + Our experimental results include measurements of the costs of the + type and liveness analyses at load time, of the incremental + benefits at run time of the liveness analysis over the + type-analysis alone, and of various map sixes and counts. We find + that the liveness analysis often produces little or no improvement + in heap size, sometimes modest improvements, and occasionally the + improvement is dramatic. While further study is in order, we + conclude that the main benefit of the liveness analysis is + preventing bad surprises. + +* .. _AEL88: + + Andrew Appel, John R. Ellis, Kai Li. 1988. "`Real-time Concurrent Collection on Stock Multiprocessors `_". ACM, SIGPLAN. ACM PLDI 88, SIGPLAN Notices 23, 7 (July 88), pp. 11--20. + + .. admonition:: Abstract + + We've designed and implemented a copying garbage-collection + algorithm that is efficient, real-time, concurrent, runs on + commercial uniprocessors and shared-memory multiprocessors, and + requires no change to compilers. The algorithm uses standard + virtual-memory hardware to detect references to "from space" + objects and to synchronize the collector and mutator threads. + We've implemented and measured a prototype running on SRC's + 5-processor Firefly. It will be straightforward to merge our + techniques with generational collection. An incremental, + non-concurrent version could be implemented easily on many + versions of Unix. + +* .. _APPLE94: + + Apple Computer, Inc. 1994. *Inside Macintosh: Memory*. Addison-Wesley. ISBN 0-201-63240-3. + + .. admonition:: Abstract + + Inside Macintosh: Memory describes the parts of the Macintosh® + Operating System that allow you to directly allocate, release, or + otherwise manipulate memory. Everyone who programs Macintosh + computers should read this book. + + Inside Macintosh: Memory shows in detail how your application can + manage the memory partition it is allocated and perform other + memory-related operations. It also provides a complete technical + reference for the Memory Manager, the Virtual Memory Manager, and + other memory-related utilities provided by the system software. + +* .. _ATTARDI94: + + Giuseppe Attardi & Tito Flagella. 1994. "`A Customisable Memory Management Framework `_". TR-94-010. + + .. admonition:: Abstract + + Memory management is a critical issue for many large + object-oriented applications, but in C++ only explicit memory + reclamation through the delete operator is generally available. We + analyse different possibilities for memory management in C++ and + present a dynamic memory management framework which can be + customised to the need of specific applications. The framework + allows full integration and coexistence of different memory + management techniques. The Customisable Memory Management (CMM) is + based on a primary collector which exploits an evolution of + Bartlett's mostly copying garbage collector. Specialised + collectors can be built for separate memory heaps. A Heap class + encapsulates the allocation strategy for each heap. We show how to + emulate different garbage collection styles or user-specific + memory management techniques. The CMM is implemented in C++ + without any special support in the language or the compiler. The + techniques used in the CMM are general enough to be applicable + also to other languages. + +* .. _AFI98: + + Giuseppe Attardi, Tito Flagella, Pietro Iglio. 1998. "`A customisable memory management framework for C++ `_". Software -- Practice and Experience. 28(11), 1143--1183. + + .. admonition:: Abstract + + Automatic garbage collection relieves programmers from the burden + of managing memory themselves and several techniques have been + developed that make garbage collection feasible in many + situations, including real time applications or within traditional + programming languages. However optimal performance cannot always + be achieved by a uniform general purpose solution. Sometimes an + algorithm exhibits a predictable pattern of memory usage that + could be better handled specifically, delaying as much as possible + the intervention of the general purpose collector. This leads to + the requirement for algorithm specific customisation of the + collector strategies. We present a dynamic memory management + framework which can be customised to the needs of an algorithm, + while preserving the convenience of automatic collection in the + normal case. The Customisable Memory Manager (CMM) organises + memory in multiple heaps. Each heap is an instance of a C++ class + which abstracts and encapsulates a particular storage discipline. + The default heap for collectable objects uses the technique of + mostly copying garbage collection, providing good performance and + memory compaction. Customisation of the collector is achieved + exploiting object orientation by defining specialised versions of + the collector methods for each heap class. The object oriented + interface to the collector enables coexistence and coordination + among the various collectors as well as integration with + traditional code unaware of garbage collection. The CMM is + implemented in C++ without any special support in the language or + the compiler. The techniques used in the CMM are general enough to + be applicable also to other languages. The performance of the CMM + is analysed and compared to other conservative collectors for + C/C++ in various configurations. + +* .. _AKPY98: + + Alain Azagury, Elliot K. Kolodner, Erez Petrank, Zvi Yehudai. 1998. "`Combining Card Marking with Remembered Sets: How to Save Scanning Time `_". ACM. ISMM'98 pp. 10--19. + + .. admonition:: Abstract + + We consider the combination of card marking with remembered sets + for generational garbage collection as suggested by Hosking and + Moss. When more than two generations are used, a naive + implementation may cause excessive and wasteful scanning of the + cards and thus increase the collection time. We offer a simple + data structure and a corresponding algorithm to keep track of + which cards need be scanned for which generation. We then extend + these ideas for the Train Algorithm of Hudson and Moss. Here, the + solution is more involved, and allows tracking of which card + should be scanned for which car-collection in the train. + +* .. _BAKER77: + + Henry G. Baker, Carl Hewitt. 1977. "`The Incremental Garbage Collection of Processes `_". ACM. SIGPLAN Notices 12, 8 (August 1977), pp. 55--59. + + .. admonition:: Abstract + + This paper investigates some problems associated with an argument + evaluation order that we call "future" order, which is different + from both call-by-name and call-by-value. In call-by-future, each + formal parameter of a function is bound to a separate process + (called a "future") dedicated to the evaluation of the + corresponding argument. This mechanism allows the fully parallel + evaluation of arguments to a function, and has been shown to + augment the expressive power of a language. + + We discuss an approach to a problem that arises in this context: + futures which were thought to be relevant when they were created + become irrelevant through being ignored in the body of the + expression where they were bound. The problem of irrelevant + processes also appears in multiprocessing problem-solving systems + which start several processors working on the same problem but + with different methods, and return with the solution which + finishes first. This "parallel method strategy" has the drawback + that the processes which are investigating the losing methods must + be identified, stopped, and reassigned to more useful tasks. + + The solution we propose is that of garbage collection. We propose + that the goal structure of the solution plan be explicitly + represented in memory as part of the graph memory (like Lisp's + heap) so that a garbage collection algorithm can discover which + processes are performing useful work, and which can be recycled + for a new task. An incremental algorithm for the unified garbage + collection of storage and processes is described. + +* .. _BAKER78: + + Henry G. Baker. 1978. "`List Processing in Real Time on a Serial Computer `_". ACM. Communications of the ACM 21, 4 (April 1978), pp. 280--294. + + .. admonition:: Abstract + + A real-time list processing system is one in which the time + required by the elementary list operations (e.g. CONS, CAR, CDR, + RPLACA, RPLACD, EQ, and ATOM in LISP) is bounded by a (small) + constant. Classical implementations of list processing systems + lack this property because allocating a list cell from the heap + may cause a garbage collection, which process requires time + proportional to the heap size to finish. A real-time list + processing system is presented which continuously reclaims + garbage, including directed cycles, while linearizing and + compacting the accessible cells into contiguous locations to avoid + fragmenting the free storage pool. The program is small and + requires no time-sharing interrupts, making it suitable for + microcode. Finally, the system requires the same average time, and + not more than twice the space, of a classical implementation, and + those space requirements can be reduced to approximately classical + proportions by compact list representation. Arrays of different + sizes, a program stack, and hash linking are simple extensions to + our system, and reference counting is found to be inferior for + many applications. + +* .. _BAKER79: + + Henry G. Baker. 1979. "`Optimizing Allocation and Garbage Collection of Spaces `_". In Winston and Brown, eds. *Artificial Intelligence: An MIT Perspective.* MIT Press. + + .. admonition:: Abstract + + MACLISP, unlike some other implementations of LISP, allocates + storage for different types of objects in noncontiguous areas + called "spaces". These spaces partition the active storage into + disjoint areas, each of which holds a different type of object. + For example, "list cells" are stored in one space, "full-word + integers" reside in another space, "full-word floating point + numbers" in another, and so on. + + Allocating space in this manner has several advantages. An + object's type can easily be computed from a pointer to it, without + any memory references to the object itself. Thus, the LISP + primitive ATOM(x) can easily compute its result without even + paging in x. Another advantage is that the type of an object does + not require any storage within the object, so that arithmetic with + hardware data types such as full-word integers can use hardware + instructions directly. + + There are problems associated with this method of storage and type + management, however. When all data types are allocated from the + same heap, there is no problem with varying demand for the + different data types; all data types require storage from the same + pool, so that only the total amount of storage is important. Once + different data types must be allocated from different spaces, + however, the relative sizes of the spaces becomes important. + +* .. _BAKER91: + + Henry G. Baker. 1991. "`Cache-Conscious Copying Collectors `_". OOPSLA'91/GC'91 Workshop on Garbage Collection. + + .. admonition:: Abstract + + Garbage collectors must minimize the scarce resources of cache + space and off-chip communications bandwidth to optimize + performance on modern single-chip computer architectures. + Strategies for achieving these goals in the context of copying + garbage collection are discussed. A multi-processor + mutator/collector system is analyzed. Finally, the Intel 80860XP + architecture is studied. + +* .. _BAKER92A: + + Henry G. Baker. 1992. "`Lively Linear Lisp -- 'Look Ma, No Garbage!' `_". ACM. SIGPLAN Notices 27, 8 (August 1992), pp. 89--98. + + .. admonition:: Abstract + + Linear logic has been proposed as one solution to the problem of + garbage collection and providing efficient "update-in-place" + capabilities within a more functional language. Linear logic + conserves accessibility, and hence provides a "mechanical + metaphor" which is more appropriate for a distributed-memory + parallel processor in which copying is explicit. However, linear + logic's lack of sharing may introduce significant inefficiencies + of its own. + + We show an efficient implementation of linear logic called "Linear + Lisp" that runs within a constant factor of non-linear logic. This + Linear Lisp allows RPLACX operations, and manages storage as + safely as a non-linear Lisp, but does not need a garbage + collector. Since it offers assignments but no sharing, it occupies + a twilight zone between functional languages and imperative + languages. Our Linear Lisp Machine offers many of the same + capabilities as combinator/graph reduction machines, but without + their copying and garbage collection problems. + +* .. _BAKER92C: + + Henry G. Baker. 1992. "`The Treadmill: Real-Time Garbage Collection Without Motion Sickness `_". ACM. SIGPLAN Notices 27, 3 (March 1992), pp. 66--70. + + .. admonition:: Abstract + + A simple real-time garbage collection algorithm is presented which + does not copy, thereby avoiding some of the problems caused by the + asynchronous motion of objects. This in-place "treadmill" garbage + collection scheme has approximately the same complexity as other + non-moving garbage collectors, thus making it usable in a + high-level language implementation where some pointers cannot be + traced. The treadmill is currently being used in a Lisp system + built in Ada. + +* .. _BAKER92: + + Henry G. Baker. 1992. "`CONS Should not CONS its Arguments, or, a Lazy Alloc is a Smart Alloc `_". ACM. SIGPLAN Notices 27, 3 (March 1992), 24--34. + + .. admonition:: Abstract + + "Lazy allocation" is a model for allocating objects on the + execution stack of a high-level language which does not create + dangling references. Our model provides safe transportation into + the heap for objects that may survive the deallocation of the + surrounding stack frame. Space for objects that do not survive the + deallocation of the surrounding stack frame is reclaimed without + additional effort when the stack is popped. Lazy allocation thus + performs a first-level garbage collection, and if the language + supports garbage collection of the heap, then our model can reduce + the amortized cost of allocation in such a heap by filtering out + the short-lived objects that can be more efficiently managed in + LIFO order. A run-time mechanism called "result expectation" + further filters out unneeded results from functions called only + for their effects. In a shared-memory multi-processor environment, + this filtering reduces contention for the allocation and + management of global memory. + + Our model performs simple local operations, and is therefore + suitable for an interpreter or a hardware implementation. Its + overheads for functional data are associated only with + *assignments*, making lazy allocation attractive for "mostly + functional" programming styles. Many existing stack allocation + optimizations can be seen as instances of this generic model, in + which some portion of these local operations have been optimized + away through static analysis techniques. + + Important applications of our model include the efficient + allocation of temporary data structures that are passed as + arguments to anonymous procedures which may or may not use these + data structures in a stack-like fashion. The most important of + these objects are functional arguments (funargs), which require + some run-time allocation to preserve the local environment. Since + a funarg is sometimes returned as a first-class value, its + lifetime can survive the stack frame in which it was created. + Arguments which are evaluated in a lazy fashion (Scheme "delays" + or "suspensions") are similarly handled. Variable-length argument + "lists" themselves can be allocated in this fashion, allowing + these objects to become "first-class". Finally, lazy allocation + correctly handles the allocation of a Scheme control stack, + allowing Scheme continuations to become first-class values. + +* .. _BAKER92B: + + Henry G. Baker. 1992. "`NREVERSAL of Fortune -- The Thermodynamics of Garbage Collection `_". Springer-Verlag. LNCS Vol. 637. + + .. admonition:: Abstract + + The need to *reverse* a computation arises in many contexts -- + debugging, editor undoing, optimistic concurrency undoing, + speculative computation undoing, trace scheduling, exception + handling undoing, database recovery, optimistic discrete event + simulations, subjunctive computing, etc. The need to *analyze* a + reversed computation arises in the context of static analysis -- + liveness analysis, strictness analysis, type inference, etc. + Traditional means for restoring a computation to a previous state + involve checkpoints; checkpoints require time to copy, as well as + space to store, the copied material. Traditional reverse abstract + interpretation produces relatively poor information due to its + inability to guess the previous values of assigned-to variables. + + We propose an abstract computer model and a programming language + -- Psi-Lisp -- whose primitive operations are injective and hence + reversible, thus allowing arbitrary undoing without the overheads + of checkpointing. Such a computer can be built from reversible + conservative logic circuits, with the serendipitous advantage of + dissipating far less heat than traditional Boolean AND/OR/NOT + circuits. Unlike functional languages, which have one "state" for + all times, Psi-Lisp has at all times one "state", with unique + predecessor and successor states. + + Compiling into a reversible pseudocode can have benefits even when + targeting a traditional computer. Certain optimizations, e.g., + update-in-place, and compile-time garbage collection may be more + easily performed, because the information may be elicited without + the difficult and time-consuming iterative abstract interpretation + required for most non-reversible models. + + In a reversible machine, garbage collection for recycling storage + can always be performed by a reversed (sub)computation. While this + "collection is reversed mutation" insight does not reduce space + requirements when used for the computation as a whole, it does + save space when used to recycle at finer scales. This insight also + provides an explanation for the fundamental importance of the + push-down stack both for recognizing palindromes and for managing + storage. + + Reversible computers are related to *Prolog*, *linear logic* and + *chemical abstract machines*. + +* .. _BAKER93: + + Henry G. Baker. 1993. "`'Infant Mortality' and Generational Garbage Collection `_". ACM. SIGPLAN Notices 28, 4 (April 1993), pp. 55--57. + + .. admonition:: Abstract + + Generation-based garbage collection has been advocated by + appealing to the intuitive but vague notion that "young objects + are more likely to die than old objects". The intuition is, that + if a generation-based garbage collection scheme focuses its effort + on scanning recently created objects, then its scanning efforts + will pay off more in the form of more recovered garbage, than if + it scanned older objects. In this note, we show a counterexample + of a system in which "infant mortality" is as high as you please, + but for which generational garbage collection is ineffective for + improving the average mark/cons ratio. Other benefits, such as + better locality and a smaller number of large delays, may still + make generational garbage collection attractive for such a system, + however. + +* .. _BAKER93A: + + Henry G. Baker. 1993. "`Equal Rights for Functional Objects or, The More Things Change, The More They Are the Same `_". ACM. OOPS Messenger 4, 4 (October 1993), pp. 2--27. + + .. admonition:: Abstract + + We argue that intensional object identity in object-oriented + programming languages and databases is best defined operationally + by side-effect semantics. A corollary is that "functional" objects + have extensional semantics. This model of object identity, which + is analogous to the normal forms of relational algebra, provides + cleaner semantics for the value-transmission operations and + built-in primitive equality predicate of a programming language, + and eliminates the confusion surrounding "call-by-value" and + "call-by-reference" as well as the confusion of multiple equality + predicates. + + Implementation issues are discussed, and this model is shown to + have significant performance advantages in persistent, parallel, + distributed and multilingual processing environments. This model + also provides insight into the "type equivalence" problem of + Algol-68, Pascal and Ada. + +* .. _BAKER94: + + Henry G. Baker. 1994. "`Minimizing Reference Count Updating with Deferred and Anchored Pointers for Functional Data Structures `_". ACM. SIGPLAN Notices 29, 9 (September 1994), pp. 38--43. + + .. admonition:: Abstract + + "Reference counting" can be an attractive form of dynamic storage + management. It recovers storage promptly and (with a garbage stack + instead of a free list) it can be made "real-time" -- i.e., all + accesses can be performed in constant time. Its major drawbacks + are its inability to reclaim cycles, its count storage, and its + count update overhead. Update overhead is especially irritating + for functional (read-only) data where updates may dirty pristine + cache lines and pages. + + We show how reference count updating can be largely eliminated for + functional data structures by using the "linear style" of + programming that is inspired by Girard's linear logic, and by + distinguishing normal pointers from "anchored pointers", which + indicate not only the object itself, but also the depth of the + stack frame that anchors the object. An "anchor" for a pointer is + essentially an enclosing data structure that is temporarily locked + from being collected for the duration of the anchored pointer's + existence by a deferred reference count. An "anchored pointer" + thus implies a reference count increment that has been deferred + until it is either cancelled or performed. + + Anchored pointers are generalizations of "borrowed" pointers and + "phantom" pointers. Anchored pointers can provide a solution to + the "derived pointer problem" in garbage collection. + +* .. _BAKER94A: + + Henry G. Baker. 1994. "`Thermodynamics and Garbage Collection `_". ACM. SIGPLAN Notices 29, 4 (April 1994), pp. 58--63. + + .. admonition:: Abstract + + We discuss the principles of statistical thermodynamics and their + application to storage management problems. We point out problems + which result from imprecise usage of the terms "information", + "state", "reversible", "conservative", etc. + +* .. _BAKER95A: + + Henry G. Baker. 1995. "`'Use-Once' Variables and Linear Objects -- Storage Management, Reflection and Multi-Threading `_". ACM. SIGPLAN Notices 30, 1 (January 1995), pp. 45--52. + + .. admonition:: Abstract + + Programming languages should have 'use-once' variables in addition + to the usual 'multiple-use' variables. 'Use-once' variables are + bound to linear (unshared, unaliased, or singly-referenced) + objects. Linear objects are cheap to access and manage, because + they require no synchronization or tracing garbage collection. + Linear objects can elegantly and efficiently solve otherwise + difficult problems of functional/mostly-functional systems -- + e.g., in-place updating and the efficient initialization of + functional objects. Use-once variables are ideal for directly + manipulating resources which are inherently linear such as + freelists and 'engine ticks' in reflective languages. + + A 'use-once' variable must be dynamically referenced exactly once + within its scope. Unreferenced use-once variables must be + explicitly killed, and multiply-referenced use-once variables must + be explicitly copied; this duplication and deletion is subject to + the constraint that some linear datatypes do not support + duplication and deletion methods. Use-once variables are bound + only to linear objects, which may reference other linear or + non-linear objects. Non-linear objects can reference other + non-linear objects, but can reference a linear object only in a + way that ensures mutual exclusion. + + Although implementations have long had implicit use-once variables + and linear objects, most languages do not provide the programmer + any help for their utilization. For example, use-once variables + allow for the safe/controlled use of reified language + implementation objects like single-use continuations. + + Linear objects and use-once variables map elegantly into dataflow + models of concurrent computation, and the graphical + representations of dataflow models make an appealing visual linear + programming language. + +* .. _BAKER95: + + Henry G. Baker. 1995. *Memory Management: International Workshop IWMM'95*. Springer-Verlag. ISBN 3-540-60368-9. + + .. admonition:: From the Preface + + The International Workshop on Memory Management 1995 (IWMM'95) is + a continuation of the excellent series started by Yves Bekkers and + Jacques Cohen with IWMM'92. The present volume assembles the + refereed and invited technical papers which were presented during + this year's workshop. + +* .. _BBW97: + + Nick Barnes, Richard Brooksby, David Jones, Gavin Matthews, Pekka P. Pirinen, Nick Dalton, P. Tucker Withington. 1997. "`A Proposal for a Standard Memory Management Interface `_". OOPSLA97 Workshop on Garbage Collection and Memory Management. + + .. admonition:: From the notes + + There is no well-defined memory-management library API which would + allow programmers to easily choose the best memory management + implementation for their application. + + Some languages allow replacement of their memory management + functions, but usually only the program API is specified, hence + replacement of the entire program interface is required. + + Few languages support multiple memory management policies within a + single program. Those that do use proprietary memory management + policies. + + We believe that the design of an abstract program API is a + prerequisite to the design of a “server” API and eventually an API + that would permit multiple cooperating memory “servers”. If the + interface is simple yet powerful enough to encompass most memory + management systems, it stands a good chance of being widely + adopted. + +* .. _ZORN93B: + + David A. Barrett, Benjamin Zorn. 1993. "`Using Lifetime Predictors to Improve Memory Allocation Performance `_". ACM. SIGPLAN'93 Conference on Programming Language Design and Implementation, pp. 187--196. + + .. admonition:: Abstract + + Dynamic storage allocation is used heavily in many application + areas including interpreters, simulators, optimizers, and + translators. We describe research that can improve all aspects of + the performance of dynamic storage allocation by predicting the + lifetimes of short-lived objects when they are allocated. Using + five significant, allocation-intensive C programs, we show that a + great fraction of all bytes allocated are short-lived (> 90% in + all cases). Furthermore, we describe an algorithm for lifetime + prediction that accurately predicts the lifetimes of 42--99% of all + objects allocated. We describe and simulate a storage allocator + that takes advantage of lifetime prediction of short-lived objects + and show that it can significantly improve a program's memory + overhead and reference locality, and even, at times, improve CPU + performance as well. + +* .. _BARRETT93: + + David A. Barrett, Benjamin Zorn. 1995. "`Garbage Collection using a Dynamic Threatening Boundary `_". ACM. SIGPLAN'95 Conference on Programming Language Design and Implementation, pp. 301--314. + + .. admonition:: Abstract + + Generational techniques have been very successful in reducing the + impact of garbage collection algorithms upon the performance of + programs. However, it is impossible for designers of collection + algorithms to anticipate the memory allocation behavior of all + applications in advance. Existing generational collectors rely + upon the applications programmer to tune the behavior of the + collector to achieve maximum performance for each application. + Unfortunately, because the many tuning parameters require detailed + knowledge of both the collection algorithm and the program + allocation behavior in order to be used effectively, such tuning + is difficult and error prone. We propose a new garbage collection + algorithm that uses just two easily understood tuning parameters + that directly reflect the maximum memory and pause time + constraints familiar to application programmers and users. + + Like generational collectors, ours divides memory into two spaces, + one for short-lived, and another for long-lived objects. Unlike + previous work, our collector dynamically adjusts the boundary + between these two spaces in order to directly meet the resource + constraints specified by the user. We describe two methods for + adjusting this boundary, compare them with several existing + algorithms, and show how effectively ours meets the specified + constraints. Our pause time collector saved memory by holding + median pause times closer to the constraint than the other pause + time constrained algorithm and, when not over-constrained, our + memory constrained collector exhibited the lowest CPU overhead of + the algorithms we measured yet was capable of maintaining a + maximum memory constraint. + +* .. _BARTLETT88: + + Joel F. Bartlett. 1988. "`Compacting Garbage Collection with Ambiguous Roots `_". Digital Equipment Corporation. + + .. admonition:: Abstract + + This paper introduces a copying garbage collection algorithm which + is able to compact most of the accessible storage in the heap + without having an explicitly defined set of pointers that contain + all the roots of all accessible storage. Using "hints" found in + the processor's registers and stack, the algorithm is able to + divide heap allocated objects into two groups: those that might be + referenced by a pointer in the stack or registers, and those that + are not. The objects which might be referenced are left in place, + and the other objects are copied into a more compact + representation. + + A Lisp compiler and runtime system which uses such a collector + need not have complete control of the processor in order to force + a certain discipline on the stack and registers. A Scheme + implementation has been done for the Digital WRL Titan processor + which uses a garbage collector based on this "mostly copying" + algorithm. Like other languages for the Titan, it uses the Mahler + intermediate language as its target. This simplifies the compiler + and allows it to take advantage of the significant machine + dependent optimizations provided by Mahler. The common + intermediate language also simplifies call-outs from Scheme + programs to functions written in other languages and call-backs + from functions in other languages. + + Measurements of the Scheme implementation show that the algorithm + is efficient, as little unneeded storage is retained and only a + very small fraction of the heap is left in place. + + Simple pointer manipulation protocols also mean that compiler + support is not needed in order to correctly handle pointers. Thus + it is reasonable to provide garbage collected storage in languages + such as C. A collector written in C which uses this algorithm is + included in the Appendix. + +* .. _BARTLETT89: + + Joel F. Bartlett. 1989. "`Mostly-Copying Garbage Collection Picks Up Generations and C++ `_". Digital Equipment Corporation. + + .. admonition:: Abstract + + The "mostly-copying" garbage collection algorithm provides a way + to perform compacting garbage collection in spite of the presence + of ambiguous pointers in the root set. As originally defined, each + collection required almost all accessible objects to be moved. + While adequate for many applications, programs that retained a + large amount of storage spent a significant amount of time garbage + collecting. To improve performance of these applications, a + generational version of the algorithm has been designed. This note + reports on this extension of the algorithm, and its application in + collectors for Scheme and C++. + +* .. _BC92: + + Yves Bekkers & Jacques Cohen. 1992. "`Memory Management, International Workshop IWMM 92 `_". Springer-Verlag. LNCS Vol. 637, ISBN 3-540-55940-X. + +* .. _BB99: + + Emery D. Berger, Robert D. Blumofe. 1999. "`Hoard: A Fast, Scalable, and Memory-Efficient Allocator for Shared-Memory Multiprocessors `_". University of Texas at Austin. UTCS TR99-22. + + .. admonition:: Abstract + + In this paper, we present Hoard, a memory allocator for + shared-memory multiprocessors. We prove that its worst-case memory + fragmentation is asymptotically equivalent to that of an optimal + uniprocessor allocator. We present experiments that demonstrate + its speed and scalability. + +* .. _BERGER01: + + Emery D. Berger, Benjamin G. Zorn, Kathryn S. McKinley. 2001. "`Composing high-performance memory allocators `_" ACM SIGPLAN Conference on Programming Language Design and Implementation 2001, pp. 114--124. + + .. admonition:: Abstract + + Current general-purpose memory allocators do not provide + sufficient speed or flexibility for modern high-performance + applications. Highly-tuned general purpose allocators have + per-operation costs around one hundred cycles, while the cost of + an operation in a custom memory allocator can be just a handful of + cycles. To achieve high performance, programmers often write + custom memory allocators from scratch -- a difficult and + error-prone process. + + In this paper, we present a flexible and efficient infrastructure + for building memory allocators that is based on C++ templates and + inheritance. This novel approach allows programmers to build + custom and general-purpose allocators as “heap layers” that can be + composed without incurring any additional runtime overhead or + additional programming cost. We show that this infrastructure + simplifies allocator construction and results in allocators that + either match or improve the performance of heavily-tuned + allocators written in C, including the Kingsley allocator and the + GNU obstack library. We further show this infrastructure can be + used to rapidly build a general-purpose allocator that has + performance comparable to the Lea allocator, one of the best + uniprocessor allocators available. We thus demonstrate a clean, + easy-to-use allocator interface that seamlessly combines the power + and efficiency of any number of general and custom allocators + within a single application. + +* .. _BW88: + + Hans-J. Boehm, Mark Weiser. 1988. "`Garbage collection in an uncooperative environment `_". Software -- Practice and Experience. 18(9):807--820. + + .. admonition:: Abstract + + We describe a technique for storage allocation and garbage + collection in the absence of significant co-operation from the + code using the allocator. This limits garbage collection overhead + to the time actually required for garbage collection. In + particular, application programs that rarely or never make use of + the collector no longer encounter a substantial performance + penalty. This approach greatly simplifies the implementation of + languages supporting garbage collection. It further allows + conventional compilers to be used with a garbage collector, either + as the primary means of storage reclamation, or as a debugging + tool. + +* .. _BDS91: + + Hans-J. Boehm, Alan J. Demers, Scott Shenker. 1991. "`Mostly Parallel Garbage Collection `_". Xerox PARC. ACM PLDI 91, SIGPLAN Notices 26, 6 (June 1991), pp. 157--164. + + .. admonition:: Abstract + + We present a method for adapting garbage collectors designed to + run sequentially with the client, so that they may run + concurrently with it. We rely on virtual memory hardware to + provide information about pages that have been updated or + "dirtied" during a given period of time. This method has been used + to construct a mostly parallel trace-and-sweep collector that + exhibits very short pause times. Performance measurements are + given. + +* .. _BC92A: + + Hans-J. Boehm, David Chase. 1992. "`A Proposal for Garbage-Collector-Safe C Compilation `_". *Journal of C Language Translation.* vol. 4, 2 (December 1992), pp. 126--141. + + .. admonition:: Abstract + + Conservative garbage collectors are commonly used in combination + with conventional C programs. Empirically, this usually works + well. However, there are no guarantees that this is safe in the + presence of "improved" compiler optimization. We propose that C + compilers provide a facility to suppress optimizations that are + unsafe in the presence of conservative garbage collection. Such a + facility can be added to an existing compiler at very minimal + cost, provided the additional analysis is done in a + machine-independent source-to-source prepass. Such a prepass may + also check the source code for garbage-collector-safety. + +* .. _BOEHM93: + + Hans-J. Boehm. 1993. "`Space Efficient Conservative Garbage Collection `_". ACM, SIGPLAN. Proceedings of the ACM SIGPLAN '91 Conference on Programming Language Design and Implementation, SIGPLAN Notices 28, 6, pp 197--206. + + .. admonition:: Abstract + + We call a garbage collector conservative if it has only partial + information about the location of pointers, and is thus forced to + treat arbitrary bit patterns as though they might be pointers, in + at least some cases. We show that some very inexpensive, but + previously unused techniques can have dramatic impact on the + effectiveness of conservative garbage collectors in reclaiming + memory. Our most significant observation is that static data that + appears to point to the heap should not result in misidentified + reference to the heap. The garbage collector has enough + information to allocate around such references. We also observe + that programming style has a significantly impact on the amount of + spuriously retained storage, typically even if the collector is + not terribly conservative. Some fairly common C and C++ + programming styles significantly decrease the effectiveness of any + garbage collector. These observations suffice to explain some of + the different assessments of conservative collection that have + appeared in the literature. + +* .. _BOEHM00: + + Hans-J. Boehm. 2000. "`Reducing Garbage Collector Cache Misses `_". ACM. ISMM'00 pp. 59--64. + + .. admonition:: Abstract + + Cache misses are currently a major factor in the cost of garbage + collection, and we expect them to dominate in the future. + Traditional garbage collection algorithms exhibit relatively litle + temporal locality; each live object in the heap is likely to be + touched exactly once during each garbage collection. We measure + two techniques for dealing with this issue: prefetch-on-grey, and + lazy sweeping. The first of these is new in this context. Lazy + sweeping has been in common use for a decade. It was introduced as + a mechanism for reducing paging and pause times; we argue that it + is also crucial for eliminating cache misses during the sweep + phase. + + Our measurements are obtained in the context of a non-moving + garbage collector. Fully copying garbage collection inherently + requires more traffic through the cache, and thus probably also + stands to benefit substantially from something like the + prefetch-on-grey technique. Generational garbage collection may + reduce the benefit of these techniques for some applications, but + experiments with a non-moving generational collector suggest that + they remain quite useful. + +* .. _BOEHM01: + + Hans-J. Boehm. 2001. "Bounding Space Usage of Conservative Garbage Collectors ``_". HP Labs technical report HPL-2001-251. + + .. admonition:: Abstract + + Conservative garbage collectors can automatically reclaim unused + memory in the absence of precise pointer location information. If + a location can possibly contain a pointer, it is treated by the + collector as though it contained a pointer. Although it is + commonly assumed that this can lead to unbounded space use due to + misidentified pointers, such extreme space use is rarely observed + in practice, and then generally only if the number of + misidentified pointers is itself unbounded. We show that if the + program manipulates only data structures satisfying a simple + GC-robustness criterion, then a bounded number of misidentified + pointers can result at most in increasing space usage by a + constant factor. We argue that nearly all common data structures + are already GC- robust, and it is typically easy to identify and + replace those that are not. Thus it becomes feasible to prove + space bounds on programs collected by mildly conservative garbage + collectors, such as the one in Barabash et al. (2001). The + worst-case space overhead introduced by such mild conservatism is + comparable to the worst-case fragmentation overhead for inherent + in any non-moving storage allocator. The same GC-robustness + criterion also ensures the absence of temporary space leaks of the + kind discussed in Rojemo (1995) for generational garbage + collectors. + +* .. _BOEHM02: + + Hans-J. Boehm. 2002. "`Destructors, Finalizers, and Synchronization `_". HP Labs technical report HPL-2002-335. + + .. admonition:: Abstract + + We compare two different facilities for running cleanup actions + for objects that are about to reach the end of their life. + Destructors, such as we find in C++, are invoked synchronously + when an object goes out of scope. They make it easier to implement + cleanup actions for objects of well-known lifetime, especially in + the presence of exceptions. Languages like Java, Modula-3, and C# + provide a different kind of "finalization" facility: Cleanup + methods may be run when the garbage collector discovers a heap + object to be otherwise inaccessible. Unlike C++ destructors, such + methods run in a separate thread at some much less well-defined + time. We argue that these are fundamentally different, and + potentially complementary, language facilities. We also try to + resolve some common misunderstandings about finalization in the + process. In particular: 1. The asynchronous nature of finalizers + is not just an accident of implementation or a shortcoming of + tracing collectors; it is necessary for correctness of client + code, fundamentally affects how finalizers must be written, and + how finalization facilities should be presented to the user. 2. An + object may legitimately be finalized while one of its methods are + still running. This should and can be addressed by the language + specification and client code. + +* .. _BM77: + + Robert S. Boyer and J. Strother Moore. 1977. "`A Fast String Searching Algorithm `_". *Communications of the ACM* 20(10):762--772. + + .. admonition:: Abstract + + An algorithm is presented that searches for the location, "*i*," + of the first occurrence of a character string, "*pat*," in another + string, "*string*." During the search operation, the characters of + *pat* are matched starting with the last character of *pat*. The + information gained by starting the match at the end of the pattern + often allows the algorithm to proceed in large jumps through the + text being searched. Thus the algorithm has the unusual property + that, in most cases, not all of the first *i* characters of + *string* are inspected. The number of characters actually + inspected (on the average) decreases as a function of the length + of *pat*. For a random English pattern of length 5, the algorithm + will typically inspect *i*/4 characters of string before finding a + match at *i*. Furthermore, the algorithm has been implemented so + that (on the average) fewer than *i* + *patlen* machine + instructions are executed. These conclusions are supported with + empirical evidence and a theoretical analysis of the average + behavior of the algorithm. The worst case behavior of the + algorithm is linear in *i* + *patlen*, assuming the availability + of array space for tables linear in *patlen* plus the size of the + alphabet. + +* .. _BL72: + + P. Branquart, J. Lewi. 1972. "A scheme of storage allocation and garbage collection for ALGOL 68". Elsevier/North-Holland. ALGOL 68 Implementation -- Proceedings of the IFIP Working Conference on ALGOL 68 Implementation, July 1970. + +* .. _BROOKSBY02: + + Richard Brooksby. 2002. "`The Memory Pool System: Thirty person-years of memory management development goes Open Source `_". ISMM'02. + + .. admonition:: Abstract + + The Memory Pool System (MPS) is a very general, adaptable, + flexible, reliable, and efficient memory management system. It + permits the flexible combination of memory management techniques, + supporting manual and automatic memory management, in-line + allocation, finalization, weakness, and multiple simultaneous + co-operating incremental generational garbage collections. It also + includes a library of memory pool classes implementing specialized + memory management policies. + + Between 1994 and 2001, Harlequin (now part of Global Graphics) + invested about thirty person-years of effort developing the MPS. + The system contained many innovative techniques and abstractions + which were kept secret. In 1997 Richard Brooksby, the manager and + chief architect of the project, and Nicholas Barnes, a senior + developer, left Harlequin to form their own consultancy company, + Ravenbrook, and in 2001, Ravenbrook acquired the MPS technology + from Global Graphics. We are happy to announce that we are + publishing the source code and documentation under an open source + licence. This paper gives an overview of the system. + +* .. _C1990: + + International Standard ISO/IEC 9899:1990. "Programming languages — C". + +* .. _C1999: + + International Standard ISO/IEC 9899:1999. "`Programming languages — C `_". + +* .. _CGZ94: + + Brad Calder, Dirk Grunwald, Benjamin Zorn. 1994. "`Quantifying Behavioral Differences Between C and C++ Programs `_". *Journal of Programming Languages.* 2(4):313--351. + + .. admonition:: Abstract + + Improving the performance of C programs has been a topic of great + interest for many years. Both hardware technology and compiler + optimization research has been applied in an effort to make C + programs execute faster. In many application domains, the C++ + language is replacing C as the programming language of choice. In + this paper, we measure the empirical behavior of a group of + significant C and C++ programs and attempt to identify and + quantify behavioral differences between them. Our goal is to + determine whether optimization technology that has been successful + for C programs will also be successful in C++ programs. We + furthermore identify behavioral characteristics of C++ programs + that suggest optimizations that should be applied in those + programs. Our results show that C++ programs exhibit behavior that + is significantly different than C programs. These results should + be of interest to compiler writers and architecture designers who + are designing systems to execute object-oriented programs. + +* .. _CPC00: + + Dante J. Cannarozzi, Michael P. Plezbert, Ron K. Cytron. 2000. "`Contaminated garbage collection `_". ACM. Proceedings of the ACM SIGPLAN '00 conference on on Programming language design and implementation, pp. 264--273. + + .. admonition:: Abstract + + We describe a new method for determining when an object can be + garbage collected. The method does not require marking live + objects. Instead, each object *X* is *dynamically* associated with + a stack frame *M*, such that *X* is collectable when *M* pops. + Because *X* could have been dead earlier, our method is + conservative. Our results demonstrate that the methos nonetheless + idenitifies a large percentage of collectable objects. The method + has been implemented in Sun's Java™ Virtual Machine interpreter, + and results are presented based on this implementation. + +* .. _CW86: + + Patrick J. Caudill, Allen Wirfs-Brock. 1986. "A Third-Generation Smalltalk-80 Implementation". ACM. SIGPLAN Notices. 21(11), OOPSLA'86 ACM Conference on Object-Oriented Systems, Languages and Applications. + + .. admonition:: Abstract + + A new, high performance Smalltalk-80™ implementation is described + which builds directly upon two previous implementation efforts. + This implementation supports a large object space while retaining + compatibility with previous Smalltalk-80™ images. The + implementation utilizes a interpreter which incorporates a + generation based garbage collector and which does not have an + object table. This paper describes the design decisions which lead + to this implementation and reports preliminary performance + results. + +* .. _CHENEY70: + + C. J. Cheney. 1970. "`A non-recursive list compacting algorithm `_". CACM. 13-11 pp. 677--678. + + .. admonition:: Abstract + + A simple nonrecursive list structure compacting scheme or garbage + collector suitable for both compact and LISP-like list structures + is presented. The algorithm avoids the need for recursion by using + the partial structure as it is built up to keep track of those + lists that have been copied. + +* .. _CHL98: + + Perry Cheng, Robert Harper, Peter Lee. 1998. "`Generational stack collection and profile-driven pretenuring `_". ACM. Proceedings of SIGPLAN'98 Conference on Programming Language Design and Implementation, pp. 162--173. + + .. admonition:: Abstract + + This paper presents two techniques for improving garbage + collection performance: generational stack collection and + profile-driven pretenuring. The first is applicable to stack-based + implementations of functional languages while the second is useful + for any generational collector. We have implemented both + techniques in a generational collector used by the TIL compiler, + and have observed decreases in garbage collection times of as much + as 70% and 30%, respectively. + + Functional languages encourage the use of recursion which can lead + to a long chain of activation records. When a collection occurs, + these activation records must be scanned for roots. We show that + scanning many activation records can take so long as to become the + dominant cost of garbage collection. However, most deep stacks + unwind very infrequently, so most of the root information obtained + from the stack remains unchanged across successive garbage + collections. *Generational stack collection* greatly reduces the + stack scan cost by reusing information from previous scans. + + Generational techniques have been successful in reducing the cost + of garbage collection. Various complex heap arrangements and + tenuring policies have been proposed to increase the effectiveness + of generational techniques by reducing the cost and frequency of + scanning and copying. In contrast, we show that by using profile + information to make lifetime predictions, *pretenuring* can avoid + copying data altogether. In essence, this technique uses a + refinement of the generational hypothesis (most data die young) + with a locality principle concerning the age of data: most + allocations sites produce data that immediately dies, while a few + allocation sites consistently produce data that survives many + collections. + +* .. _CL98: + + Trishul M. Chilimbi, James R. Larus. 1998. "`Using Generational Garbage Collection To Implement Cache-Conscious Data Placement `_". ACM. ISMM'98 pp. 37--48. + + .. admonition:: Abstract + + Processor and memory technology trends show a continual increase + in the cost of accessing main memory. Machine designers have tried + to mitigate the effect of this trend through a variety of + techniques that attempt to reduce or tolerate memory latency. + These techniques, unfortunately, have only been partially + successful for pointer-manipulating programs. Recent research has + demonstrated that these programs can benefit greatly from the + complementary approach of reorganizing pointer data structures to + improve cache locality. This paper describes how a generational + garbage collector can be used to achieve a cache-conscious data + layout, in which objects with high temporal affinity are placed + next to each other, so they are likely to reside in the same cache + block. The paper demonstrates the feasibility of collecting low + overhead, real-time profiling information about data access + patterns for object-oriented languages, and describes a new + copying algorithm that utilizes this information to produce a + cache-conscious object layout. Preliminary results indicate that + this technique reduces cache miss rates by 21-42\%, and improves + program performance by 14-37\%. + +* .. _CH97: + + William D Clinger & Lars T Hansen. 1997. "`Generational Garbage Collection and the Radioactive Decay Model `_". ACM. Proceedings of PLDI 1997. + + .. admonition:: Abstract + + If a fixed exponentially decreasing probability distribution + function is used to model every object's lifetime, then the age of + an object gives no information about its future life expectancy. + This *radioactive decay model* implies that there can be no + rational basis for deciding which live objects should be promoted + to another generation. Yet there remains a rational basis for + deciding how many objects to promote, when to collect garbage, and + which generations to collect. + + Analysis of the model leads to a new kind of generational garbage + collector whose effectiveness does not depend upon heuristics that + predict which objects will live longer than others. + + This result provides insight into the computational advantages of + generational garbage collection, with implications for the + management of objects whose life expectancies are difficult to + predict. + +* .. _COHEN81: + + Jacques Cohen. 1981. "Garbage collection of linked data structures". Computing Surveys. Vol. 13, no. 3. + + .. admonition:: Abstract + + A concise and unified view of the numerous existing algorithms for + performing garbage collection of linked data structures is + presented. The emphasis is on garbage collection proper, rather + than on storage allocation. + + First, the classical garbage collection algorithms and their + marking and collecting phases, with and without compacting, are + discussed. + + Algorithms describing these phases are classified according to the + type of cells to be collected: those for collecting single-sized + cells are simpler than those for varisized cells. Recently + proposed algorithms are presented and compared with the classical + ones. Special topics in garbage collection are also covered. A + bibliography with topical annotations is included. + +* .. _CCZ98: + + Dominique Colnet, Philippe Coucaud, Olivier Zendra. 1998. "`Compiler Support to Customize the Mark and Sweep Algorithm `_". ACM. ISMM'98 pp. 154--165. + + .. admonition:: Abstract + + Mark and sweep garbage collectors (GC) are classical but still + very efficient automatic memory management systems. Although + challenged by other kinds of systems, such as copying collectors, + mark and sweep collectors remain among the best in terms of + performance. + + This paper describes our implementation of an efficient mark and + sweep garbage collector tailored to each program. Compiler support + provides the type information required to statically and + automatically generate this customized garbage collector. The + segregation of object by type allows the production of a more + efficient GC code. This technique, implemented in SmallEiffel, our + compiler for the object-oriented language Eiffel, is applicable to + other languages and other garbage collection algorithms, be they + distributed or not. + + We present the results obtained on programs featuring a variety of + programming styles and compare our results to a well-known and + high-quality garbage collector. + +* .. _CWZ93: + + Jonathan E. Cook, Alexander L. Wolf, Benjamin Zorn. 1994. "`Partition Selection Policies in Object Database Garbage Collection `_". ACM. SIGMOD. International Conference on the Management of Data (SIGMOD'94), pp. 371--382. + + .. admonition:: Abstract + + The automatic reclamation of storage for unreferenced objects is + very important in object databases. Existing language system + algorithms for automatic storage reclamation have been shown to be + inappropriate. In this paper, we investigate methods to improve + the performance of algorithms for automatic storage reclamation of + object databases. These algorithms are based on a technique called + partitioned garbage collection, in which a subset of the entire + database is collected independently of the rest. Specifically, we + investigate the policy that is used to select what partition in + the database should be collected. The new partition selection + policies that we propose and investigate are based on the + intuition that the values of overwritten pointers provide good + hints about where to find garbage. Using trace-driven simulation, + we show that one of our policies requires less I/O to collect more + garbage than any existing implementable policy and performs close + to an impractical-to-implement but near-optimal policy over a wide + range of database sizes and connectivities. + +* .. _CKWZ96: + + Jonathan E. Cook, Artur Klauser, Alexander L. Wolf, Benjamin Zorn. 1996. "`Semi-automatic, Self-adaptive Control of Garbage Collection Rates in Object Databases `_". ACM, SIGMOD. International Conference on the Management of Data (SIGMOD'96), pp. 377--388. + + .. admonition:: Abstract + + A fundamental problem in automating object database storage + reclamation is determining how often to perform garbage + collection. We show that the choice of collection rate can have a + significant impact on application performance and that the "best" + rate depends on the dynamic behavior of the application, tempered + by the particular performance goals of the user. We describe two + semi-automatic, self-adaptive policies for controlling collection + rate that we have developed to address the problem. Using + trace-driven simulations, we evaluate the performance of the + policies on a test database application that demonstrates two + distinct reclustering behaviors. Our results show that the + policies are effective at achieving user-specified levels of I/O + operations and database garbage percentage. We also investigate + the sensitivity of the policies over a range of object + connectivities. The evaluation demonstrates that semi-automatic, + self-adaptive policies are a practical means for flexibly + controlling garbage collection rate. + +* .. _CNS92: + + Eric Cooper, Scott Nettles, Indira Subramanian. 1992. "Improving the Performance of SML Garbage Collection using Application-Specific Virtual Memory Management". ACM Conference on LISP and Functional Programming, pp. 43--52. + + .. admonition:: Abstract + + We improved the performance of garbage collection in the Standard ML of + New Jersey system by using the virtual memory facilities provided by + the Mach kernel. We took advantage of Mach's support for large sparse + address spaces and user-defined paging servers. We decreased the + elapsed time for realistic applications by as much as a factor of 4. + +* .. _DACONTA93: + + Michael C. Daconta. 1993. *C Pointers and Dynamic Memory Management.* Wiley. ISBN 0-471-56152-5. + +* .. _DACONTA95: + + Michael C. Daconta. 1995. *C++ Pointers and Dynamic Memory Management.* Wiley. ISBN 0-471-04998-0. + + .. admonition:: From the back cover + + Using techniques developed in the classroom at America Online's + Programmer's University, Michael Daconta deftly pilots programmers + through the intricacies of the two most difficult aspects of C++ + programming: pointers and dynamic memory management. Written by a + programmer for programmers, this no-nonsense, nuts-and-bolts guide + shows you how to fully exploit advanced C++ programming features, + such as creating class-specific allocators, understanding + references versus pointers, manipulating multidimensional arrays + with pointers, and how pointers and dynamic memory are the core of + object-oriented constructs like inheritance, name-mangling, and + virtual functions. + +* .. _DAHL63: + + O.-J. Dahl. 1963. "The SIMULA Storage Allocation Scheme". Norsk Regnesentral. NCC Document no. 162. + +* .. _DENNING68: + + P. J. Denning. 1968. "`Thrashing: Its Causes and Prevention `_". Proceedings AFIPS,1968 Fall Joint Computer Conference, vol. 33, pp. 915--922. + + .. admonition:: From the introduction + + A particularly troublesome phenomenon, thrashing, may seriously + interfere with the performance of paged memory systems, reducing + computing giants (Multics, IBM System 360, and others not + necessarily excepted) to computing dwarfs. The term thrashing + denotes excessive overhead and severe performance degradation or + collapse caused by too much paging. Thrashing inevitably turns a + shortage of memory space into a surplus of processor time. + +* .. _DENNING70: + + P. J. Denning. 1970. "`Virtual Memory `_". ACM. ACM Computing Surveys, vol. 2, no. 3, pp. 153--190, Sept. 1970. + + .. admonition:: Abstract + + The need for automatic storage allocation arises from desires for + program modularity, machine independence, and resource sharing. + Virtual memory is an elegant way of achieving these objectives. In + a virtual memory, the addresses a program may use to identify + information are distinguished from the addresses the memory system + uses to identify physical storage sites, and program-generated + addresses are translated automatically to the corresponding + machine addresses. Two principal methods for implementing virtual + memory, segmentation and paging, are compared and contrasted. Many + contemporary implementations have experienced one or more of these + problems: poor utilization of storage, thrashing, and high costs + associated with loading information into memory. These and + subsidiary problems are studied from a theoretic view, and are + shown to be controllable by a proper combination of hardware and + memory management policies. + +* .. _DS72: + + P. J. Denning, S. C. Schwartz. 1972. "`Properties of the Working-set Model `_". CACM. vol. 15, no. 3, pp. 191--198. + + .. admonition:: Abstract + + A program's working set *W*\ (*t*, *T*) at time *t* is the set of + distinct pages among the *T* most recently referenced pages. + Relations between the average working-set size, the missing-page + rate, and the interreference-interval distribution may be derived + both from time-average definitions and from ensemble-average + (statistical) definitions. An efficient algorithm for estimating + these quantities is given. The relation to LRU (least recently + used) paging is characterized. The independent-reference model, in + which page references are statistically independent, is used to + assess the effects of interpage dependencies on working-set size + observations. Under general assumptions, working-set size is shown + to be normally distributed. + +* .. _DETLEFS92: + + David L. Detlefs. 1992. "`Garbage collection and runtime typing as a C++ library `_". USENIX C++ Conference. + + .. admonition:: From the introduction + + Automatic storage management, or *garbage collection*, is a + feature that can ease program development and enhance program + reliability. Many high-level languages other than C++ provide + garbage collection. This paper proposes the use of "smart pointer" + template classes as an interface for the use of garbage collection + in C++. Template classes and operator overloading are techniques + allowing language extension at the level of user code; I claim + that using these techniques to create smart pointer classes + provdes a syntax for manipulating garbage-collected storage safely + and conveniently. Further, the use of a smart-pointer template + class offers the possibility of implementing the collector at the + user-level, without requiring support from the compiler. If such a + compiler-independent implementation is possible with adequate + performance, then programmers can start to write code using + garbage collection without waiting for language and compiler + modifications. If the use of such a garbage collection interface + becomes widespread, then C++ compilation systems can be built to + specially support tht garbage collection interface, thereby + allowing the use of collection algorithms with enhanced + performance. + +* .. _ZORN93: + + David L. Detlefs, Al Dosser, Benjamin Zorn. 1994. "`Memory Allocation Costs in Large C and C++ Programs `_". Software -- Practice and Experience. 24(6):527--542. + + .. admonition:: Abstract + + Dynamic storage allocation is an important part of a large class + of computer programs written in C and C++. High-performance + algorithms for dynamic storage allocation have been, and will + continue to be, of considerable interest. This paper presents + detailed measurements of the cost of dynamic storage allocation in + 11 diverse C and C++ programs using five very different dynamic + storage allocation implementations, including a conservative + garbage collection algorithm. Four of the allocator + implementations measured are publicly-available on the Internet. A + number of the programs used in these measurements are also + available on the Internet to facilitate further research in + dynamic storage allocation. Finally, the data presented in this + paper is an abbreviated version of more extensive statistics that + are also publicly-available on the Internet. + +* .. _DB76: + + L. Peter Deutsch, Daniel G. Bobrow. 1976. "`An Efficient, Incremental, Automatic Garbage Collector `_". CACM. vol. 19, no. 9, pp. 522--526. + + .. admonition:: Abstract + + This paper describes a new way of solving the storage reclamation + problem for a system such as Lisp that allocates storage + automatically from a heap, and does not require the programmer to + give any indication that particular items are no longer useful or + accessible. A reference count scheme for reclaiming + non-self-referential structures, and a linearizing, compacting, + copying scheme to reorganize all storage at the users discretion + are proposed. The algorithms are designed to work well in systems + which use multiple levels of storage, and large virtual address + space. They depend on the fact that most cells are referenced + exactly once, and that reference counts need only be accurate when + storage is about to be reclaimed. A transaction file stores + changes to reference counts, and a multiple reference table stores + the count for items which are referenced more than once. + +* .. _DLMSS76: + + E. W. Dijkstra, Leslie Lamport, A. J. Martin, C. S. Scholten, E. F. M. Steffens. 1976. "`On-the-fly Garbage Collection: An Exercise in Cooperation `_". Springer-Verlag. Lecture Notes in Computer Science, Vol. 46. + + .. admonition:: Abstract + + As an example of cooperation between sequential processes with + very little mutual interference despite frequent manipulations of + a large shared data space, a technique is developed which allows + nearly all of the activity needed for garbage detection and + collection to be performed by an additional processor operating + con- currently with the processor devoted to the computation + proper. Exclusion and synchronization constraints have been kept + as weak as could be achieved; the severe complexities engendered + by doing so are illustrated. + +* .. _DMH92: + + Amer Diwan, Richard L. Hudson, J. Eliot B. Moss. 1992. "`Compiler Support for Garbage Collection in a Statically Typed Language `_". ACM. Proceedings of the 5th ACM SIGPLAN conference on Programming language design and implementation, pp. 273--282. + + .. admonition:: Abstract + + We consider the problem of supporting compacting garbage + collection in the presence of modern compiler optimizations. Since + our collector may move any heap object, it must accurately locate, + follow, and update all pointers and values derived from pointers. + To assist the collector, we extend the compiler to emit tables + describing live pointers, and values derived from pointers, at + each program location where collection may occur. Significant + results include identification of a number of problems posed by + optimizations, solutions to those problems, a working compiler, + and experimental data concerning table sizes, table compression, + and time overhead of decoding tables during collection. While gc + support can affect the code produced, our sample programs show no + significant changes, the table sizes are a modest fraction of the + size of the optimized code, and stack tracing is a small fraction + of total gc time. Since the compiler enhancements are also modest, + we conclude that the approach is practical. + +* .. _DTM93: + + Amer Diwan, David Tarditi, J. Eliot B. Moss. 1993. "`Memory Subsystem Performance of Programs with Intensive Heap Allocation `_". Carnegie Mellon University. CMU-CS-93-227. + + .. admonition:: Abstract + + Heap allocation with copying garbage collection is a general + storage management technique for modern programming languages. It + is believed to have poor memory subsystem performance. To + investigate this, we conducted an in-depth study of the memory + subsystem performance of heap allocation for memory subsystems + found on many machines. We studied the performance of + mostly-functional Standard ML programs which made heavy use of + heap allocation. We found that most machines support heap + allocation poorly. However, with the appropriate memory subsystem + organization, heap allocation can have good performance. The + memory subsystem property crucial for achieving good performance + was the ability to allocate and initialize a new object into the + cache without a penalty. This can be achieved by having subblock + placement with a subblock size of one word with a write allocate + policy, along with fast page-mode writes or a write buffer. For + caches with subblock placement, the data cache overhead was under + 9% for a 64k or larger data cache; without subblock placement the + overhead was often higher than 50%. + +* .. _DTM93A: + + Amer Diwan, David Tarditi, J. Eliot B. Moss. 1994. "`Memory Subsystem Performance of Programs Using Copying Garbage Collection `_". ACM. CMU-CS-93-210, also in POPL '94. + + .. admonition:: Abstract + + Heap allocation with copying garbage collection is believed to + have poor memory subsystem performance. We conducted a study of + the memory subsystem performance of heap allocation for memory + subsystems found on many machines. We found that many machines + support heap allocation poorly. However, with the appropriate + memory subsystem organization, heap allocation can have good + memory subsystem performance. + +* .. _DOLIGEZ93: + + Damien Doligez & Xavier Leroy. 1993. "`A concurrent, generational garbage collector for a multithreaded implementation of ML `_". ACM. POPL '93, 113--123. + + .. admonition:: Abstract + + This paper presents the design and implementation of a "quasi + real-time" garbage collector for Concurrent Caml Light, an + implementation of ML with threads. This two-generation system + combines a fast, asynchronous copying collector on the young + generation with a non-disruptive concurrent marking collector on + the old generation. This design crucially relies on the ML + compile-time distinction between mutable and immutable objects. + +* .. _DOLIGEZ94: + + Damien Doligez & Georges Gonthier. 1994. "`Portable, unobtrusive garbage collection for multiprocessor systems `_". ACM. POPL '94, 70--83. + + .. admonition:: Abstract + + We describe and prove the correctness of a new concurrent + mark-and-sweep garbage collection algorithm. This algorithm + derives from the classical on-the-fly algorithm from Dijkstra et + al. A distinguishing feature of our algorithm is that it supports + multiprocessor environments where the registers of running + processes are not readily accessible, without imposing any + overhead on the elementary operations of loading a register or + reading or initializing a field. Furthermore our collector never + blocks running mutator processes except possibly on requests for + free memory; in particular, updating a field or creating or + marking or sweeping a heap object does not involve + system-dependent synchronization primitives such as locks. We also + provide support for process creation and deletion, and for + managing an extensible heap of variable-sized objects. + +* .. _DBE93: + + R. Kent Dybvig, Carl Bruggeman, David Eby. 1993. "`Guardians in a Generation-Based Garbage Collector `_". SIGPLAN. Proceedings of the ACM SIGPLAN '93 Conference on Programming Language Design and Implementation, June 1993. + + .. admonition:: Abstract + + This paper describes a new language feature that allows + dynamically allocated objects to be saved from deallocation by an + automatic storage management system so that clean-up or other + actions can be performed using the data stored within the objects. + The program has full control over the timing of clean-up actions, + which eliminates several potential problems and often eliminates + the need for critical sections in code that interacts with + clean-up actions. Our implementation is "generation-friendly" in + the sense that the additional overhead within the mutator is + proportional to the number of clean-up actions actually performed. + +* .. _EDELSON92A: + + Daniel R. Edelson. 1992. "`Smart pointers: They're smart, but they're not pointers `_". USENIX C++ Conference. + + .. admonition:: From the introduction + + This paper shows hhow the behaviour of smart pointers diverges + from that of pointers in certain common C++ constructs. Given + this, we conclude that the C++ programming language does not + support seamless smart pointers: smart pointers cannot + transparently replace raw pointers in all ways except declaration + syntax. We show that this conclusion also applies to *accessors*. + +* .. _EDELSON92: + + Daniel R. Edelson. 1992. "`Comparing Two Garbage Collectors for C++ `_". University of California at Santa Cruz. Technical Report UCSC-CRL-93-20. + + .. admonition:: Abstract + + Our research is concerned with compiler- independent, tag-free + garbage collection for the C++ programming language. This paper + presents a mark-and-sweep collector, and explains how it + ameliorates shortcomings of a previous copy collector. The new + collector, like the old, uses C++'s facilities for creating + abstract data types to define a *tracked reference* type, called + *roots*, at the level of the application program. A programmer + wishing to utilize the garbage collection service uses these roots + in place of normal, raw pointers. We present a detailed study of + the cost of using roots, as compared to both normal pointers and + reference counted pointers, in terms of instruction counts. We + examine the efficiency of a small C++ application using roots, + reference counting, manual reclamation, and conservative + collection. Coding the application to use garbage collection, and + analyzing the resulting efficiency, helped us identify a number of + memory leaks and inefficiencies in the original, manually + reclaimed version. We find that for this program, garbage + collection using roots is much more efficient than reference + counting, though less efficient than manual reclamation. It is + hard to directly compare our collector to the conservative + collector because of the differing efficiencies of their + respective memory allocators. + +* .. _EDWARDS: + + Daniel J. Edwards. n.d. "`Lisp II Garbage Collector `_". MIT. AI Memo 19 (AIM-19). + + .. admonition:: Our summary + + (This short memo doesn't have an abstract. Basically, it describes + the plan for the LISP II Relocating Garbage Collector. It has four + phases: marking, collection, relocation and moving. Marking is by + recursive descent using a bit table. The remaining phases are + linear sweeps through the bit table. The collection phase + calculates how much everything needs to move, storing this + information in the free blocks. The relocation phase updates all + relocatable addresses. The moving phase moves the surviving + objects into one contiguous block.) + +* .. _ELLIS93: + + John R. Ellis, David L. Detlefs. 1993. "`Safe, Efficient Garbage Collection for C++ `_". Xerox PARC. + + .. admonition:: Abstract + + We propose adding safe, efficient garbage collection to C++, + eliminating the possibility of storage-management bugs and making + the design of complex, object-oriented systems much easier. This + can be accomplished with almost no change to the language itself + and only small changes to existing implementations, while + retaining compatibility with existing class libraries. + +* .. _FERREIRA96: + + Paulo Ferreira. 1996. "`Larchant: garbage collection in a cached distributed shared store with persistence by reachability `_". Université Paris VI. Thése de doctorat. + + .. admonition:: Abstract + + The model of Larchant is that of a *Shared Address Space* + (spanning every site in a network including secondary storage) + with *Persistence By Reachability*. To provide the illusion of a + shared address space across the network, despite the fact that + site memories are disjoint, Larchant implements a *distributed + shared memory* mechanism. Reachability is accessed by tracing the + pointer graph, starting from the persistent root, and reclaiming + unreachable objects. This is the task of *Garbage Collection* + (GC). + + GC was until recently thought to be intractable in a large-scale + system, due to problems of scale, incoherence, asynchrony, and + performance. This thesis presents the solutions that Larchant + proposes to these problems. + + The GC algorithm in Larchant combines tracing and + reference-listing. It traces whenever economically feasible, i.e., + as long as the memory subset being collected remains local to a + site, and counts references that would cost I/O traffic to trace. + GC is orthogonal to coherence, i.e., makes progress even if only + incoherent replicas are locally available. The garbage collector + runs concurrently and asynchronously to applications. The + reference-listing boundary changes dynamically and seamlessly, and + independently at each site, in order to collect cycles of + unreachable objects. + + We prove formally that our GC algorithm is correct, i.e., it is + safe and live. The performance results from our Larchant prototype + show that our design goals (scalability, coherence orthogonality, + and good performance) are fulfilled. + +* .. _FS98: + + Paulo Ferreira & Marc Shapiro. 1998. "`Modelling a Distributed Cached Store for Garbage Collection `_". Springer-Verlag. Proceedings of 12th European Conference on Object-Oriented Programming, ECOOP98, LNCS 1445. + + .. admonition:: Abstract + + Caching and persistence support efficient, convenient and + transparent distributed data sharing. The most natural model of + persistence is persistence by reachability, managed automatically + by a garbage collector (GC). We propose a very general model of + such a system (based on distributed shared memory) and a scalable, + asynchronous distributed GC algorithm. Within this model, we show + sufficient and widely applicable correctness conditions for the + interactions between applications, store, memory, coherence, and + GC. + + The GC runs as a set of processes (local to each participating + machine) communicating by asynchronous messages. Collection does + not interfere with applications by setting locks, polluting + caches, or causing I/O; this requirement raised some novel and + interesting challenges which we address in this article. The + algorithm is safe and live; it is not complete, i.e. it collects + some distributed cycles of garbage but not necessarily all. + +* .. _FW76: + + Daniel P Friedman, David S. Wise. 1976. "`Garbage collecting a heap which includes a scatter table `_". *Information Processing Letters.* 5, 6 (December 1976): 161--164. + + .. admonition:: Abstract + + A new algorithm is introduced for garbage collecting a heap which + contains shared data structures accessed from a scatter table. The + scheme provides for the purging of useless entries from the + scatter table with no traverslas beyond the two required by + classic collection schemes. For languages which use scatter tables + to sustain unique existence of complex structures, like natural + variables of SNOBOL, it indirectly allows liberal use of a single + scatter table by ensuring efficient deletion of useless entries. + Since the scatter table is completely restructured during the + course of execution, the hashing scheme itself is easily altered + during garbage collection whenever skewed loading of the scatter + table warrants abandonment of the old hashing. This procedure is + applicable to the maintenance of dynamic structures such as those + in information retrieval schemes or in languages like LISP and + SNOBOL. + +* .. _FW77: + + Daniel P Friedman, David S. Wise. 1977. "`The One Bit Reference Count `_". *BIT.* (17)3: 351--359. + + .. admonition:: Abstract + + Deutsch and Bobrow propose a storage reclamation scheme for a heap + which is a hybrid of garbage collection and reference counting. + The point of the hybrid scheme is to keep track of very low + reference counts between necessary invocation of garbage + collection so that nodes which are allocated and rather quickly + abandoned can be returned to available space, delaying necessity + for garbage collection. We show how such a scheme may be + implemented using the mark bit already required in every node by + the garbage collector. Between garbage collections that bit is + used to distinguish nodes with a reference count known to be one. + A significant feature of our scheme is a small cache of references + to nodes whose implemented counts "ought to be higher" which + prevents the loss of logical count information in simple + manipulations of uniquely referenced structures. + +* .. _FW79: + + Daniel P Friedman, David S. Wise. 1979. "`Reference counting can manage the circular environments of mutual recursion `_". *Information Processing Letters.* 8, 1 (January 1979): 41--45. + + .. admonition:: From the introduction + + In this note we advance reference counting as a storage management + technique viable for implementing recursive languages like ISWIM + or pure LISP with the ``labels`` construct for implementing mutual + recursion from SCHEME. ``Labels`` is derived from ``letrec`` and + displaces the ``label`` operator, a version of the paradoxical + Y-combinator. The key observation is that the requisite circular + structure (which ordinarily cripples reference counts) occurs only + within the language--rather than the user--structure, and that the + references into this structure are well-controlled. + +* .. _GZH93: + + Dirk Grunwald, Benjamin Zorn, R. Henderson. 1993. "`Improving the Cache Locality of Memory Allocation `_". SIGPLAN. SIGPLAN '93, Conference on PLDI, June 1993, Albuquerque, New Mexico. + + .. admonition:: Abstract + + The allocation and disposal of memory is a ubiquitous operation in + most programs. Rarely do programmers concern themselves with + details of memory allocators; most assume that memory allocators + provided by the system perform well. This paper presents a + performance evaluation of the reference locality of dynamic + storage allocation algorithms based on trace-driven simulation of + five large allocation-intensive C programs. In this paper, we show + how the design of a memory allocator can significantly affect the + reference locality for various applications. Our measurements show + that poor locality in sequential-fit algorithms reduces program + performance, both by increasing paging and cache miss rates. While + increased paging can be debilitating on any architecture, cache + misses rates are also important for modern computer architectures. + We show that algorithms attempting to be space-efficient, by + coalescing adjacent free objects show poor reference locality, + possibly negating the benefits of space efficiency. At the other + extreme, algorithms can expend considerable effort to increase + reference locality yet gain little in total execution performance. + Our measurements suggest an allocator design that is both very + fast and has good locality of reference. + +* .. _GRUN92: + + Dirk Grunwald & Benjamin Zorn. 1993. "`CustoMalloc: Efficient Synthesized Memory Allocators `_". Software -- Practice and Experience. 23(8):851--869. + + .. admonition:: Abstract + + The allocation and disposal of memory is a ubiquitous operation in + most programs. Rarely do programmers concern themselves with + details of memory allocators; most assume that memory allocators + provided by the system perform well. Yet, in some applications, + programmers use domain-specific knowledge in an attempt to improve + the speed or memory utilization of memory allocators. In this + paper, we describe a program (CustoMalloc) that synthesizes a + memory allocator customized for a specific application. Our + experiments show that the synthesized allocators are uniformly + faster than the common binary-buddy (BSD) allocator, and are more + space efficient. Constructing a custom allocator requires little + programmer effort. The process can usually be accomplished in a + few minutes, and yields results superior even to domain-specific + allocators designed by programmers. Our measurements show the + synthesized allocators are from two to ten times faster than + widely used allocators. + +* .. _GUDEMAN93: + + David Gudeman. 1993. "`Representing Type Information in Dynamically Typed Languages `_". University of Arizona at Tucson. Technical Report TR 93-27. + + .. admonition:: Abstract + + This report is a discussion of various techniques for representing + type information in dynamically typed languages, as implemented on + general-purpose machines (and costs are discussed in terms of + modern RISC machines). It is intended to make readily available a + large body of knowledge that currently has to be absorbed + piecemeal from the literature or re-invented by each language + implementor. This discussion covers not only tagging schemes but + other forms of representation as well, although the discussion is + strictly limited to the representation of type information. It + should also be noted that this report does not purport to contain + a survey of the relevant literature. Instead, this report gathers + together a body of folklore, organizes it into a logical + structure, makes some generalizations, and then discusses the + results in terms of modern hardware. + +* .. _HARRIS99: + + Timothy Harris. 1999. "`Early storage reclamation in a tracing garbage collector `_". ACM. ACM SIG-PLAN Notices 34:4, pp. 46--53. + + .. admonition:: Abstract + + This article presents a technique for allowing the early recovery + of storage space occupied by garbage data. The idea is similar to + that of generational garbage collection, except that the heap is + partitioned based on a static analysis of data type definitions + rather than on the approximate age of allocated objects. A + prototype implementation is presented, along with initial results + and ideas for future work. + +* .. _HENRIK94: + + Roger Henriksson. 1994. "Scheduling Real Time Garbage Collection". Department of Computer Science at Lund University. LU-CS-TR:94-129. + + .. admonition:: Abstract + + This paper presents a new model for scheduling the work of an + incremental garbage collector in a system with hard real time + requirements. The method utilizes the fact that just some of the + processes in the system have to meet hard real time requirements + and that these processes typically run periodically, a fact that + we can make use of when scheduling the garbage collection. The + work of the collector is scheduled to be performed in the pauses + between the critical processes and is suspended when the processes + with hard real time requirements run. It is shown that this + approach is feasible for many real time systems and that it leaves + the time-critical parts of the system undisturbed from garbage + collection induced delays. + +* .. _HENRIK96: + + Roger Henriksson. 1996. "`Adaptive Scheduling of Incremental Copying Garbage Collection for Interactive Applications `_". NWPER96. + + .. admonition:: Abstract + + Incremental algorithms are often used to interleave the work of a + garbage collector with the execution of an application program, + the intention being to avoid long pauses. However, overestimating + the worst-case storage needs of the program often causes all the + garbage collection work to be performed in the beginning of the + garbage collection cycles, slowing down the application program to + an unwanted degree. This paper explores an approach to + distributing the work more evenly over the garbage collection + cycle. + +* .. _HENRIKSSON98: + + Roger Henriksson. 1998. "`Scheduling Garbage Collection in Embedded Systems `_". Department of Computer Science at Lund University. Ph.D. thesis. + + .. admonition:: Abstract + + The complexity of systems for automatic control and other + safety-critical applications grows rapidly. Computer software + represents an increasing part of the complexity. As larger systems + are developed, we need to find scalable techniques to manage the + complexity in order to guarantee high product quality. Memory + management is a key quality factor for these systems. Automatic + memory management, or garbage collection, is a technique that + significantly reduces the complex problem of correct memory + management. The risk of software errors decreases and development + time is reduced. + + Garbage collection techniques suitable for interactive and soft + real-time systems exist, but few approaches are suitable for + systems with hard real-time requirements, such as control systems + (embedded systems). One part of the problem is solved by + incremental garbage collection algorithms, which have been + presented before. We focus on the scheduling problem which forms + the second part of the problem, i.e. how the work of a garbage + collector should be scheduled in order to disturb the application + program as little as possible. It is studied how a priori + scheduling analysis of systems with automatic memory management + can be made. The field of garbage collection research is thus + joined with the field of scheduling analysis in order to produce a + practical synthesis of the two fields. + + A scheduling strategy is presented that employs the properties of + control systems to ensure that no garbage collection work is + performed during the execution of critical processes. The hard + real-time part of the system is thus never disturbed by garbage + collection work. Existing incremental garbage collection + algorithms are adapted to the presented strategy. Necessary + modifications of the algorithms and the real-time kernel are + discussed. A standard scheduling analysis technique, rate + monotonic analysis, is extended in order to make a priori analysis + of the schedulability of the garbage collector possible. + + The scheduling algorithm has been implemented in an industrially + relevant real-time environment in order to show that the strategy + is feasible in practice. The experimental evaluation shows that + predictable behaviour and sub-millisecond worst-case delays can be + achieved on standard hardware even by a non-optimized prototype + garbage collector. + +* .. _HOSKING91: + + Antony L. Hosking. 1991. "`Main memory management for persistence `_". ACM. Proceedings of the ACM OOPSLA'91 Workshop on Garbage Collection. + + .. admonition:: Abstract + + Reachability-based persistence imposes new requirements for main + memory management in general, and garbage collection in + particular. After a brief introduction to the characteristics and + requirements of reachability-based persistence, we present the + design of a run-time storage manager for Persistent Smalltalk and + Persistent Modula-3, which allows the reclamation of storage from + both temporary objects and buffered persistent objects. + +* .. _HMS92: + + Antony L. Hosking, J. Eliot B. Moss, Darko Stefanovic. 1992. "`A comparative performance evaluation of write barrier implementations `_". ACM. OOPSLA'92 Conference Proceedings, ACM SIGPLAN Notices 27(10), pp 92--109. + + .. admonition:: Abstract + + Generational garbage collectors are able to achieve very small + pause times by concentrating on the youngest (most recently + allocated) objects when collecting, since objects have been + observed to die young in many systems. Generational collectors + must keep track of all pointers from older to younger generations, + by “monitoring” all stores into the heap. This *write barrier* has + been implemented in a number of ways, varying essentially in the + granularity of the information observed and stored. Here we + examine a range of write barrier implementations and evaluate + their relative performance within a generation scavenging garbage + collector for Smalltalk. + +* .. _HH93: + + Antony L. Hosking, Richard L. Hudson. 1993. "`Remembered sets can also play cards `_". ACM. Proceedings of the ACM OOPSLA'93 Workshop on Memory Management and Garbage Collection. + + .. admonition:: Abstract + + Remembered sets and dirty bits have been proposed as alternative + implementations of the write barrier for garbage collection. There + are advantages to both approaches. Dirty bits can be efficiently + maintained with minimal, bounded overhead per store operation, + while remembered sets concisely, and accurately record the + necessary information. Here we present evidence to show that + hybrids can combine the virtues of both schemes and offer + competitive performance. Moreover, we argue that a hybrid can + better avoid the devils that are the downfall of the separate + alternatives. + +* .. _HM93: + + Antony L. Hosking, J. Eliot B. Moss. 1993. "`Protection traps and alternatives for memory management of an object-oriented language `_". ACM. Proceedings of the Fourteenth ACM Symposium on Operating Systems Principles, ACM Operating Systems Review 27(5), pp 106--119. + + .. admonition:: Abstract + + Many operating systems allow user programs to specify the + protection level (inaccessible, read-only, read-write) of pages in + their virtual memory address space, and to handle any protection + violations that may occur. Such page-protection techniques have + been exploited by several user-level algorithms for applications + including generational garbage collection and persistent stores. + Unfortunately, modern hardware has made efficient handling of page + protection faults more difficult. Moreover, page-sized granularity + may not match the natural granularity of a given application. In + light of these problems, we reevaluate the usefulness of + page-protection primitives in such applications, by comparing the + performance of implementations that make use of the primitives + with others that do not. Our results show that for certain + applications software solutions outperform solutions that rely on + page-protection or other related virtual memory primitives. + +* .. _HMDW91: + + Richard L. Hudson, J. Eliot B. Moss, Amer Diwan, Christopher F. Weight. 1991. "`A Language-Independent Garbage Collector Toolkit `_". University of Massachusetts at Amherst. COINS Technical Report 91--47. + + .. admonition:: Abstract + + We describe a memory management toolkit for language implementors. + It offers efficient and flexible generation scavenging garbage + collection. In addition to providing a core of + language-independent algorithms and data structures, the toolkit + includes auxiliary components that ease implementation of garbage + collection for programming languages. We have detailed designs for + Smalltalk and Modula-3 and are confident the toolkit can be used + with a wide variety of languages. The toolkit approach is itself + novel, and our design includes a number of additional innovations + in flexibility, efficiency, accuracy, and cooperation between the + compiler and the collector. + +* .. _HM92: + + Richard L. Hudson, J. Eliot B. Moss. 1992. "`Incremental Collection of Mature Objects `_". Springer-Verlag. LNCS #637 International Workshop on Memory Management, St. Malo, France, Sept. 1992, pp. 388--403. + + .. admonition:: Abstract + + We present a garbage collection algorithm that extends + generational scavenging to collect large older generations (mature + objects) non-disruptively. The algorithm's approach is to process + bounded-size pieces of mature object space at each collection; the + subtleties lie in guaranteeing that it eventually collects any and + all garbage. The algorithm does not assume any special hardware or + operating system support, e.g., for forwarding pointers or + protection traps. The algorithm copies objects, so it naturally + supports compaction and reclustering. + +* .. _HMMM97: + + Richard L. Hudson, Ron Morrison, J. Eliot B. Moss, David S. Munro. 1997. "`Garbage Collecting the World: One Car at a Time `_". ACM. Proc. OOPSLA 97, pp. 162--175. + + .. admonition:: Abstract + + A new garbage collection algorithm for distributed object systems, + called DMOS (Distributed Mature Object Space), is presented. It is + derived from two previous algorithms, MOS (Mature Object Space), + sometimes called the train algorithm, and PMOS (Persistent Mature + Object Space). The contribution of DMOS is that it provides the + following unique combination of properties for a distributed + collector: safety, completeness, non-disruptiveness, + incrementality, and scalability. Furthermore, the DMOS collector + is non-blocking and does not use global tracing. + +* .. _JOHNSTONE97: + + Mark S. Johnstone. 1997. "`Non-Compacting Memory Allocation and Real-Time Garbage Collection `_". University of Texas at Austin. + + .. admonition:: Abstract + + Dynamic memory use has been widely recognized to have profound + effects on program performance, and has been the topic of many + research studies over the last forty years. In spite of years of + research, there is considerable confusion about the effects of + dynamic memory allocation. Worse, this confusion is often + unrecognized, and memory allocators are widely thought to be + fairly well understood. + + In this research, we attempt to clarify many issues for both + manual and automatic non-moving memory management. We show that + the traditional approaches to studying dynamic memory allocation + are unsound, and develop a sound methodology for studying this + problem. We present experimental evidence that fragmentation costs + are much lower than previously recognized for most programs, and + develop a framework for understanding these results and enabling + further research in this area. For a large class of programs using + well-known allocation policies, we show that fragmentation costs + are near zero. We also study the locality effects of memory + allocation on programs, a research area that has been almost + completely ignored. We show that these effects can be quite + dramatic, and that the best allocation policies in terms of + fragmentation are also among the best in terms of locality at both + the cache and virtual memory levels of the memory hierarchy. + + We extend these fragmentation and locality results to real-time + garbage collection. We have developed a hard real-time, + non-copying generational garbage collector which uses a + write-barrier to coordinate collection work only with + modifications of pointers, therefore making coordination costs + cheaper and more predictable than previous approaches. We combine + this write-barrier approach with implicit non-copying reclamation, + which has most of the advantages of copying collection (notably + avoiding both the sweep phase required by mark-sweep collectors, + and the referencing of garbage objects when reclaiming their + space), without the disadvantage of having to actually copy the + objects. In addition, we present a model for non-copying + implicit-reclamation garbage collection. We use this model to + compare and contrast our work with that of others, and to discuss + the tradeoffs that must be made when developing such a garbage + collector. + +* .. _JW98: + + Mark S. Johnstone, Paul R. Wilson. 1998. "`The Memory Fragmentation Problem: Solved? `_". ACM. ISMM'98 pp. 26--36. + + .. admonition:: Abstract + + We show that for 8 real and varied C and C++ programs, several + conventional dynamic storage allocators provide near-zero + fragmentation, once overheads due to implementation details + (headers, alignment, etc.) are properly accounted for. This + substantially strengthens our previous results showing that the + memory fragmentation problem has generally been misunderstood, and + that good allocator policies can provide good memory usage for + most programs. The new results indicate that for most programs, + excellent allocator policies are readily available, and efficiency + of implementation is the major challenge. While we believe that + our experimental results are state-of-the-art and our methodology + is superior to most previous work, more work should be done to + identify and study unusual problematic program behaviors not + represented in our sample. + +* .. _JONES92: + + Richard E. Jones. 1992. "`Tail recursion without space leaks `_". *Journal of Functional Programming.* 2(1):73--79. + + .. admonition:: Abstract + + The G-machine is a compiled graph reduction machine for lazy + functional languages. The G-machine compiler contains many + optimisations to improve performance. One set of such + optimisations is designed to improve the performance of tail + recursive functions. Unfortunately the abstract machine is subject + to a space leak--objects are unnecessarily preserved by the + garbage collector. + + This paper analyses why a particular form of space leak occurs in + the G-machine, and presents some ideas for fixing this problem. + This phenomena in other abstract machines is also examined + briefly. + +* .. _JL92: + + Richard E. Jones, Rafael Lins. 1992. "`Cyclic weighted reference counting without delay `_". Computing Laboratory, The University of Kent at Canterbury. Technical Report 28-92. + + .. admonition:: Abstract + + Weighted Reference Counting is a low-communication distributed + storage reclamation scheme for loosely-coupled multiprocessors. + The algorithm we present herein extends weighted reference + counting to allow the collection of cyclic data structures. To do + so, the algorithm identifies candidate objects that may be part of + cycles and performs a tricolour mark-scan on their subgraph in a + lazy manner to discover whether the subgraph is still in use. The + algorithm is concurrent in the sense that multiple useful + computation processes and garbage collection processes can be + performed simultaneously. + +* .. _JONES96: + + Richard E. Jones, Rafael Lins. 1996. "`Garbage Collection: Algorithms for Automatic Dynamic Memory Management `_". Wiley. ISBN 0-471-94148-4. + + .. admonition:: From the back cover + + The memory storage requirements of complex programs are extremely + difficult to manage correctly by hand. A single error may lead to + indeterminate and inexplicable program crashes. Worse still, + failures are often unrepeatable and may surface only long after + the program has been delivered to the customer. The eradication of + memory errors typically consumes a substantial amount of + development time. And yet the answer is relatively easy -- garbage + collection; removing the clutter of memory management from module + interfaces, which then frees the programmer to concentrate on the + problem at hand rather than low-level book-keeping details. For + this reason, most modern object-oriented languages such as + Smalltalk, Eiffel, Java and Dylan, are supported by garbage + collection. Garbage collecting libraries are even available for + such uncooperative languages as C and C++. + + This book considers how dynamic memory can be recycled + automatically to guarantee error-free memory management. There is + an abundant but disparate literature on the subject, largely + confined to research papers. This book sets out to pool this + experience in a single accessible and unified framework. + + Each of the important algorithms is explained in detail, often + with illustrations of its characteristic features and animations + of its use. Techniques are described and compared for declarative + and imperative programming styles, for sequential, concurrent and + distributed architectures. + + For professionals developing programs from simple software tools + to complex systems, as well as for researchers and students + working in compiler construction, functional, logic and + object-oriented programming design, this book will provide not + only a clear introduction but also a convenient reference source + for modern garbage collection techniques. + +* .. _ACM98: + + Richard E. Jones. 1998. "`ISMM'98 International Symposium on Memory Management `_". ACM. ISBN 1-58113-114-3. + + .. admonition:: From the Preface + + The International Symposium on Memory Management is a forum for + research in several related areas of memory management, especially + garbage collectors and dynamic storage allocators. [...] The + nineteen papers selected for publication in this volume cover a + remarkably broad range of memory management topics from explicit + malloc-style allocation to automatic memory management, from + cache-conscious data layout to efficient management of distributed + references, from conservative to type-accurate garbage collection, + for applications ranging from user application to long-running + servers, supporting languages as different as C, C++, Modula-3, + Java, Eiffel, Erlang, Scheme, ML, Haskell and Prolog. + +* .. _JONES12: + + Richard E. Jones, Antony Hosking, and Eliot Moss. 2012. "`The Garbage Collection Handbook `_". Chapman & Hall. + +* .. _JOYNER96: + + Ian Joyner. 1996. "`C++??: A Critique of C++ `_.". + + .. admonition:: Abstract + + The C++?? Critique is an analysis of some of the flaws of C++. It + is by no means exhaustive, nor does it attempt to document every + little niggle with C++, rather concentrating on main themes. The + critique uses Java and Eiffel as comparisons to C++ to give a more + concrete feel to the criticisms, viewing conceptual differences + rather than syntactic ones as being more important. Some C++ + authors realising there are glaring deficiencies in C++ have + chosen to defend C++ by also being critical within their own work. + Most notable are Bjarne Stroustup's "Design and Evolution of C++," + and Scott Meyers' "Effective" and "More Effective C++." These warn + of many traps and pitfalls, but reach the curious conclusion that + since "good" C++ programmers are aware of these problems and know + how to avoid them, C++ is alright. + + The C++ critique makes many of the same criticisms, but comes to + the different conclusion that these pitfalls are not acceptable, + and should not be in a language used for modern large scale + software engineering. Clean design is more important than after + the fact warnings, and it is inconceivable that purchasers of end + user software would tolerate this tactic on the part of vendors. + The critique also takes a look at C, and concludes that many of + the features of C should be left out of modern languages, and that + C is a flawed base for a language. + +* .. _KANEFSKY89: + + Bob Kanefsky. 1989. "`Recursive Memory Allocation `_". Bob Kanefsky. Songworm 3, p.?. + +* .. _KQH98: + + Jin-Soo Kim, Xiaohan Qin, Yarsun Hsu. 1998. "`Memory Characterization of a Parallel Data Mining Workload `_". IEEE. Proc. Workload Characterization: Methodology and Case Studies, pp. . + + .. admonition:: Abstract + + This paper studies a representative of an important class of + emerging applications, a parallel data mining workload. The + application, extracted from the IBM Intelligent Miner, identifies + groups of records that are mathematically similar based on a + neural network model called self-organizing map. We examine and + compare in details two implementations of the application: + (1) temporal locality or working set sizes; (2) spatial locality + and memory block utilization; (3) communication characteristics + and scalability; and (4) TLB performance. + + First, we find that the working set hierarchy of the application + is governed by two parameters, namely the size of an input record + and the size of prototype array; it is independent of the number + of input records. Second, the application shows good spatial + locality, with the implementation optimized for sparse data sets + having slightly worse spatial locality. Third, due to the batch + update scheme, the application bears very low communication. + Finally, a 2-way set associative TLB may result in severely skewed + TLB performance in a multiprocessor environment caused by the + large discrepancy in the amount of conflict misses. Increasing the + set associativity is more effective in mitigating the problem than + increasing the TLB size. + +* .. _KH00: + + Jin-Soo Kim & Yarsun Hsu. 2000. "Memory system behavior of Java programs: methodology and analysis". ACM. Proc. International conference on measurements and modeling of computer systems, pp. 264--274. + + .. admonition:: Abstract + + This paper studies the memory system behavior of Java programs by + analyzing memory reference traces of several SPECjvm98 + applications running with a Just-In-Time (JIT) compiler. Trace + information is collected by an exception-based tracing tool called + JTRACE, without any instrumentation to the Java programs or the + JIT compiler.First, we find that the overall cache miss ratio is + increased due to garbage collection, which suffers from higher + cache misses compared to the application. We also note that going + beyond 2-way cache associativity improves the cache miss ratio + marginally. Second, we observe that Java programs generate a + substantial amount of short-lived objects. However, the size of + frequently-referenced long-lived objects is more important to the + cache performance, because it tends to determine the application's + working set size. Finally, we note that the default heap + configuration which starts from a small initial heap size is very + inefficient since it invokes a garbage collector frequently. + Although the direct costs of garbage collection decrease as we + increase the available heap size, there exists an optimal heap + size which minimizes the total execution time due to the + interaction with the virtual memory performance. + +* .. _KOLODNER92: + + Elliot K. Kolodner. 1992. "Atomic Incremental Garbage Collection and Recovery for a Large Stable Heap". Laboratory for Computer Science at MIT. MIT-LCS-TR-534. + + .. admonition:: Abstract + + A stable heap is a storage that is managed automatically using + garbage collection, manipulated using atomic transactions, and + accessed using a uniform storage model. These features enhance + reliability and simplify programming by preventing errors due to + explicit deallocation, by masking failures and concurrency using + transactions, and by eliminating the distinction between accessing + temporary storage and permanent storage. Stable heap management is + useful for programming language for reliable distributed + computing, programming languages with persistent storage, and + object-oriented database systems. Many applications that could + benefit from a stable heap (e.g., computer-aided design, + computer-aided software engineering, and office information + systems) require large amounts of storage, timely responses for + transactions, and high availability. We present garbage collection + and recovery algorithms for a stable heap implementation that meet + these goals and are appropriate for stock hardware. The collector + is incremental: it does not attempt to collect the whole heap at + once. The collector is also atomic: it is coordinated with the + recovery system to prevent problems when it moves and modifies + objects . The time for recovery is independent of heap size, and + can be shortened using checkpoints. + +* .. _LK98: + + Per-Åke Larson & Murali Krishnan. 1998. "`Memory Allocation for Long-Running Server Applications `_". ACM. ISMM'98 pp. 176--185. + + .. admonition:: Abstract + + Prior work on dynamic memory allocation has largely neglected + long-running server applications, for example, web servers and + mail servers. Their requirements differ from those of one-shot + applications like compilers or text editors. We investigated how + to build an allocator that is not only fast and memory efficient + but also scales well on SMP machines. We found that it is not + sufficient to focus on reducing lock contention. Only limited + improvement can be achieved this way; higher speedups require a + reduction in cache misses and cache invalidation traffic. We then + designed and prototyped a new allocator, called Lkmalloc, targeted + for both traditional applications and server applications. + LKmalloc uses several subheaps, each one with a separate set of + free lists and memory arena. A thread always allocates from the + same subheap but can free a block belonging to any subheap. A + thread is assigned to a subheap by hashing on its thread ID. We + compared its performance with several other allocators on a + server-like, simulated workload and found that it indeed scales + well and is quite fast but could use memory more efficiently. + +* .. _LH83: + + Henry Lieberman & Carl Hewitt. 1983. "`A real-time garbage collector based on the lifetimes of objects `_". ACM. 26(6):419--429. + + .. admonition:: Abstract + + In previous heap storage systems, the cost of creating objects and + garbage collection is independent of the lifetime of the object. + Since objects with short lifetimes account for a large portion of + storage use, it is worth optimizing a garbage collector to reclaim + storage for these objects more quickly. The garbage collector + should spend proportionately less effort reclaiming objects with + longer lifetimes. We present a garbage collection algorithm that + (1) makes storage for short-lived objects cheaper than storage for + long-lived objects, (2) that operates in real-time--object + creation and access times are bounded, (3) increases locality of + reference, for better virtual memory performance, (4) works well + with multiple processors and a large address space. + +* .. _MM59: + + J. McCarthy, M. L. Minsky. 1959. "`Artificial Intelligence, Quarterly Progress Report no. 53 `_". Research Laboratory of Electronics at MIT. + +* .. _MCCARTHY60: + + J. McCarthy. 1960. "`Recursive Functions of Symbolic Expressions and Their Computation by Machine `_". CACM. + + .. admonition:: Abstract + + A programming system called LISP (for LISt Processor) has been + developed for the IBM 704 computer by the Artificial Intelligence + group at M.I.T. The system was designed to facilitate experiments + with a proposed system called the Advice Taker, whereby a machine + could be instructed to handle declarative as well as imperative + sentences and could exhibit "common sense" in carrying out its + instructions. The original proposal for the Advice Taker was made + in November 1958. The main requirement was a programming system + for manipulating expressions representing formalized declarative + and imperative sentences so that the Advice Taker could make + deductions. + + In the course of its development the LISP system went through + several stages of simplification and eventually came to be based + on a scheme for representing the partial recursive functions of a + certain class of symbolic expressions. This representation is + independent of the IBM 704 computer, or of any other electronic + computer, and it now seems expedient to expound the system by + starting with the class of expressions called S-expressions and + the functions called S-functions. + +* .. _MCCARTHY79: + + John McCarthy. 1979. "`History of Lisp `_". In *History of programming languages I*, pp. 173--185. ACM. + +* .. _PTM98: + + Veljko Milutinovic, Jelica Protic, Milo Tomasevic. 1997. "`Distributed shared memory: concepts and systems `_". IEEE Computer Society Press. ISBN 0-8186-7737-6. + + .. admonition:: From the publisher's catalog + + Presents a survey of both distributed shared memory (DSM) efforts + and commercial DSM systems. The book discusses relevant issues + that make the concept of DSM one of the most attractive approaches + for building large-scale, high-performance multiprocessor systems. + Its text provides a general introduction to the DSM field as well + as a broad survey of the basic DSM concepts, mechanisms, design + issues, and systems. + + Distributed Shared Memory concentrates on basic DSM algorithms, + their enhancements, and their performance evaluation. In addition, + it details implementations that employ DSM solutions at the + software and the hardware level. The book is a research and + development reference that provides state-of-the art information + that will be useful to architects, designers, and programmers of + DSM systems. + +* .. _MINSKY63: + + M. L. Minsky. 1963. "`A LISP Garbage Collector Algorithm Using Serial Secondary Storage `_". MIT. Memorandum MAC-M-129, Artificial Intelligence Project, Memo 58 (revised). + + .. admonition:: Abstract + + This paper presents an algorithm for reclaiming unused free + storage memory cells is LISP. It depends on availability of a fast + secondary storage device, or a large block of available temporary + storage. For this price, we get 1. Packing of free-storage into a + solidly packed block. 2. Smooth packing of arbitrary linear blocks + and arrays. 3. The collector will handle arbitrarily complex + re-entrant list structure with no introduction of spurious copies. + 4. The algorithm is quite efficient; the marking pass visits words + at most twice and usually once, and the loading pass is linear. + 5. The system is easily modified to allow for increase in size of + already fixed consecutive blocks, provide one can afford to + initiate a collection pass or use a modified array while waiting + for such a pass to occur. + +* .. _MOON84: + + David Moon. 1984. "`Garbage Collection in a Large Lisp System `_". ACM. Symposium on Lisp and Functional Programming, August 1984. + + .. admonition:: Abstract + + This paper discusses garbage collection techniques used in a + high-performance Lisp implementation with a large virtual memory, + the Symbolics 3600. Particular attention is paid to practical + issues and experience. In a large system problems of scale appear + and the most straightforward garbage-collection techniques do not + work well. Many of these problems involve the interaction of the + garbage collector with demand-paged virtual memory. Some of the + solutions adopted in the 3600 are presented, including incremental + copying garbage collection, approximately depth-first copying, + ephemeral objects, tagged architecture, and hardware assists. We + discuss techniques for improving the efficiency of garbage + collection by recognizing that objects in the Lisp world have a + variety of lifetimes. The importance of designing the architecture + and the hardware to facilitate garbage collection is stressed. + +* .. _MOON85: + + David Moon. 1985. "Architecture of the Symbolics 3600". IEEE. 12th International Symposium on Computer Architecture, pp. 76--83. + +* .. _MOON87: + + David Moon. 1990. "Symbolics Architecture". Wiley. Chapter 3 of *Computers for Artificial Intelligence Processing*, ISBN 0-471-84811-5. + +* .. _MOON91: + + David Moon. 1991. "Genera Retrospective". IEEE. 1991 International Workshop on Object Orientation in Operating Systems, order #2265. + +* .. _MORDEC84: + + Ben-Ari Mordechai. 1984. "Algorithms for On-the-fly Garbage Collection". *TOPLAS* 6(3): 333--344 (1984). + +* .. _MOREAU98: + + Luc Moreau. 1998. "`Hierarchical Distributed Reference Counting `_". ACM. ISMM'98 pp. 57--67. + + .. admonition:: Abstract + + Massively distributed computing is a challenging problem for + garbage collection algorithm designers as it raises the issue of + scalability. The high number of hosts involved in a computation + can require large tables for reference listing, whereas the lack + of information sharing between hosts in a same locality can entail + redundant GC traffic. In this paper, we argue that a conceptual + hierarchical organisation of massive distributed computations can + solve this problem. By conceptual hierarchical organisation, we + mean that processors are still able to communicate in a peer to + peer manner using their usual communication mechanism, but GC + messages will be routed as if processors were organised in + hierarchy. We present an extension of a distributed reference + counting algorithm that uses such a hierarchical organisation. It + allows us to bound table sizes by the number of hosts in a domain, + and it allows us to share GC information between hosts in a same + locality in order to reduce cross-network GC traffic. + +* .. _MFH95: + + Greg Morrisett, Matthias Felleisen, Robert Harper. 1995. "`Abstract Models of Memory Management `_". Carnegie Mellon University. CMU-CS-FOX-95-01. + + .. admonition:: Abstract + + Most specifications of garbage collectors concentrate on the + low-level algorithmic details of how to find and preserve + accessible objects. Often, they focus on bit-level manipulations + such as "scanning stack frames," "marking objects," "tagging + data," etc. While these details are important in some contexts, + they often obscure the more fundamental aspects of memory + management: what objects are garbage and why? + + We develop a series of calculi that are just low-level enough that + we can express allocation and garbage collection, yet are + sufficiently abstract that we may formally prove the correctness + of various memory management strategies. By making the heap of a + program syntactically apparent, we can specify memory actions as + rewriting rules that allocate values on the heap and automatically + dereference pointers to such objects when needed. This formulation + permits the specification of garbage collection as a relation that + removes portions of the heap without affecting the outcome of + evaluation. + + Our high-level approach allows us to specify in a compact manner a + wide variety of memory management techniques, including standard + trace-based garbage collection (i.e., the family of copying and + mark/sweep collection algorithms), generational collection, and + type-based, tag-free collection. Furthermore, since the definition + of garbage is based on the semantics of the underlying language + instead of the conservative approximation of inaccessibility, we + are able to specify and prove the idea that type inference can be + used to collect some objects that are accessible but never used. + +* .. _MBMM99: + + David S. Munro, Alfred Brown, Ron Morrison, J. Eliot B. Moss. 1999. "`Incremental Garbage Collection of a Persistent Object Store using PMOS `_". Morgan Kaufmann. in Advances in Persistent Object Systems, pp. 78--91. + + .. admonition:: Abstract + + PMOS is an incremental garbage collector designed specifically to + reclaim space in a persistent object store. It is complete in that + it will, after a finite number of invocations, reclaim all + unreachable storage. PMOS imposes minimum constraints on the order + of collection and offers techniques to reduce the I/O traffic + induced by the collector. Here we present the first implementation + of the PMOS collector called PMOS#1. The collector has been + incorporated into the stable heap layer of the generic persistent + object store used to support a number of languages including + Napier88. Our main design goals are to maintain the independence + of the language from the store and to retain the existing store + interface. The implementation has been completed and tested using + a Napier88 system. The main results of this work show that the + PMOS collector is implementable in a persistent store and that it + can be built without requiring changes to the language + interpreter. Initial performance measurements are reported. These + results suggest however, that effective use of PMOS requires + greater co-operation between language and store. + +* .. _NOPH92: + + Scott Nettles, James O'Toole, David Pierce, Nickolas Haines. 1992. "`Replication-Based Incremental Copying Collection `_". IWMM'92. + + .. admonition:: Abstract + + We introduce a new replication-based copying garbage collection + technique. We have implemented one simple variation of this method + to provide incremental garbage collection on stock hardware with + no special operating system or virtual memory support. The + performance of the prototype implementation is excellent: major + garbage collection pauses are completely eliminated with only a + slight increase in minor collection pause times. + + Unlike the standard copying algorithm, the replication-based + method does not destroy the original replica when a copy is + created. Instead, multiple copies may exist, and various standard + strategies for maintaining consistency may be applied. In our + implementation for Standard ML of New Jersey, the mutator + continues to use the from-space replicas until the collector has + achieved a consistent replica of all live data in to-space. + + We present a design for a concurrent garbage collector using the + replication-based technique. We also expect replication-based GC + methods to be useful in providing services for persistence and + distribution, and briefly discuss these possibilities. + +* .. _NETTLES92: + + Scott Nettles. 1992. "`A Larch Specification of Copying Garbage Collection `_". Carnegie Mellon University. CMU-CS-92-219. + + .. admonition:: Abstract + + Garbage collection (GC) is an important part of many language + implementations. One of the most important garbage collection + techniques is copying GC. This paper consists of an informal but + abstract description of copying collection, a formal specification + of copying collection written in the Larch Shared Language and the + Larch/C Interface Language, a simple implementation of a copying + collector written in C, an informal proof that the implementation + satisfies the specification, and a discussion of how the + specification applies to other types of copying GC such as + generational copying collectors. Limited familiarity with copying + GC or Larch is needed to read the specification. + +* .. _NO93A: + + Scott Nettles & James O'Toole. 1993. "Implementing Orthogonal Persistence: A Simple Optimization Using Replicating Collection". USENIX. IWOOOS'93. + + .. admonition:: Abstract + + Orthogonal persistence provides a safe and convenient model of + object persistence. We have implemented a transaction system which + supports orthogonal persistence in a garbage-collected heap. In + our system, replicating collection provides efficient concurrent + garbage collection of the heap. In this paper, we show how + replicating garbage collection can also be used to reduce commit + operation latencies in our implementation. + + We describe how our system implements transaction commit. We + explain why the presence of non-persistent objects can add to the + cost of this operation. We show how to eliminate these additional + costs by using replicating garbage collection. The resulting + implementation of orthogonal persistence should provide + transaction performance that is independent of the quantity of + non-persistent data in use. We expect efficient support for + orthogonal persistence to be valuable in operating systems + applications which use persistent data. + +* .. _NO93: + + Scott Nettles & James O'Toole. 1993. "`Real-Time Replication Garbage Collection `_". ACM. PLDI'93. + + .. admonition:: Abstract + + We have implemented the first copying garbage collector that + permits continuous unimpeded mutator access to the original + objects during copying. The garbage collector incrementally + replicates all accessible objects and uses a mutation log to bring + the replicas up-to-date with changes made by the mutator. An + experimental implementation demonstrates that the costs of using + our algorithm are small and that bounded pause times of 50 + milliseconds can be readily achieved. + +* .. _NIELSEN77: + + Norman R. Nielsen. 1977. "Dynamic Memory Allocation in Computer Simulation". ACM. CACM 20:11. + + .. admonition:: Abstract + + This paper investigates the performance of 35 dynamic memory + allocation algorithms when used to service simulation programs as + represented by 18 test cases. Algorithm performance was measured + in terms of processing time, memory usage, and external memory + fragmentation. Algorithms maintaining separate free space lists + for each size of memory block used tended to perform quite well + compared with other algorithms. Simple algorithms operating on + memory ordered lists (without any free list) performed + surprisingly well. Algorithms employing power-of-two block sizes + had favorable processing requirements but generally unfavorable + memory usage. Algorithms employing LIFO, FIFO, or memory ordered + free lists generally performed poorly compared with others. + +* .. _OTOOLE90: + + James O'Toole. 1990. "Garbage Collecting Locally". + + .. admonition:: Abstract + + Generational garbage collection is a simple technique for + automatic partial memory reclamation. In this paper, I present the + basic mechanics of generational collection and discuss its + characteristics. I compare several published algorithms and argue + that fundamental considerations of locality, as reflected in the + changing relative speeds of processors, memories, and disks, + strongly favor a focus on explicit optimization of I/O + requirements during garbage collection. I show that this focus on + I/O costs due to memory hierarchy debunks a well-known claim about + the relative costs of garbage collection and stack allocation. I + suggest two directions for future research in this area and + discuss some simple architectural changes in virtual memory + interfaces which may enable efficient garbage collector + utilization of standard virtual memory hardware. + +* .. _ON94: + + James O'Toole & Scott Nettles. 1994. "`Concurrent Replicating Garbage Collection `_". ACM. LFP'94. + + .. admonition:: Abstract + + We have implemented a concurrent copying garbage collector that + uses replicating garbage collection. In our design, the client can + continuously access the heap during garbage collection. No + low-level synchronization between the client and the garbage + collector is required on individual object operations. The garbage + collector replicates live heap objects and periodically + synchronizes with the client to obtain the client's current root + set and mutation log. An experimental implementation using the + Standard ML of New Jersey system on a shared-memory multiprocessor + demonstrates excellent pause time performance and moderate + execution time speedups. + +* .. _JRR99: + + Simon Peyton Jones, Norman Ramsey, Fermin Reig. 1999. "`C--: a portable assembly language that supports garbage collection `_". Springer-Verlag. International Conference on Principles and Practice of Declarative Programming 1999, LNCS 1702, pp. 1--28. + + .. admonition:: Abstract + + For a compiler writer, generating good machine code for a variety + of platforms is hard work. One might try to reuse a retargetable + code generator, but code generators are complex and difficult to + use, and they limit one's choice of implementation language. One + might try to use C as a portable assembly language, but C limits + the compiler writer's flexibility and the performance of the + resulting code. The wide use of C, despite these drawbacks, argues + for a portable assembly language. C-- is a new language designed + expressly for this purpose. The use of a portable assembly + language introduces new problems in the support of such high-level + run-time services as garbage collection, exception handling, + concurrency, profiling, and debugging. We address these problems + by combining the C-- language with a C-- run-time interface. The + combination is designed to allow the compiler writer a choice of + source-language semantics and implementation techniques, while + still providing good performance. + +* .. _PIEPER93: + + John S. Pieper. 1993. "Compiler Techniques for Managing Data Motion". Carnegie Mellon University. Technical report number CMU-CS-93-217. + + .. admonition:: Abstract + + Software caching, automatic algorithm blocking, and data overlays + are different names for the same problem: compiler management of + data movement throughout the memory hierarchy. Modern + high-performance architectures often omit hardware support for + moving data between levels of the memory hierarchy: iWarp does not + include a data cache, and Cray supercomputers do not have virtual + memory. These systems have effectively traded a more complicated + programming model for performance by replacing a + hardware-controlled memory hierarchy with a simple fast memory. + The simpler memories have less logic in the critical path, so the + cycle time of the memories is improved. + + For programs which fit in the resulting memory, the extra + performance is great. Unfortunately, the driving force behind + supercomputing today is a class of very large scientific problems, + both in terms of computation time and in terms of the amount of + data used. Many of these programs do not fit in the memory of the + machines available. When architects trade hardware support for + data migration to gain performance, control of the memory + hierarchy is left to the programmer. Either the program size must + be cut down to fit into the machine, or every loop which accesses + more data than will fit into memory must be restructured by hand. + This thesis describes how a compiler can relieve the programmer of + this burden, and automate data motion throughout the memory + hierarchy without direct hardware support. + + This works develops a model of how data is accessed within a + nested loop by typical scientific programs. It describes + techniques which can be used by compilers faced with the task of + managing data motion. The concentration is on nested loops which + process large data arrays using linear array subscripts. Because + the array subscripts are linear functions of the loop indices and + the loop indices form an integer lattice, linear algebra can be + applied to solve many compilation problems. + + The approach it to tile the iteration space of the loop nest. + Tiling allows the compiler to improve locality of reference. The + tiling basis matrix is chosen from a set of candidate vectors + which neatly divide the data set. The execution order of the tiles + is selected to maximize locality between tiles. Finally, the tile + sizes are chosen to minimize execution time. + + The approach has been applied to several common scientific loop + nests: matrix-matrix multiplication, QR-decomposition, and + LU-decomposition. In addition, an illustrative example from the + Livermore Loop benchmark set is examined. Although more compiler + time can be required in some cases, this technique produces better + code at no cost for most programs. + +* .. _PIRINEN98: + + Pekka P. Pirinen. 1998. "Barrier techniques for incremental tracing". ACM. ISMM'98 pp. 20--25. + + .. admonition:: Abstract + + This paper presents a classification of barrier techniques for + interleaving tracing with mutator operation during an incremental + garbage collection. The two useful tricolour invariants are + derived from more elementary considerations of graph traversal. + Barrier techniques for maintaining these invariants are classified + according to the action taken at the barrier (such as scanning an + object or changing its colour), and it is shown that the + algorithms described in the literature cover all the possibilities + except one. Unfortunately, the new technique is impractical. Ways + of combining barrier techniques are also discussed. + +* .. _PRINTEZIS96: + + Tony Printezis. 1996. "Disk Garbage Collection Strategies for Persistent Java". Proceedings of the First International Workshop on Persistence and Java. + + .. admonition:: Abstract + + This paper presents work currently in progress on Disk Garbage + Collection issues for PJava, an orthogonally persistent version of + Java. In particular, it concentrates on the initial Prototype of + the Disk Garbage Collector of PJava0 which has already been + implemented. This Prototype was designed to be very simple and + modular in order to be easily changed, evolved, improved, and + allow experimentation. Several experiments were performed in order + to test possible optimisations; these experiments concentrated on + the following four areas: a) efficient access to the store; b) + page-replacement algorithms; c) efficient discovery of live + objects during compaction; and d) dealing with forward references. + The paper presents a description of the Prototype's architecture, + the results of these experiments and related discussion, and some + future directions based on the experience gained from this work. + +* .. _PC96: + + Tony Printezis & Quentin Cutts. 1996. "Measuring the Allocation Rate of Napier88". Department of Computing Science at University of Glasgow. TR ?. + +* .. _REINHOLD93: + + M. B. Reinhold. 1993. "`Cache Performance of Garbage Collected Programming Languages `_". Laboratory for Computer Science at MIT. MIT/LCS/TR-581. + + .. admonition:: Abstract + + As processor speeds continue to improve relative to main-memory + access times, cache performance is becoming an increasingly + important component of program performance. Prior work on the + cache performance of garbage-collected programming languages has + either assumed or argued that conventional garbage-collection + methods will yield poor performance, and has therefore + concentrated on new collection algorithms designed specifically to + improve cache-level reference locality. This dissertation argues + to the contrary: Many programs written in garbage-collected + languages are naturally well-suited to the direct-mapped caches + typically found in modern computer systems. + + Using a trace-driven cache simulator and other analysis tools, + five nontrivial, long-running Scheme programs are studied. A + control experiment shows that the programs have excellent cache + performance without any garbage collection at all. A second + experiment indicates that the programs will perform well with a + simple and infrequently-run generational compacting collector. + + An analysis of the test programs' memory usage patterns reveals + that the mostly-functional programming style typically used in + Scheme programs, in combination with simple linear storage + allocation, causes most data objects to be dispersed in time and + space so that references to them cause little cache interference. + From this it follows that other Scheme programs, and programs + written in similar styles in different languages, should perform + well with a simple generational compacting collector; + sophisticated collectors intended to improve cache performance are + unlikely to be effective. The analysis also suggests that, as + locality becomes ever more important to program performance, + programs written in garbage-collected languages may turn out to + have significant performance advantage over programs written in + more conventional languages. + +* .. _ROBSON77: + + J. M. Robson. 1977. "Worst case fragmentation of first fit and best fit storage allocation strategies". ACM. ACM Computer Journal, 20(3):242--244. + +* .. _RR97: + + Gustavo Rodriguez-Rivera & Vince Russo. 1997. "Non-intrusive Cloning Garbage Collection with Stock Operating System Support". Software -- Practice and Experience. 27:8. + + .. admonition:: Abstract + + It is well accepted that automatic garbage collection simplifies + programming, promotes modularity, and reduces development effort. + However it is commonly believed that these advantages do not + counteract the perceived price: excessive overheads, possible long + pause times while garbage collections occur, and the need to + modify existing code. Even though there are publically available + garbage collector implementations that can be used in existing + programs, they do not guarantee short pauses, and some + modification of the application using them is still required. In + this paper we describe a snapshot-at-beginning concurrent garbage + collector algorithm and its implementation. This algorithm + guarantees short pauses, and can be easily implemented on stock + UNIX-like operating systems. Our results show that our collector + performs comparable to other garbage collection implementations on + uniprocessor machines and outperforms similar collectors on + multiprocessor machines. We also show our collector to be + competitive in performance with explicit deallocation. Our + collector has the added advantage of being non-intrusive. Using a + dynamic linking technique and effective root set inferencing, we + have been able to successfully run our collector even in + commercial programs where only the binary executable and no source + code is available. In this paper we describe our algorithm, its + implementation, and provide both an algorithmic and a performance + comparison between our collector and other similar garbage + collectors. + +* .. _ROJEMO95: + + Niklas Röjemo. 1995. "Highlights from nhc -- a space-efficient Haskell compiler". Chalmers University of Technology. + + .. admonition:: Abstract + + Self-compiling implementations of Haskell, i.e., those written in + Haskell, have been and, except one, are still space consuming + monsters. Object code size for the compilers themselves are 3-8Mb, + and they need 12-20Mb to recompile themselves. One reason for the + huge demands for memory is that the main goal for these compilers + is to produce fast code. However, the compiler described in this + paper, called "nhc" for "Nearly a Haskell Compiler", is the one + above mentioned exception. This compiler concentrates on keeping + memory usage down, even at a cost in time. The code produced is + not fast, but nhc is usable, and the resulting programs can be run + on computers with small memory. + + This paper describes some of the implementation choices done, in + the Haskell part of the source code, to reduce memory consumption + in nhc. It is possible to use these also in other Haskell + compilers with no, or very small, changes to their run-time + systems. + + Time is neither the main focus of nhc nor of this paper, but there + is nevertheless a small section about the speed of nhc. The most + notable observation concerning speed is that nhc spends + approximately half the time processing interface files, which is + much more than needed in the type checker. Processing interface + files is also the most space consuming part of nhc in most cases. + It is only when compiling source files with large sets of mutually + recursive functions that more memory is needed to type check than + to process interface files. + +* .. _ROJEMO95A: + + Niklas Röjemo. 1995. "Generational garbage collection for lazy functional languages without temporary space leaks". Chalmers University of Technology. + + .. admonition:: Abstract + + Generational garbage collection is an established method for + creating efficient garbage collectors. Even a simple + implementation where all nodes that survive one garbage collection + are *tenured*, i.e., moved to an old generation, works well in + strict languages. In lazy languages, however, such an + implementation can create severe *temporary space leaks*. The + temporary space leaks appear in programs that traverse large + lazily built data structures, e.g., a lazy list representing a + large file, where only a small part is needed at any time. A + simple generational garbage collector cannot reclaim the memory, + used by the lazily built list, at minor collections. The reason is + that at least one of the nodes in the list belongs to the old + generation, after the first minor collection, and will hold on to + the rest of the nodes in the list until the next major collection. + +* .. _RR96: + + Niklas Röjemo & Colin Runciman. 1996. "Lag, drag, void and use -- heap profiling and space-efficient compilation revisited". ACM, SIGPLAN. ICFP'96, ACM SIGPLAN Notices 31:6, ISBN 0-89791-770-7, pp. 34--41. + + .. admonition:: Abstract + + The context for this paper is functional computation by graph + reduction. Our overall aim is more efficient use of memory. The + specific topic is the detection of dormant cells in the live graph + -- those retained in heap memory though not actually playing a + useful role in computation. We describe a profiler that can + identify heap consumption by such 'useless' cells. Unlike heap + profilers based on traversals of the live heap, this profiler + works by examining cells post-mortem. The new profiler has + revealed a surprisingly large proportion of 'useless' cells, even + in some programs that previously seemed space-efficient such as + the bootstrapping Haskell compiler "nhc". + +* .. _RW99: + + David J. Roth, David S. Wise. 1999. "`One-bit counts between unique and sticky `_". ACM. ISMM'98, pp. 49--56. + + .. admonition:: Abstract + + Stoye's one-bit reference tagging scheme can be extended to local + counts of two or more via two strategies. The first, suited to + pure register transactions, is a cache of referents to two shared + references. The analog of Deutch's and Bobrow's multiple-reference + table, this cache is sufficient to manage small counts across + successive assignment statements. Thus, accurate reference counts + above one can be tracked for short intervals, like that bridging + one function's environment to its successor's. + + The second, motivated by runtime stacks that duplicate references, + avoids counting any references from the stack. It requires a local + pointer-inversion protocol in the mutator, but one still local to + the referent and the stack frame. Thus, an accurate reference + count of one can be maintained regardless of references from the + recursion stack. + +* .. _ROVNER85: + + Paul Rovner. 1985. "`On Adding Garbage Collection and Runtime Types to a Strongly-Typed, Statically-Checked, Concurrent Language `_". Xerox PARC. TR CSL-84-7. + + .. admonition:: Abstract + + Enough is known now about garbage collection, runtime types, + strong-typing, static-checking and concurrency that it is possible + to explore what happens when they are combined in a real + programming system. + + Storage management is one of a few central issues through which + one can get a good view of the design of an entire system. + Tensions between ease of integration and the need for protection; + between generality, simplicity, flexibility, extensibility and + efficiency are all manifest when assumptions and attitudes about + managing storage are studied. And deep understanding follows best + from the analysis of systems that people use to get real work + done. + + This paper is not for those who seek arguments pro or con about + the need for these features in programming systems; such issues + are for other papers. This one assumes these features to be good + and describes how they combine and interact in Cedar, a + programming language and environment designed to help programmers + build moderate-sized experimental systems for moderate numbers of + people to test and use. + +* .. _RUNCIMAN92: + + Colin Runciman & David Wakeling. 1992. "`Heap Profiling of Lazy Functional Programs `_". University of York. + + .. admonition:: Abstract + + We describe the design, implementation, and use of a new kind of + profiling tool that yields valuable information about the memory + use of lazy functional programs. The tool has two parts: a + modified functional language implementation which generated + profiling implementation during the execution of programs, and a + separate program which converts this information to graphical + form. With the aid of profile graphs, one can make alterations to + a functional program which dramatically reduce its space + consumption. We demonstrate that this is the case of a genuine + example -- the first to which the tool has been applied -- for + which the results are strikingly successful. + +* .. _RR94: + + Colin Runciman & Niklas Röjemo. 1994. "`New dimensions in heap profiling `_". University of York. + + .. admonition:: Abstract + + First-generation heap profilers for lazy functional languages have + proved to be effective tools for locating some kinds of space + faults, but in other cases they cannot provide sufficient + information to solve the problem. This paper describes the design, + implementation and use of a new profiler that goes beyond the + two-dimensional "who produces what" view of heap cells to provide + information about their more dynamic and structural attributes. + Specifically, the new profiler can distinguish between cells + according to their *eventual lifetime*, or on the basis of the + *closure retainers* by virtue of which they remain part of the + live heap. A bootstrapping Haskell compiler (nhc) hosts the + implementation: among examples of the profiler's use we include + self-application to nhc. Another example is the original + heap-profiling case study "clausify", which now consumes even less + memory and is much faster. + +* .. _RR96A: + + Colin Runciman & Niklas Röjemo. 1996. "Two-pass heap profiling: a matter of life and death". Department of Computer Science, University of York. + + .. admonition:: Abstract + + A heap profile is a chart showing the contents of heap memory + throughout a computation. Contents are depicted abstractly by + showing how much space is occupied by memory cells in each of + several classes. A good heap profiler can use a variety of + attributes of memory cells to de-fine a classification. Effective + profiling usually involves a combination of attributes. The ideal + profiler gives full support for combination in two ways. First, a + section of the heap of interest to the programmer can be specified + by constraining the values of any combination of cell attributes. + Secondly, no matter what attributes are used to specify such a + section, a heap profile can be obtained for that section only, and + any other attribute can be used to define the classification. + + Achieving this ideal is not simple For some combinations of + attributes. A heap profile is derived by interpolation of a series + of censuses of heap contents at different stages. The obvious way + to obtain census data is to traverse the live heap at intervals + throughout the computation. This is fine for static attributes + (e.g. What type of value does this memory cell represent?), and + for dynamic attributes that can be determined for each cell by + examining the heap at any given moment (e.g. From which function + closures can this cell be reached?). But some attributes of cells + can only be determined retrospectively by post-mortem inspection + asa cell is overwritten or garbage-collected (e.g. Is this cell + ever used again?). Now we see the problem: if a profiler supports + both live and pose-mortem attributes, how can we implement the + ideal of unrestricted combinations? That is the problem me solve + in this paper. We give techniques for profiling a. heap section + specified in terms of both live and post-mortem attributes. We + show how to generate live-attribute profiles of a section of the + heal, specified using post-mortem attributes, and vice versa. + +* .. _SG95: + + Jacob Seligmann & Steffen Grarup. 1995. "`Incremental Mature Garbage Collection Using the Train Algorithm `_". Springer-Verlag. ECOOP'95, Lecture Notes in Computer Science, Vol. 952, pp. 235--252, ISBN 3-540-60160-0. + + .. admonition:: Abstract + + We present an implementation of the Train Algorithm, an + incremental collection scheme for reclamation of mature garbage in + generation-based memory management systems. To the best of our + knowledge, this is the first Train Algorithm implementation ever. + Using the algorithm, the traditional mark-sweep garbage collector + employed by the Mjølner run-time system for the + object-oriented BETA programming language was replaced by a + non-disruptive one, with only negligible time and storage + overheads. + +* .. _SB00: + + Manuel Serrano, Hans-J. Boehm. 2000. "`Understanding memory allocation of Scheme programs `_". ACM. Proceedings of International Conference on Functional Programming 2000. + + .. admonition:: Abstract + + Memory is the performance bottleneck of modern architectures. + Keeping memory consumption as low as possible enables fast and + unobtrusive applications. But it is not easy to estimate the + memory use of programs implemented in functional languages, due to + both the complex translations of some high level constructs, and + the use of automatic memory managers. To help understand memory + allocation behavior of Scheme programs, we have designed two + complementary tools. The first one reports on frequency of + allocation, heap configurations and on memory reclamation. The + second tracks down memory leaks. We have applied these tools to + our Scheme compiler, the largest Scheme program we have been + developing. This has allowed us to drastically reduce the amount + of memory consumed during its bootstrap process, without requiring + much development time. Development tools will be neglected unless + they are both conveniently accessible and easy to use. In order to + avoid this pitfall, we have carefully designed the user interface + of these two tools. Their integration into a real programming + environment for Scheme is detailed in the paper. + +* .. _SHAPIRO94: + + Marc Shapiro & Paulo Ferreira. 1994. "`Larchant-RDOSS: a distributed shared persistent memory and its garbage collector `_". INRIA. INRIA Rapport de Recherche no. 2399; Cornell Computer Science TR94-1466. + + .. admonition:: Abstract + + Larchant-RDOSS is a distributed shared memory that persists on + reliable storage across process lifetimes. Memory management is + automatic: including consistent caching of data and of locks, + collecting objects unreachable from the persistent root, writing + reachable objects to disk, and reducing store fragmentation. + Memory management is based on a novel garbage collection + algorithm, that approximates a global trace by a series of local + traces, with no induced I/O or locking traffic, and no + synchronization between the collector and the application + processes. This results in a simple programming model, and + expected minimal added application latency. The algorithm is + designed for the most unfavorable environment (uncontrolled + programming language, reference by pointers, distributed system, + non-coherent shared memory) and should work well also in more + favorable settings. + +* .. _SHAW87: + + Robert A. Shaw. 1987. "Improving Garbage Collector Performance in Virtual Memory". Stanford University. CSL-TR-87-323. + +* .. _SHAW88: + + Robert A. Shaw. 1988. "Empirical Analysis of a LISP System". Stanford University. CSL-TR-88-351. + +* .. _SINGHAL92: + + Vivek Singhal, Sheetal V. Kakkad, Paul R. Wilson. 1992. "`Texas: An Efficient, Portable Persistent Store `_". University of Texas at Austin. + + .. admonition:: Abstract + + Texas is a persistent storage system for C++, providing high + performance while emphasizing simplicity, modularity and + portability. A key component of the design is the use of pointer + swizzling at page fault time, which exploits existing virtual + memory features to implement large address spaces efficiently on + stock hardware, with little or no change to existing compilers. + Long pointers are used to implement an enormous address space, but + are transparently converted to the hardware-supported pointer + format when pages are loaded into virtual memory. + + Runtime type descriptors and slightly modified heap allocation + routines support pagewise pointer swizzling by allowing objects + and their pointer fields to be identified within pages. If + compiler support for runtime type identification is not available, + a simple preprocessor can be used to generate type descriptors. + + This address translation is largely independent of issues of data + caching, sharing, and checkpointing; it employs operating systems' + existing virtual memories for caching, and a simple and flexible + log-structured storage manager to improve checkpointing + performance. + + Pagewise virtual memory protections are also used to detect writes + for logging purposes, without requiring any changes to compiled + code. This may degrade checkpointing performance for small + transactions with poor locality of writes, but page diffing and + sub-page logging promise to keep performance competitive with + finer-grained checkpointing schemes. + + Texas presents a simple programming interface; an application + creates persistent objects by simply allocating them on the + persistent heap. In addition, the implementation is relatively + small, and is easy to incorporate into existing applications. The + log-structured storage module easily supports advanced extensions + such as compressed storage, versioning, and adaptive + reorganization. + +* .. _SOBALVARRO88: + + P. G. Sobalvarro. 1988. "`A Lifetime-based Garbage Collector for LISP Systems on General-Purpose Computers `_". MIT. AITR-1417. + + .. admonition:: Abstract + + Garbage collector performance in LISP systems on custom hardware has been substantially improved by the adoption of lifetime-based garbage collection techniques. To date, however, successful lifetime-based garbage collectors have required special-purpose hardware, or at least privileged access to data structures maintained by the virtual memory system. I present here a lifetime-based garbage collector requiring no special-purpose hardware or virtual memory system support, and discuss its performance. + +* .. _STEELE75: + + Guy L. Steele. 1975. "Multiprocessing Compactifying Garbage Collection". CACM. 18:9 pp. 495--508. + + .. admonition:: Abstract + + Algorithms for a multiprocessing compactifying garbage collector + are presented and discussed. The simple case of two processors, + one performing LISP-like list operations and the other performing + garbage collection continuously, is thoroughly examined. The + necessary capabilities of each processor are defined, as well as + interprocessor communication and interlocks. Complete procedures + for garbage collection and for standard list processing primitives + are presented and thoroughly explained. Particular attention is + given to the problems of marking and relocating list cells while + another processor may be operating on them. The primary aim + throughout is to allow the list processor to run unimpeded while + the other processor reclaims list storage The more complex case + involving several list processors and one or more garbage + collection processors are also briefly discussed. + +* .. _STEELE76: + + Guy L. Steele. 1976. "Corrigendum: Multiprocessing Compactifying Garbage Collection". CACM. 19:6 p.354. + +* .. _STEELE77: + + Guy L. Steele. 1977. "`Data Representation in PDP-10 MACLISP `_". MIT. AI Memo 420. + + .. admonition:: Abstract + + The internal representations of the various MacLISP data types are + presented and discussed. Certain implementation tradeoffs are + considered. The ultimate decisions on these tradeoffs are + discussed in the light of MacLISP's prime objective of being an + efficient high-level language for the implementation of large + systems such as MACSYMA. The basic strategy of garbage collection + is outlined, with reference to the specific representations + involved. Certain "clever tricks" are explained and justified. The + "address space crunch" is explained and some alternative solutions + explored. + +* .. _SLC99: + + James M. Stichnoth, Guei-Yuan Lueh, Michal Cierniak. 1999. "`Support for Garbage Collection at Every Instruction in a Java Compiler `_". SIGPLAN. Proceedings of the 1999 ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI). SIGPLAN Notices 34(5). pp. 118--127. + + .. admonition:: Abstract + + A high-performance implementation of a Java Virtual Machine + requires a compiler to translate Java bytecodes into native + instructions, as well as an advanced garbage collector (e.g., + copying or generational). When the Java heap is exhausted and the + garbage collector executes, the compiler must report to the + garbage collector all live object references contained in physical + registers and stack locations. Typical compilers only allow + certain instructions (e.g., call instructions and backward + branches) to be GC-safe; if GC happens at some other instruction, + the compiler may need to advance execution to the next GC-safe + point. Until now, no one has ever attempted to make every + compiler-generated instruction GC-safe, due to the perception that + recording this information would require too much space. This kind + of support could improve the GC performance in multithreaded + applications. We show how to use simple compression techniques to + reduce the size of the GC map to about 20% of the generated code + size, a result that is competitive with the best previously + published results. In addition, we extend the work of Agesen, + Detlefs, and Moss, regarding the so-called “JSR Problem” (the + single exception to Java’s type safety property), in a way that + eliminates the need for extra runtime overhead in the generated + code. + +* .. _SCN84: + + Will R Stoye, T J W Clarke, Arthur C Norman. 1984. "Some Practical Methods for Rapid Combinator Reduction". In LFP 1984, 159--166. + + .. admonition:: Abstract + + The SKIM II processor is a microcoded hardware machine for the + rapid evaluation of functional languages. This paper gives details + of some of the more novel methods employed by SKIM II, and + resulting performance measurements. The authors conclude that + combinator reduction can still form the basis for the efficient + implementation of a functional language. + +* .. _TD95: + + David Tarditi & Amer Diwan. 1995. "`Measuring the Cost of Storage Management `_". Carnegie Mellon University. CMU-CS-94-201. + + .. admonition:: Abstract + + We study the cost of storage management for garbage-collected + programs compiled with the Standard ML of New Jersey compiler. We + show that the cost of storage management is not the same as the + time spent garbage collecting. For many of the programs, the time + spent garbage collecting is less than the time spent doing other + storage-management tasks. + +* .. _TJ94: + + Stephen Thomas, Richard E. Jones. 1994. "Garbage Collection for Shared Environment Closure Reducers". Computing Laboratory, The University of Kent at Canterbury. Technical Report 31-94. + + .. admonition:: Abstract + + Shared environment closure reducers such as Fairbairn and Wray's + TIM incur a comparatively low cost when creating a suspension, and + so provide an elegant method for implementing lazy functional + evaluation. However, comparatively little attention has been given + to the problems involved in identifying which portions of a shared + environment are needed (and ignoring those which are not) during a + garbage collection. Proper consideration of this issue has subtle + consequences when implementing a storage manager in a TIM-like + system. We describe the problem and illustrate the negative + consequences of ignoring it. + + We go on to describe a solution in which the compiler determines + statically which portions of that code's environment are required + for each piece of code it generates, and emits information to + assist the run-time storage manager to scavenge environments + selectively. We also describe a technique for expressing this + information directly as executable code, and demonstrate that a + garbage collector implemented in this way can perform + significantly better than an equivalent, table-driven interpretive + collector. + +* .. _THOMAS95: + + Stephen Thomas. 1995. "Garbage Collection in Shared-Environment Closure Reducers: Space-Efficient Depth First Copying using a Tailored Approach". *Information Processing Letters.* 56:1, pp. 1--7. + + .. admonition:: Abstract + + Implementations of abstract machines such as the OP-TIM and the + PG-TIM need to use a tailored garbage collector which seems to + require an auxiliary stack,with a potential maximum size that is + directly proportional to the amount of live data in the heap. + However, it turns out that it is possible to build a recursive + copying collector that does not require additional space by + reusing already-scavenged space. This paper is a description of + this technique. + +* .. _TT97: + + Mads Tofte & Jean-Pierre Talpin. 1997. "`Region-Based Memory Management `_". Information and Computation 132(2), pp. 109--176. + + .. admonition:: Abstract + + This paper describes a memory management discipline for programs + that perform dynamic memory allocation and de-allocation. At + runtime, all values are put into regions. The store consists of a + stack of regions. All points of region allocation and + de-allocation are inferred automatically, using a type and effect + based program analysis. The scheme does not assume the presence of + a garbage collector. The scheme was first presented in 1994 (M. + Tofte and J.-P. Talpin, in *Proceedings of the 21st ACM + SIGPLAN-SIGACT Symposium on Principles of Programming Languages,* + pp. 188--201); subsequently, it has been tested in the ML Kit with + Regions, a region-based, garbage-collection free implementation of + the Standard ML Core Language, which includes recursive datatypes, + higher-order functions and updatable references (L. Birkedal, M. + Tofte, and M. Vejlstrup, (1996), in *Proceedings of the 23rd ACM + SIGPLAN-SIGACT Symposium on Principles of Programming Languages,* + pp. 171--183). This paper defines a region-based dynamic semantics + for a skeletal programming language extracted from Standard ML. We + present the inference system which specifies where regions can be + allocated and de-allocated and a detailed proof that the system is + sound with respect to a standard semantics. We conclude by giving + some advice on how to write programs that run well on a stack of + regions, based on practical experience with the ML Kit. + +* .. _UNGAR84: + + Dave Ungar. 1984. "`Generation Scavenging: A Non-disruptive High Performance Storage Reclamation Algorithm `_". ACM, SIGSOFT, SIGPLAN. Practical Programming Environments Conference. + + .. admonition:: Abstract + + Many interactive computing environments provide automatic storage + reclamation and virtual memory to ease the burden of managing + storage. Unfortunately, many storage reclamation algorithms impede + interaction with distracting pauses. *Generation Scavenging* is a + reclamation algorithm that has no noticeable pauses, eliminates + page faults for transient objects, compacts objects without + resorting to indirection, and reclaims circular structures, in one + third the time of traditional approaches. + +* .. _UNGAR88: + + Dave Ungar & Frank Jackson. 1988. "`Tenuring Policies for Generation-Based Storage Reclamation `_". SIGPLAN. OOPSLA '88 Conference Proceedings, ACM SIGPLAN Notices, Vol. 23, No. 11, pp. 1--17. + + .. admonition:: Abstract + + One of the most promising automatic storage reclamation + techniques, generation-based storage reclamation, suffers poor + performance if many objects live for a fairly long time and then + die. We have investigated the severity of the problem by + simulating Generation Scavenging automatic storage reclamation + from traces of actual four-hour sessions. There was a wide + variation in the sample runs, with garbage-collection overhead + ranging from insignificant, during interactive runs, to sever, + during a single non-interactive run. All runs demonstrated that + performance could be improved with two techniques: segregating + large bitmaps and strings, and mediating tenuring with demographic + feedback. These two improvements deserve consideration for any + generation-based storage reclamation strategy. + +* .. _VO96: + + Kiem-Phong Vo. 1996. "Vmalloc: A General and Efficient Memory Allocator". Software -- Practice and Experience. 26(3): 357--374 (1996). + + .. admonition:: Abstract + + On C/Unix systems, the malloc interface is standard for dynamic + memory allocation. Despite its popularity, malloc's shortcomings + frequently cause programmers to code around it. The new library + Vmalloc generalizes malloc to give programmers more control over + memory allocation. Vmalloc introduces the idea of organizing + memory into separate regions, each with a discipline to get raw + memory and a method to manage allocation. Applications can write + their own disciplines to manipulate arbitrary type of memory or + just to better organize memory in a region by creating new regions + out of its memory. The provided set of allocation methods include + general purpose allocations, fast special cases and aids for + memory debugging or profiling. A compatible malloc interface + enables current applications to select allocation methods using + environment variables so they can tune for performance or perform + other tasks such as profiling memory usage, generating traces of + allocation calls or debugging memory errors. A performance study + comparing Vmalloc and currently popular malloc implementations + shows that Vmalloc is competitive to the best of these allocators. + Applications can gain further performance improvement by using the + right mixture of regions with different Vmalloc methods. + +* .. _WW76: + + Daniel C. Watson, David S. Wise. 1976. "Tuning Garwick's algorithm for repacking sequential storage". *BIT.* 16, 4 (December 1976): 442--450. + + .. admonition:: Abstract + + Garwick's algorithm, for repacking LIFO lists stored in a + contiguous block of memory, bases the allocation of remaining + space upon both sharing and previous stack growth. A system + whereby the weight applied to each method can be adjusted + according to the current behaviour of the stacks is discussed. + + We also investigate the problem of determining during memory + repacking that the memory is used to saturation and the driving + program should therefore be aborted. The tuning parameters studied + here seem to offer no new grasp on this problem. + +* .. _WLM92: + + Paul R. Wilson, Michael S. Lam, Thomas G. Moher. 1992. "Caching Considerations for Generational Garbage Collection". ACM. L&FP 92. + + .. admonition:: Abstract + + GC systems allocate and reuse memory cyclically; this imposes a + cyclic pattern on memory accesses that has its own distinctive + locality characteristics. The cyclic reuse of memory tends to + defeat caching strategies if the reuse cycle is too large to fit + in fast memory. Generational GCs allow a smaller amount of memory + to be reused more often. This improves VM performance, because the + frequently-reused area stays in main memory. The same principle + can be applied at the level of high-speed cache memories, if the + cache is larger than the youngest generation. Because of the + repeated cycling through a fixed amount of memory, however, + generational GC interacts with cache design in unusual ways, and + modestly set-associative caches can significantly outperform + direct-mapped caches. + + While our measurements do not show very high miss rates for GCed + systems, they indicate that performance problems are likely in + faster next-generation systems, where second-level cache misses + may cost scores of cycles. Software techniques can improve cache + performance of garbage-collected systems, by decreasing the cache + "footprint" of the youngest generation; compiler techniques that + reduce the amount of heap allocation also improve locality. Still, + garbage-collected systems with a high rate of heap allocation + require somewhat more cache capacity and/or main memory bandwidth + than conventional systems. + +* .. _WIL92A: + + Paul R. Wilson, Sheetal V. Kakkad. 1992. "`Pointer Swizzling at Page Fault Time `_". University of Texas at Austin. + + .. admonition:: Abstract + + Pointer swizzling at page fault time is a novel address + translation mechanism that exploits conventional address + translation hardware. It can support huge address spaces + efficiently without long hardware addresses; such large address + spaces are attractive for persistent object stores, distributed + shared memories, and shared address space operating systems. This + swizzling scheme can be used to provide data compatibility across + machines with different word sizes, and even to provide binary + code compatibility across machines with different hardware address + sizes. + + Pointers are translated ("swizzled") from a long format to a + shorter hardware-supported format at page fault time. No extra + hardware is required, and no continual software overhead is + incurred by presence checks of indirection of pointers. This + pagewise technique exploits temporal and spatial locality in much + the same way as normal virtual memory; this gives it many + desirable performance characteristics, especially given the trend + toward larger main memories. It is easy to implement using common + compilers and operating systems. + +* .. _WIL94: + + Paul R. Wilson. 1994. "`Uniprocessor Garbage Collection Techniques `_". University of Texas. + + .. admonition:: Abstract + + We survey basic garbage collection algorithms, and variations such + as incremental and generational collection; we then discuss + low-level implementation considerations and the relationships + between storage management systems, languages, and compilers. + Throughout, we attempt to present a unified view based on abstract + traversal strategies, addressing issues of conservatism, + opportunism, and immediacy of reclamation; we also point out a + variety of implementation details that are likely to have a + significant impact on performance. + +* .. _WIL95: + + Paul R. Wilson, Mark S. Johnstone, Michael Neely, David Boles. 1995. "`Dynamic Storage Allocation: A Survey and Critical Review `_". University of Texas at Austin. + + .. admonition:: Abstract + + Dynamic memory allocation has been a fundamental part of most + computer systems since roughly 1960, and memory allocation is + widely considered to be either a solved problem or an insoluble + one. In this survey, we describe a variety of memory allocator + designs and point out issues relevant to their design and + evaluation. We then chronologically survey most of the literature + on allocators between 1961 and 1995. (Scores of papers are + discussed, in varying detail, and over 150 references are given.) + + We argue that allocator designs have been unduly restricted by an + emphasis on mechanism, rather than policy, while the latter is + more important; higher-level strategic issues are still more + important, but have not been given much attention. + + Most theoretical analyses and empirical allocator evaluations to + date have relied on very strong assumptions of randomness and + independence, but real program behavior exhibits important + regularities that must be exploited if allocators are to perform + well in practice. + +* .. _WISE78: + + David S. Wise. 1978. "`The double buddy system `_". Department of Computer Science at Indiana University. Technical Report 79. + + .. admonition:: Abstract + + A new buddy system is described in which the region of storage + being managed is partitioned into two sub-regions, each managed by + a fairly standard "binary" buddy system. Like the weighted buddy + systems of Shen and Peterson, the block sizes are of sizes 2\ + :superscript:`n+1` or 3·2\ :superscript:`n`, but unlike theirs + there is no extra overhead for typing information or for buddy + calculation, and an allocation which requires splitting an extant + available block only rarely creates a block smaller than the one + being allocated. Such smaller blocks are carved out only when the + boundary between the two subregions floats; the most interesting + property of this system is that the procedures for allocation and + deallocation are designed to keep blocks immediately adjacent to + the subregion boundary free, so that the boundary may be moved + within a range of unused space without disturbing blocks in use. + This option is attained with a minimum of extra computation beyond + that of a binary buddy system, and provides this scheme with a new + approach to the problem of external fragmentation. + +* .. _WISE79: + + David S. Wise. 1979. "`Morris's garbage compaction algorithm restores reference counts `_". TOPLAS. 1, 1 (July 1979): 115--120. + + .. admonition:: Abstract + + The two-pass compaction algorithm of F.L. Morris, which follows + upon the mark phase in a garbage collector, may be modified to + recover reference counts for a hybrid storage management system. + By counting the executions of two loops in that algorithm where + upward and downward references, respectively, are forwarded to the + relocation address of one node, we can initialize a count of + active references and then update it but once. The reference count + may share space with the mark bit in each node, but it may not + share the additional space required in each pointer by Morris's + algorithm, space which remains unused outside the garbage + collector. + +* .. _WISE85: + + David S. Wise. 1985. "`Design for a multiprocessing heap with on-board reference counting `_". Springer-Verlag. In J.-P. Jouannaud (ed.), Functional Programming Languages and Computer Architecture, Lecture Notes in Computer Science 201: 289--304. + + .. admonition:: Abstract + + A project to design a pair of memory chips with a modicum of + intelligence is described. Together, the two allow simple + fabrication of a small memory bank, a heap of binary (LISP-like) + nodes that offers the following features: 64-bit nodes; two + pointer fields per node up to 29 bits each; reference counts + implicitly maintained on writes; 2 bits per node for marking + (uncounted) circular references; 4 bits per node for + conditional-store testing at the memory; provision for + processor-driven, recounting garbage collection. + +* .. _WISE92: + + .. _WISE93: + + David S. Wise. 1993. "`Stop-and-copy and one-bit reference counting `_". *Information Processing Letters.* 46, 5 (July 1993): 243--249. + + .. admonition:: Abstract + + A stop-and-copy garbage collector updates one-bit reference + counting with essentially no extra space and minimal memory cycles + beyond the conventional collection algorithm. Any object that is + uniquely referenced during a collection becomes a candidate for + cheap recovery before the next one, or faster recopying then if it + remains uniquely referenced. Since most objects stay uniquely + referenced, subsequent collections run faster even if none are + recycled between garbage collections. This algorithm extends to + generation scavenging, it admits uncounted references from roots, + and it corrects conservatively stuck counters, that result from + earlier uncertainty whether references were unique. + +* .. _WW95: + + David S. Wise, Joshua Walgenbach. 1996. "`Static and Dynamic Partitioning of Pointers as Links and Threads `_". SIGPLAN. Proc. 1996 ACM SIGPLAN Intl. Conf. on Functional Programming, SIGPLAN Not. 31, 6 (June 1996), pp. 42--49. + + .. admonition:: Abstract + + Identifying some pointers as invisible threads, for the purposes + of storage management, is a generalization from several widely + used programming conventions, like threaded trees. The necessary + invariant is that nodes that are accessible (without threads) emit + threads only to other accessible nodes. Dynamic tagging or static + typing of threads ameliorates storage recycling both in functional + and imperative languages. + + We have seen the distinction between threads and links sharpen + both hardware- and software-supported storage management in + SCHEME, and also in C. Certainly, therefore, implementations of + languages that already have abstract management and concrete + typing, should detect and use this as a new static type. + +* .. _WHHHO94: + + David S. Wise, Brian Heck, Caleb Hess, Willie Hunt, Eric Ost. 1997. "`Uniprocessor Performance of a Reference-Counting Hardware Heap `_". *LISP and Symbolic Computation.* 10, 2 (July 1997), pp. 159--181. + + .. admonition:: Abstract + + A hardware self-managing heap memory (RCM) for languages like + LISP, SMALLTALK, and JAVA has been designed, built, tested and + benchmarked. On every pointer write from the processor, + reference-counting transactions are performed in real time within + this memory, and garbage cells are reused without processor + cycles. A processor allocates new nodes simply by reading from a + distinguished location in its address space. The memory hardware + also incorporates support for off-line, multiprocessing, + mark-sweep garbage collection. + + Performance statistics are presented from a partial implementation + of SCHEME over five different memory models and two garbage + collection strategies, from main memory (no access to RCM) to a + fully operational RCM installed on an external bus. The + performance of the RCM memory is more than competitive with main + memory. + +* .. _WITHINGTON91: + + P. Tucker Withington. 1991. "`How Real is 'Real-Time' Garbage Collection? `_". ACM. OOPSLA/ECOOP '91 Workshop on Garbage Collection in Object-Oriented Systems. + + .. admonition:: Abstract + + A group at Symbolics is developing a Lisp runtime kernel, derived + from its Genera operating system, to support real-time control + applications. The first candidate application has strict + response-time requirements (so strict that it does not permit the + use of paged virtual memory). Traditionally, Lisp's automatic + storage-management mechanism has made it unsuitable to real-time + systems of this nature. A number of garbage collector designs and + implementations exist (including the Genera garbage collector) + that purport to be "real-time", but which actually have only + mitigated the impact of garbage collection sufficiently that it + usually goes unnoticed by humans. Unfortunately, + electro-mechanical systems are not so forgiving. This paper + examines the limitations of existing real-time garbage collectors + and describes the avenues that we are exploring in our work to + develop a CLOS-based garbage collector that can meet the real-time + requirements of real real-time systems. + +* .. _YIP91: + + G. May Yip. 1991. "`Incremental, Generational Mostly-Copying Garbage Collection in Uncooperative Environments `_". Digital Equipment Corporation. + + .. admonition:: Abstract + + The thesis of this project is that incremental collection can be + done feasibly and efficiently in an architecture and compiler + independent manner. The design and implementation of an + incremental, generational mostly-copying garbage collector for C++ + is presented. The collector achieves, simultaneously, real-time + performance (from incremental collection), low total garbage + collection delay (from generational collection), and the ability + to function without hardware and compiler support (from + mostly-copying collection). + + The incremental collector runs on commercially-available + uniprocessors, such as the DECStation 3100, without any special + hardware support. It uses UNIX's user controllable page protection + facility (mprotect) to synchronize between the scanner (of the + collector) and the mutator (of the application program). Its + implementation does not require any modification to the C++ + compiler. The maximum garbage collection pause is well within the + 100-millisecond limit imposed by real-time applications executing + on interactive workstations. Compared to its non-incremental + version, the total execution time of the incremental collector is + not adversely affected. + +* .. _YUASA90: + + Taiichi Yuasa. 1990. "Real-Time Garbage Collection on General-Purpose Machines". Journal of Software and Systems. 11:3 pp. 181--198. + + .. admonition:: Abstract + + An algorithm for real-time garbage collection is presented, proved + correct, and evaluated. This algorithm is intended for + list-processing systems on general-purpose machines, i.e., Von + Neumann style serial computers with a single processor. On these + machines, real-time garbage collection inevitably causes some + overhead on the overall execution of the list-processing system, + because some of the primitive list-processing operations must + check the status of garbage collection. By removing such overhead + from frequently used primitives such as pointer references (e.g., + Lisp car and cdr) and stack manipulations, the presented algorithm + reduces the execution overhead to a great extent. Although the + algorithm does not support compaction of the whole data space, it + efficiently supports partial compaction such as array relocation. + +* .. _ZORN88: + + Benjamin Zorn & Paul Hilfinger. 1988. "`A Memory Allocation Profiler for C and Lisp Programs `_". USENIX. Proceedings for the Summer 1988 USENIX Conference, pp. 223--237. + + .. admonition:: Abstract + + This paper describes inprof, a tool used to study the memory + allocation behavior of programs. mprof records the amount of + memory each function allocates, breaks down allocation information + by type and size, and displays a program's dynamic cal graph so + that functions indirectly responsible for memory allocation are + easy to identify. mprof is a two-phase tool. The monitor phase is + linked into executing programs and records information each time + memory is allocated. The display phase reduces the data generated + by the monitor and displays the information to the user in several + tables. mprof has been implemented for C and Kyoto Common Lisp. + Measurements of these implementations are presented. + +* .. _ZORN89: + + Benjamin Zorn. 1989. "`Comparative Performance Evaluation of Garbage Collection Algorithms `_". Computer Science Division (EECS) of University of California at Berkeley. Technical Report UCB/CSD 89/544 and PhD thesis. + + .. admonition:: Abstract + + This thesis shows that object-level, trace-driven simulation can + facilitate evaluation of language runtime systems and reaches new + conclusions about the relative performance of important garbage + collection algorithms. In particular, I reach the unexpected + conclusion that mark-and-sweep garbage collection, when augmented + with generations, shows comparable CPU performance and much better + reference locality than the more widely used copying algorithms. + In the past, evaluation of garbage collection algorithms has been + limited by the high cost of implementing the algorithms. + Substantially different algorithms have rarely been compared in a + systematic way. + + With the availability of high-performance, low-cost workstations, + trace-driven performance evaluation of these algorithms is now + economical. This thesis describes MARS, a runtime system simulator + that is driven by operations on program objects, and not memory + addresses. MARS has been attached to a commercial Common Lisp + system and eight large Lisp applications are used in the thesis as + test programs. To illustrate the advantages of the object-level + tracing technique used by MARS, this thesis compares the relative + performance of stop-and-copy, incremental, and mark-and-sweep + collection algorithms, all organized with multiple generations. + The comparative evaluation is based on several metrics: CPU + overhead, reference locality, and interactive availability. + + Mark-and-sweep collection shows slightly higher CPU overhead than + stop-and-copy ability (5 percent), but requires significantly less + physical memory to achieve the same page fault rate (30-40 + percent). Incremental collection has very good interactive + availability, but implementing the read barrier on stock hardware + incurs a substantial CPU overhead (30-60 percent). In the future, + I will use MARS to investigate other performance aspects of + sophisticated runtime systems. + +* .. _ZORN90B: + + Benjamin Zorn. 1990. "Comparing Mark-and-sweep and Stop-and-copy Garbage Collection". ACM. Conference on Lisp and Functional Programming, pp. 87--98. + + .. admonition:: Abstract + + Stop-and-copy garbage collection has been preferred to + mark-and-sweep collection in the last decade because its + collection time is proportional to the size of reachable data and + not to the memory size. This paper compares the CPU overhead and + the memory requirements of the two collection algorithms extended + with generations, and finds that mark-and-sweep collection + requires at most a small amount of additional CPU overhead (3-6%) + but requires an average of 20% (and up to 40%) less memory to + achieve the same page fault rate. The comparison is based on + results obtained using trace-driven simulation with large Common + Lisp programs. + +* .. _ZORN90: + + Benjamin Zorn. 1990. "`Barrier Methods for Garbage Collection `_". University of Colorado at Boulder. Technical Report CU-CS-494-90. + + .. admonition:: Abstract + + Garbage collection algorithms have been enhanced in recent years + with two methods: generation-based collection and Baker + incremental copying collection. Generation-based collection + requires special actions during certain store operations to + implement the "write barrier". Incremental collection requires + special actions on certain load operations to implement the "read + barrier". This paper evaluates the performance of different + implementations of the read and write barriers and reaches several + important conclusions. First, the inlining of barrier checks + results in surprisingly low overheads, both for the write barrier + (2%-6%) and the read barrier (< 20%). Contrary to previous + belief, these results suggest that a Baker-style read barrier can + be implemented efficiently without hardware support. Second, the + use of operating system traps to implement garbage collection + methods results in extremely high overheads because the cost of + trap handling is so high. Since this large overhead is completely + unnecessary, operating system memory protection traps should be + reimplemented to be as fast as possible. Finally, the performance + of these approaches on several machine architectures is compared + to show that the results are generally applicable. + +* .. _ZORN91: + + Benjamin Zorn. 1991. "`The Effect of Garbage Collection on Cache Performance `_". University of Colorado at Boulder. Technical Report CU-CS-528-91. + + .. admonition:: Abstract + + Cache performance is an important part of total performance in + modern computer systems. This paper describes the use of + trace-driven simulation to estimate the effect of garbage + collection algorithms on cache performance. Traces from four large + Common Lisp programs have been collected and analyzed with an + all-associativity cache simulator. While previous work has focused + on the effect of garbage collection on page reference locality, + this evaluation unambiguously shows that garbage collection + algorithms can have a profound effect on cache performance as + well. On processors with a direct-mapped cache, a generation + stop-and-copy algorithm exhibits a miss rate up to four times + higher than a comparable generation mark-and-sweep algorithm. + Furthermore, two-way set-associative caches are shown to reduce + the miss rate in stop-and-copy algorithms often by a factor of two + and sometimes by a factor of almost five over direct-mapped + caches. As processor speeds increase, cache performance will play + an increasing role in total performance. These results suggest + that garbage collection algorithms will play an important part in + improving that performance. + +* .. _ZORN92B: + + Benjamin Zorn & Dirk Grunwald. 1992. "`Empirical Measurements of Six Allocation-intensive C Programs `_". ACM, SIGPLAN. SIGPLAN notices, 27(12):71--80. + + .. admonition:: Abstract + + Dynamic memory management is an important part of a large class of + computer programs and high-performance algorithms for dynamic + memory management have been, and will continue to be, of + considerable interest. This paper presents empirical data from a + collection of six allocation-intensive C programs. Extensive + statistics about the allocation behavior of the programs measured, + including the distributions of object sizes, lifetimes, and + interarrival times, are presented. This data is valuable for the + following reasons: first, the data from these programs can be used + to design high-performance algorithms for dynamic memory + management. Second, these programs can be used as a benchmark test + suite for evaluating and comparing the performance of different + dynamic memory management algorithms. Finally, the data presented + gives readers greater insight into the storage allocation patterns + of a broad range of programs. The data presented in this paper is + an abbreviated version of more extensive statistics that are + publicly available on the internet. + +* .. _ZORN92: + + Benjamin Zorn. 1993. "`The Measured Cost of Conservative Garbage Collection `_". Software -- Practice and Experience. 23(7):733--756. + + .. admonition:: Abstract + + Because dynamic memory management is an important part of a large + class of computer programs, high-performance algorithms for + dynamic memory management have been, and will continue to be, of + considerable interest. Experience indicates that for many + programs, dynamic storage allocation is so important that + programmers feel compelled to write and use their own + domain-specific allocators to avoid the overhead of system + libraries. Conservative garbage collection has been suggested as + an important algorithm for dynamic storage management in C + programs. In this paper, I evaluate the costs of different dynamic + storage management algorithms, including domain-specific + allocators; widely-used general-purpose allocators; and a publicly + available conservative garbage collection algorithm. Surprisingly, + I find that programmer enhancements often have little effect on + program performance. I also find that the true cost of + conservative garbage collection is not the CPU overhead, but the + memory system overhead of the algorithm. I conclude that + conservative garbage collection is a promising alternative to + explicit storage management and that the performance of + conservative collection is likely to be improved in the future. C + programmers should now seriously consider using conservative + garbage collection instead of malloc/free in programs they write. + +* .. _ZORN92A: + + Benjamin Zorn & Dirk Grunwald. 1994. "`Evaluating Models of Memory Allocation `_". ACM. Transactions on Modeling and Computer Simulation 4(1):107--131. + + .. admonition:: Abstract + + Because dynamic memory management is an important part of a large + class of computer programs, high-performance algorithms for + dynamic memory management have been, and will continue to be, of + considerable interest. We evaluate and compare models of the + memory allocation behavior in actual programs and investigate how + these models can be used to explore the performance of memory + management algorithms. These models, if accurate enough, provide + an attractive alternative to algorithm evaluation based on + trace-driven simulation using actual traces. We explore a range of + models of increasing complexity including models that have been + used by other researchers. Based on our analysis, we draw three + important conclusions. First, a very simple model, which generates + a uniform distribution around the mean of observed values, is + often quite accurate. Second, two new models we propose show + greater accuracy than those previously described in the + literature. Finally, none of the models investigated appear + adequate for generating an operating system workload. + diff --git a/manual/source/conf.py b/manual/source/conf.py index b3c561d615..733438fc25 100644 --- a/manual/source/conf.py +++ b/manual/source/conf.py @@ -22,6 +22,34 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('.')) +# -- Project configuration ----------------------------------------------------- + +# The same set of sources builds the Memory Pool System documentation +# and the Memory Management Reference, depending on whether the MMREF +# environment variable is set. + +if os.environ.get('MMREF'): + project = u'Memory Management Reference' + master_doc = 'index' + html_theme = 'mmref' + version = '4' + release = '4.0' +else: + project = u'Memory Pool System' + master_doc = 'index' + html_theme = 'mps' + html_sidebars = { + '**': ['localtoc.html', 'relations.html', 'links.html', 'contact.html'], + } + with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), + '../../code/version.c')) as f: + for line in f: + m = re.match(r'#define MPS_RELEASE "release/((\d+\.\d+)\.\d+)"', line) + if m: + release, version = m.groups() + break + + # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. @@ -40,24 +68,9 @@ # The encoding of source files. #source_encoding = 'utf-8-sig' -# The master toctree document. -master_doc = 'index' - # General information about the project. -project = u'Memory Pool System' copyright = date.today().strftime(u'%Y, Ravenbrook Limited') -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), - '../../code/version.c')) as f: - for line in f: - m = re.match(r'#define MPS_RELEASE "release/((\d+\.\d+)\.\d+)"', line) - if m: - release, version = m.groups() - break - # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None @@ -98,10 +111,6 @@ # -- Options for HTML output --------------------------------------------------- -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'mps' - # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. @@ -119,7 +128,7 @@ # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = 'diagrams/logo.png' +html_logo = 'images/logo.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 @@ -129,7 +138,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = [] +html_static_path = ['images'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. @@ -139,11 +148,6 @@ # typographically correct entities. html_use_smartypants = True -# Custom sidebar templates, maps document names to template names. -html_sidebars = { - '**': ['localtoc.html', 'relations.html', 'links.html', 'contact.html'], -} - # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} diff --git a/manual/source/contributing.rst b/manual/source/contributing.rst new file mode 100644 index 0000000000..9c004b268e --- /dev/null +++ b/manual/source/contributing.rst @@ -0,0 +1,8 @@ +.. index:: + single: building + single: compiling + single: installing + +.. _contributing: + +.. include:: ../../contributing.rst diff --git a/manual/source/copyright.rst b/manual/source/copyright.rst index 98b2dea1b4..234ae604da 100644 --- a/manual/source/copyright.rst +++ b/manual/source/copyright.rst @@ -1,7 +1,7 @@ -.. _license: - .. index:: single: copyright single: license +.. _license: + .. include:: ../../license.txt diff --git a/manual/source/design/.p4ignore b/manual/source/design/.p4ignore index 0e9be69cee..2457ad1919 100644 --- a/manual/source/design/.p4ignore +++ b/manual/source/design/.p4ignore @@ -1,5 +1,6 @@ # The files in this directory are generated by the "mps" extension to Sphinx, # except the index and the "old designs" index. *.rst +*.svg !index.rst !old.rst diff --git a/manual/source/design/index.rst b/manual/source/design/index.rst index 3cf6719ac7..b8da67a433 100644 --- a/manual/source/design/index.rst +++ b/manual/source/design/index.rst @@ -7,13 +7,33 @@ Design :numbered: abq + an + bootstrap cbs config critical-path + exec-env + failover freelist guide.hex.trans guide.impl.c.format + guide.impl.c.naming + guide.review + interface-c keyword-arguments + land + lock + nailboard + prmc + prot range ring + sp sig + splay + ss + testthr + thread-manager + type + vm + writef diff --git a/manual/source/design/old.rst b/manual/source/design/old.rst index 38d22aa270..8a8e71be1a 100644 --- a/manual/source/design/old.rst +++ b/manual/source/design/old.rst @@ -25,10 +25,8 @@ Old design diag finalize fix - interface-c io lib - lock locus message message-gc @@ -43,8 +41,6 @@ Old design poolmv poolmvt poolmvff - prot - protan protli protsu protocol @@ -54,19 +50,13 @@ Old design scan seg shield - splay sso1al strategy telemetry tests - thread-manager thread-safety trace - type version-library version - vm - vman vmo1 vmso - writef diff --git a/manual/source/extensions/mps/designs.py b/manual/source/extensions/mps/designs.py index 200052db8d..42acd049d6 100644 --- a/manual/source/extensions/mps/designs.py +++ b/manual/source/extensions/mps/designs.py @@ -12,16 +12,21 @@ import os.path import glob import re +import shutil import sys from sphinx.util.console import bold TYPES = ''' - AccessSet Accumulation Addr Align AP Arg Arena Attr Bool BT Buffer - Byte Clock Compare Count Epoch Format Fun Index LD Lock Message - Pointer Pool PThreadext Rank RankSet Ref Res Reservoir Ring Root - RootVar ScanState Seg Serial Shift Sig Size Space SplayNode - SplayTree Thread Trace TraceId TraceSet ULongest VM Word + AccessSet Accumulation Addr Align AllocFrame AllocPattern AP Arg + Arena Attr Bool BootBlock BT Buffer BufferMode Byte Chain Chunk + Clock Compare Count Epoch FindDelete Format FrameState Fun GenDesc + Globals Index Land LD Lock LocusPref LocusPrefKind Message + MessageType MutatorFaultContext Page Pointer Pool PoolGen + PThreadext Range Rank RankSet ReadonlyAddr Ref RefSet Res + Reservoir Ring Root RootMode RootVar ScanState Seg SegBuf Serial + Shift Sig Size Space SplayNode SplayTree StackContext Thread Trace + TraceId TraceSet TraceStartWhy TraceState ULongest VM Word ZoneSet ''' @@ -37,6 +42,8 @@ func = re.compile(r'``([A-Za-z][A-Za-z0-9_]+\(\))``') typename = re.compile(r'``({0}|[A-Z][A-Za-z0-9_]*(?:Class|Struct|Method)|mps_[a-z_]+_[stu])``(?: )?' .format('|'.join(map(re.escape, TYPES.split())))) +design_ref = re.compile(r'^( *\.\. _design\.mps\.(?:[^:\n]+): (?:[^#:\n]+))$', re.MULTILINE) +design_frag_ref = re.compile(r'^( *\.\. _design\.mps\.([^:\n]+)\.([^:\n]+): (?:[^#:\n]+))#\3$', re.MULTILINE) history = re.compile(r'^Document History\n.*', re.MULTILINE | re.IGNORECASE | re.DOTALL) @@ -111,6 +118,8 @@ def convert_file(name, source, dest): s = macro.sub(r':c:macro:`\1`', s) s = secnum.sub(secnum_sub, s) s = citation.sub(citation_sub, s) + s = design_ref.sub(r'\1.html', s) + s = design_frag_ref.sub(r'\1.html#design.mps.\2.\3', s) s = history.sub('', s) try: os.makedirs(os.path.dirname(dest)) @@ -119,6 +128,15 @@ def convert_file(name, source, dest): with open(dest, 'wb') as out: out.write(s.encode('utf-8')) +def newer(src, target): + """Return True if src is newer (that is, modified more recently) than + target, False otherwise. + + """ + return (not os.path.isfile(target) + or os.path.getmtime(target) < os.path.getmtime(src) + or os.path.getmtime(target) < os.path.getmtime(__file__)) + # Mini-make def convert_updated(app): app.info(bold('converting MPS design documents')) @@ -126,8 +144,11 @@ def convert_updated(app): name = os.path.splitext(os.path.basename(design))[0] if name == 'index': continue converted = 'source/design/%s.rst' % name - if (not os.path.isfile(converted) - or os.path.getmtime(converted) < os.path.getmtime(design) - or os.path.getmtime(converted) < os.path.getmtime(__file__)): + if newer(design, converted): app.info('converting design %s' % name) convert_file(name, design, converted) + for diagram in glob.iglob('../design/*.svg'): + target = os.path.join('source/design/', os.path.basename(diagram)) + if newer(diagram, target): + shutil.copyfile(diagram, target) + diff --git a/manual/source/glossary/a.rst b/manual/source/glossary/a.rst index a7ab2de1bd..ec4da5f86d 100644 --- a/manual/source/glossary/a.rst +++ b/manual/source/glossary/a.rst @@ -103,6 +103,26 @@ Memory Management Glossary: A .. seealso:: :term:`virtual address space`, :term:`physical address space`. + address space layout randomization + + .. aka:: *ASLR*. + + The random placement in :term:`address space` of the + :term:`stack`, data segment, :term:`heap`, and so on, of a + process. + + The purpose of ASLR is to make it harder for an attacker to + exploit buffer overflow bugs, by making it harder to determine + the addresses of data structures. + + .. mps:specific:: + + ASLR also makes it hard to prepare a repeatable test case + for a program that performs computation based on the + addresses of objects, for example, hashing objects by + their address. See :ref:`guide-debug-aslr` for techniques + to deal with this. + address translation cache .. see:: :term:`translation lookaside buffer`. @@ -182,7 +202,11 @@ Memory Management Glossary: A .. mps:specific:: An alignment is represented by the unsigned integral type - :c:type:`mps_align_t`. It must be a positive power of 2. + :c:type:`mps_align_t`. It must be a power of 2. The + alignment of objects allocated in a :term:`pool` may be + specified by passing the :c:macro:`MPS_KEY_ALIGN` + :term:`keyword argument` when calling + :c:func:`mps_pool_create_k`. alive @@ -389,6 +413,10 @@ Memory Management Glossary: A class of :term:`arenas`. Arena classes include :term:`client arenas` and :term:`virtual memory arenas`. + ASLR + + .. see:: :term:`address space layout randomization`. + assertion A declaration in a program of a condition that is expected @@ -452,6 +480,12 @@ Memory Management Glossary: A .. opposite:: :term:`manual memory management`. + .. mps:specific:: + + The MPS provides automatic memory management through + :term:`pool classes` such as :ref:`pool-amc`, + :ref:`pool-ams`, and :ref:`pool-awl`. + automatic storage duration In :term:`C`, :term:`objects` that are declared with diff --git a/manual/source/glossary/b.rst b/manual/source/glossary/b.rst index 8356404267..12d4838e46 100644 --- a/manual/source/glossary/b.rst +++ b/manual/source/glossary/b.rst @@ -182,9 +182,22 @@ Memory Management Glossary: B .. relevance:: Bitmaps are sometimes used to represent the marks in a - :term:`mark-sweep` collector, or the used memory in a - :term:`bitmapped fits` :term:`allocator`. - + :term:`mark-sweep` collector (see :term:`bitmap marking`), + or the used memory in a :term:`bitmapped fits` + :term:`allocator`. + + bitmap marking + + In :term:`mark-sweep` collectors, bitmap marking is a + technique for :term:`marking` objects that stores the mark + bits for the objects in a contiguous range of memory in a + separate :term:`bitmap`. This improves the collector's + :term:`locality of reference` and cache performance, because + it avoids setting the :term:`dirty bit` on the :term:`pages` + containing the marked objects. + + .. bibref:: :ref:`Zorn (1989) `. + bitmapped fit A class of :term:`allocation mechanisms` that use a diff --git a/manual/source/glossary/c.rst b/manual/source/glossary/c.rst index b2d8f2b140..ffdf189924 100644 --- a/manual/source/glossary/c.rst +++ b/manual/source/glossary/c.rst @@ -131,7 +131,7 @@ Memory Management Glossary: C A cactus stack is a :term:`stack` with branches. When diagrammed, its shape resembles that of a `saguaro cactus - `_. + `_. In languages that support :term:`continuations`, :term:`activation records` can have :term:`indefinite extent`. @@ -581,16 +581,6 @@ Memory Management Glossary: C valid, even functions on the :term:`critical path`. See :ref:`guide-build`. Compare :term:`hot` and :term:`rash`. - copy method - - .. mps:specific:: - - A copy method is one of the methods in an :term:`object - format`. Formerly, the MPS called this method to copy a - :term:`formatted object` during :term:`moving garbage - collection `. Now it just copies - the bytes and the copy method is ignored. - copying garbage collection .. aka:: *scavenging garbage collection*. @@ -615,6 +605,12 @@ Memory Management Glossary: C .. seealso:: :term:`broken heart`, :term:`forwarding pointer`, :term:`two-space collector`. + .. mps:specific:: + + The :ref:`pool-amc` pool class implements copying garbage + collection (more precisely, :term:`mostly-copying garbage + collection`). + core A historical synonym for :term:`main memory`, deriving from diff --git a/manual/source/glossary/f.rst b/manual/source/glossary/f.rst index 540cc048d4..a6d648abe4 100644 --- a/manual/source/glossary/f.rst +++ b/manual/source/glossary/f.rst @@ -26,6 +26,11 @@ Memory Management Glossary: F .. similar:: :term:`in-band header`. + .. mps:specific:: + + :term:`Debugging pools` use fenceposts. See + :ref:`topic-debugging`. + fencepost error fence post error @@ -409,10 +414,34 @@ Memory Management Glossary: F .. seealso:: :term:`free block`, :term:`free block chain`. - free store - .. see:: :term:`heap`. + freestanding + + In the :term:`C` programming language as defined by + :term:`C90`, a freestanding implementation "accepts any + strictly conforming program in which the use of the features + specified in the library section is confined to the contents + of the standard headers ````, ````, + ````, and ````." The :term:`C99` standard + adds ````, ````, and ```` to + this list. + + In particular, a freestanding implementation need not provide + the other features of the standard C library, including I/O, + time, and string operations. + .. opposite:: :term:`hosted`. + + .. mps:specific:: + + The MPS is designed to be portable to a freestanding + implementation, by restricting the use of other features + either to :term:`platform`\-specific modules or to the + replaceable :term:`plinth` modules. + + .. bibref:: :ref:`ISO/IEC 9899:1990 `, :ref:`ISO/IEC 9899:1999 `. + + free store freestore .. see:: :term:`heap`. diff --git a/manual/source/glossary/g.rst b/manual/source/glossary/g.rst index 25feb89490..4dc74e6622 100644 --- a/manual/source/glossary/g.rst +++ b/manual/source/glossary/g.rst @@ -89,7 +89,7 @@ Memory Management Glossary: G This term is often used when referring to particular implementations or algorithms, for example, "the - Boehm-Demers-Weiser *collector*". + Boehm--Demers--Weiser *collector*". GB @@ -132,16 +132,16 @@ Memory Management Glossary: G .. mps:specific:: The :term:`client program` specifies the generational - structure of a :term:`pool` using a :term:`generation - chain`. See :ref:`topic-collection`. + structure of a :term:`pool` (or group of pools) using a + :term:`generation chain`. See :ref:`topic-collection`. generation chain .. mps:specific:: A data structure that specifies the structure of the - :term:`generations` in a :term:`pool`. See - :ref:`topic-collection`. + :term:`generations` in a :term:`pool` (or group of pools). + See :ref:`topic-collection`. generation scavenging @@ -174,6 +174,11 @@ Memory Management Glossary: G .. seealso:: :term:`remembered set`. + .. mps:specific:: + + The :ref:`pool-amc` and :ref:`pool-amcz` pool classes + support generational garbage collection. + generational hypothesis .. aka:: *infant mortality*. diff --git a/manual/source/glossary/h.rst b/manual/source/glossary/h.rst index 43f4c99891..8d292f9837 100644 --- a/manual/source/glossary/h.rst +++ b/manual/source/glossary/h.rst @@ -98,6 +98,16 @@ Memory Management Glossary: H .. opposite:: :term:`miss rate`. + hosted + + In the :term:`C` programming language, a hosted implementation + is one that provides all the features of the standard C + library. + + .. opposite:: :term:`freestanding`. + + .. bibref:: :ref:`ISO/IEC 9899:1990 `, :ref:`ISO/IEC 9899:1999 `. + hot .. mps:specific:: diff --git a/manual/source/glossary/i.rst b/manual/source/glossary/i.rst index 9ce2a3f9ce..b174241959 100644 --- a/manual/source/glossary/i.rst +++ b/manual/source/glossary/i.rst @@ -133,6 +133,12 @@ Memory Management Glossary: I .. bibref:: :ref:`Appel et al. (1988) `, :ref:`Boehm et al. (1991) `. + .. mps:specific:: + + The MPS uses incremental collection, except for + collections started by calling + :c:func:`mps_arena_collect`. + incremental update Incremental-update algorithms for :term:`tracing `, diff --git a/manual/source/glossary/index.rst b/manual/source/glossary/index.rst index bb55975bf9..6c15cbd4e8 100644 --- a/manual/source/glossary/index.rst +++ b/manual/source/glossary/index.rst @@ -7,6 +7,7 @@ Memory Management Glossary .. toctree:: :maxdepth: 1 + :hidden: a b @@ -31,3 +32,594 @@ Memory Management Glossary v w z + +All +=== + +:term:`absolute address ` +:term:`activation frame ` +:term:`activation record` +:term:`activation stack ` +:term:`active ` +:term:`address` +:term:`address space` +:term:`address space layout randomization` +:term:`address translation cache ` +:term:`address-ordered first fit` +:term:`aging space` +:term:`algebraic data type` +:term:`alignment` +:term:`alive ` +:term:`allocate` +:term:`allocation frame` +:term:`allocation mechanism` +:term:`allocation pattern` +:term:`allocation point` +:term:`allocation point protocol` +:term:`allocation policy` +:term:`allocation strategy` +:term:`allocator` +:term:`ambiguous reference` +:term:`ambiguous root` +:term:`arena` +:term:`arena class` +:term:`ASLR
` +:term:`assertion` +:term:`asynchronous garbage collector` +:term:`ATC ` +:term:`atomic object ` +:term:`automatic memory management` +:term:`automatic storage duration` + +:term:`backing store` +:term:`barrier (1)` +:term:`barrier (2)` +:term:`barrier hit ` +:term:`base pointer` +:term:`best fit` +:term:`BIBOP` +:term:`big bag of pages ` +:term:`binary buddies` +:term:`bit array ` +:term:`bit table ` +:term:`bit vector ` +:term:`bitmap` +:term:`bitmap marking` +:term:`bitmapped fit` +:term:`bitmask` +:term:`bitset ` +:term:`black` +:term:`blacklisting` +:term:`black-listing` +:term:`block` +:term:`bounds error ` +:term:`boxed` +:term:`break-table` +:term:`brk` +:term:`broken heart` +:term:`bucket` +:term:`buddy system` +:term:`buffer` +:term:`bus error` +:term:`byte (1)` +:term:`byte (2)` +:term:`byte (3)` +:term:`byte (4)` + +:term:`C89 ` +:term:`C90` +:term:`C99` +:term:`cache (1)` +:term:`cache (2)` +:term:`cache memory ` +:term:`cache policy` +:term:`caching (3)` +:term:`cactus stack` +:term:`card` +:term:`card marking` +:term:`cell ` +:term:`Cheney collector` +:term:`Cheney scan ` +:term:`clamped state` +:term:`client arena` +:term:`client object` +:term:`client pointer` +:term:`client program ` +:term:`closure` +:term:`coalesce` +:term:`collect` +:term:`collection ` +:term:`collection cycle` +:term:`collector (1) ` +:term:`collector (2)` +:term:`color` +:term:`colour` +:term:`commit limit` +:term:`committed (1) ` +:term:`committed (2)` +:term:`compactifying ` +:term:`compaction` +:term:`composite object` +:term:`comprehensive` +:term:`concurrent garbage collection ` +:term:`condemned set` +:term:`connected` +:term:`cons (1)` +:term:`cons (2) ` +:term:`conservative garbage collection` +:term:`constant root` +:term:`constructor (1)` +:term:`constructor (2)` +:term:`continuation` +:term:`control stack` +:term:`cool` +:term:`copying garbage collection` +:term:`core` +:term:`creation space` +:term:`critical path` +:term:`crossing map` +:term:`cyclic data structure` + +:term:`dangling pointer` +:term:`data stack` +:term:`dead` +:term:`deallocate ` +:term:`debugging pool` +:term:`deferred coalescing` +:term:`deferred reference counting` +:term:`dependent object` +:term:`derived pointer ` +:term:`derived type` +:term:`destructor (1)` +:term:`destructor (2)` +:term:`DGC ` +:term:`direct method` +:term:`dirty bit` +:term:`distributed garbage collection` +:term:`double buddies` +:term:`double free` +:term:`doubleword` +:term:`doubly weak hash table` +:term:`DRAM ` +:term:`dynamic allocation ` +:term:`dynamic extent` +:term:`dynamic memory` +:term:`dynamic RAM ` + +:term:`ecru ` +:term:`edge` +:term:`entry table (1)` +:term:`entry table (2)` +:term:`exact garbage collection` +:term:`exact reference` +:term:`exact root` +:term:`exact segregated fit` +:term:`execution stack ` +:term:`exit table` +:term:`extent ` +:term:`external fragmentation` + +:term:`fencepost` +:term:`fence post` +:term:`fencepost error` +:term:`fence post error` +:term:`Fibonacci buddies` +:term:`FIFO-ordered first fit` +:term:`file mapping ` +:term:`finalization` +:term:`finalized block` +:term:`first fit` +:term:`fix` +:term:`flip` +:term:`floating garbage` +:term:`foreign code` +:term:`format` +:term:`format method` +:term:`formatted object` +:term:`forward method` +:term:`forwarding marker` +:term:`forwarding object` +:term:`forwarding pointer` +:term:`fragmentation` +:term:`frame ` +:term:`free (1)` +:term:`free (2)` +:term:`free (3)` +:term:`free (4) ` +:term:`free block` +:term:`free block chain` +:term:`free list` +:term:`free store ` +:term:`freestore ` +:term:`from space` +:term:`fromspace` +:term:`function pointer` +:term:`function record ` + +:term:`garbage` +:term:`garbage collection` +:term:`garbage collector` +:term:`GB ` +:term:`GC ` +:term:`General Protection Fault` +:term:`generation` +:term:`generation chain` +:term:`generation scavenging ` +:term:`generational garbage collection` +:term:`generational hypothesis` +:term:`gigabyte` +:term:`good fit` +:term:`GPF ` +:term:`grain` +:term:`graph` +:term:`gray` +:term:`grey` +:term:`gray list` +:term:`grey list` + +:term:`handle` +:term:`header ` +:term:`heap` +:term:`heap allocation` +:term:`hit` +:term:`hit rate` +:term:`hot` +:term:`huge page` + +:term:`immediate data` +:term:`immune set` +:term:`immutable` +:term:`immutable object ` +:term:`in-band header` +:term:`in parameter` +:term:`in/out parameter` +:term:`incremental garbage collection` +:term:`incremental update` +:term:`indefinite extent` +:term:`indexed fit` +:term:`indirect method` +:term:`infant mortality ` +:term:`inline allocation (1)` +:term:`inline allocation (2)` +:term:`inter-generational pointer` +:term:`interior pointer` +:term:`internal fragmentation` +:term:`invalid page fault` +:term:`inverted page table` +:term:`inverted page-table` +:term:`is-forwarded method` + +:term:`kB ` +:term:`keyword argument` +:term:`kilobyte` + +:term:`large object area` +:term:`large page ` +:term:`leaf object` +:term:`leak ` +:term:`life ` +:term:`lifetime` +:term:`LIFO-ordered first fit` +:term:`limited-field reference count` +:term:`linear addressing` +:term:`live` +:term:`load` +:term:`locality of reference` +:term:`location ` +:term:`location dependency` +:term:`lock free` +:term:`logical address ` +:term:`longword ` + +:term:`machine word ` +:term:`main memory` +:term:`malloc` +:term:`manual memory management` +:term:`mapped` +:term:`mapping` +:term:`mark-compact` +:term:`mark-sweep` +:term:`mark-and-sweep` +:term:`marking` +:term:`MB ` +:term:`megabyte` +:term:`memoization ` +:term:`memory (1)` +:term:`memory (2)` +:term:`memory (3)
` +:term:`memory (4)` +:term:`memory bandwidth` +:term:`memory cache ` +:term:`memory hierarchy ` +:term:`memory leak` +:term:`memory location` +:term:`memory management` +:term:`Memory Management Unit ` +:term:`memory manager` +:term:`memory mapping` +:term:`memory protection ` +:term:`message` +:term:`message queue` +:term:`message type` +:term:`misaligned ` +:term:`miss` +:term:`miss rate` +:term:`mmap` +:term:`MMU` +:term:`mostly-copying garbage collection` +:term:`mostly-exact garbage collection ` +:term:`mostly-precise garbage collection ` +:term:`moving garbage collector` +:term:`moving memory manager` +:term:`mutable` +:term:`mutator` + +:term:`nailing ` +:term:`natural alignment` +:term:`nepotism` +:term:`next fit` +:term:`new space` +:term:`newspace ` +:term:`node` +:term:`non-moving garbage collector` +:term:`non-moving memory manager` +:term:`nursery generation ` +:term:`nursery space` + +:term:`object` +:term:`object format` +:term:`object pointer` +:term:`off-white` +:term:`old space ` +:term:`oldspace ` +:term:`one-bit reference count` +:term:`opaque type` +:term:`out parameter` +:term:`out-of-band header` +:term:`overcommit` +:term:`overwriting error` + +:term:`padding` +:term:`padding method` +:term:`padding object` +:term:`page` +:term:`page fault` +:term:`page marking` +:term:`page protection ` +:term:`page table` +:term:`paged in` +:term:`paged out` +:term:`paging` +:term:`palimpsest` +:term:`parallel garbage collection` +:term:`parked state` +:term:`perfect fit` +:term:`phantom reachable` +:term:`phantomly reachable` +:term:`phantom reference` +:term:`physical address` +:term:`physical address space` +:term:`physical memory (1)` +:term:`physical memory (2)` +:term:`physical storage ` +:term:`pig in the python` +:term:`pig in the snake ` +:term:`pinning` +:term:`placement policy ` +:term:`platform` +:term:`plinth` +:term:`pointer` +:term:`pool` +:term:`pool class` +:term:`precise garbage collection ` +:term:`precise reference ` +:term:`precise root ` +:term:`premature free` +:term:`premature promotion ` +:term:`premature tenuring` +:term:`primary storage
` +:term:`promotion` +:term:`protectable root` +:term:`protection` +:term:`protection exception ` +:term:`protection fault` +:term:`protection violation ` + +:term:`quadword` + +:term:`RAM` +:term:`random access memory ` +:term:`ramp allocation` +:term:`rank` +:term:`rash` +:term:`raw ` +:term:`reachable` +:term:`read barrier` +:term:`read fault` +:term:`read-only memory ` +:term:`real memory (1)` +:term:`real memory (2) ` +:term:`reclaim` +:term:`recycle` +:term:`reference` +:term:`reference counting` +:term:`reference object` +:term:`region inference` +:term:`register` +:term:`register set partitioning` +:term:`relocation` +:term:`remembered set` +:term:`remote reference` +:term:`replicating garbage collector` +:term:`reserved` +:term:`resident` +:term:`resident set` +:term:`result code` +:term:`resurrection` +:term:`ROM` +:term:`root` +:term:`root description` +:term:`root mode` +:term:`root set` + +:term:`sbrk` +:term:`scalar data type` +:term:`scan` +:term:`scan method` +:term:`scan state` +:term:`scavenging garbage collection ` +:term:`SDRAM` +:term:`segmentation violation` +:term:`segmented addressing` +:term:`segregated allocation cache` +:term:`segregated fit` +:term:`segregated free list` +:term:`segregated free-list` +:term:`semi-conservative garbage collection` +:term:`semi-space` +:term:`semi-space collector ` +:term:`sequential fit` +:term:`sequential store buffer` +:term:`shared memory` +:term:`simple object` +:term:`simple segregated storage` +:term:`size` +:term:`size class` +:term:`skip method` +:term:`smart pointer` +:term:`snap-out` +:term:`snapshot at the beginning` +:term:`soft reference` +:term:`softly reachable` +:term:`space leak ` +:term:`spare commit limit` +:term:`spare committed memory` +:term:`spaghetti stack ` +:term:`splat` +:term:`split` +:term:`SRAM ` +:term:`SSB ` +:term:`stack` +:term:`stack allocation` +:term:`stack frame` +:term:`stack record ` +:term:`static allocation` +:term:`static memory (1)` +:term:`static memory (2)` +:term:`static object` +:term:`static RAM ` +:term:`static storage duration` +:term:`stepper function` +:term:`sticky reference count ` +:term:`stop-and-copy collection` +:term:`storage ` +:term:`storage hierarchy` +:term:`storage level` +:term:`storage management ` +:term:`store (1)` +:term:`store (2) ` +:term:`stretchy vector` +:term:`strict segregated fit` +:term:`strong reference` +:term:`strong root` +:term:`strong tri-color invariant` +:term:`strong tri-colour invariant` +:term:`strong tricolor invariant` +:term:`strong tricolour invariant` +:term:`strongly reachable` +:term:`suballocator` +:term:`subgraph` +:term:`superpage ` +:term:`sure reference ` +:term:`swap space` +:term:`swapped in` +:term:`swapped out` +:term:`swapping` +:term:`sweeping` +:term:`synchronous garbage collector` + +:term:`tabling ` +:term:`tag` +:term:`tagged architecture` +:term:`tagged reference` +:term:`TB (1) ` +:term:`TB (2) ` +:term:`telemetry filter` +:term:`telemetry label` +:term:`telemetry stream` +:term:`tenuring ` +:term:`terabyte` +:term:`termination ` +:term:`thrash` +:term:`thread` +:term:`threatened set ` +:term:`TLB ` +:term:`to space` +:term:`tospace` +:term:`trace` +:term:`tracing garbage collection` +:term:`translation buffer` +:term:`translation lookaside buffer` +:term:`transparent alias` +:term:`transparent type` +:term:`transport` +:term:`transport snap-out ` +:term:`treadmill` +:term:`tri-color invariant` +:term:`tri-colour invariant` +:term:`tricolor invariant` +:term:`tricolour invariant` +:term:`tri-color marking` +:term:`tri-colour marking` +:term:`tricolor marking` +:term:`tricolour marking` +:term:`two-space collector` +:term:`two space collector` +:term:`type-accurate garbage collection ` +:term:`type punning` + +:term:`unaligned` +:term:`unboxed` +:term:`unclamped state` +:term:`undead` +:term:`unmapped` +:term:`unreachable` +:term:`unsure reference ` +:term:`unwrapped` +:term:`use after free ` + +:term:`value object` +:term:`variety` +:term:`vector data type` +:term:`virtual address` +:term:`virtual address space` +:term:`virtual memory` +:term:`virtual memory arena` +:term:`visitor function ` +:term:`VM (1) ` +:term:`VM (2)` + +:term:`weak-key hash table` +:term:`weak-value hash table` +:term:`weak hash table` +:term:`weak reference (1)` +:term:`weak reference (2)` +:term:`weak root` +:term:`weak tri-color invariant` +:term:`weak tri-colour invariant` +:term:`weak tricolor invariant` +:term:`weak tricolour invariant` +:term:`weakly reachable` +:term:`weighted buddies` +:term:`weighted reference counting` +:term:`white` +:term:`word` +:term:`working set` +:term:`worst fit` +:term:`wrapped` +:term:`wrapper` +:term:`write barrier` +:term:`write fault` + +:term:`ZCT ` +:term:`zero count table` diff --git a/manual/source/glossary/k.rst b/manual/source/glossary/k.rst index 4033cce5fd..d84d07711e 100644 --- a/manual/source/glossary/k.rst +++ b/manual/source/glossary/k.rst @@ -14,7 +14,7 @@ Memory Management Glossary: K keyword argument - A argument to a function call that's identified by an + An argument to a function call that's identified by an associated keyword rather than by its position in the argument list. @@ -35,5 +35,3 @@ Memory Management Glossary: K The standard abbreviation is "kB", but "KB" is often used by people unfamiliar with the metric system. - - diff --git a/manual/source/glossary/l.rst b/manual/source/glossary/l.rst index 5a143e55f6..3e4f0db427 100644 --- a/manual/source/glossary/l.rst +++ b/manual/source/glossary/l.rst @@ -81,9 +81,14 @@ Memory Management Glossary: L If leaf objects can be identified, a :term:`garbage collector` can make certain optimizations: leaf objects do not have to be :term:`scanned ` for references nor - are :term:`barrier (1)` needed to detect + are :term:`barriers (1)` needed to detect and maintain references in the object. + .. mps:specific:: + + The :ref:`pool-amcz` and :ref:`pool-lo` pool classes are + designed for the storage of leaf objects. + leak .. see:: :term:`memory leak`. diff --git a/manual/source/glossary/m.rst b/manual/source/glossary/m.rst index 4562d30d7b..92abdcf274 100644 --- a/manual/source/glossary/m.rst +++ b/manual/source/glossary/m.rst @@ -207,7 +207,11 @@ Memory Management Glossary: M though any conservative representation of a predicate on the :term:`memory location` of the object can be used. In particular, storing the mark bit within the object can lead to - poor :term:`locality of reference`. + poor :term:`locality of reference` and to poor cache + performance, because the marking phases ends up setting the + :term:`dirty bit` on all :term:`pages` in the :term:`working + set`. An alternative is to store the mark bits separately: + see :term:`bitmap marking`. .. seealso:: :term:`sweep `, :term:`compact `. @@ -535,6 +539,11 @@ Memory Management Glossary: M .. bibref:: :ref:`Bartlett (1989) `, :ref:`Yip (1991) `. + .. mps:specific:: + + The :ref:`pool-amc` pool class implements mostly-copying + garbage collection. + mostly-exact garbage collection .. see:: :term:`semi-conservative garbage collection`. diff --git a/manual/source/glossary/n.rst b/manual/source/glossary/n.rst index 24ddcf66ce..67fbd65a37 100644 --- a/manual/source/glossary/n.rst +++ b/manual/source/glossary/n.rst @@ -117,4 +117,10 @@ Memory Management Glossary: N The size of the nursery space must be chosen carefully. Often it is related to the size of :term:`physical memory (1)`. + .. mps:specific:: + By default, a garbage-collected :term:`pool` allocates + into the first :term:`generation` in its :term:`generation + chain`, but this can be altered by setting the + :c:macro:`MPS_KEY_GEN` :term:`keyword argument` when + calling :c:func:`mps_pool_create_k`. diff --git a/manual/source/glossary/p.rst b/manual/source/glossary/p.rst index fc08d9c2ab..4ca1dbcd27 100644 --- a/manual/source/glossary/p.rst +++ b/manual/source/glossary/p.rst @@ -167,7 +167,7 @@ Memory Management Glossary: P mutator changing :term:`objects` while collection occurs. The problem is similar to that of :term:`incremental GC `, but harder. The solution - typically involves :term:`barrier (1)`. + typically involves :term:`barriers (1)`. .. similar:: :term:`incremental `. @@ -222,7 +222,7 @@ Memory Management Glossary: P .. link:: - `Class java.lang.ref.PhantomReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.PhantomReference `_, `Reference Objects and Garbage Collection `_. phantom reference @@ -239,7 +239,7 @@ Memory Management Glossary: P .. link:: - `Class java.lang.ref.PhantomReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.PhantomReference `_, `Reference Objects and Garbage Collection `_. physical address @@ -302,15 +302,15 @@ Memory Management Glossary: P .. aka:: *pig in the snake*. In a :term:`generational ` - collector, when a large and long-lived :term:`object` is + collector, when long-lived :term:`objects` are :term:`allocated` in :term:`nursery space`, collection effort - will be wasted as that object survives and is :term:`promoted - ` from :term:`generation` to generation. This is - especially noticeable in a :term:`copying collector `, where the large object will be copied - many times. This difficulty is similar to that of a python - which swallows its prey whole and is somewhat immobilized as - it digests it. + will be wasted as those objects survive and are + :term:`promoted ` from :term:`generation` to + generation. This is especially noticeable in a :term:`copying + collector `, where long-lived + objects will be copied many times. This difficulty is similar + to that of a python which swallows its prey whole and is + somewhat immobilized as it digests it. Modern collectors permit objects to be allocated directly into appropriate generations or pools to avoid this problem. @@ -321,6 +321,14 @@ Memory Management Glossary: P .. seealso:: :term:`generational garbage collection`. + .. mps:specific:: + + A :term:`pool` can be configured to allocate into a + specific :term:`generation` in its :term:`generation + chain` by setting the :c:macro:`MPS_KEY_GEN` + :term:`keyword argument` when calling + :c:func:`mps_pool_create_k`. + pig in the snake .. see:: :term:`pig in the python`. @@ -390,8 +398,8 @@ Memory Management Glossary: P .. mps:specific:: - A value of type :c:type:`mps_class_t` describing a class - of :term:`pools` that manage memory according to + A value of type :c:type:`mps_pool_class_t` describing a + class of :term:`pools` that manage memory according to particular policy. See :ref:`pool`. precise garbage collection diff --git a/manual/source/glossary/r.rst b/manual/source/glossary/r.rst index a521c1d7bd..cbab9a255f 100644 --- a/manual/source/glossary/r.rst +++ b/manual/source/glossary/r.rst @@ -39,7 +39,7 @@ Memory Management Glossary: R .. mps:specific:: A value of :c:type:`mps_rank_t` indicating whether a - :term:`root` is :term:`ambiguous ` + :term:`reference` is :term:`ambiguous ` (:c:func:`mps_rank_ambig`), :term:`exact ` (:c:func:`mps_rank_exact`) or :term:`weak ` (:c:func:`mps_rank_weak`). @@ -93,7 +93,7 @@ Memory Management Glossary: R .. link:: - `Package java.lang.ref `_, `Reference Objects and Garbage Collection `_. + `Package java.lang.ref `_, `Reference Objects and Garbage Collection `_. read barrier @@ -317,7 +317,7 @@ Memory Management Glossary: R .. link:: - `Package java.lang.ref `_, `Reference Objects and Garbage Collection `_. + `Package java.lang.ref `_, `Reference Objects and Garbage Collection `_. .. bibref:: :ref:`Dybvig et al. (1993) `. @@ -471,6 +471,11 @@ Memory Management Glossary: R .. seealso:: :term:`mapping`, :term:`mmap`. + .. mps:specific:: + + The function :c:func:`mps_arena_reserved` returns the + total address space reserved by an arena. + resident In a :term:`cache (2)` system, that part of the cached storage @@ -510,6 +515,14 @@ Memory Management Glossary: R .. mps:specific:: See :ref:`topic-finalization`. + retention + + The failure to :term:`recycle` :term:`floating garbage`, due + to some approximation or optimization in the :term:`garbage + collector`; also the amount of memory thus retained. + + .. bibref:: :ref:`Boehm (2001) `. + ROM .. aka:: *read-only memory*. diff --git a/manual/source/glossary/s.rst b/manual/source/glossary/s.rst index 41389cb210..dc9355f2a0 100644 --- a/manual/source/glossary/s.rst +++ b/manual/source/glossary/s.rst @@ -333,7 +333,7 @@ Memory Management Glossary: S By overloading certain operators it is possible for the class to present the illusion of being a pointer, so that - ``operator\*``, ``operator-\>``, etc. can be used as normal. + ``operator*``, ``operator->``, etc. can be used as normal. Reference counting allows the objects that are referred to using the smart pointer class to have their :term:`memory (1)` automatically :term:`reclaimed` when they are no longer @@ -429,7 +429,7 @@ Memory Management Glossary: S .. link:: - `Class java.lang.ref.SoftReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.SoftReference `_, `Reference Objects and Garbage Collection `_. softly reachable @@ -453,7 +453,7 @@ Memory Management Glossary: S .. link:: - `Class java.lang.ref.SoftReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.SoftReference `_, `Reference Objects and Garbage Collection `_. space leak @@ -785,6 +785,26 @@ Memory Management Glossary: S .. see:: :term:`memory (1)`. + stretchy vector + + A :term:`vector ` that may grow or shrink to + accommodate adding or removing elements. Named after the + ```` abstract class in Dylan. + + .. relevance:: + + In the presence of an :term:`asynchronous garbage + collector`, the vector and its size may need to be updated + atomically. + + .. link:: + + `Dylan Reference Manual: Collections `_. + + .. mps:specific:: + + See :ref:`guide-stretchy-vector`. + strict segregated fit A :term:`segregated fit` :term:`allocation mechanism` which @@ -806,7 +826,7 @@ Memory Management Glossary: S collection>`, a strong reference is a :term:`reference` that keeps the :term:`object` it refers to :term:`alive `. - A strong reference is the usual sort of reference; The term is + A strong reference is the usual sort of reference: the term is usually used to draw a contrast with :term:`weak reference (1)`. @@ -819,7 +839,7 @@ Memory Management Glossary: S A strong root is a :term:`root` such that all :term:`references` in it are :term:`strong references`. - A strong root is the usual sort of root. The term is usually + A strong root is the usual sort of root: the term is usually used to draw a contrast with :term:`weak root`. .. opposite:: :term:`weak root`. diff --git a/manual/source/glossary/w.rst b/manual/source/glossary/w.rst index 7d061294ce..46d8d9edd0 100644 --- a/manual/source/glossary/w.rst +++ b/manual/source/glossary/w.rst @@ -70,7 +70,7 @@ Memory Management Glossary: W .. link:: - `Class java.lang.ref.WeakReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.WeakReference `_, `Reference Objects and Garbage Collection `_. weak root @@ -134,7 +134,7 @@ Memory Management Glossary: W .. link:: - `Class java.lang.ref.WeakReference `_, `Reference Objects and Garbage Collection `_. + `Class java.lang.ref.WeakReference `_, `Reference Objects and Garbage Collection `_. weighted buddies diff --git a/manual/source/guide/advanced.rst b/manual/source/guide/advanced.rst index 69a9934823..b56edf14fa 100644 --- a/manual/source/guide/advanced.rst +++ b/manual/source/guide/advanced.rst @@ -28,14 +28,6 @@ call ``close-input-file``, then the underlying file handle should still be closed when the port object :term:`dies `. This procedure is known as :term:`finalization`. -.. note:: - - It's generally a bad idea to depend on finalization to release your - resources (see the :ref:`topic-finalization-cautions` section in - :ref:`topic-finalization`). Treat it as a last resort when more - reliable mechanisms for releasing resources (like Scheme's - ``with-open-input-file``) aren't available. - Any block in an :term:`automatically managed ` :term:`pool` can be registered for finalization by calling :c:func:`mps_finalize`. In the toy Scheme interpreter, this can be done @@ -138,26 +130,37 @@ Here's an example session showing finalization taking place: not_condemned 0 clock: 3807 -The toy Scheme interpreter :dfn:`definalizes` ports by calling -:c:func:`mps_definalize` when they are closed. This is purely an -optimization: setting ``stream`` to ``NULL`` ensures that the file -handle wouldn't be closed more than once, even if the port object were -later finalized. +It's wise not to depend on finalization as the only method for +releasing resources (see the :ref:`topic-finalization-cautions` +section in :ref:`topic-finalization`), because the garbage collector +does not promise to collect particular objects at particular times, +and in any case it does so only when it can prove that the object is +:term:`dead`. So it is best to provide a reliable mechanism for +releasing the resource (here, the Scheme function +``close-input-port``), and use finalization as a backup strategy. + +But this raises the possibility that a port will be closed twice: once +via ``close-input-port`` and a second time via finalization. So it's +necessary to make ports robust against be closed multiple times. The +toy Scheme interpreter does so by setting ``stream`` to ``NULL``: this +ensures that the file handle won't be closed more than once. .. code-block:: c - :emphasize-lines: 8 + :emphasize-lines: 6 static void port_close(obj_t port) { assert(TYPE(port) == TYPE_PORT); if(port->port.stream != NULL) { - mps_addr_t port_ref = port; fclose(port->port.stream); port->port.stream = NULL; - mps_definalize(arena, &port_ref); } } +Note that because finalization messages are processed synchronously +via the message queue (and the Scheme interpreter is single-threaded) +there is no need for a lock here. + It's still possible that the toy Scheme interpreter might run out of open file handles despite having some or all of its port objects being finalizable. That's because the arena's message queue is only polled @@ -228,12 +231,12 @@ not been updated to match. 11736, 1> ht #[hashtable (two 2) (three 3) (one 1)] -The MPS solves this problem with its :dfn:`location dependency` feature: -a structure of type :c:type:`mps_ld_s` encapsulates a set of +The MPS solves this problem with its :dfn:`location dependency` +feature: a structure of type :c:type:`mps_ld_s` encapsulates a set of dependencies on the locations of blocks. You add addresses to the -location dependency, and then test to see if it has been made -:dfn:`stale`: that is, if any of the blocks whose location has been -depended on might have moved since their location was depended upon. +location dependency, and then later test an address to see if it is +:dfn:`stale`: that is, if the block at that address might have moved +since its location was depended upon. You need to provide space for the :c:type:`mps_ld_s` structure. In the case of a hash table, it is most convenient to inline it in the hash @@ -324,9 +327,9 @@ any of its keys. .. note:: - The garbage collector may run at any time, so the table may become - be stale at any time after calling :c:func:`mps_ld_add`, perhaps - even before you've added the new key. + The garbage collector may run at any time, so a key may become + stale at any time after calling :c:func:`mps_ld_add`, perhaps even + before you've added it! It's best to postpone worrying about this until this key is actually looked up, when the staleness will be discovered. After all, it may @@ -339,19 +342,22 @@ function :c:func:`mps_ld_isstale` tells you if a block whose location you depended upon since the last call to :c:func:`mps_ld_reset` might have moved. +In the toy Scheme interpreter this behaviour is encapsulated into ``table_find``: + .. code-block:: c - :emphasize-lines: 6 + :emphasize-lines: 7 - static obj_t table_ref(obj_t tbl, obj_t key) + static struct bucket_s *table_find(obj_t tbl, obj_t key, int add) { - struct bucket_s *b = buckets_find(tbl, tbl->table.buckets, key, NULL); - if (b && b->key != NULL && b->key != obj_deleted) - return b->value; - if (mps_ld_isstale(&tbl->table.ld, arena, key)) { + struct bucket_s *b; + assert(TYPE(tbl) == TYPE_TABLE); + b = buckets_find(tbl, tbl->table.buckets, key, add); + if ((b == NULL || b->key == NULL || b->key == obj_deleted) + && mps_ld_isstale(&tbl->table.ld, arena, key)) + { b = table_rehash(tbl, tbl->table.buckets->buckets.length, key); - if (b) return b->value; } - return NULL; + return b; } It's important to test :c:func:`mps_ld_isstale` only in case of @@ -380,8 +386,9 @@ By adding the line:: puts("stale!"); -after :c:func:`mps_ld_isstale` returns true, it's possible to see when -the location dependency becomes stale and the table has to be rehashed. +in ``table_find`` after :c:func:`mps_ld_isstale` returns true, it's +possible to see when the location dependency becomes stale and the +table has to be rehashed: .. code-block:: none :emphasize-lines: 21, 23 @@ -422,45 +429,30 @@ the location dependency becomes stale and the table has to be rehashed. move in the collection, so it's not found in the table, and that causes :c:func:`mps_ld_isstale` to be tested. -Don't forget to check the location dependency for staleness if you are -about to delete a key from a hash table but discover that it's not -there. In the toy Scheme interpreter, deletion looks like this: - -.. code-block:: c - :emphasize-lines: 6 - - static void table_delete(obj_t tbl, obj_t key) - { - struct bucket_s *b; - assert(TYPE(tbl) == TYPE_TABLE); - b = buckets_find(tbl, tbl->table.buckets, key, NULL); - if ((b == NULL || b->key == NULL) && mps_ld_isstale(&tbl->table.ld, arena, key)) { - b = table_rehash(tbl, tbl->table.buckets->buckets.length, key); - } - if (b != NULL && b->key != NULL) { - b->key = obj_deleted; - ++ tbl->table.buckets->buckets.deleted; - } - } - -Again, by adding the line ``puts("stale!");`` after -:c:func:`mps_ld_isstale` returns true, it's possible to see when the -location dependency becomes stale and the table has to be rehashed: +Don't forget to check the location dependency for staleness when +setting a value for key in a hash table, and when deleting a key from +a hash table. Here's an interaction with the toy Scheme interpreter +showing a key being found to be stale when setting and when deleting +it: .. code-block:: none MPS Toy Scheme Example 13248, 0> (define ht (make-eq-hashtable)) ht - 13624, 0> (hashtable-set! ht 'one 1) + 13624, 0> (hashtable-set! ht 'a 1) 13808, 0> (gc) + 13832, 1> (hashtable-set! ht 'a 2) + stale! 13832, 1> (hashtable-delete! ht 'one) stale! - 14112, 1> ht + 14152, 1> (gc) + 14176, 2> (hashtable-delete! ht 'a) + stale! + 14456, 2> ht #[hashtable] - .. topics:: :ref:`topic-location`. diff --git a/manual/source/guide/debug.rst b/manual/source/guide/debug.rst index 678cc39859..060928c2ba 100644 --- a/manual/source/guide/debug.rst +++ b/manual/source/guide/debug.rst @@ -42,21 +42,20 @@ General debugging advice in production), and can generate profiling output in the form of the :term:`telemetry stream`. -#. .. index:: - single: ASLR - single: address space layout randomization +#. If your program triggers an assertion failure in the MPS, consult + :ref:`topic-error-cause` for suggestions as to the possible cause. - Prepare a reproducible test case if possible. The MPS may be +#. Prepare a reproducible test case if possible. The MPS may be :term:`asynchronous `, but it is deterministic, so in single-threaded applications you should be - able to get consistent results. (But you need to beware of `address - space layout randomization`_: if you perform computation based on - the addresses of objects, for example, hashing objects by their - address, then ASLR will cause your hash tables to be laid out - differently on each run, which may affect the order of memory - management operations.) + able to get consistent results. - .. _address space layout randomization: http://en.wikipedia.org/wiki/Address_space_layout_randomization + However, you need to beware of :term:`address space layout + randomization`: if you perform computation based on the addresses + of objects, for example, hashing objects by their address, then + ASLR will cause your hash tables to be laid out differently on each + run, which may affect the order of memory management operations. + See :ref:`guide-debug-aslr` below. A fact that assists with reproducibility is that the more frequently the collector runs, the sooner and more reliably errors @@ -66,7 +65,10 @@ General debugging advice result, by having a mode for testing in which you run frequent collections (by calling :c:func:`mps_arena_collect` followed by :c:func:`mps_arena_release`), perhaps as frequently as every - allocation. + allocation. (This will of course make the system run very slowly, + but it ensures that if there are roots or references that are not + being scanned then the failure will occur close in time to the cause, + making it easier to diagnose.) #. .. index:: single: debugger @@ -86,10 +88,109 @@ General debugging advice handle SIGSEGV pass nostop noprint - On OS X barrier hits do not use signals and so do not enter the debugger. + On these operating systems, you can add this command to your + ``.gdbinit`` if you always want it to be run. + + On OS X, barrier hits do not use signals and so do not enter the + debugger. + + +.. index:: + single: ASLR + single: address space layout randomization + +.. _guide-debug-aslr: + +Address space layout randomization +---------------------------------- + +:term:`Address space layout randomization` (ASLR) makes it hard to +prepare a repeatable test case for a program that performs computation +based on the addresses of objects, for example, hashing objects by +their address. If this is affecting you, you'll find it useful to +disable ASLR when testing. + +Here's a small program that you can use to check if ASLR is enabled on +your system. It outputs addresses from four key memory areas in a +program (data segment, text segment, stack and heap): + +.. code-block:: c + + #include + #include + + int data; + + int main() { + void *heap = malloc(4); + int stack = 0; + printf("data: %p text: %p stack: %p heap: %p\n", + &data, (void *)main, &stack, heap); + return 0; + } + +When ASLR is turned on, running this program outputs different +addresses on each run. For example, here are four runs on OS X +10.9.3:: + + data: 0x10a532020 text: 0x10a531ed0 stack: 0x7fff556ceb1c heap: 0x7f9f80c03980 + data: 0x10d781020 text: 0x10d780ed0 stack: 0x7fff5247fb1c heap: 0x7fe498c03980 + data: 0x10164b020 text: 0x10164aed0 stack: 0x7fff5e5b5b1c heap: 0x7fb783c03980 + data: 0x10c7f8020 text: 0x10c7f7ed0 stack: 0x7fff53408b1c heap: 0x7f9740403980 + +By contrast, here are four runs on FreeBSD 8.3:: + + data: 0x8049728 text: 0x8048470 stack: 0xbfbfebfc heap: 0x28201088 + data: 0x8049728 text: 0x8048470 stack: 0xbfbfebfc heap: 0x28201088 + data: 0x8049728 text: 0x8048470 stack: 0xbfbfebfc heap: 0x28201088 + data: 0x8049728 text: 0x8048470 stack: 0xbfbfebfc heap: 0x28201088 + +Here's the situation on each of the operating systems supported by the MPS: + +* **FreeBSD** (as of version 10.0) does not support ASLR, so there's + nothing to do. + +* On **Windows** (Vista or later), ASLR is a property of the + executable, and it can be turned off at link time using the + |DYNAMICBASE|_. + + .. |DYNAMICBASE| replace:: ``/DYNAMICBASE:NO`` linker option + .. _DYNAMICBASE: http://msdn.microsoft.com/en-us/library/bb384887.aspx + +* On **Linux** (kernel version 2.6.12 or later), ASLR can be turned + off for a single process by running |setarch|_ with the ``-R`` + option:: + + -R, --addr-no-randomize + Disables randomization of the virtual address space + + .. |setarch| replace:: ``setarch`` + .. _setarch: http://man7.org/linux/man-pages/man8/setarch.8.html + + For example:: + + $ setarch $(uname -m) -R ./myprogram + +* On **OS X** (10.7 or later), ASLR can be disabled for a single + process by starting the process using :c:func:`posix_spawn`, passing + the undocumented attribute ``0x100``, like this: + + .. code-block:: c + + #include + + pid_t pid; + posix_spawnattr_t attr; + + posix_spawnattr_init(&attr); + posix_spawnattr_setflags(&attr, 0x100); + posix_spawn(&pid, argv[0], NULL, &attr, argv, environ); - (On these operating systems, you can add these commands to your - ``.gdbinit`` if you always want them to be run.) + The MPS provides the source code for a command-line tool + implementing this (``tool/noaslr.c``). We've confirmed that this + works on OS X 10.9.3, but since the technique is undocumented, it + may well break in future releases. (If you know of a documented way + to achieve this, please :ref:`contact us `.) .. index:: diff --git a/manual/source/guide/index.rst b/manual/source/guide/index.rst index abd9ef242c..fd98e0481e 100644 --- a/manual/source/guide/index.rst +++ b/manual/source/guide/index.rst @@ -9,7 +9,8 @@ Guide overview build lang + vector debug perf advanced - + malloc diff --git a/manual/source/guide/lang.rst b/manual/source/guide/lang.rst index 3b71f61ff2..88c7ec93eb 100644 --- a/manual/source/guide/lang.rst +++ b/manual/source/guide/lang.rst @@ -266,6 +266,25 @@ code for creating the object format for the toy Scheme interpreter:: } MPS_ARGS_END(args); if (res != MPS_RES_OK) error("Couldn't create obj format"); +The keyword arguments specify the :term:`alignment` and the +:term:`format methods` required by the AMC pool class. These are +described in the following sections. + +.. topics:: + + :ref:`topic-format`. + + +.. index:: + single: alignment + single: alignment; object + single: Scheme; object alignment + +.. _guide-lang-alignment: + +Alignment +^^^^^^^^^ + The argument for the keyword :c:macro:`MPS_KEY_FMT_ALIGN` is the :term:`alignment` of objects belonging to this format. Determining the alignment is hard to do portably, because it depends on the target @@ -294,13 +313,19 @@ memory. Here are some things you might try: #define ALIGNMENT sizeof(mps_word_t) -The other keyword arguments specify the :term:`format methods` -required by the AMC pool class, which are described in the following -sections. +#. The MPS interface provides the type :c:type:`MPS_PF_ALIGN`, which + is the :term:`natural alignment` of the platform: the largest + alignment that might be required. So as a last resort, you can + use:: -.. topics:: + #define ALIGNMENT MPS_PF_ALIGN - :ref:`topic-format`. + But this may be larger than necessary and so waste space. For + example, on Windows on x86-64, :c:type:`MPS_PF_ALIGN` is 16 bytes, + but this is only necessary for SSE_ types; ordinary types on this + platform require no more than 8-byte alignment. + + .. _SSE: http://msdn.microsoft.com/en-us/library/t467de55.aspx .. index:: @@ -748,7 +773,7 @@ And third, the global symbol table:: static size_t symtab_size; You tell the MPS how to scan these by writing root scanning functions -of type :c:type:`mps_reg_scan_t`. These functions are similar to the +of type :c:type:`mps_root_scan_t`. These functions are similar to the :ref:`scan method ` in an :term:`object format`, described above. @@ -823,7 +848,7 @@ after the rehash has completed, de-registering the old root by calling :c:func:`mps_root_destroy`. It would be possible to write a root scanning function of type -:c:type:`mps_reg_scan_t`, as described above, to fix the references in +:c:type:`mps_root_scan_t`, as described above, to fix the references in the global symbol table, but the case of a table of references is sufficiently common that the MPS provides a convenient (and optimized) function, :c:func:`mps_root_create_table`, for registering it:: @@ -905,6 +930,10 @@ changes size:: 3. The order of operations at the end is important: the old root must be de-registered before its memory is freed. + 4. When calling :c:func:`mps_root_create_table`, take care to + avoid undefined behaviour due to :term:`type punning`. See the + :ref:`warning `. + .. topics:: :ref:`topic-root`. @@ -1138,28 +1167,28 @@ tracking down the causes, appear in the chapter :ref:`guide-debug`. Tidying up ---------- -When your program is done with the MPS, it's good practice to -:term:`park ` the arena (by calling -:c:func:`mps_arena_park`) and then tear down all the MPS data -structures. This causes the MPS to check the consistency of its data -structures and report any problems it detects. It also causes the MPS -to flush its :term:`telemetry stream`. +When your program is done with the MPS, you should :term:`park ` the arena (by calling :c:func:`mps_arena_park`) to ensure that +no incremental garbage collection is in progress, and then tear down +all the MPS data structures. This causes the MPS to check the +consistency of its data structures and report any problems it detects. +It also causes the MPS to flush its :term:`telemetry stream`. MPS data structures must be destroyed or deregistered in the reverse order to that in which they were registered or created. So you must -destroy all :term:`allocation points` created in a -:term:`pool` before destroying the pool; destroy all :term:`roots` and pools, and deregister all :term:`threads`, that -were created in an :term:`arena` before destroying the arena, and so -on. +destroy all :term:`allocation points` created in a :term:`pool` before +destroying the pool; destroy all :term:`roots` and pools, and +deregister all :term:`threads`, that were created in an :term:`arena` +before destroying the arena, and so on. For example:: mps_arena_park(arena); /* ensure no collection is running */ mps_ap_destroy(obj_ap); /* destroy ap before pool */ mps_pool_destroy(obj_pool); /* destroy pool before fmt */ - mps_fmt_destroy(obj_fmt); /* destroy fmt before arena */ - mps_root_destroy(reg_root); /* destroy root before arena */ + mps_root_destroy(reg_root); /* destroy root before thread */ mps_thread_dereg(thread); /* deregister thread before arena */ + mps_fmt_destroy(obj_fmt); /* destroy fmt before arena */ mps_arena_destroy(arena); /* last of all */ diff --git a/manual/source/guide/malloc.rst b/manual/source/guide/malloc.rst new file mode 100644 index 0000000000..843fc2371e --- /dev/null +++ b/manual/source/guide/malloc.rst @@ -0,0 +1,70 @@ +.. index:: + single: malloc; implementing + single: free; implementing + +.. _guide-malloc: + +Implementing malloc and free +============================ + +The MPS function :c:func:`mps_free` is unlike the Standard C Library +function :c:func:`free` in that it takes a ``size`` argument. That's +because it's nearly always the case that either the size of a block is +known statically based on its type (for example, a structure), or else +the size of the block is easily computed from information that needs +to be stored anyway (for example, a vector), and so memory can be +saved by not storing the size separately. It's also better for virtual +memory performance, as a block does not have to be touched in order +to free it. + +But sometimes you need to interact with :term:`foreign code` which +requires :c:func:`malloc` and :c:func:`free` (or a pair of functions +with the same interface). In this situation you can implement this +interface using a global pool variable, and putting the size of each +block into its header, like this:: + + #include "mps.h" + + static mps_pool_t malloc_pool; + + typedef union { + size_t size; + char alignment[MPS_PF_ALIGN]; /* see note below */ + } header_u; + + void *malloc(size_t size) { + mps_res_t res; + mps_addr_t p; + header_u *header; + size += sizeof *header; + res = mps_alloc(&p, malloc_pool, size); + if (res != MPS_RES_OK) + return NULL; + header = p; + header->size = size; + return header + 1; + } + + void free(void *p) { + if (p) { + header_u *header = ((header_u *)p) - 1; + mps_free(malloc_pool, header, header->size); + } + } + +The ``alignment`` member of ``union header_u`` ensures that +allocations are aligned to the platform's :term:`natural alignment` +(see :ref:`guide-lang-alignment`). + +The pool needs to belong to a :term:`manually managed ` pool class, for example :ref:`pool-mvff` (or its +:ref:`debugging counterpart `):: + + #include "mpscmvff.h" + + void malloc_pool_init(mps_arena_t arena) { + mps_res_t res; + res = mps_pool_create_k(&malloc_pool, arena, mps_class_mvff(), mps_args_none); + if (res != RES_OK) + abort(); + } diff --git a/manual/source/guide/overview.rst b/manual/source/guide/overview.rst index 0292536106..3156469f27 100644 --- a/manual/source/guide/overview.rst +++ b/manual/source/guide/overview.rst @@ -37,6 +37,7 @@ Ravenbrook by arrangement. Please :ref:`contact us ` at `mps-questions@ravenbrook.com `_ for details. +.. comment: Keep this section synchronized with readme.txt .. index:: single: Memory Pool System; supported target platforms @@ -47,24 +48,30 @@ Supported target platforms The MPS is currently supported for deployment on: -- Windows XP or later on IA-32 and x86-64, using Microsoft Visual C/C++; +- Windows XP or later, on IA-32 and x86-64, using Microsoft Visual C/C++; -- Linux (Ubuntu 11 and RHEL 6.3 known good, otherwise YMMV) on IA-32 and x86-64, using GCC; +- Linux 2.4 or later, on IA-32 using GCC and on x86-64 using GCC or + Clang/LLVM; - FreeBSD 7 or later, on IA-32 and x86-64, using GCC; -- OS X 10.4 or later, on IA-32 and x86-64 (single threaded only), using Clang/LLVM. - -The MPS will *not* work in a multi-threaded 32-bit application on 64-bit -Windows 7. This is due to a serious fault in Microsoft's WOW64 emulator -that we are powerless to correct. It is due to be fixed in Windows 8. -See `WOW64 bug: GetThreadContext() may return stale contents `_. +- OS X 10.4 or later, on IA-32 and x86-64, using Clang/LLVM. The MPS is highly portable and has run on many other processors and operating systems in the past (see :ref:`guide-build`). Most of the MPS is written in very pure ANSI C and compiles without warnings on anything. +.. warning:: + + If you are running a multi-threaded 32-bit application on 64-bit + Windows 7 via the WOW64 emulator, then you must install `this + hotfix from Microsoft + `_. See `WOW64 bug: + GetThreadContext() may return stale contents + `_ + for a description of the problem. + .. index:: single: Memory Pool System; technical introduction diff --git a/manual/source/guide/vector.rst b/manual/source/guide/vector.rst new file mode 100644 index 0000000000..8b9452ac1f --- /dev/null +++ b/manual/source/guide/vector.rst @@ -0,0 +1,152 @@ +.. index:: + single: stretchy vectors + single: atomic updates + +.. _guide-stretchy-vector: + +The stretchy vector problem +============================ + +The :ref:`previous chapter ` pointed out that: + + Because the MPS is :term:`asynchronous `, it might be scanning, moving, or collecting, at any + point in time. + +The consequences of this can take a while to sink in, so this chapter +discusses a particular instance that catches people out: the *stretchy +vector* problem (named after the |stretchy-vector|_ abstract class in +Dylan). + +.. |stretchy-vector| replace:: ```` +.. _stretchy-vector: http://opendylan.org/books/drm/Collection_Classes#stretchy-vector + +A *stretchy vector* is a vector that can change length dynamically. +Such a vector is often implemented using two objects: an array, and a +header object that stores the length and a pointer to an array. +Stretching (or shrinking) such a vector involves five steps: + +1. allocate a new array; +2. copy elements from the old array to the new array; +3. clear unused elements in the new array (if stretching); +4. update the pointer to the array in the header; +5. update the length in the header. + +For example: + +.. code-block:: c + + typedef struct vector_s { + type_t type; /* TYPE_VECTOR */ + size_t length; /* number of elements */ + obj_t *array; /* array of elements */ + } vector_s, *vector_t; + + void resize_vector(vector_t vector, size_t new_length) { + obj_t *new_array = realloc(vector->array, new_length * sizeof(obj_t)); + if (new_array == NULL) + error("out of memory in resize_vector"); + if (vector->length < new_length) { + memset(&vector->array[vector->length], 0, + (new_length - vector->length) * sizeof(obj_t)); + } + vector->array = new_array; + vector->length = new_length; + } + +When adapting this code to the MPS, the following problems must be +solved: + +1. During step 2, the new array must be :term:`reachable` from the + roots, and :term:`scannable `. (If it's not reachable, then + it may be collected; if it's not scannable, then references it + contains will not be updated when they are moved by the collector.) + + This can solved by storing the new array in a :term:`root` until + the header has been updated. If the thread's stack has been + registered as a root by calling :c:func:`mps_root_create_reg` then + any local variable will do. + +2. References in the new array must not be scanned until they have been + copied or cleared. (Otherwise they will be invalid.) + + This can be solved by clearing the new array before calling + :c:func:`mps_commit`. + +3. The old array must be scanned at the old length (otherwise the scan + may run off the end of the old array when the vector grows), and + the new array must be scanned at the new length (otherwise the scan + may run off the end of the old array when the vector shrinks). + +4. The array object must be scannable without referring to the header + object. (Because the header object may have been protected by the + MPS: see :ref:`topic-format-cautions`.) + +Problems 3 and 4 can be solved by storing the length in the array. The +revised data structures and resizing code might look like this: + +.. code-block:: c + + typedef struct vector_s { + type_t type; /* TYPE_VECTOR */ + obj_t array; /* TYPE_ARRAY object */ + } vector_s, *vector_t; + + typedef struct array_s { + type_t type; /* TYPE_ARRAY */ + size_t length; /* number of elements */ + obj_t array[0]; /* array of elements */ + } array_s, *array_t; + + void resize_vector(vector_t vector, size_t new_length) { + size_t size = ALIGN_OBJ(offsetof(array_s, array) + new_length * sizeof(obj_t)); + mps_addr_t addr; + array_t array; + + do { + mps_res_t res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) error("out of memory in resize_vector"); + array = addr; + array->type = TYPE_ARRAY; + array->length = new_length; + memset(array->array, 0, new_length * sizeof(obj_t)); + /* Now the new array is scannable, and it is reachable via the + * local variable 'array', so it is safe to commit it. */ + } while(!mps_commit(ap, addr, size)); + + /* Copy elements after committing, so that the collector will + * update them if they move. */ + memcpy(array->array, vector->array->array, + min(vector->array->length, new_length) * sizeof(obj_t)); + vector->array = array; + } + +Similar difficulties can arise even when adapting code written for +other garbage collectors. For example, here's the function +|setarrayvector|_ from Lua_: + +.. |setarrayvector| replace:: ``setarrayvector()`` +.. _setarrayvector: http://www.lua.org/source/5.2/ltable.c.html#setarrayvector +.. _Lua: http://www.lua.org + +.. code-block:: c + + static void setarrayvector (lua_State *L, Table *t, int size) { + int i; + luaM_reallocvector(L, t->array, t->sizearray, size, TValue); + for (i=t->sizearray; iarray[i]); + t->sizearray = size; + } + +Lua's garbage collector is :term:`synchronous `, so it can be assumed that there cannot be a garbage +collection between the assignment to ``t->array`` (resulting from the +expansion of the |luaM_reallocvector|_ macro) and the assignment to +``t->sizearray``, and so the collector will always consistently see +either the old array or the new array, with the correct size. This +assumption will no longer be correct if this code is adapted to the +MPS. + +.. |luaM_reallocvector| replace:: ``luaM_reallocvector()`` +.. _luaM_reallocvector: http://www.lua.org/source/5.2/lmem.h.html#luaM_reallocvector diff --git a/manual/source/diagrams/logo.png b/manual/source/images/logo.png similarity index 100% rename from manual/source/diagrams/logo.png rename to manual/source/images/logo.png diff --git a/manual/source/index.rst b/manual/source/index.rst index 161a13ad3b..57758fea8e 100644 --- a/manual/source/index.rst +++ b/manual/source/index.rst @@ -1,7 +1,3 @@ -.. Memory Pool System documentation master file, created by - sphinx-quickstart on Tue Oct 9 11:21:17 2012. - - Memory Pool System ################## @@ -15,26 +11,27 @@ Memory Pool System design/old -Memory Management Reference -########################### - -.. toctree:: - :maxdepth: 2 - - mmref/index - mmref/bib - mmref/credit - - Appendices ########## .. toctree:: :maxdepth: 1 + bib glossary/index copyright contact + contributing release * :ref:`genindex` + + +.. toctree:: + :hidden: + + mmref/index + mmref-index + mmref/faq + mmref-copyright + mmref/credit diff --git a/manual/source/mmref-copyright.rst b/manual/source/mmref-copyright.rst new file mode 100644 index 0000000000..b2e95ecd3a --- /dev/null +++ b/manual/source/mmref-copyright.rst @@ -0,0 +1,26 @@ +Copyright +********* + + +Use subject to copyright restrictions +===================================== + +The copyright in The Memory Management Reference is owned by +`Ravenbrook Limited`_. + +.. _Ravenbrook Limited: http://www.ravenbrook.com/ + +Permission to copy part or all of The Memory Management Reference for +personal or classroom use is granted without fee, provided that copies +are not made or distributed for profit or commercial advantage; that +the copyright notice, the title of the publication, and its date +appear; and that notice is given that copying is by permission of +Ravenbrook Limited. To copy otherwise, to republish, to post on +servers, or to redistribute to lists requires prior specific +permission. + + +Warranty disclaimer +=================== + +The Memory Management Reference is provided "as is" without warranty of any kind, express or implied, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement. diff --git a/manual/source/mmref-index.rst b/manual/source/mmref-index.rst new file mode 100644 index 0000000000..a91b7b702c --- /dev/null +++ b/manual/source/mmref-index.rst @@ -0,0 +1,57 @@ +Home +**** + +Welcome to the **Memory Management Reference**! This is a resource for programmers and computer scientists interested in :term:`memory management` and :term:`garbage collection`. + + +.. admonition:: :ref:`glossary` + + A glossary of more than 500 :term:`memory management` terms, from + :term:`absolute address` to :term:`zero count table`. + + .. image:: diagrams/treadmill.svg + :target: glossary_ + + .. _glossary: glossary/index.html#glossary + + +.. admonition:: :ref:`mmref-intro` + + Articles giving a beginner's overview of :term:`memory + management`. + + .. image:: diagrams/address.svg + :target: intro_ + + .. _intro: mmref/index.html#mmref-intro + + +.. admonition:: :ref:`bibliography` + + Books and research papers related to :term:`memory management`. + + .. image:: diagrams/copying.svg + :target: bib_ + + .. _bib: bib.html#bibliography + + +.. admonition:: :ref:`mmref-faq` + + Frequently asked questions about :term:`memory management`. + + .. image:: diagrams/snap-out.svg + :target: faq_ + + .. _faq: mmref/faq.html#mmref-faq + +The Memory Management Reference is maintained by `Ravenbrook +Limited`_. We also maintain the `Memory Pool System`_ (an open-source, +thread-safe, :term:`incremental garbage collector `), and we are happy to provide advanced :term:`memory +management` solutions to language and application developers through +our `consulting service`_. + +.. _Ravenbrook Limited: http://www.ravenbrook.com/ +.. _consulting service: http://www.ravenbrook.com/services/mm/ +.. _Memory Pool System: http://www.ravenbrook.com/project/mps/ diff --git a/manual/source/mmref/bib.rst b/manual/source/mmref/bib.rst deleted file mode 100644 index aba18413d6..0000000000 --- a/manual/source/mmref/bib.rst +++ /dev/null @@ -1,965 +0,0 @@ -.. _bibliography: - -Bibliography -************ - -* .. _AD97: - - Ole Agesen, David L. Detlefs. 1997. "`Finding References in Java Stacks `_". Sun Labs. OOPSLA97 Workshop on Garbage Collection and Memory Management. - - .. abstract: ad97.html - -* .. _ADM98: - - Ole Agesen, David L. Detlefs, J. Eliot B. Moss. 1998. "`Garbage Collection and Local Variable Type-precision and Liveness in Java Virtual Machines `_". ACM. Proceedings of the ACM SIGPLAN '98 conference on Programming language design and implementation, pp. 269--279. - - .. abstract: adm98.html - -* .. _AEL88: - - Andrew Appel, John R. Ellis, Kai Li. 1988. "`Real-time Concurrent Collection on Stock Multiprocessors `_". ACM, SIGPLAN. ACM PLDI 88, SIGPLAN Notices 23, 7 (July 88), pp. 11--20. - - .. abstract: ael88.html - -* .. _APPLE94: - - Apple Computer, Inc. 1994. *Inside Macintosh: Memory*. Addison-Wesley. ISBN 0-201-63240-3. - - .. abstract: apple94.html - -* .. _ATTARDI94: - - Giuseppe Attardi & Tito Flagella. 1994. "`A Customisable Memory Management Framework `_". TR-94-010. - - .. abstract: attardi94.html - -* .. _AFI98: - - Giuseppe Attardi, Tito Flagella, Pietro Iglio. 1998. "`A customisable memory management framework for C++ `_". Software -- Practice and Experience. 28(11), 1143--1183. - - .. abstract: afi98.html - -* .. _AKPY98: - - Alain Azagury, Elliot K. Kolodner, Erez Petrank, Zvi Yehudai. 1998. "`Combining Card Marking with Remembered Sets: How to Save Scanning Time `_". ACM. ISMM'98 pp. 10--19. - - .. abstract: akpy98.html - -* .. _BAKER77: - - Henry G. Baker, Carl Hewitt. 1977. "`The Incremental Garbage Collection of Processes `_". ACM. SIGPLAN Notices 12, 8 (August 1977), pp. 55--59. - - .. abstract: baker77.html - -* .. _BAKER78: - - Henry G. Baker. 1978. "`List Processing in Real Time on a Serial Computer `_". ACM. Communications of the ACM 21, 4 (April 1978), pp. 280--294. - - .. abstract: baker78.html - -* .. _BAKER79: - - Henry G. Baker. 1979. "`Optimizing Allocation and Garbage Collection of Spaces `_". In Winston and Brown, eds. *Artificial Intelligence: An MIT Perspective.* MIT Press. - - .. abstract: baker79.html - -* .. _BAKER91: - - Henry G. Baker. 1991. "`Cache-Conscious Copying Collectors `_". OOPSLA'91/GC'91 Workshop on Garbage Collection. - - .. abstract: baker91.html - -* .. _BAKER92A: - - Henry G. Baker. 1992. "`Lively Linear Lisp -- 'Look Ma, No Garbage!' `_". ACM. SIGPLAN Notices 27, 8 (August 1992), pp. 89--98. - - .. abstract: baker92a.html - -* .. _BAKER92C: - - Henry G. Baker. 1992. "`The Treadmill: Real-Time Garbage Collection Without Motion Sickness `_". ACM. SIGPLAN Notices 27, 3 (March 1992), pp. 66--70. - - .. abstract: baker92c.html - -* .. _BAKER92: - - Henry G. Baker. 1992. "`CONS Should not CONS its Arguments, or, a Lazy Alloc is a Smart Alloc `_". ACM. SIGPLAN Notices 27, 3 (March 1992), 24--34. - - .. abstract: baker92.html - -* .. _BAKER92B: - - Henry G. Baker. 1992. "`NREVERSAL of Fortune -- The Thermodynamics of Garbage Collection `_". Springer-Verlag. LNCS Vol. 637. - - .. abstract: baker92b.html - -* .. _BAKER93: - - Henry G. Baker. 1993. "`'Infant Mortality' and Generational Garbage Collection `_". ACM. SIGPLAN Notices 28, 4 (April 1993), pp. 55--57. - - .. abstract: baker93.html - -* .. _BAKER93A: - - Henry G. Baker. 1993. "`Equal Rights for Functional Objects or, The More Things Change, The More They Are the Same `_". ACM. OOPS Messenger 4, 4 (October 1993), pp. 2--27. - - .. abstract: baker93a.html - -* .. _BAKER94: - - Henry G. Baker. 1994. "`Minimizing Reference Count Updating with Deferred and Anchored Pointers for Functional Data Structures `_". ACM. SIGPLAN Notices 29, 9 (September 1994), pp. 38--43. - - .. abstract: baker94.html - -* .. _BAKER94A: - - Henry G. Baker. 1994. "`Thermodynamics and Garbage Collection `_". ACM. SIGPLAN Notices 29, 4 (April 1994), pp. 58--63. - - .. abstract: baker94a.html - -* .. _BAKER95A: - - Henry G. Baker. 1995. "`'Use-Once' Variables and Linear Objects -- Storage Management, Reflection and Multi-Threading `_". ACM. SIGPLAN Notices 30, 1 (January 1995), pp. 45--52. - - .. abstract: baker95a.html - -* .. _BAKER95: - - Henry G. Baker. 1995. *Memory Management: International Workshop IWMM'95*. Springer-Verlag. ISBN 3-540-60368-9. - - .. abstract: baker95.html - -* .. _BBW97: - - Nick Barnes, Richard Brooksby, David Jones, Gavin Matthews, Pekka P. Pirinen, Nick Dalton, P. Tucker Withington. 1997. "`A Proposal for a Standard Memory Management Interface `_". OOPSLA97 Workshop on Garbage Collection and Memory Management. - -* .. _ZORN93B: - - David A. Barrett, Benjamin Zorn. 1993. "`Using Lifetime Predictors to Improve Memory Allocation Performance `_". ACM. SIGPLAN'93 Conference on Programming Language Design and Implementation, pp. 187--196. - - .. abstract: zorn93b.html - -* .. _BARRETT93: - - David A. Barrett, Benjamin Zorn. 1995. "`Garbage Collection using a Dynamic Threatening Boundary `_". ACM. SIGPLAN'95 Conference on Programming Language Design and Implementation, pp. 301--314. - - .. abstract: barrett93.html - -* .. _BARTLETT88: - - Joel F. Bartlett. 1988. "`Compacting Garbage Collection with Ambiguous Roots `_". Digital Equipment Corporation. - - .. abstract: bartlett88.html - -* .. _BARTLETT89: - - Joel F. Bartlett. 1989. "`Mostly-Copying Garbage Collection Picks Up Generations and C++ `_". Digital Equipment Corporation. - - .. abstract: bartlett89.html - -* .. _BC92: - - Yves Bekkers & Jacques Cohen. 1992. "`Memory Management, International Workshop IWMM 92 `_". Springer-Verlag. LNCS Vol. 637, ISBN 3-540-55940-X. - -* .. _BB99: - - Emery D. Berger, Robert D. Blumofe. 1999. "`Hoard: A Fast, Scalable, and Memory-Efficient Allocator for Shared-Memory Multiprocessors `_". University of Texas at Austin. UTCS TR99-22. - - .. abstract: bb99.html - -* .. _BERGER01: - - Emery D. Berger, Benjamin G. Zorn, Kathryn S. McKinley. 2001. "`Composing high-performance memory allocators `_" ACM SIGPLAN Conference on Programming Language Design and Implementation 2001, pp. 114--124. - -* .. _BW88: - - Hans-J. Boehm, Mark Weiser. 1988. "`Garbage collection in an uncooperative environment `_". Software -- Practice and Experience. 18(9):807--820. - - .. abstract: bw88.html - -* .. _BDS91: - - Hans-J. Boehm, Alan J. Demers, Scott Shenker. 1991. "`Mostly Parallel Garbage Collection `_". Xerox PARC. ACM PLDI 91, SIGPLAN Notices 26, 6 (June 1991), pp. 157--164. - - .. abstract: bds91.html - -* .. _BC92A: - - Hans-J. Boehm, David Chase. 1992. "A Proposal for Garbage-Collector-Safe C Compilation". *Journal of C Language Translation.* vol. 4, 2 (December 1992), pp. 126--141. - -* .. _BOEHM93: - - Hans-J. Boehm. 1993. "`Space Efficient Conservative Garbage Collection `_". ACM, SIGPLAN. Proceedings of the ACM SIGPLAN '91 Conference on Programming Language Design and Implementation, SIGPLAN Notices 28, 6, pp 197--206. - - .. abstract: boehm93.html - -* .. _BOEHM00: - - Hans-J. Boehm. 2000. "`Reducing Garbage Collector Cache Misses `_". ACM. ISMM'00 pp. 59--64. - - .. abstract: boehm00.html - -* .. _BOEHM02: - - Hans-J. Boehm. 2002. "`Destructors, Finalizers, and Synchronization `_". HP Labs technical report HPL-2002-335. - -* .. _BM77: - - Robert S. Boyer and J. Strother Moore. 1977. "`A Fast String Searching Algorithm `_". *Communications of the ACM* 20(10):762--772. - -* .. _BL72: - - P. Branquart, J. Lewi. 1972. "A scheme of storage allocation and garbage collection for ALGOL 68". Elsevier/North-Holland. ALGOL 68 Implementation -- Proceedings of the IFIP Working Conference on ALGOL 68 Implementation, July 1970. - -* .. _BROOKSBY02: - - Richard Brooksby. 2002. "`The Memory Pool System: Thirty person-years of memory management development goes Open Source `_". ISMM'02. - -* .. _C1990: - - International Standard ISO/IEC 9899:1990. "Programming languages — C". - -* .. _C1999: - - International Standard ISO/IEC 9899:1999. "`Programming languages — C `_". - -* .. _CGZ94: - - Brad Calder, Dirk Grunwald, Benjamin Zorn. 1994. "`Quantifying Behavioral Differences Between C and C++ Programs `_". *Journal of Programming Languages.* 2(4):313--351. - - .. abstract: cgz94.html - -* .. _CPC00: - - Dante J. Cannarozzi, Michael P. Plezbert, Ron K. Cytron. 2000. "`Contaminated garbage collection `_". ACM. Proceedings of the ACM SIGPLAN '00 conference on on Programming language design and implementation, pp. 264--273. - -* .. _CW86: - - Patrick J. Caudill, Allen Wirfs-Brock. 1986. "A Third-Generation Smalltalk-80 Implementation". ACM. SIGPLAN Notices. 21(11), OOPSLA'86 ACM Conference on Object-Oriented Systems, Languages and Applications. - -* .. _CHENEY70: - - C. J. Cheney. 1970. "A non-recursive list compacting algorithm". CACM. 13-11 pp. 677--678. - -* .. _CHL98: - - Perry Cheng, Robert Harper, Peter Lee. 1998. "`Generational stack collection and profile-driven pretenuring `_". ACM. Proceedings of SIGPLAN'98 Conference on Programming Language Design and Implementation, pp. 162--173. - -* .. _CL98: - - Trishul M. Chilimbi, James R. Larus. 1998. "`Using Generational Garbage Collection To Implement Cache-Conscious Data Placement `_". ACM. ISMM'98 pp. 37--48. - - .. abstract: cl98.html - -* .. _CH97: - - William D Clinger & Lars T Hansen. 1997. "`Generational Garbage Collection and the Radioactive Decay Model `_". ACM. Proceedings of PLDI 1997. - - .. abstract: ch97.html - -* .. _COHEN81: - - Jacques Cohen. 1981. "Garbage collection of linked data structures". Computing Surveys. Vol. 13, no. 3. - - .. abstract: cohen81.html - -* .. _CCZ98: - - Dominique Colnet, Philippe Coucaud, Olivier Zendra. 1998. "`Compiler Support to Customize the Mark and Sweep Algorithm `_". ACM. ISMM'98 pp. 154--165. - - .. abstract: ccz98.html - -* .. _CWZ93: - - Jonathan E. Cook, Alexander L. Wolf, Benjamin Zorn. 1994. "`Partition Selection Policies in Object Database Garbage Collection `_". ACM. SIGMOD. International Conference on the Management of Data (SIGMOD'94), pp. 371--382. - - .. abstract: cwz93.html - -* .. _CKWZ96: - - Jonathan E. Cook, Artur Klauser, Alexander L. Wolf, Benjamin Zorn. 1996. "`Semi-automatic, Self-adaptive Control of Garbage Collection Rates in Object Databases `_". ACM, SIGMOD. International Conference on the Management of Data (SIGMOD'96), pp. 377--388. - -* .. _CNS92: - - Eric Cooper, Scott Nettles, Indira Subramanian. 1992. "Improving the Performance of SML Garbage Collection using Application-Specific Virtual Memory Management". ACM Conference on LISP and Functional Programming, pp. 43--52. - - .. abstract: cns92.html - -* .. _DACONTA93: - - Michael C. Daconta. 1993. *C Pointers and Dynamic Memory Management.* Wiley. ISBN 0-471-56152-5. - -* .. _DACONTA95: - - Michael C. Daconta. 1995. *C++ Pointers and Dynamic Memory Management.* Wiley. ISBN 0-471-04998-0. - - .. abstract: daconta95.html - -* .. _DAHL63: - - O.-J. Dahl. 1963. "The SIMULA Storage Allocation Scheme". Norsk Regnesentral. NCC Document no. 162. - -* .. _DENNING68: - - P. J. Denning. 1968. "Thrashing: Its Causes and Prevention". Proceedings AFIPS,1968 Fall Joint Computer Conference, vol. 33, pp. 915--922. - -* .. _DENNING70: - - P. J. Denning. 1970. "`Virtual Memory `_". ACM. ACM Computing Surveys, vol. 2, no. 3, pp. 153--190, Sept. 1970. - -* .. _DS72: - - P. J. Denning, S. C. Schwartz. 1972. "`Properties of the Working-set Model `_". CACM. vol. 15, no. 3, pp. 191--198. - -* .. _DETLEFS92: - - David L. Detlefs. 1992. "`Garbage collection and runtime typing as a C++ library `_". USENIX C++ Conference. - -* .. _ZORN93: - - David L. Detlefs, Al Dosser, Benjamin Zorn. 1994. "`Memory Allocation Costs in Large C and C++ Programs `_". Software -- Practice and Experience. 24(6):527--542. - - .. abstract: zorn93.html - -* .. _DB76: - - L. Peter Deutsch, Daniel G. Bobrow. 1976. "`An Efficient, Incremental, Automatic Garbage Collector `_". CACM. vol. 19, no. 9, pp. 522--526. - -* .. _DLMSS76: - - E. W. Dijkstra, Leslie Lamport, A. J. Martin, C. S. Scholten, E. F. M. Steffens. 1976. "`On-the-fly Garbage Collection: An Exercise in Cooperation `_". Springer-Verlag. Lecture Notes in Computer Science, Vol. 46. - -* .. _DMH92: - - Amer Diwan, Richard L. Hudson, J. Eliot B. Moss. 1992. "`Compiler Support for Garbage Collection in a Statically Typed Language `_". ACM. Proceedings of the 5th ACM SIGPLAN conference on Programming language design and implementation, pp. 273--282. - - .. abstract: dmh92.html - -* .. _DTM93: - - Amer Diwan, David Tarditi, J. Eliot B. Moss. 1993. "`Memory Subsystem Performance of Programs with Intensive Heap Allocation `_". Carnegie Mellon University. CMU-CS-93-227. - - .. abstract: dtm93.html - -* .. _DTM93A: - - Amer Diwan, David Tarditi, J. Eliot B. Moss. 1994. "`Memory Subsystem Performance of Programs Using Copying Garbage Collection `_". ACM. CMU-CS-93-210, also in POPL '94. - - .. abstract: dtm93a.html - -* .. _DOLIGEZ93: - - Damien Doligez & Xavier Leroy. 1993. "`A concurrent, generational garbage collector for a multithreaded implementation of ML `_". ACM. POPL '93, 113--123. - - .. abstract: doligez93.html - -* .. _DOLIGEZ94: - - Damien Doligez & Georges Gonthier. 1994. "`Portable, unobtrusive garbage collection for multiprocessor systems `_". ACM. POPL '94, 70--83. - - .. abstract: doligez94.html - -* .. _DBE93: - - R. Kent Dybvig, Carl Bruggeman, David Eby. 1993. "`Guardians in a Generation-Based Garbage Collector `_". SIGPLAN. Proceedings of the ACM SIGPLAN '93 Conference on Programming Language Design and Implementation, June 1993. - - .. abstract: dbe93.html - -* .. _EDELSON92A: - - Daniel R. Edelson. 1992. "`Smart pointers: They're smart, but they're not pointers `_". USENIX C++ Conference. - -* .. _EDELSON92: - - Daniel R. Edelson. 1992. "Comparing Two Garbage Collectors for C++". University of California at Santa Cruz. Technical Report UCSC-CRL-93-20. - -* .. _EDWARDS: - - Daniel J. Edwards. n.d. "`Lisp II Garbage Collector `_". MIT. AI Memo 19 (AIM-19). - - .. abstract: edwards.html - -* .. _ELLIS93: - - John R. Ellis, David L. Detlefs. 1993. "`Safe, Efficient Garbage Collection for C++ `_". Xerox PARC. - - .. abstract: ellis93.html - -* .. _FERREIRA96: - - Paulo Ferreira. 1996. "`Larchant: garbage collection in a cached distributed shared store with persistence by reachability `_". Université Paris VI. Thése de doctorat. - - .. abstract: ferreira96.html - -* .. _FS98: - - Paulo Ferreira & Marc Shapiro. 1998. "`Modelling a Distributed Cached Store for Garbage Collection `_". Springer-Verlag. Proceedings of 12th European Conference on Object-Oriented Programming, ECOOP98, LNCS 1445. - -* .. _FW76: - - Daniel P Friedman, David S. Wise. 1976. "`Garbage collecting a heap which includes a scatter table `_". *Information Processing Letters.* 5, 6 (December 1976): 161--164. - -* .. _FW77: - - Daniel P Friedman, David S. Wise. 1977. "`The One Bit Reference Count `_". *BIT.* (17)3: 351--359. - - .. abstract: fw77.html - -* .. _FW79: - - Daniel P Friedman, David S. Wise. 1979. "`Reference counting can manage the circular environments of mutual recursion `_". *Information Processing Letters.* 8, 1 (January 1979): 41--45. - -* .. _GZH93: - - Dirk Grunwald, Benjamin Zorn, R. Henderson. 1993. "`Improving the Cache Locality of Memory Allocation `_". SIGPLAN. SIGPLAN '93, Conference on PLDI, June 1993, Albuquerque, New Mexico. - - .. abstract: gzh93.html - -* .. _GRUN92: - - Dirk Grunwald & Benjamin Zorn. 1993. "`CustoMalloc: Efficient Synthesized Memory Allocators `_". Software -- Practice and Experience. 23(8):851--869. - - .. abstract: grun92.html - -* .. _GUDEMAN93: - - David Gudeman. 1993. "`Representing Type Information in Dynamically Typed Languages `_". University of Arizona at Tucson. Technical Report TR 93-27. - - .. abstract: gudeman93.html - -* .. _HARRIS99: - - Timothy Harris. 1999. "`Early storage reclamation in a tracing garbage collector `_". ACM. ACM SIG-PLAN Notices 34:4, pp. 46--53. - - .. abstract: harris99.html - -* .. _HENRIK94: - - Roger Henriksson. 1994. "Scheduling Real Time Garbage Collection". Department of Computer Science at Lund University. LU-CS-TR:94-129. - - .. abstract: henrik94.html - -* .. _HENRIK96: - - Roger Henriksson. 1996. "`Adaptive Scheduling of Incremental Copying Garbage Collection for Interactive Applications `_". NWPER96. - - .. abstract: henrik96.html - -* .. _HENRIKSSON98: - - Roger Henriksson. 1998. "`Scheduling Garbage Collection in Embedded Systems `_". Department of Computer Science at Lund University. Ph.D. thesis. - - .. abstract: henriksson98.html - -* .. _HOSKING91: - - Antony L. Hosking. 1991. "`Main memory management for persistence `_". ACM. Proceedings of the ACM OOPSLA'91 Workshop on Garbage Collection. - -* .. _HMS92: - - Antony L. Hosking, J. Eliot B. Moss, Darko Stefanovic. 1992. "`A comparative performance evaluation of write barrier implementations `_". ACM. OOPSLA'92 Conference Proceedings, ACM SIGPLAN Notices 27(10), pp 92--109. - -* .. _HH93: - - Antony L. Hosking, Richard L. Hudson. 1993. "`Remembered sets can also play cards `_". ACM. Proceedings of the ACM OOPSLA'93 Workshop on Memory Management and Garbage Collection. - -* .. _HM93: - - Antony L. Hosking, J. Eliot B. Moss. 1993. "`Protection traps and alternatives for memory management of an object-oriented language `_". ACM. Proceedings of the Fourteenth ACM Symposium on Operating Systems Principles, ACM Operating Systems Review 27(5), pp 106--119. - -* .. _HMDW91: - - Richard L. Hudson, J. Eliot B. Moss, Amer Diwan, Christopher F. Weight. 1991. "`A Language-Independent Garbage Collector Toolkit `_". University of Massachusetts at Amherst. COINS Technical Report 91--47. - - .. abstract: hmdw91.html - -* .. _HM92: - - Richard L. Hudson, J. Eliot B. Moss. 1992. "`Incremental Collection of Mature Objects `_". Springer-Verlag. LNCS #637 International Workshop on Memory Management, St. Malo, France, Sept. 1992, pp. 388--403. - - .. abstract: hm92.html - -* .. _HMMM97: - - Richard L. Hudson, Ron Morrison, J. Eliot B. Moss, David S. Munro. 1997. "`Garbage Collecting the World: One Car at a Time `_". ACM. Proc. OOPSLA 97, pp. 162--175. - - .. abstract: hmmm97.html - -* .. _ISO90: - - "International Standard ISO/IEC 9899:1990 Programming languages — C". - -* .. _JOHNSTONE97: - - Mark S. Johnstone. 1997. "`Non-Compacting Memory Allocation and Real-Time Garbage Collection `_". University of Texas at Austin. - - .. abstract: johnstone97.html - -* .. _JW98: - - Mark S. Johnstone, Paul R. Wilson. 1998. "`The Memory Fragmentation Problem: Solved? `_". ACM. ISMM'98 pp. 26--36. - - .. abstract: jw98.html - -* .. _JONES92: - - Richard E. Jones. 1992. "`Tail recursion without space leaks `_". *Journal of Functional Programming.* 2(1):73--79. - -* .. _JL92: - - Richard E. Jones, Rafael Lins. 1992. "`Cyclic weighted reference counting without delay `_". Computing Laboratory, The University of Kent at Canterbury. Technical Report 28-92. - - .. abstract: jl92.html - -* .. _JONES96: - - Richard E. Jones, Rafael Lins. 1996. "`Garbage Collection: Algorithms for Automatic Dynamic Memory Management `_". Wiley. ISBN 0-471-94148-4. - - .. abstract: jones96.html - -* .. _ACM98: - - Richard E. Jones. 1998. "`ISMM'98 International Symposium on Memory Management `_". ACM. ISBN 1-58113-114-3. - - .. abstract: acm98.html - -* .. _JONES12: - - Richard E. Jones, Antony Hosking, and Eliot Moss. 2012. "`The Garbage Collection Handbook `_". Chapman & Hall. - -* .. _JOYNER96: - - Ian Joyner. 1996. "`C++??: A Critique of C++ `_.". - -* .. _KANEFSKY89: - - Bob Kanefsky. 1989. "`Recursive Memory Allocation `_". Bob Kanefsky. Songworm 3, p.?. - -* .. _KQH98: - - Jin-Soo Kim, Xiaohan Qin, Yarsun Hsu. 1998. "`Memory Characterization of a Parallel Data Mining Workload `_". IEEE. Proc. Workload Characterization: Methodology and Case Studies, pp. . - - .. abstract: kqh98.html - -* .. _KH00: - - Jin-Soo Kim & Yarsun Hsu. 2000. "Memory system behavior of Java programs: methodology and analysis". ACM. Proc. International conference on measurements and modeling of computer systems, pp. 264--274. - -* .. _KOLODNER92: - - Elliot K. Kolodner. 1992. "Atomic Incremental Garbage Collection and Recovery for a Large Stable Heap". Laboratory for Computer Science at MIT. MIT-LCS-TR-534. - - .. abstract: kolodner92.html - -* .. _LK98: - - Per-Åke Larson & Murali Krishnan. 1998. "`Memory Allocation for Long-Running Server Applications `_". ACM. ISMM'98 pp. 176--185. - - .. abstract: lk98.html - -* .. _LH83: - - Henry Lieberman & Carl Hewitt. 1983. "`A real-time garbage collector based on the lifetimes of objects `_". ACM. 26(6):419--429. - -* .. _MM59: - - J. McCarthy, M. L. Minsky. 1959. "Artificial Intelligence, Quarterly Progress Report no. 53". Research Laboratory of Electronics at MIT. - -* .. _MCCARTHY60: - - J. McCarthy. 1960. "`Recursive Functions of Symbolic Expressions and Their Computation by Machine `_". CACM. - - .. abstract: mccarthy60.html - -* .. _MCCARTHY79: - - John McCarthy. 1979. "`History of Lisp `_". In *History of programming languages I*, pp. 173–185. ACM. - -* .. _PTM98: - - Veljko Milutinovic, Jelica Protic, Milo Tomasevic. 1997. "`Distributed shared memory: concepts and systems `_". IEEE Computer Society Press. ISBN 0-8186-7737-6. - - .. abstract: ptm98.html - -* .. _MINSKY63: - - M. L. Minsky. 1963. "A LISP Garbage Collector Algorithm Using Serial Secondary Storage". MIT. Memorandum MAC-M-129, Artificial Intelligence Project, Memo 58 (revised). - -* .. _MOON84: - - David Moon. 1984. "`Garbage Collection in a Large Lisp System `_". ACM. Symposium on Lisp and Functional Programming, August 1984. - -* .. _MOON85: - - David Moon. 1985. "Architecture of the Symbolics 3600". IEEE. 12th International Symposium on Computer Architecture, pp. 76--83. - -* .. _MOON87: - - David Moon. 1990. "Symbolics Architecture". Wiley. Chapter 3 of *Computers for Artificial Intelligence Processing*, ISBN 0-471-84811-5. - -* .. _MOON91: - - David Moon. 1991. "Genera Retrospective". IEEE. 1991 International Workshop on Object Orientation in Operating Systems, order #2265. - -* .. _MORDEC84: - - Ben-Ari Mordechai. 1984. "Algorithms for On-the-fly Garbage Collection". *TOPLAS* 6(3): 333--344 (1984). - -* .. _MOREAU98: - - Luc Moreau. 1998. "`Hierarchical Distributed Reference Counting `_". ACM. ISMM'98 pp. 57--67. - -* .. _MFH95: - - Greg Morrisett, Matthias Felleisen, Robert Harper. 1995. "`Abstract Models of Memory Management `_". Carnegie Mellon University. CMU-CS-FOX-95-01. - - .. abstract: mfh95.html - -* .. _MBMM99: - - David S. Munro, Alfred Brown, Ron Morrison, J. Eliot B. Moss. 1999. "`Incremental Garbage Collection of a Persistent Object Store using PMOS `_". Morgan Kaufmann. in Advances in Persistent Object Systems, pp. 78--91. - - .. abstract: mbmm99.html - -* .. _NOPH92: - - Scott Nettles, James O'Toole, David Pierce, Nickolas Haines. 1992. "`Replication-Based Incremental Copying Collection `_". IWMM'92. - - .. abstract: noph92.html - -* .. _NETTLES92: - - Scott Nettles. 1992. "`A Larch Specification of Copying Garbage Collection `_". Carnegie Mellon University. CMU-CS-92-219. - - .. abstract: nettles92.html - -* .. _NO93A: - - Scott Nettles & James O'Toole. 1993. "Implementing Orthogonal Persistence: A Simple Optimization Using Replicating Collection". USENIX. IWOOOS'93. - - .. abstract: no93a.html - -* .. _NO93: - - Scott Nettles & James O'Toole. 1993. "`Real-Time Replication Garbage Collection `_". ACM. PLDI'93. - - .. abstract: no93.html - -* .. _NIELSEN77: - - Norman R. Nielsen. 1977. "Dynamic Memory Allocation in Computer Simulation". ACM. CACM 20:11. - - .. abstract: nielsen77.html - -* .. _OTOOLE90: - - James O'Toole. 1990. "Garbage Collecting Locally". - - .. abstract: otoole90.html - -* .. _ON94: - - James O'Toole & Scott Nettles. 1994. "`Concurrent Replicating Garbage Collection `_". ACM. LFP'94. - - .. abstract: on94.html - -* .. _JRR99: - - Simon Peyton Jones, Norman Ramsey, Fermin Reig. 1999. "`C--: a portable assembly language that supports garbage collection `_". Springer-Verlag. International Conference on Principles and Practice of Declarative Programming 1999, LNCS 1702, pp. 1--28. - - .. abstract: jrr99.html - -* .. _PIEPER93: - - John S. Pieper. 1993. "Compiler Techniques for Managing Data Motion". Carnegie Mellon University. Technical report number CMU-CS-93-217. - - .. abstract: pieper93.html - -* .. _PIRINEN98: - - Pekka P. Pirinen. 1998. "Barrier techniques for incremental tracing". ACM. ISMM'98 pp. 20--25. - - .. abstract: pirinen98.html - -* .. _PRINTEZIS96: - - Tony Printezis. 1996. "Disk Garbage Collection Strategies for Persistent Java". Proceedings of the First International Workshop on Persistence and Java. - - .. abstract: printezis96.html - -* .. _PC96: - - Tony Printezis & Quentin Cutts. 1996. "Measuring the Allocation Rate of Napier88". Department of Computing Science at University of Glasgow. TR ?. - -* .. _REINHOLD93: - - M. B. Reinhold. 1993. "`Cache Performance of Garbage Collected Programming Languages `_". Laboratory for Computer Science at MIT. MIT/LCS/TR-581. - - .. abstract: reinhold93.html - -* .. _ROBSON77: - - J. M. Robson. 1977. "Worst case fragmentation of first fit and best fit storage allocation strategies". ACM. ACM Computer Journal, 20(3):242--244. - -* .. _RR97: - - Gustavo Rodriguez-Rivera & Vince Russo. 1997. "Non-intrusive Cloning Garbage Collection with Stock Operating System Support". Software -- Practice and Experience. 27:8. - - .. abstract: rr97.html - -* .. _ROJEMO95: - - Niklas Röjemo. 1995. "Highlights from nhc -- a space-efficient Haskell compiler". Chalmers University of Technology. - - .. abstract: rojemo95.html - -* .. _ROJEMO95A: - - Niklas Röjemo. 1995. "Generational garbage collection for lazy functional languages without temporary space leaks". Chalmers University of Technology. - -* .. _RR96: - - Niklas Röjemo & Colin Runciman. 1996. "Lag, drag, void and use -- heap profiling and space-efficient compilation revisited". ACM, SIGPLAN. ICFP'96, ACM SIGPLAN Notices 31:6, ISBN 0-89791-770-7, pp. 34--41. - - .. abstract: rr96.html - -* .. _RW99: - - David J. Roth, David S. Wise. 1999. "`One-bit counts between unique and sticky `_". ACM. ISMM'98, pp. 49--56. - - .. abstract: rw99.html - -* .. _ROVNER85: - - Paul Rovner. 1985. "`On Adding Garbage Collection and Runtime Types to a Strongly-Typed, Statically-Checked, Concurrent Language `_". Xerox PARC. TR CSL-84-7. - -* .. _RUNCIMAN92: - - Colin Runciman & David Wakeling. 1992. "`Heap Profiling of Lazy Functional Programs `_". University of York. - - .. abstract: runciman92.html - -* .. _RR94: - - Colin Runciman & Niklas Röjemo. 1994. "`New dimensions in heap profiling `_". University of York. - - .. abstract: rr94.html - -* .. _RR96A: - - Colin Runciman & Niklas Röjemo. 1996. "Two-pass heap profiling: a matter of life and death". Department of Computer Science, University of York. - -* .. _SG95: - - Jacob Seligmann & Steffen Grarup. 1995. "`Incremental Mature Garbage Collection Using the Train Algorithm `_". Springer-Verlag. ECOOP'95, Lecture Notes in Computer Science, Vol. 952, pp. 235--252, ISBN 3-540-60160-0. - - .. abstract: sg95.html - -* .. _SB00: - - Manuel Serrano, Hans-J. Boehm. 2000. "`Understanding memory allocation of Scheme programs `_". ACM. Proceedings of International Conference on Functional Programming 2000. - -* .. _SHAPIRO94: - - Marc Shapiro & Paulo Ferreira. 1994. "`Larchant-RDOSS: a distributed shared persistent memory and its garbage collector `_". INRIA. INRIA Rapport de Recherche no. 2399; Cornell Computer Science TR94-1466. - - .. abstract: shapiro94.html - -* .. _SHAW87: - - Robert A. Shaw. 1987. "Improving Garbage Collector Performance in Virtual Memory". Stanford University. CSL-TR-87-323. - -* .. _SHAW88: - - Robert A. Shaw. 1988. "Empirical Analysis of a LISP System". Stanford University. CSL-TR-88-351. - -* .. _SINGHAL92: - - Vivek Singhal, Sheetal V. Kakkad, Paul R. Wilson. 1992. "`Texas: An Efficient, Portable Persistent Store `_". University of Texas at Austin. - - .. abstract: singhal92.html - -* .. _SOBALVARRO88: - - P. G. Sobalvarro. 1988. "`A Lifetime-based Garbage Collector for LISP Systems on General-Purpose Computers `_". MIT. AITR-1417. - - .. abstract: sobalvarro88.html - -* .. _STEELE75: - - Guy L. Steele. 1975. "`Multiprocessing Compactifying Garbage Collection `_". CACM. 18:9 pp. 495--508. - -* .. _STEELE76: - - Guy L. Steele. 1976. "Corrigendum: Multiprocessing Compactifying Garbage Collection". CACM. 19:6 p.354. - -* .. _STEELE77: - - Guy L. Steele. 1977. "Data Representation in PDP-10 MACLISP". MIT. AI Memo 421. - -* .. _SLC99: - - James M. Stichnoth, Guei-Yuan Lueh, Michal Cierniak. 1999. "`Support for Garbage Collection at Every Instruction in a Java Compiler `_". SIGPLAN. Proceedings of the 1999 ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI). SIGPLAN Notices 34(5). pp. 118--127. - -* .. _SCN84: - - Will R Stoye, T J W Clarke, Arthur C Norman. 1984. "Some Practical Methods for Rapid Combinator Reduction". In LFP 1984, 159--166. - -* .. _TD95: - - David Tarditi & Amer Diwan. 1995. "`Measuring the Cost of Storage Management `_". Carnegie Mellon University. CMU-CS-94-201. - - .. abstract: td95.html - -* .. _TJ94: - - Stephen Thomas, Richard E. Jones. 1994. "Garbage Collection for Shared Environment Closure Reducers". Computing Laboratory, The University of Kent at Canterbury. Technical Report 31-94. - - .. abstract: tj94.html - -* .. _THOMAS95: - - Stephen Thomas. 1995. "Garbage Collection in Shared-Environment Closure Reducers: Space-Efficient Depth First Copying using a Tailored Approach". *Information Processing Letters.* 56:1, pp. 1--7. - -* .. _TT97: - - Mads Tofte & Jean-Pierre Talpin. 1997. "`Region-Based Memory Management `_". Information and Computation 132(2), pp. 109--176. - - .. abstract: tt97.html - -* .. _UNGAR84: - - Dave Ungar. 1984. "`Generation Scavenging: A Non-disruptive High Performance Storage Reclamation Algorithm `_". ACM, SIGSOFT, SIGPLAN. Practical Programming Environments Conference. - -* .. _UNGAR88: - - Dave Ungar & Frank Jackson. 1988. "`Tenuring Policies for Generation-Based Storage Reclamation `_". SIGPLAN. OOPSLA '88 Conference Proceedings, ACM SIGPLAN Notices, Vol. 23, No. 11, pp. 1--17. - - .. abstract: ungar88.html - -* .. _VO96: - - Kiem-Phong Vo. 1996. "Vmalloc: A General and Efficient Memory Allocator". Software -- Practice and Experience. 26(3): 357--374 (1996). - - .. abstract: vo96.html - -* .. _WW76: - - Daniel C. Watson, David S. Wise. 1976. "Tuning Garwick's algorithm for repacking sequential storage". *BIT.* 16, 4 (December 1976): 442--450. - -* .. _WLM92: - - Paul R. Wilson, Michael S. Lam, Thomas G. Moher. 1992. "Caching Considerations for Generational Garbage Collection". ACM. L&FP 92. - - .. abstract: wlm92.html - -* .. _WIL92A: - - Paul R. Wilson, Sheetal V. Kakkad. 1992. "`Pointer Swizzling at Page Fault Time `_". University of Texas at Austin. - - .. abstract: wil92a.html - -* .. _WIL94: - - Paul R. Wilson. 1994. "`Uniprocessor Garbage Collection Techniques `_". University of Texas. - - .. abstract: wil94.html - -* .. _WIL95: - - Paul R. Wilson, Mark S. Johnstone, Michael Neely, David Boles. 1995. "`Dynamic Storage Allocation: A Survey and Critical Review `_". University of Texas at Austin. - - .. abstract: wil95.html - -* .. _WISE78: - - David S. Wise. 1978. "`The double buddy system `_". Department of Computer Science at Indiana University. Technical Report 79. - -* .. _WISE79: - - David S. Wise. 1979. "`Morris's garbage compaction algorithm restores reference counts `_". TOPLAS. 1, 1 (July l979): 115--120. - -* .. _WISE85: - - David S. Wise. 1985. "`Design for a multiprocessing heap with on-board reference counting `_". Springer-Verlag. In J.-P. Jouannaud (ed.), Functional Programming Languages and Computer Architecture, Lecture Notes in Computer Science 201: 289--304. - -* .. _WISE92: - - .. _WISE93: - - David S. Wise. 1993. "`Stop-and-copy and one-bit reference counting `_". *Information Processing Letters.* 46, 5 (July 1993): 243--249. - - .. abstract: wise92.html - -* .. _WW95: - - David S. Wise, Joshua Walgenbach. 1996. "`Static and Dynamic Partitioning of Pointers as Links and Threads `_". SIGPLAN. Proc. 1996 ACM SIGPLAN Intl. Conf. on Functional Programming, SIGPLAN Not. 31, 6 (June 1996), pp. 42--49. - -* .. _WHHHO94: - - David S. Wise, Brian Heck, Caleb Hess, Willie Hunt, Eric Ost. 1997. "`Uniprocessor Performance of a Reference-Counting Hardware Heap `_". *LISP and Symbolic Computation.* 10, 2 (July 1997), pp. 159--181. - -* .. _WITHINGTON91: - - P. Tucker Withington. 1991. "`How Real is 'Real-Time' Garbage Collection? `_". ACM. OOPSLA/ECOOP '91 Workshop on Garbage Collection in Object-Oriented Systems. - - .. abstract: withington91.html - -* .. _YIP91: - - G. May Yip. 1991. "`Incremental, Generational Mostly-Copying Garbage Collection in Uncooperative Environments `_". Digital Equipment Corporation. - - .. abstract: yip91.html - -* .. _YUASA90: - - Taiichi Yuasa. 1990. "Real-Time Garbage Collection on General-Purpose Machines". Journal of Software and Systems. 11:3 pp. 181--198. - -* .. _ZORN88: - - Benjamin Zorn & Paul Hilfinger. 1988. "`A Memory Allocation Profiler for C and Lisp Programs `_". USENIX. Proceedings for the Summer 1988 USENIX Conference, pp. 223--237. - - .. abstract: zorn88.html - -* .. _ZORN89: - - Benjamin Zorn. 1989. "`Comparative Performance Evaluation of Garbage Collection Algorithms `_". Computer Science Division (EECS) of University of California at Berkeley. Technical Report UCB/CSD 89/544 and PhD thesis. - - .. abstract: zorn89.html - -* .. _ZORN90B: - - Benjamin Zorn. 1990. "Comparing Mark-and-sweep and Stop-and-copy Garbage Collection". ACM. Conference on Lisp and Functional Programming, pp. 87--98. - - .. abstract: zorn90b.html - -* .. _ZORN90: - - Benjamin Zorn. 1990. "`Barrier Methods for Garbage Collection `_". University of Colorado at Boulder. Technical Report CU-CS-494-90. - - .. abstract: zorn90.html - -* .. _ZORN91: - - Benjamin Zorn. 1991. "`The Effect of Garbage Collection on Cache Performance `_". University of Colorado at Boulder. Technical Report CU-CS-528-91. - - .. abstract: zorn91.html - -* .. _ZORN92B: - - Benjamin Zorn & Dirk Grunwald. 1992. "`Empirical Measurements of Six Allocation-intensive C Programs `_". ACM, SIGPLAN. SIGPLAN notices, 27(12):71--80. - - .. abstract: zorn92b.html - -* .. _ZORN92: - - Benjamin Zorn. 1993. "`The Measured Cost of Conservative Garbage Collection `_". Software -- Practice and Experience. 23(7):733--756. - - .. abstract: zorn92.html - -* .. _ZORN92A: - - Benjamin Zorn & Dirk Grunwald. 1994. "`Evaluating Models of Memory Allocation `_". ACM. Transactions on Modeling and Computer Simulation 4(1):107--131. - - .. abstract: zorn92a.html - diff --git a/manual/source/mmref/credit.rst b/manual/source/mmref/credit.rst index 816cfb0c87..0ce0bd0c8b 100644 --- a/manual/source/mmref/credit.rst +++ b/manual/source/mmref/credit.rst @@ -27,7 +27,7 @@ Reference. The Adaptive Memory Management Group no longer exists, and Harlequin has become a part of `Global Graphics `_. However, most of the group's work -has been aquired by `Ravenbrook Limited`, whose directors are Richard +has been aquired by `Ravenbrook Limited`_, whose directors are Richard Brooksby, the group's chief architect and manager, and Nick Barnes, a senior group member. diff --git a/manual/source/mmref/faq.rst b/manual/source/mmref/faq.rst index 190ecaecdb..594f1656b9 100644 --- a/manual/source/mmref/faq.rst +++ b/manual/source/mmref/faq.rst @@ -22,11 +22,12 @@ garbage collection>` for :term:`C` exist as add-on libraries. .. link:: - `Boehm–Weiser collector `_. + `Memory Pool System `_, + `Boehm–Demers–Weiser collector `_. -Why do I need to test the return value from ``malloc``? Surely it always succeeds? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Why do I need to test the return value from malloc? Surely it always succeeds? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For small programs, and during light testing, it is true that :term:`malloc` usually succeeds. Unfortunately, there are all sorts of @@ -73,8 +74,8 @@ when out of memory, wrap :term:`malloc` in something like this:: Undefined behavior is worth eliminating even in small programs. -What's the point of having a garbage collector? Why not use ``malloc`` and ``free``? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +What's the point of having a garbage collector? Why not use malloc and free? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :term:`Manual memory management`, such as :term:`malloc` and :term:`free (2)`, forces the programmer to keep track of which memory @@ -90,12 +91,12 @@ problem, rather than the tedious details of the implementation. .. seealso:: :term:`garbage collection` -What's wrong with ANSI ``malloc`` in the C library? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +What's wrong with ANSI malloc in the C library? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -:term:`Malloc` provides a very basic :term:`manual memory management` -service. However, it does not provide the following things, which may -be desirable in your memory manager: +The :term:`malloc` function provides a very basic :term:`manual memory +management` service. However, it does not provide the following +things, which may be desirable in your memory manager: * high performance for specified block sizes; * :term:`tagged references`; @@ -130,11 +131,12 @@ semi-conservative garbage collectors for C++. .. link:: - `Boehm–Weiser collector `_. + `Memory Pool System `_, + `Boehm–Demers–Weiser collector `_. -Why is ``delete`` so slow? -^^^^^^^^^^^^^^^^^^^^^^^^^^ +Why is delete so slow? +^^^^^^^^^^^^^^^^^^^^^^ Often ``delete`` must perform a more complex task than simply freeing the memory associated with an object; this is known as @@ -163,12 +165,12 @@ In :term:`C++`, it may be that class libraries expect you to call Failing this, if there is a genuine :term:`memory leak` in a class library for which you don't have the source, then the only thing you -can try is to add a :term:`garbage collector`. The Boehm–Weiser -collector will work with C++. +can try is to add a :term:`garbage collector`. .. link:: - `Boehm–Weiser collector `_. + `Memory Pool System `_, + `Boehm–Demers–Weiser collector `_. Can't I get all the benefits of garbage collection using C++ constructors and destructors? @@ -400,7 +402,7 @@ Where can I find out more about garbage collection? Many modern languages have :term:`garbage collection` built in, and the language documentation should give details. For some other languages, garbage collection can be added, for example via the -Boehm–Weiser collector. +Memory Pool System, or the Boehm–Demers–Weiser collector. .. seealso:: :term:`garbage collection` @@ -408,22 +410,25 @@ Boehm–Weiser collector. .. link:: - `Boehm–Weiser collector `_, + `Memory Pool System `_, + `Boehm–Demers–Weiser collector `_, `GC-LIST FAQ `_. Where can I get a garbage collector? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The Boehm–Weiser collector is suitable for C or C++. The best way to -get a garbage collector, however, is to program in a language that -provides garbage collection. +The Memory Pool System and the Boehm–Demers–Weiser collector are +suitable for C or C++. The best way to get a garbage collector, +however, is to program in a language that provides garbage collection +natively. .. seealso:: :term:`garbage collection` .. link:: - `Boehm–Weiser collector `_. + `Memory Pool System `_, + `Boehm–Demers–Weiser collector `_. Why does my program use so much memory? diff --git a/manual/source/mmref/index.rst b/manual/source/mmref/index.rst index eb6ef48257..d87db74339 100644 --- a/manual/source/mmref/index.rst +++ b/manual/source/mmref/index.rst @@ -1,3 +1,5 @@ +.. _mmref-intro: + Introduction to memory management ################################# @@ -9,5 +11,3 @@ Introduction to memory management alloc recycle lang - faq - diff --git a/manual/source/mmref/lang.rst b/manual/source/mmref/lang.rst index 0d081b82a3..d00ffa49c3 100644 --- a/manual/source/mmref/lang.rst +++ b/manual/source/mmref/lang.rst @@ -53,8 +53,9 @@ Memory management in various languages library functions for :term:`memory (2)` management in C, :term:`malloc` and :term:`free (2)`, have become almost synonymous with :term:`manual memory management`), although - with the Boehm-Weiser :term:`collector (1)`, it is now - possible to use :term:`garbage collection`. + with the Memory Pool System, or the Boehm–Demers–Weiser + collector, it is now possible to use :term:`garbage + collection`. The language is notorious for fostering memory management bugs, including: @@ -86,7 +87,8 @@ Memory management in various languages .. link:: - `Boehm-Weiser collector `_, + `Memory Pool System `_, + `Boehm–Demers–Weiser collector `_, `C standardization `_, `comp.lang.c Frequently Asked Questions `_. @@ -148,11 +150,11 @@ Memory management in various languages The :term:`garbage collector` in the .NET Framework is configurable to run in soft real time, or in batch mode. - The Mono runtime comes with two collectors: the Boehm–Weiser - :term:`conservative collector `, and a :term:`generational ` :term:`copying collector `. + The Mono runtime comes with two collectors: the + Boehm–Demers–Weiser :term:`conservative collector + `, and a :term:`generational + ` :term:`copying collector + `. .. link:: @@ -173,9 +175,9 @@ Memory management in various languages abstraction level of C++ makes the bookkeeping required for :term:`manual memory management` even harder. Although the standard library provides only manual memory management, with - the Boehm-Weiser :term:`collector (1)`, it is now possible to - use :term:`garbage collection`. :term:`Smart pointers` are - another popular solution. + the Memory Pool System, or the Boehm–Demers–Weiser collector, + it is now possible to use :term:`garbage collection`. + :term:`Smart pointers` are another popular solution. The language is notorious for fostering memory management bugs, including: @@ -222,6 +224,8 @@ Memory management in various languages .. link:: + `Memory Pool System `_, + `Boehm–Demers–Weiser collector `_, `comp.lang.c++ FAQ `_, `C++ standardization `_. @@ -436,6 +440,11 @@ Memory management in various languages Lisp Machines are used in today's implementations, at a cost of a few more cycles. + .. link:: + + `Lisp Machine Manual, 6th edition `_, + `The Garbage Collector `_. + Lua Lua is a dynamically typed language created by Roberto diff --git a/manual/source/pool/amc.rst b/manual/source/pool/amc.rst index 37d03f263e..a21594202c 100644 --- a/manual/source/pool/amc.rst +++ b/manual/source/pool/amc.rst @@ -24,8 +24,8 @@ except for blocks that are :term:`pinned ` by It uses :term:`generational garbage collection`. That is, it exploits assumptions about object lifetimes and inter-connection variously -referred to as "the generational hypothesis". In particular, the -following tendencies will be efficiently exploited by an AMC pool: +referred to as "the :term:`generational hypothesis`". In particular, +the following tendencies will be efficiently exploited by an AMC pool: - most objects die young; @@ -72,8 +72,10 @@ AMC properties * Blocks are :term:`scanned `. -* Blocks may only be referenced by :term:`base pointers` (unless they - have :term:`in-band headers`). +* Blocks may be referenced by :term:`interior pointers` (unless + :c:macro:`MPS_KEY_INTERIOR` is set to ``FALSE``, in which case only + :term:`base pointers`, or :term:`client pointers` if the blocks + have :term:`in-band headers`, are supported). * Blocks may be protected by :term:`barriers (1)`. @@ -99,7 +101,7 @@ AMC interface #include "mpscamc.h" -.. c:function:: mps_class_t mps_class_amc(void) +.. c:function:: mps_pool_class_t mps_class_amc(void) Return the :term:`pool class` for an AMC (Automatic Mostly-Copying) :term:`pool`. @@ -113,12 +115,24 @@ AMC interface method`, a :term:`forward method`, an :term:`is-forwarded method` and a :term:`padding method`. - It accepts one optional keyword argument: + It accepts three optional keyword arguments: * :c:macro:`MPS_KEY_CHAIN` (type :c:type:`mps_chain_t`) specifies the :term:`generation chain` for the pool. If not specified, the pool will use the arena's default chain. + * :c:macro:`MPS_KEY_INTERIOR` (type :c:type:`mps_bool_t`, default + ``TRUE``) specifies whether :term:`ambiguous ` :term:`interior pointers` to blocks in the pool keep + objects alive. If this is ``FALSE``, then only :term:`client + pointers` keep objects alive. + + * :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`, + 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:: MPS_ARGS_BEGIN(args) { @@ -126,20 +140,12 @@ AMC interface res = mps_pool_create_k(&pool, arena, mps_class_amc(), args); } MPS_ARGS_END(args); - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the format and - chain like this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_amc(), - mps_fmt_t fmt, - mps_chain_t chain) - .. index:: pair: AMC; introspection +.. _pool-amc-introspection: + AMC introspection ----------------- diff --git a/manual/source/pool/amcz.rst b/manual/source/pool/amcz.rst index 70d38ffa6a..4f65d205d1 100644 --- a/manual/source/pool/amcz.rst +++ b/manual/source/pool/amcz.rst @@ -54,7 +54,7 @@ AMCZ interface #include "mpscamc.h" -.. c:function:: mps_class_t mps_class_amcz(void) +.. c:function:: mps_pool_class_t mps_class_amcz(void) Return the :term:`pool class` for an AMCZ (Automatic Mostly-Copying Zero-rank) :term:`pool`. @@ -68,12 +68,18 @@ AMCZ interface method`, an :term:`is-forwarded method` and a :term:`padding method`. - It accepts one optional keyword argument: + It accepts two optional keyword arguments: * :c:macro:`MPS_KEY_CHAIN` (type :c:type:`mps_chain_t`) specifies the :term:`generation chain` for the pool. If not specified, the pool will use the arena's default chain. + * :c:macro:`MPS_KEY_INTERIOR` (type :c:type:`mps_bool_t`, default + ``TRUE``) specifies whether :term:`ambiguous ` :term:`interior pointers` to blocks in the pool keep + objects alive. If this is ``FALSE``, then only :term:`client + pointers` keep objects alive. + For example:: MPS_ARGS_BEGIN(args) { @@ -81,12 +87,11 @@ AMCZ interface res = mps_pool_create_k(&pool, arena, mps_class_amcz(), args); } MPS_ARGS_END(args); - .. deprecated:: starting with version 1.112. + +.. index:: + pair: AMCZ; introspection - When using :c:func:`mps_pool_create`, pass the format and - chain like this:: +AMCZ introspection +------------------ - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_amcz(), - mps_fmt_t fmt, - mps_chain_t chain) +See :ref:`pool-amc-introspection`. diff --git a/manual/source/pool/ams.rst b/manual/source/pool/ams.rst index cb7966661a..4d66fdc0fc 100644 --- a/manual/source/pool/ams.rst +++ b/manual/source/pool/ams.rst @@ -41,7 +41,8 @@ AMS properties * Supports allocation via :term:`allocation points`. If an allocation point is created in an AMS pool, the call to - :c:func:`mps_ap_create_k` takes no keyword arguments. + :c:func:`mps_ap_create_k` takes one optional keyword argument, + :c:macro:`MPS_KEY_RANK`. * Supports :term:`allocation frames` but does not use them to improve the efficiency of stack-like allocation. @@ -55,10 +56,11 @@ AMS properties never promoted out of the generation in which they are allocated. * Blocks may contain :term:`exact references` to blocks in the same or - other pools, or :term:`ambiguous references` (if the + other pools, or :term:`ambiguous references` (unless the :c:macro:`MPS_KEY_AMS_SUPPORT_AMBIGUOUS` keyword argument is set to - true when creating the pool). Blocks may not contain :term:`weak - references (1)`, and may not use :term:`remote references`. + ``FALSE`` when creating the pool). Blocks may not contain + :term:`weak references (1)`, and may not use :term:`remote + references`. * Allocations may be variable in size. @@ -97,7 +99,7 @@ AMS interface #include "mpscams.h" -.. c:function:: mps_class_t mps_class_ams(void) +.. c:function:: mps_pool_class_t mps_class_ams(void) Return the :term:`pool class` for an AMS (Automatic Mark & Sweep) :term:`pool`. @@ -126,30 +128,18 @@ AMS interface blocks remain in this generation and are not promoted. * :c:macro:`MPS_KEY_AMS_SUPPORT_AMBIGUOUS` (type - :c:type:`mps_bool_t`, default false) specifies whether references - may be ambiguous. + :c:type:`mps_bool_t`, default ``TRUE``) specifies whether + references to blocks in the pool may be ambiguous. For example:: MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); - MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, 1); res = mps_pool_create_k(&pool, arena, mps_class_ams(), args); } MPS_ARGS_END(args); - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the format, - chain, and ambiguous flag like this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_ams(), - mps_fmt_t fmt, - mps_chain_t chain, - mps_bool_t support_ambiguous) - When creating an :term:`allocation point` on an AMS pool, - :c:func:`mps_ap_create_k` accepts one keyword argument: + :c:func:`mps_ap_create_k` accepts one optional keyword argument: * :c:macro:`MPS_KEY_RANK` (type :c:type:`mps_rank_t`, default :c:func:`mps_rank_exact`) specifies the :term:`rank` of references @@ -166,33 +156,16 @@ AMS interface res = mps_ap_create_k(&ap, ams_pool, args); } MPS_ARGS_END(args); - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_ap_create`, pass the rank like this:: - - mps_res_t mps_ap_create(mps_ap_t *ap_o, mps_pool_t pool, - mps_rank_t rank) - -.. c:function:: mps_class_t mps_class_ams_debug(void) +.. c:function:: mps_pool_class_t mps_class_ams_debug(void) A :ref:`debugging ` version of the AMS pool class. When creating a debugging AMS pool, :c:func:`mps_pool_create_k` - takes three keyword arguments: :c:macro:`MPS_KEY_FORMAT` and - :c:macro:`MPS_KEY_CHAIN` are as described above, and - :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` specifies the debugging - options. See :c:type:`mps_debug_option_s`. - - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the format, - chain, and debugging options like this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_ams_debug(), - mps_debug_option_s debug_option, - mps_fmt_t fmt, - mps_chain_t chain, - mps_bool_t support_ambiguous) + accepts the following keyword arguments: + :c:macro:`MPS_KEY_FORMAT`, :c:macro:`MPS_KEY_CHAIN`, + :c:macro:`MPS_KEY_GEN`, and + :c:macro:`MPS_KEY_AMS_SUPPORT_AMBIGUOUS` are as described above, + and :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` specifies the debugging + options. See :c:type:`mps_pool_debug_option_s`. diff --git a/manual/source/pool/awl.rst b/manual/source/pool/awl.rst index 4128339227..63eca08635 100644 --- a/manual/source/pool/awl.rst +++ b/manual/source/pool/awl.rst @@ -59,7 +59,7 @@ AWL properties * Supports allocation via :term:`allocation points`. If an allocation point is created in an AWL pool, the call to - :c:func:`mps_ap_create_k` accepts one keyword argument, + :c:func:`mps_ap_create_k` accepts one optional keyword argument, :c:macro:`MPS_KEY_RANK`. * Supports :term:`allocation frames` but does not use them to improve @@ -118,8 +118,9 @@ deletion of keys in a :term:`weak-value hash table`), an AWL pool allows each object to have a :dfn:`dependent object`. (This is where the "Linked" in the name of the pool class comes from.) -The dependent object is specified by the ``find_dependent`` argument -to :c:func:`mps_pool_create` when creating an AWL pool. This is a +The dependent object is specified by the +:c:macro:`MPS_KEY_AWL_FIND_DEPENDENT` keyword argument to +:c:func:`mps_pool_create_k` when creating an AWL pool. This is a function of type :c:type:`mps_awl_find_dependent_t` that takes the address of an object in the pool and returns the address of its dependent object (or a null pointer if there is no corresponding @@ -282,11 +283,10 @@ the format of objects allocated in it: that it does not look like an aligned pointer. "Aligned pointer" means a word whose numeric value (that is, its - value when treated as an unsigned integer) is a multiple of the - architecture's :term:`natural alignment` (see - :c:macro:`MPS_PF_ALIGN`). If you're using a 32-bit architecture, - that means that an aligned pointer is a multiple of 4 and its bottom - two bits are both zero. + value when treated as an unsigned integer) is a multiple of the size + of a pointer. If you're using a 64-bit architecture, that means that + an aligned pointer is a multiple of 8 and its bottom three bits are + zero. The bottom line is that references from an object in an AWL pool must be untagged and aligned, and integers must be tagged with a @@ -308,25 +308,26 @@ AWL interface #include "mpscawl.h" -.. c:function:: mps_class_t mps_class_awl(void) +.. c:function:: mps_pool_class_t mps_class_awl(void) Return the :term:`pool class` for an AWL (Automatic Weak Linked) :term:`pool`. When creating an AWL pool, :c:func:`mps_pool_create_k` requires - two :term:`keyword arguments`: + one :term:`keyword argument`: * :c:macro:`MPS_KEY_FORMAT` (type :c:type:`mps_fmt_t`) specifies the :term:`object format` for the objects allocated in the pool. The format must provide a :term:`scan method` and a :term:`skip method`. + It accepts three optional keyword arguments: + * :c:macro:`MPS_KEY_AWL_FIND_DEPENDENT` (type :c:type:`mps_awl_find_dependent_t`) is a function that specifies how to find the :term:`dependent object` for an object in the - pool. - - It accepts two optional keyword arguments: + pool. This defaults to a function that always returns ``NULL`` + (meaning that there is no dependent object). * :c:macro:`MPS_KEY_CHAIN` (type :c:type:`mps_chain_t`) specifies the :term:`generation chain` for the pool. If not specified, the @@ -349,18 +350,8 @@ AWL interface res = mps_pool_create_k(&pool, arena, mps_class_awl(), args); } MPS_ARGS_END(args); - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the format and - find-dependent function like this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_awl(), - mps_fmt_t fmt, - mps_awl_find_dependent_t find_dependent) - When creating an :term:`allocation point` on an AWL pool, - :c:func:`mps_ap_create_k` accepts one keyword argument: + :c:func:`mps_ap_create_k` accepts one optional keyword argument: * :c:macro:`MPS_KEY_RANK` (type :c:type:`mps_rank_t`, default :c:func:`mps_rank_exact`) specifies the :term:`rank` of @@ -377,13 +368,6 @@ AWL interface res = mps_ap_create_k(&ap, awl_pool, args); } MPS_ARGS_END(args); - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_ap_create`, pass the rank like this:: - - mps_res_t mps_ap_create(mps_ap_t *ap_o, mps_pool_t pool, - mps_rank_t rank) - .. c:type:: mps_addr_t (*mps_awl_find_dependent_t)(mps_addr_t addr) diff --git a/manual/source/pool/intro.rst b/manual/source/pool/intro.rst index ea2a03ce56..17b97cb06a 100644 --- a/manual/source/pool/intro.rst +++ b/manual/source/pool/intro.rst @@ -100,35 +100,35 @@ it makes no sense to ask whether they may contain :term:`weak references (1)`. -============================================= ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== -Property AMC AMCZ AMS AWL LO MFS MV MVFF MVT SNC -============================================= ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== -Supports :c:func:`mps_alloc`? no no no no no yes yes yes no no -Supports :c:func:`mps_free`? no no no no no yes yes yes yes no -Supports allocation points? yes yes yes yes yes no yes yes yes yes -Supports allocation frames? yes yes yes yes yes no no yes yes yes -Supports segregated allocation caches? no no no no no yes yes yes no no -Timing of collections? [2]_ auto auto auto auto auto --- --- --- --- --- -May contain references? [3]_ yes no yes yes no no no no no yes -May contain exact references? [4]_ yes --- yes yes --- --- --- --- --- yes -May contain ambiguous references? [4]_ no --- no no --- --- --- --- --- no -May contain weak references? [4]_ no --- no yes --- --- --- --- --- no -Allocations fixed or variable in size? var var var var var fixed var var var var -Alignment? [5]_ conf conf conf conf conf [6]_ [6]_ [7]_ [7]_ conf -Dependent objects? [8]_ no --- no yes --- --- --- --- --- no -May use remote references? [9]_ no --- no no --- --- --- --- --- no -Blocks are automatically managed? [10]_ yes yes yes yes yes no no no no no -Blocks are promoted between generations yes yes no no no --- --- --- --- --- -Blocks are manually managed? [10]_ no no no no no yes yes yes yes yes -Blocks are scanned? [11]_ yes no yes yes no no no no no yes -Blocks support base pointers only? [12]_ yes yes yes yes yes --- --- --- --- yes -Blocks support internal pointers? [12]_ no no no no no --- --- --- --- no -Blocks may be protected by barriers? yes no yes yes yes no no no no yes -Blocks may move? yes yes no no no no no no no no -Blocks may be finalized? yes yes yes yes yes no no no no no -Blocks must be formatted? [11]_ yes yes yes yes yes no no no no yes -Blocks may use :term:`in-band headers`? yes yes yes yes yes --- --- --- --- no -============================================= ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== +.. csv-table:: + :header: "Property", ":ref:`AMC `", ":ref:`AMCZ `", ":ref:`AMS `", ":ref:`AWL `", ":ref:`LO `", ":ref:`MFS `", ":ref:`MV `", ":ref:`MVFF `", ":ref:`MVT `", ":ref:`SNC `" + :widths: 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + + Supports :c:func:`mps_alloc`?, no, no, no, no, no, yes, yes, yes, no, no + Supports :c:func:`mps_free`?, no, no, no, no, no, yes, yes, yes, yes, no + Supports allocation points?, yes, yes, yes, yes, yes, no, yes, yes, yes, yes + Supports allocation frames?, yes, yes, yes, yes, yes, no, no, yes, yes, yes + Supports segregated allocation caches?, no, no, no, no, no, yes, yes, yes, no, no + Timing of collections? [2]_, auto, auto, auto, auto, auto, ---, ---, ---, ---, --- + May contain references? [3]_, yes, no, yes, yes, no, no, no, no, no, yes + May contain exact references? [4]_, yes, ---, yes, yes, ---, ---, ---, ---, ---, yes + May contain ambiguous references? [4]_, no, ---, no, no, ---, ---, ---, ---, ---, no + May contain weak references? [4]_, no, ---, no, yes, ---, ---, ---, ---, ---, no + Allocations fixed or variable in size?, var, var, var, var, var, fixed, var, var, var, var + Alignment? [5]_, conf, conf, conf, conf, conf, [6]_, conf, [7]_, [7]_, conf + Dependent objects? [8]_, no, ---, no, yes, ---, ---, ---, ---, ---, no + May use remote references? [9]_, no, ---, no, no, ---, ---, ---, ---, ---, no + Blocks are automatically managed? [10]_, yes, yes, yes, yes, yes, no, no, no, no, no + Blocks are promoted between generations, yes, yes, no, no, no, ---, ---, ---, ---, --- + Blocks are manually managed? [10]_, no, no, no, no, no, yes, yes, yes, yes, yes + Blocks are scanned? [11]_, yes, no, yes, yes, no, no, no, no, no, yes + Blocks support base pointers only? [12]_, no, no, yes, yes, yes, ---, ---, ---, ---, yes + Blocks support internal pointers? [12]_, yes, yes, no, no, no, ---, ---, ---, ---, no + Blocks may be protected by barriers?, yes, no, yes, yes, yes, no, no, no, no, yes + Blocks may move?, yes, yes, no, no, no, no, no, no, no, no + Blocks may be finalized?, yes, yes, yes, yes, yes, no, no, no, no, no + Blocks must be formatted? [11]_, yes, yes, yes, yes, yes, no, no, no, no, yes + Blocks may use :term:`in-band headers`?, yes, yes, yes, yes, yes, ---, ---, ---, ---, no .. note:: @@ -151,13 +151,13 @@ Blocks may use :term:`in-band headers`? yes yes yes yes yes .. [5] "Alignment" is "conf" if the client program may specify :term:`alignment` for each pool. - .. [6] The alignment of blocks allocated from :ref:`pool-mv` pools - is platform-dependent. + .. [6] The alignment of blocks allocated from :ref:`pool-mfs` + pools is the platform's :term:`natural alignment`, + :c:macro:`MPS_PF_ALIGN`. .. [7] :ref:`pool-mvt` and :ref:`pool-mvff` pools have - configurable alignment, but it may not be smaller than the - :term:`natural alignment` for the :term:`platform` (see - :c:macro:`MPS_PF_ALIGN`). + configurable alignment, but it may not be smaller than + ``sizeof(void *)``. .. [8] In pools with this property, each object may specify an :term:`dependent object` which the client program @@ -193,6 +193,10 @@ Blocks may use :term:`in-band headers`? yes yes yes yes yes just past the end of the header) is considered to be a reference to the block. + Pools that support internal pointers can be switched to + base pointers only, by setting the optional keyword + argument :c:macro:`MPS_KEY_INTERIOR` to ``FALSE`` when + calling :c:func:`mps_pool_create_k`. .. index:: single: pool class; writing diff --git a/manual/source/pool/lo.rst b/manual/source/pool/lo.rst index e1723a42e0..197e8b5713 100644 --- a/manual/source/pool/lo.rst +++ b/manual/source/pool/lo.rst @@ -103,7 +103,7 @@ LO interface #include "mpsclo.h" -.. c:function:: mps_class_t mps_class_lo(void) +.. c:function:: mps_pool_class_t mps_class_lo(void) Return the :term:`pool class` for an LO (Leaf Object) :term:`pool`. @@ -136,12 +136,3 @@ LO interface MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); res = mps_pool_create_k(&pool, arena, mps_class_lo(), args); } MPS_ARGS_END(args); - - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the format like - this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_lo(), - mps_fmt_t fmt) diff --git a/manual/source/pool/mfs.rst b/manual/source/pool/mfs.rst index cfaad56e6a..c1e13a5479 100644 --- a/manual/source/pool/mfs.rst +++ b/manual/source/pool/mfs.rst @@ -75,7 +75,7 @@ MFS interface #include "mpscmfs.h" -.. c:function:: mps_class_t mps_class_mfs(void) +.. c:function:: mps_pool_class_t mps_class_mfs(void) Return the :term:`pool class` for an MFS (Manual Fixed Small) :term:`pool`. @@ -87,14 +87,15 @@ MFS interface :term:`size` of blocks that will be allocated from this pool, in :term:`bytes (1)`. It must be at least one :term:`word`. - In addition, :c:func:`mps_pool_create_k` may take: + In addition, :c:func:`mps_pool_create_k` accepts one optional + keyword argument: - * :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`, default 65536) is the - :term:`size` of segment that the pool will request from the - :term:`arena`. It must be at least as big as the unit size - specified by the :c:macro:`MPS_KEY_MFS_UNIT_SIZE` keyword - argument. If this is not a multiple of the unit size, there will - be wasted space in each segment. + * :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`, + default 65536) is the :term:`size` of block that the pool will + request from the :term:`arena`. It must be at least as big as + the unit size specified by the :c:macro:`MPS_KEY_MFS_UNIT_SIZE` + keyword argument. If this is not a multiple of the unit size, + there will be wasted space in each block. For example:: @@ -103,13 +104,3 @@ MFS interface MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, 1024 * 1024); res = mps_pool_create_k(&pool, arena, mps_class_mfs(), args); } MPS_ARGS_END(args); - - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the segment size and - unit size like this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_mfs(), - size_t extend_size, - size_t unit_size) diff --git a/manual/source/pool/mv.rst b/manual/source/pool/mv.rst index 433525f736..27e691ed3a 100644 --- a/manual/source/pool/mv.rst +++ b/manual/source/pool/mv.rst @@ -7,10 +7,6 @@ MV (Manual Variable) ==================== -.. deprecated:: starting with version 1.111. - - :ref:`pool-mvff` or :ref:`pool-mvt` should be used instead. - **MV** is a general-purpose :term:`manually managed ` :term:`pool class` that manages :term:`blocks` of variable size. @@ -38,9 +34,7 @@ MV properties * Allocations may be variable in size. -* The :term:`alignment` of blocks is not configurable: it is the - :term:`natural alignment` of the platform (see - :c:macro:`MPS_PF_ALIGN`). +* The :term:`alignment` of blocks is configurable. * Blocks do not have :term:`dependent objects`. @@ -67,25 +61,33 @@ MV interface #include "mpscmv.h" -.. c:function:: mps_class_t mps_class_mv(void) +.. c:function:: mps_pool_class_t mps_class_mv(void) Return the :term:`pool class` for an MV (Manual Variable) :term:`pool`. - When creating an MV pool, :c:func:`mps_pool_create_k` may take - three :term:`keyword arguments`: + When creating an MV pool, :c:func:`mps_pool_create_k` takes four + optional :term:`keyword arguments`: - * :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`, default 65536) is the - :term:`size` of segment that the pool will request from the - :term:`arena`. + * :c:macro:`MPS_KEY_ALIGN` (type :c:type:`mps_align_t`, default is + :c:macro:`MPS_PF_ALIGN`) is the + :term:`alignment` of addresses for allocation (and freeing) in + the pool. If an unaligned size is passed to :c:func:`mps_alloc` or + :c:func:`mps_free`, it will be rounded up to the pool's alignment. - * :c:macro:`MPS_KEY_MEAN_SIZE` (type :c:type:`size_t`, default 32) is the - predicted mean size of blocks that will be allocated from the - pool. + * :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`, + default 65536) is the :term:`size` of block that the pool will + request from the :term:`arena`. - * :c:macro:`MPS_KEY_MAX_SIZE` (type :c:type:`size_t`, default 65536) is the - predicted maximum size of blocks that will be allocated from the - pool. + * :c:macro:`MPS_KEY_MEAN_SIZE` (type :c:type:`size_t`, default 32) + is the predicted mean size of blocks that will be allocated from + the pool. This value must be smaller than, or equal to, the + value for :c:macro:`MPS_KEY_EXTEND_BY`. + + * :c:macro:`MPS_KEY_MAX_SIZE` (type :c:type:`size_t`, + default 65536) is the predicted maximum size of blocks that will + be allocated from the pool. This value must be larger than, or + equal to, the value for :c:macro:`MPS_KEY_EXTEND_BY`. The mean and maximum sizes are *hints* to the MPS: the pool will be less efficient if these are wrong, but nothing will break. @@ -99,66 +101,15 @@ MV interface res = mps_pool_create_k(&pool, arena, mps_class_mfs(), args); } MPS_ARGS_END(args); - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the segment size, - mean size, and maximum size like this:: - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_mv(), - size_t extend_size, - size_t average_size, - mps_size_t maximum_size) - - -.. c:function:: mps_class_t mps_class_mv_debug(void) +.. c:function:: mps_pool_class_t mps_class_mv_debug(void) A :ref:`debugging ` version of the MV pool class. When creating a debugging MV pool, :c:func:`mps_pool_create_k` - takes four keyword arguments: :c:macro:`MPS_KEY_EXTEND_SIZE`, - :c:macro:`MPS_KEY_MEAN_SIZE`, :c:macro:`MPS_KEY_MAX_SIZE` are as - described above, and :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` - specifies the debugging options. See :c:type:`mps_debug_option_s`. - - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the debugging - options, segment size, mean size, and maximum size like this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_mv_debug(), - mps_debug_option_s debug_option, - mps_size_t extend_size, - mps_size_t average_size, - mps_size_t maximum_size) - - -.. index:: - pair: MV; introspection - -MV introspection ----------------- - -:: - - #include "mpscmv.h" - -.. c:function:: size_t mps_mv_free_size(mps_pool_t pool) - - Return the total amount of free space in an MV pool. - - ``pool`` is the MV pool. - - Returns the total free space in the pool, in :term:`bytes (1)`. - - -.. c:function:: size_t mps_mv_size(mps_pool_t pool) - - Return the total size of an MV pool. - - ``pool`` is the MV pool. - - Returns the total size of the pool, in :term:`bytes (1)`. This - is the sum of allocated space and free space. + takes five optional keyword arguments: :c:macro:`MPS_KEY_ALIGN`, + :c:macro:`MPS_KEY_EXTEND_SIZE`, :c:macro:`MPS_KEY_MEAN_SIZE`, + :c:macro:`MPS_KEY_MAX_SIZE` are as described above, and + :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` specifies the debugging + options. See :c:type:`mps_pool_debug_option_s`. diff --git a/manual/source/pool/mvff.rst b/manual/source/pool/mvff.rst index e688a00d09..04a7d48603 100644 --- a/manual/source/pool/mvff.rst +++ b/manual/source/pool/mvff.rst @@ -42,16 +42,6 @@ on the same pool, because the worst-fit policy of buffer filling will grab all the large blocks, leading to severe fragmentation. If you need both forms of allocation, use two separate pools. -Note that buffered allocation can't allocate across segment boundaries -(see :ref:`topic-allocation-point-implementation` for the technical -reason). This can cause added external fragmentation if objects are -allocated that are a significant fraction of the segment size. - -.. note:: - - If you need to allocate large objects in an MVFF pool, - :ref:`contact us `. - .. index:: single: MVFF; properties @@ -80,7 +70,7 @@ MVFF properties * Allocations may be variable in size. * The :term:`alignment` of blocks is configurable, but may not be - smaller than the :term:`natural alignment` of the platform. + smaller than ``sizeof(void *)``. * Blocks do not have :term:`dependent objects`. @@ -107,16 +97,16 @@ MVFF interface #include "mpscmvff.h" -.. c:function:: mps_class_t mps_class_mvff(void) +.. c:function:: mps_pool_class_t mps_class_mvff(void) Return the :term:`pool class` for an MVFF (Manual Variable First Fit) :term:`pool`. - When creating an MVFF pool, :c:func:`mps_pool_create_k` may take - the following :term:`keyword arguments`: + When creating an MVFF pool, :c:func:`mps_pool_create_k` accepts + seven optional :term:`keyword arguments`: * :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`, default - 65536) is the :term:`size` of segment that the pool will request + 65536) is the :term:`size` of block that the pool will request from the :term:`arena`. * :c:macro:`MPS_KEY_MEAN_SIZE` (type :c:type:`size_t`, default 32) @@ -127,24 +117,30 @@ MVFF interface * :c:macro:`MPS_KEY_ALIGN` (type :c:type:`mps_align_t`, default is :c:macro:`MPS_PF_ALIGN`) is the :term:`alignment` of addresses for allocation (and freeing) in - the pool. If an unaligned size is passed to :c:func:`mps_alloc` or - :c:func:`mps_free`, it will be rounded up to the pool's alignment. - The minimum alignment supported by pools of this class is - ``sizeof(void *)``. + the pool. If an unaligned size is passed to :c:func:`mps_alloc` + or :c:func:`mps_free`, it will be rounded up to the pool's + alignment. The minimum alignment supported by pools of this + class is ``sizeof(void *)``. + + * :c:macro:`MPS_KEY_SPARE` (type :c:type:`double`, default 0.75) + is the maximum proportion of memory that the pool will keep + spare for future allocations. If the proportion of memory that's + free exceeds this, then the pool will return some of it to the + arena for use by other pools. * :c:macro:`MPS_KEY_MVFF_ARENA_HIGH` (type :c:type:`mps_bool_t`, - default false) determines whether new segments are acquired at high + default false) determines whether new blocks are acquired at high addresses (if true), or at low addresses (if false). - * :c:macro:`MPS_KEY_MVFF_SLOT_HIGH` [#not-ap]_ (type :c:type:`mps_bool_t`, - default false) determines whether to search for the highest - addressed free area (if true) or lowest (if false) when allocating - using :c:func:`mps_alloc`. + * :c:macro:`MPS_KEY_MVFF_SLOT_HIGH` [#not-ap]_ (type + :c:type:`mps_bool_t`, default false) determines whether to + search for the highest addressed free area (if true) or lowest + (if false) when allocating using :c:func:`mps_alloc`. - * :c:macro:`MPS_KEY_MVFF_FIRST_FIT` [#not-ap]_ (type :c:type:`mps_bool_t`, default - true) determines whether to allocate from the highest address in a - found free area (if true) or lowest (if false) when allocating - using :c:func:`mps_alloc`. + * :c:macro:`MPS_KEY_MVFF_FIRST_FIT` [#not-ap]_ (type + :c:type:`mps_bool_t`, default true) determines whether to + allocate from the highest address in a found free area (if true) + or lowest (if false) when allocating using :c:func:`mps_alloc`. .. [#not-ap] @@ -154,12 +150,12 @@ MVFF interface They use a worst-fit policy in order to maximise the number of in-line allocations. - The defaults yield a a simple first-fit allocator. Specify + The defaults yield a a simple first-fit allocator. Specify :c:macro:`MPS_KEY_MVFF_ARENA_HIGH` and :c:macro:`MPS_KEY_MVFF_SLOT_HIGH` true, and :c:macro:`MPS_KEY_MVFF_FIRST_FIT` false to get a first-fit - allocator that works from the top of memory downwards. - Other combinations may be useful in special circumstances. + allocator that works from the top of memory downwards. Other + combinations may be useful in special circumstances. For example:: @@ -173,76 +169,18 @@ MVFF interface res = mps_pool_create_k(&pool, arena, mps_class_mvff(), args); } MPS_ARGS_END(args); - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the arguments like - this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_mvff(), - size_t extend_size, - size_t average_size, - mps_align_t alignment, - mps_bool_t slot_high, - mps_bool_t arena_high, - mps_bool_t first_fit) - -.. c:function:: mps_class_t mps_class_mvff_debug(void) +.. c:function:: mps_pool_class_t mps_class_mvff_debug(void) A :ref:`debugging ` version of the MVFF pool class. When creating a debugging MVFF pool, :c:func:`mps_pool_create_k` - takes seven :term:`keyword arguments`. - - * :c:macro:`MPS_KEY_EXTEND_BY`, :c:macro:`MPS_KEY_MEAN_SIZE`, - :c:macro:`MPS_KEY_ALIGN`, :c:macro:`MPS_KEY_MVFF_ARENA_HIGH`, - :c:macro:`MPS_KEY_MVFF_SLOT_HIGH`, and - :c:macro:`MPS_KEY_MVFF_FIRST_FIT` are as described above, and - :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` specifies the debugging - options. See :c:type:`mps_debug_option_s`. - - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the debugging - options, and other arguments like this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_mvff_debug(), - mps_debug_option_s debug_option, - size_t extend_size, - size_t average_size, - mps_align_t alignment, - mps_bool_t slot_high, - mps_bool_t arena_high, - mps_bool_t first_fit) - - -.. index:: - pair: MVFF; introspection - -MVFF introspection ------------------- - -:: - - #include "mpscmvff.h" - -.. c:function:: size_t mps_mvff_free_size(mps_pool_t pool) - - Return the total amount of free space in an MVFF pool. - - ``pool`` is the MVFF pool. - - Returns the total free space in the pool, in :term:`bytes (1)`. - - -.. c:function:: size_t mps_mvff_size(mps_pool_t pool) - - Return the total size of an MVFF pool. - - ``pool`` is the MVFF pool. - - Returns the total size of the pool, in :term:`bytes (1)`. This - is the sum of allocated space and free space. + accepts eight optional :term:`keyword arguments`: + :c:macro:`MPS_KEY_EXTEND_BY`, :c:macro:`MPS_KEY_MEAN_SIZE`, + :c:macro:`MPS_KEY_ALIGN`, :c:macro:`MPS_KEY_SPARE`, + :c:macro:`MPS_KEY_MVFF_ARENA_HIGH`, + :c:macro:`MPS_KEY_MVFF_SLOT_HIGH`, and + :c:macro:`MPS_KEY_MVFF_FIRST_FIT` are as described above, and + :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` specifies the debugging + options. See :c:type:`mps_pool_debug_option_s`. diff --git a/manual/source/pool/mvt.rst b/manual/source/pool/mvt.rst index f3bbba705a..da2ce024ba 100644 --- a/manual/source/pool/mvt.rst +++ b/manual/source/pool/mvt.rst @@ -78,6 +78,9 @@ MVT properties * Allocations may be variable in size. +* The :term:`alignment` of blocks is configurable, but may not be + smaller than ``sizeof(void *)``. + * Blocks do not have :term:`dependent objects`. * Blocks are not automatically :term:`reclaimed`. @@ -103,13 +106,13 @@ MVT interface #include "mpscmvt.h" -.. c:function:: mps_class_t mps_class_mvt(void) +.. c:function:: mps_pool_class_t mps_class_mvt(void) Return the :term:`pool class` for an MVT (Manual Variable Temporal) :term:`pool`. - When creating an MVT pool, :c:func:`mps_pool_create_k` may take - six :term:`keyword arguments`: + When creating an MVT pool, :c:func:`mps_pool_create_k` accepts six + optional :term:`keyword arguments`: * :c:macro:`MPS_KEY_ALIGN` (type :c:type:`mps_align_t`, default is :c:macro:`MPS_PF_ALIGN`) is the @@ -117,7 +120,7 @@ MVT interface the pool. If an unaligned size is passed to :c:func:`mps_alloc` or :c:func:`mps_free`, it will be rounded up to the pool's alignment. The minimum alignment supported by pools of this class is - ``sizeof(void *)``. + ``sizeof(void *)``. * :c:macro:`MPS_KEY_MIN_SIZE` (type :c:type:`size_t`, default is :c:macro:`MPS_PF_ALIGN`) is the @@ -139,7 +142,7 @@ MVT interface that will break is the partial freeing of large blocks. * :c:macro:`MPS_KEY_MVT_RESERVE_DEPTH` (type - :c:type:`mps_count_t`, default 1024) is the expected hysteresis + :c:type:`mps_word_t`, default 1024) is the expected hysteresis of the population of the pool. When blocks are freed, the pool will retain sufficient storage to allocate this many blocks of the mean size for near term allocations (rather than immediately @@ -193,51 +196,3 @@ MVT interface MPS_ARGS_ADD(args, MPS_KEY_MVT_FRAG_LIMIT, 0.5); res = mps_pool_create_k(&pool, arena, mps_class_mvt(), args); } MPS_ARGS_END(args); - - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the arguments like - this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_mvt(), - size_t minimum_size, - size_t mean_size, - size_t maximum_size, - mps_count_t reserve_depth, - mps_count_t fragmentation_limit) - - .. note:: - - The fragmentation_limit is a percentage from 0 to 100 - inclusive when passed to :c:func:`mps_pool_create`, not a - double from 0.0 to 1.0 as in :c:func:`mps_pool_create_k`. - - -.. index:: - pair: MVT; introspection - -MVT introspection ------------------ - -:: - - #include "mpscmvt.h" - -.. c:function:: size_t mps_mvt_free_size(mps_pool_t pool) - - Return the total amount of free space in an MVT pool. - - ``pool`` is the MVT pool. - - Returns the total free space in the pool, in :term:`bytes (1)`. - - -.. c:function:: size_t mps_mvt_size(mps_pool_t pool) - - Return the total size of an MVT pool. - - ``pool`` is the MVT pool. - - Returns the total size of the pool, in :term:`bytes (1)`. This - is the sum of allocated space and free space. diff --git a/manual/source/pool/snc.rst b/manual/source/pool/snc.rst index 2146a41755..d39a392c61 100644 --- a/manual/source/pool/snc.rst +++ b/manual/source/pool/snc.rst @@ -39,7 +39,7 @@ SNC properties * Supports allocation via :term:`allocation points` only. If an allocation point is created in an SNC pool, the call to - :c:func:`mps_ap_create_k` requires one keyword argument, + :c:func:`mps_ap_create_k` accepts one optional keyword argument, :c:macro:`MPS_KEY_RANK`. * Does not support deallocation via :c:func:`mps_free`. @@ -83,15 +83,15 @@ SNC properties .. index:: single: SNC; interface -SNC introspection ------------------ +SNC interface +------------- :: #include "mpscsnc.h" -.. c:function:: mps_class_t mps_class_snc(void) +.. c:function:: mps_pool_class_t mps_class_snc(void) Return the :term:`pool class` for an SNC (Stack No Check) :term:`pool`. @@ -111,21 +111,12 @@ SNC introspection res = mps_pool_create_k(&pool, arena, mps_class_snc(), args); } MPS_ARGS_END(args); - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_pool_create`, pass the format like - this:: - - mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, - mps_class_t mps_class_snc(), - mps_fmt_t fmt) - When creating an :term:`allocation point` on an SNC pool, - :c:func:`mps_ap_create_k` requires one keyword argument: + :c:func:`mps_ap_create_k` accepts one optional keyword argument: - * :c:macro:`MPS_KEY_RANK` (type :c:type:`mps_rank_t`) specifies - the :term:`rank` of references in objects allocated on this - allocation point. It must be :c:func:`mps_rank_exact`. + * :c:macro:`MPS_KEY_RANK` (type :c:type:`mps_rank_t`, default + :c:func:`mps_rank_exact`) specifies the :term:`rank` of references + in objects allocated on this allocation point. For example:: @@ -133,10 +124,3 @@ SNC introspection MPS_ARGS_ADD(args, MPS_KEY_RANK, mps_rank_exact()); res = mps_ap_create_k(&ap, awl_pool, args); } MPS_ARGS_END(args); - - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_ap_create`, pass the rank like this:: - - mps_res_t mps_ap_create(mps_ap_t *ap_o, mps_pool_t pool, - mps_rank_t rank) diff --git a/manual/source/release.rst b/manual/source/release.rst index 07e5870870..a7e028fae2 100644 --- a/manual/source/release.rst +++ b/manual/source/release.rst @@ -3,6 +3,322 @@ Release notes ============= + +.. _release-notes-1.115: + +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_COMMIT_LIMIT` + sets the :term:`commit limit` for the arena, and + :c:macro:`MPS_KEY_SPARE_COMMIT_LIMIT` sets the :term:`spare + commit limit` for the arena. + + +Interface changes +................. + +#. The type of pool classes is now :c:type:`mps_pool_class_t`. The old + name :c:type:`mps_class_t` is still available via a ``typedef``, + but is deprecated. + +#. The functions :c:func:`mps_mv_free_size`, :c:func:`mps_mv_size`, + :c:func:`mps_mvff_free_size`, :c:func:`mps_mvff_size`, + :c:func:`mps_mvt_free_size` and :c:func:`mps_mvt_size` are now + deprecated in favour of the generic functions + :c:func:`mps_pool_free_size` and :c:func:`mps_pool_total_size`. + +Other changes +............. + +#. :c:func:`mps_arena_committed` now returns a meaningful value (the + amount of memory marked as in use in the page tables) for + :term:`client arenas`. See job001887_. + + .. _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/ + +#. Unfinalizable objects can no longer be registered for finalization. + Previously the objects would be registered but never finalized. See + job003865_. + + .. _job003865: https://www.ravenbrook.com/project/mps/issue/job003865/ + +#. :c:func:`mps_arena_has_addr` now returns the correct result for + objects allocated from the :ref:`pool-mfs`, :ref:`pool-mv`, and + :ref:`pool-mvff` pools. See job003866_. + + .. _job003866: https://www.ravenbrook.com/project/mps/issue/job003866/ + + +.. _release-notes-1.114: + +Release 1.114.0 +--------------- + +New features +............ + +#. :term:`Ambiguous ` :term:`interior pointers` + now keep objects in :ref:`pool-amc` and :ref:`pool-amcz` pools + alive. + + This means that if the compiler optimizes away a pointer to the + base of an object, leaving an interior pointer as the only + reference keeping the object alive, this does not cause the object + to be incorrectly collected. Or, if you are writing your own + compiler, you can now perform such an optimization safely. + + If you require the old behaviour (in which ambiguous interior + pointers were ignored) then you can set the + :c:macro:`MPS_KEY_INTERIOR` keyword argument to ``FALSE`` when + calling :c:func:`mps_pool_create_k`. + +#. The logic for deciding which generations should be collected has + changed. Now, a chain may be scheduled for collection if the new + size of *any* of its generations exceeds its capacity, and when a + chain is collected, all generations are collected up to, and + including, the highest generation whose new size exceeds its + capacity. This ensures that all generations are collected reliably + on chains where there is no allocation into the nursery generation. + See :ref:`topic-collection-schedule`. + + (Previously, only the nursery generation in each chain + was considered, and a chain was collected up to, but not including, + the lowest generation whose new size was within its capacity.) + + As a result of this change, we recommend that you retune your + generation sizes. (This is not necessary, but may improve + performance.) + +#. New pool introspection functions :c:func:`mps_pool_free_size` and + :c:func:`mps_pool_total_size`. + + +Interface changes +................. + +#. The granularity with which the arena manages memory can now be + specified using the :c:macro:`MPS_KEY_ARENA_GRAIN_SIZE` keyword + argument to :c:func:`mps_arena_create_k`. See + :c:func:`mps_arena_class_cl` and :c:func:`mps_arena_class_vm`. + +#. There is now a default value (currently 256 \ :term:`megabytes`) + for the :c:macro:`MPS_KEY_ARENA_SIZE` keyword argument to + :c:func:`mps_arena_create_k` when creating a virtual memory arena. + See :c:func:`mps_arena_class_vm`. + +#. The keyword argument :c:macro:`MPS_KEY_AMS_SUPPORT_AMBIGUOUS` now + defaults to ``TRUE`` in order to better support the general case: + the value ``FALSE`` is appropriate only when you know that all + references are exact. See :ref:`pool-ams`. + +#. There is now a default value for the + :c:macro:`MPS_KEY_AWL_FIND_DEPENDENT` keyword argument to + :c:func:`mps_pool_create_k` when creating an :ref:`pool-awl` pool. + The default value is a function that always returns ``NULL`` + (meaning that there is no dependent object). + +#. It is now possible to configure the alignment of objects allocated + in a :ref:`pool-mv` pool, by passing the :c:macro:`MPS_KEY_ALIGN` + keyword argument to :c:func:`mps_pool_create_k`. + +#. The :ref:`pool-mvff` pool class takes a new keyword argument + :c:macro:`MPS_KEY_SPARE`. This specifies the maximum proportion of + memory that the pool will keep spare for future allocations. + +#. The alignment requirements for :ref:`pool-mvff` and :ref:`pool-mvt` + pools have been relaxed on the platforms ``w3i3mv`` and ``w3i6mv``. + On all platforms it is now possible to specify alignments down to + ``sizeof(void *)`` as the alignment for pools of these classes. + +#. The sizes of the templates in a :c:type:`mps_pool_debug_option_s` + structure no longer have to be related to the alignment of the + pools that they are used with. This makes it easier to reuse these + structures. + + +Other changes +............. + +#. The :ref:`pool-ams` pool class no longer triggers the assertion + ``!AMS_IS_INVALID_COLOUR(seg, i)`` under rare circumstances + (namely, detaching an :term:`allocation point` from a :term:`grey` + segment when :c:macro:`MPS_KEY_AMS_SUPPORT_AMBIGUOUS` is + ``FALSE``). See job001549_. + + .. _job001549: https://www.ravenbrook.com/project/mps/issue/job001549/ + +#. :c:func:`mps_arena_roots_walk` no longer triggers an assertion + failure when run twice in succession. See job003496_. + + .. _job003496: https://www.ravenbrook.com/project/mps/issue/job003496/ + +#. The alignment of :ref:`pool-awl` pools is now configurable via the + object format, as documented, and is no longer always + :c:macro:`MPS_PF_ALIGN`. See job003745_. + + .. _job003745: https://www.ravenbrook.com/project/mps/issue/job003745/ + +#. The debugging version of the :ref:`pool-mvff` pool class, + :c:func:`mps_class_mvff_debug`, no longer triggers an assertion + failure if you allocate a large object. See job003751_. + + .. _job003751: https://www.ravenbrook.com/project/mps/issue/job003751/ + +#. :program:`mpseventtxt` now successfully processes a telemetry log + containing multiple labels associated with the same address. See + job003756_. + + .. _job003756: https://www.ravenbrook.com/project/mps/issue/job003756/ + +#. :ref:`pool-ams`, :ref:`pool-awl` and :ref:`pool-lo` pools get + reliably collected, even in the case where the pool is the only + pool on its generation chain and is allocating into some generation + other than the nursery. See job003771_. + + .. _job003771: https://www.ravenbrook.com/project/mps/issue/job003771/ + +#. Allocation into :ref:`pool-awl` pools again reliably provokes + garbage collections of the generation that the pool belongs to. (In + 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/ + +#. All unreachable objects in :ref:`pool-lo` pools are finalized. + (Previously, objects on a segment attached to an allocation point + were not finalized until the allocation point was full.) See + job003773_. + + .. _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 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: + +Release 1.113.0 +--------------- + +New features +............ + +#. In previous releases there was an implicit connection between + blocks allocated by :ref:`pool-awl` and :ref:`pool-lo` pools, and + blocks allocated by other automatically managed pool classes. + + In particular, blocks allocated by AWL and LO pools were garbage + collected together with blocks allocated by :ref:`pool-ams` pools, + and blocks allocated by :ref:`pool-amc` pools in generation 1 of + their chains. + + This is no longer the case: to arrange for blocks to be collected + together you need to ensure that they are allocated in the *same* + generation chain, using the :c:macro:`MPS_KEY_CHAIN` and + :c:macro:`MPS_KEY_GEN` keyword arguments to + :c:func:`mps_pool_create_k`. + + So if you have code like this:: + + res = mps_pool_create(&my_amc, arena, mps_class_amc(), my_chain); + res = mps_pool_create(&my_awl, arena, mps_class_awl()); + + and you want to retain the connection between these pools, then you + must ensure that they use the same generation chain:: + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, my_chain); + res = mps_pool_create_k(&my_amc, arena, mps_class_amc(), args); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, my_chain); + MPS_ARGS_ADD(args, MPS_KEY_GEN, 1); + res = mps_pool_create_k(&my_awl, arena, mps_class_awl(), args); + } MPS_ARGS_END(args); + + +Interface changes +................. + +#. When creating a list of keyword arguments, there is no longer any + need to call :c:func:`MPS_ARGS_DONE`. See :ref:`topic-keyword`. + +#. When creating an automatically managed pool using + :c:func:`mps_pool_create_k`, it is no longer necessary to pass in a + generation chain. The arena has a default generation chain and this + is used by all automatically managed pools where no generation + chain was specified. + +#. It is now possible to specify a generation chain for + :ref:`pool-awl` and :ref:`pool-lo` pool classes, by using the + optional :c:macro:`MPS_KEY_CHAIN` keyword argument to + :c:func:`mps_pool_create_k`. + +#. It is now possible to specify which generation the :ref:`pool-ams`, + :ref:`pool-awl`, and :ref:`pool-lo` pool classes allocate new + objects into, using the optional :c:macro:`MPS_KEY_GEN` keyword + argument to :c:func:`mps_pool_create_k`. + + +Other changes +............. + +#. The MPS now retains some unused memory instead of returning it to + the operating system. This reduces unnecessary overhead due to + system calls, thrashing the operating system's page table, and + zeroing memory when re-allocated. See job003700_. + + .. _job003700: https://www.ravenbrook.com/project/mps/issue/job003700/ + + .. _release-notes-1.112: Release 1.112.0 @@ -36,22 +352,28 @@ Interface changes along indefinitely. See :ref:`topic-error-assertion-handling`. #. The behaviour when an assertion is triggered is now configurable in - the standard ANSI :term:`plinth` by installing an assertion - handler. See :c:func:`mps_lib_assert_fail_install`. + the default :term:`plinth` by installing an assertion handler. See + :c:func:`mps_lib_assert_fail_install`. #. Functions that take a variable number of arguments (:c:func:`mps_arena_create`, :c:func:`mps_pool_create`, - :c:func:`mps_ap_create`, :c:func:`mps_fmt_create_A`) and their - ``va_list`` alternatives (:c:func:`mps_arena_create_v` etc.) are - now deprecated in favour of functions that use a :term:`keyword - argument` interface (:c:func:`mps_arena_create_k`, - :c:func:`mps_pool_create_k`, :c:func:`mps_ap_create_k`, - :c:func:`mps_fmt_create_k`). The new interface provides better - reporting of errors, provides default values for arguments, and - provides forward compatibility. See :ref:`topic-keyword`. - - The old interface continues to be supported, but new features will - become available through the keyword interface only. + :c:func:`mps_ap_create`) and their ``va_list`` alternatives + (:c:func:`mps_arena_create_v` etc.) are now deprecated in favour of + functions that use a :term:`keyword argument` interface + (:c:func:`mps_arena_create_k`, :c:func:`mps_pool_create_k`, + :c:func:`mps_ap_create_k`). + + Similarly, the object format variant structures + (:c:type:`mps_fmt_A_s` etc.) and the functions that take them as + arguments (:c:func:`mps_fmt_create_A` etc.) are now deprecated in + favour of :c:func:`mps_fmt_create_k`. + + The new interfaces provide better reporting of errors, default + values for arguments, and forward compatibility. See + :ref:`topic-keyword`. + + The old interfaces continue to be supported for now, but new + features will become available through the keyword interface only. #. :ref:`pool-mfs` pools no longer refuse to manage blocks that are smaller than the platform alignment. They now round up smaller @@ -85,8 +407,9 @@ Other changes .. _job003435: https://www.ravenbrook.com/project/mps/issue/job003435/ -#. :ref:`pool-mvt` no longer triggers an assertion failure when it - runs out of space on its reserved block queue. See job003486_. +#. An :ref:`pool-mvt` pool no longer triggers an assertion failure + when it runs out of space on its reserved block queue. See + job003486_. .. _job003486: https://www.ravenbrook.com/project/mps/issue/job003486/ @@ -105,3 +428,157 @@ Other changes exception while the MPS is holding the arena lock. See job003640_. .. _job003640: https://www.ravenbrook.com/project/mps/issue/job003640/ + + +.. _release-notes-1.111: + +Release 1.111.0 +--------------- + +New features +............ + +#. Reporting features have been removed from the :ref:`mpseventcnv + ` utility. Instead, the telemetry system + comes with two new utility programs to assist with reporting and + analysis: :ref:`mpseventtxt ` converts an + event stream into human-readable form, and :ref:`mpseventsql + ` loads an event stream into a SQLite + database for further analysis. See :ref:`topic-telemetry`. + +#. The new pool class :ref:`pool-mfs` provides manually managed + allocation of fixed-size objects. + +#. The new pool class :ref:`pool-mvt` provides manually managed + allocation of variable-size objects using a *temporal fit* + allocation policy (that is, objects that are allocated togther are + expected to be freed together). + + +Interface changes +................. + +#. It is no longer necessary for client programs to use + :c:func:`mps_tramp` to ensure that exceptions due to barrier hits + are caught. This function is now deprecated. + +#. You can set the environment variable + :envvar:`MPS_TELEMETRY_CONTROL` to ``all`` to make the telemetry + system output all events. See :ref:`topic-telemetry`. + +#. New functions :c:func:`mps_telemetry_get`, + :c:func:`mps_telemetry_set` and :c:func:`mps_telemetry_reset` + provide a more convenient interface to telemetry control than + :c:func:`mps_telemetry_control`, which is now deprecated. See + :ref:`topic-telemetry`. + +#. The pool classes :ref:`pool-mv` and :ref:`pool-snc` are now + deprecated. + +#. Allocation frames are now deprecated. See :ref:`topic-frame`. + +#. Additionally, the functions :c:func:`mps_arena_expose`, + :c:func:`mps_arena_unsafe_expose_remember_protection`, + :c:func:`mps_arena_unsafe_restore_protection`, + :c:func:`mps_arena_roots_walk`, and :c:func:`mps_fix` are now + deprecated. + + +Other changes +............. + +#. :c:func:`mps_arena_step` no longer unclamps the arena as a side + effect. If the arena is clamped or parked before calling + :c:func:`mps_arena_step`, it is clamped afterwards. See job003320_. + + .. _job003320: https://www.ravenbrook.com/project/mps/issue/job003320/ + +#. The ambiguous stack scanner, :c:func:`mps_stack_scan_ambig`, no + longer asserts on Linux when there are multiple threads. See + job003412_. + + .. _job003412: https://www.ravenbrook.com/project/mps/issue/job003412/ + +#. It is no longer possible for the "ramp" allocation pattern, + :c:func:`mps_alloc_pattern_ramp()`, to get stuck. Now + :c:func:`mps_ap_alloc_pattern_end` reliably clears this pattern. + See job003454_. + + .. _job003454: https://www.ravenbrook.com/project/mps/issue/job003454/ + +#. The build system now correctly detects the FreeBSD operating system + running on the x86-64 architecture, for FreeBSD version 9.1 or + later. See job003473_. + + .. _job003473: https://www.ravenbrook.com/project/mps/issue/job003473/ + + +.. _release-notes-1.110: + +Release 1.110.0 +--------------- + +New features +............ + +#. New supported platforms: + + * ``fri6gc`` (FreeBSD, x86-64, GCC) + * ``lii6gc`` (Linux, x86-64, GCC) + * ``w3i6mv`` (Windows, x86-64, Microsoft Visual C) + * ``xci3ll`` (OS X, IA-32, Clang/LLVM) + * ``xci6gc`` (OS X, x86-64, GCC) + * ``xci6ll`` (OS X, x86-64, Clang/LLVM) + +#. Support removed for platforms: + + * ``iam4cc`` (Irix 6, MIPS R4000, MIPSpro C) + * ``lii3eg`` (Linux, IA-32, EGCS) + * ``lippgc`` (Linux, PowerPC, GCC) + * ``o1alcc`` (OSF/1, Alpha, Digital C) + * ``o1algc`` (OSF/1, Alpha, GCC) + * ``s7ppmw`` (System 7, PowerPC, MetroWerks C) + * ``sos8gc`` (Solaris, SPARC 8, GCC) + * ``sos9sc`` (Solaris, SPARC 9, SunPro C) + * ``sus8gc`` (SunOS, SPARC 8, GCC) + * ``xcppgc`` (OS X, PowerPC, GCC) + +#. On Unix platforms, the MPS can now be built and installed by + running ``./configure && make install``. See :ref:`guide-build`. + +#. The MPS can be compiled in a single step via the new source file + ``mps.c``. This also allows you to compile the MPS in the same + compilation unit as your object format, allowing the compiler to + perform global optimizations between the two. See + :ref:`guide-build`. + +#. The set of build varieties has been reduced to three: the + :term:`cool` variety for development and debugging, the :term:`hot` + variety for production, and the :term:`rash` variety for people who + like to live dangerously. See :ref:`topic-error-variety`. + +#. The environment variable :envvar:`MPS_TELEMETRY_CONTROL` can now be + set to a space-separated list of event kinds. See + :ref:`topic-telemetry`. + +#. Telemetry output is now emitted to the file named by the + environment variable :envvar:`MPS_TELEMETRY_FILENAME`, if it is + set. See :ref:`topic-telemetry`. + + +Interface changes +................. + +#. Deprecated constants ``MPS_MESSAGE_TYPE_FINALIZATION``, + ``MPS_MESSAGE_TYPE_GC`` and ``MPS_MESSAGE_TYPE_GC_START`` have been + removed. Use :c:func:`mps_message_type_finalization`, + :c:func:`mps_message_type_gc` and + :c:func:`mps_message_type_gc_start` instead. + +#. Deprecated constants ``MPS_RANK_AMBIG``, ``MPS_RANK_EXACT`` and + ``MPS_RANK_WEAK`` have been removed. Use :c:func:`mps_rank_ambig`, + :c:func:`mps_rank_exact` and :c:func:`mps_rank_weak` instead. + +#. Deprecated functions with names starting ``mps_space_`` have been + removed. Use the functions with names starting ``mps_arena_`` + instead. diff --git a/manual/source/themes/mmref/layout.html b/manual/source/themes/mmref/layout.html new file mode 100644 index 0000000000..961baeda42 --- /dev/null +++ b/manual/source/themes/mmref/layout.html @@ -0,0 +1,41 @@ +{# + scrolls/layout.html + ~~~~~~~~~~~~~~~~~~~ + + Sphinx layout template for the scrolls theme, originally written + by Armin Ronacher. + + :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- extends "basic/layout.html" %} +{% set script_files = script_files + ['_static/theme_extras.js'] %} +{% set css_files = css_files + ['_static/print.css'] %} +{# do not display relbars #} +{% block relbar1 %}{% endblock %} +{% block relbar2 %}{% endblock %} +{% block content %} +
+ +
+ {%- if prev and '/' not in prev.link and 'mmref-' not in prev.link %} + « {{ prev.title }} | + {%- endif %} + {{ title }} + {%- if next and '/' not in next.link and 'mmref-' not in next.link %} + | {{ next.title }} » + {%- endif %} +
+
+ {%- if display_toc %} +
+

{{ _('Table Of Contents') }}

+ {{ toc }} +
+ {%- endif %} + {% block body %}{% endblock %} +
+
+{% endblock %} diff --git a/manual/source/themes/mmref/static/metal.png b/manual/source/themes/mmref/static/metal.png new file mode 100644 index 0000000000..2f9f1ad084 Binary files /dev/null and b/manual/source/themes/mmref/static/metal.png differ diff --git a/manual/source/themes/mmref/static/mmref.css_t b/manual/source/themes/mmref/static/mmref.css_t new file mode 100644 index 0000000000..f16758a739 --- /dev/null +++ b/manual/source/themes/mmref/static/mmref.css_t @@ -0,0 +1,161 @@ +/* -*- css -*- */ + +@import url('scrolls.css'); + +sup { + vertical-align: top; + font-size: 80%; +} + +dl.glossary dt { + font-family: {{ theme_headfont }}; +} + +div.header { + background-image: none; + background-color: {{ theme_headerbg }}; + border-top: none; +} + +h1.heading { + height: auto; + text-align: center; + padding-top: 10px; + padding-bottom: 10px; +} + +h1.heading:hover { + background: {{ theme_headerhover }}; +} + +h1.heading a { + background-image: none; + display: block; + width: 100%; + height: auto; + font-size: 150%; +} + +h1.heading span { + display: block; + color: {{ theme_textcolor }}; +} + +a, a:visited, a.reference.internal { + text-decoration: none; +} + +a.reference em { + font-style: normal; +} + +a.reference.internal:hover { + text-decoration: none; + border-bottom: 1px solid {{ theme_underlinecolor }}; +} + +.xref.std-term { + font-style: normal; + color: {{ theme_textcolor }}; + border-bottom: 1px dotted {{ theme_underlinecolor }}; +} + +div.seealso, div.admonition { + background: url(metal.png); + border: none; +} + +p.admonition_title:after { + content: ":"; +} + +div.admonition p.admonition-title + p + p { + margin-top: 1em; +} + +div.figure { + margin-top: 1em; + margin-bottom: 1em; +} + +div.figure img { + max-width: 100%; +} + +.align-center { + text-align: center; +} + +img.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +dl.glossary dt { + font-size: 120%; + margin-top: 1em; +} + +p.glossary-alphabet { + font-weight: bold; + text-align: center; +} + + +div.admonition-ref-glossary, div.admonition-ref-bibliography, div.admonition-ref-mmref-intro, div.admonition-ref-mmref-faq { + width: 45%; + display: inline-block; + vertical-align: top; +} + +div.admonition-ref-glossary, div.admonition-ref-mmref-intro { + height:400px; +} + +div.admonition-ref-bibliography, div.admonition-ref-mmref-faq { + height:230px; +} + +div.admonition-ref-glossary, div.admonition-ref-bibliography { + margin-right: 1%; +} + +div.admonition-ref-mmref-intro, div.admonition-ref-mmref-faq { + margin-left: 1%; +} + +div.admonition a.image-reference img { + width: 90%; + margin-left: 5%; + margin-top: 5px; +} + +div#home h1 { + display: none; +} + +div#home h1 + p { + margin-top: 0; + padding-top: 15px; +} + +/* Format the glossary index in two columns. */ + +div#memory-management-glossary div#all { + -webkit-columns: 2; + -moz-columns: 2; + -o-columns: 2; + -ms-columns: 2; + columns: 2; + padding-top: 1em; +} + +div#memory-management-glossary div#all h2 { + display: none; +} + +div#memory-management-glossary div#all a.reference.internal:after { + content: "\A"; + white-space: pre; +} diff --git a/manual/source/themes/mmref/static/watermark.png b/manual/source/themes/mmref/static/watermark.png new file mode 100644 index 0000000000..5bca5d2008 Binary files /dev/null and b/manual/source/themes/mmref/static/watermark.png differ diff --git a/manual/source/themes/mmref/static/watermark.svg b/manual/source/themes/mmref/static/watermark.svg new file mode 100644 index 0000000000..ca9159234e --- /dev/null +++ b/manual/source/themes/mmref/static/watermark.svg @@ -0,0 +1,237 @@ + + + + + + + + + + image/svg+xml + + + + + + + + 2e2f 6d70 7369 2e63 0073 697a 6520 3e203000 6d70 735f 6172 656e 615f 6f20 213d204e 554c 4c00 6d70 735f 706f 6f6c 5f6f2021 3d20 4e55 4c4c 006d 7073 5f66 6d745f6f 2021 3d20 4e55 4c4c 006d 7073 5f666d74 5f41 2021 3d20 4e55 4c4c 006d 70735f66 6d74 5f42 2021 3d20 4e55 4c4c 006d7073 5f66 6d74 2021 3d20 4e55 4c4c 006d7073 5f66 6d74 5f66 6978 6564 2021 3d204e55 4c4c 0054 4553 5454 2846 6f72 6d61742c 2066 6f72 6d61 7429 0054 4553 54542850 6f6f 6c2c 2070 6f6f 6c29 0070 5f6f2021 3d20 4e55 4c4c 006d 7073 5f61 705f6f20 213d 204e 554c 4c00 6d70 735f 61702021 3d20 4e55 4c4c 0054 4553 5454 28427566 6665 722c 2062 7566 2900 5445 53545428 4275 6666 6572 2c20 4275 6666 65724f66 4150 286d 7073 5f61 7029 2900 6d70735f 6170 2d3e 696e 6974 203d 3d20 6d70735f 6170 2d3e 616c 6c6f 6300 7020 213d204e 554c 4c00 7020 3d3d 206d 7073 5f61702d 3e69 6e69 7400 2876 6f69 6420 2a292828 6368 6172 202a 296d 7073 5f61 702d3e69 6e69 7420 2b20 7369 7a65 2920 3d3d206d 7073 5f61 702d 3e61 6c6c 6f63 00667261 6d65 5f6f 2021 3d20 4e55 4c4c 0053697a 6549 7341 6c69 676e 6564 2873 697a652c 2042 7566 6665 7250 6f6f 6c28 62756629 2d3e 616c 6967 6e6d 656e 7429 006d7073 5f73 6163 5f6f 2021 3d20 4e55 4c4c0054 4553 5454 2853 4143 2c20 7361 63290054 4553 5454 2853 4143 2c20 5341 434f6645 7874 6572 6e61 6c53 4143 286d 7073 + + diff --git a/manual/source/themes/mmref/theme.conf b/manual/source/themes/mmref/theme.conf new file mode 100644 index 0000000000..e0ef30ece1 --- /dev/null +++ b/manual/source/themes/mmref/theme.conf @@ -0,0 +1,18 @@ +# Colour scheme: + +[theme] +inherit = scrolls +stylesheet = mmref.css + +[options] +headerbg = transparent +headerhover = #81A8B8 +subheadlinecolor = #000000 +linkcolor = #5D7985 +visitedlinkcolor = #5D7985 +admonitioncolor = #A4BCC2 +textcolor = #000000 +underlinecolor = #A4BCC2 + +bodyfont = 'Optima', sans-serif +headfont = 'Verdana', sans-serif diff --git a/manual/source/themes/mps/static/mps.css_t b/manual/source/themes/mps/static/mps.css_t index b21939b7af..5904c16558 100644 --- a/manual/source/themes/mps/static/mps.css_t +++ b/manual/source/themes/mps/static/mps.css_t @@ -139,6 +139,9 @@ dl.glossary dt, dl.type dt, dl.function dt, dl.macro dt { margin-top: 2em; margin-bottom: 1em; font-size: 120%; +} + +dl.type dt, dl.function dt, dl.macro dt { /* Use a hanging indent so that long wrapped prototypes are easier to read. */ padding-left: 4em; text-indent: -4em; @@ -185,7 +188,7 @@ p.glossary-alphabet { } sup { - vertical-align: 20%; + vertical-align: top; font-size: 80%; } @@ -217,3 +220,22 @@ li.toctree-l1, li.toctree-l2, li.toctree-l3 { padding-top: 0 !important; } +/* Format the glossary index in two columns. */ + +div#memory-management-glossary div#all { + -webkit-columns: 2; + -moz-columns: 2; + -o-columns: 2; + -ms-columns: 2; + columns: 2; + padding-top: 1em; +} + +div#memory-management-glossary div#all h2 { + display: none; +} + +div#memory-management-glossary div#all a.reference.internal:after { + content: "\A"; + white-space: pre; +} diff --git a/manual/source/topic/allocation.rst b/manual/source/topic/allocation.rst index 41c5919069..4075cfe006 100644 --- a/manual/source/topic/allocation.rst +++ b/manual/source/topic/allocation.rst @@ -119,37 +119,6 @@ many small objects. They must be used according to the :term:`thread`: each thread must create its own allocation point or points. - .. note:: - - There's an alternative function :c:func:`mps_ap_create_v` that - takes its extra arguments using the standard :term:`C` - ``va_list`` mechanism. - - -.. c:function:: mps_res_t mps_ap_create(mps_ap_t *ap_o, mps_pool_t pool, ...) - - .. deprecated:: starting with version 1.112. - - Use :c:func:`mps_ap_create_k` instead: the :term:`keyword - arguments` interface is more reliable and produces better - error messages. - - An alternative to :c:func:`mps_ap_create_k` that takes its extra - arguments using the standard :term:`C` variable argument list - mechanism. - - -.. c:function:: mps_res_t mps_ap_create_v(mps_ap_t *ap_o, mps_pool_t pool, va_list args) - - .. deprecated:: starting with version 1.112. - - Use :c:func:`mps_ap_create_k` instead: the :term:`keyword - arguments` interface is more reliable and produces better - error messages. - - An alternative to :c:func:`mps_ap_create_k` that takes its extra - arguments using the standard :term:`C` ``va_list`` mechanism. - .. c:function:: void mps_ap_destroy(mps_ap_t ap) @@ -246,7 +215,8 @@ is thus:: size_t aligned_size = ALIGN(size); /* see note 1 */ do { mps_res_t res = mps_reserve(&p, ap, aligned_size); - if (res != MPS_RES_OK) /* handle the error */; + if (res != MPS_RES_OK) + /* handle the error */; /* p is now an ambiguous reference to the reserved block */ obj = p; /* initialize obj */ @@ -595,8 +565,9 @@ The *reserve* operation thus looks like this:: } } -The critical path consists of an add, a store, and a branch (and -branch prediction should work well since the test usually succeeds). +The critical path consists of three loads, an add, two stores, and a +branch (and branch prediction should work well since the test usually +succeeds). .. note:: @@ -629,8 +600,9 @@ The *commit* operation thus looks like this:: /* p is valid */ } -The critical path here consists of a store and a branch (and again, -branch prediction should work well since the test almost never fails). +The critical path here consists of three loads, a store and a branch +(and again, branch prediction should work well since the test almost +never fails). .. note:: diff --git a/manual/source/topic/arena.rst b/manual/source/topic/arena.rst index 5220ece400..f98de4ba56 100644 --- a/manual/source/topic/arena.rst +++ b/manual/source/topic/arena.rst @@ -92,32 +92,6 @@ the way that they acquire the memory to be managed. :c:func:`mps_arena_destroy`. -.. c:function:: mps_res_t mps_arena_create(mps_arena_t *arena_o, mps_arena_class_t arena_class, ...) - - .. deprecated:: starting with version 1.112. - - Use :c:func:`mps_arena_create_k` instead: the :term:`keyword - arguments` interface is more reliable and produces better - error messages. - - An alternative to :c:func:`mps_arena_create_k` that takes its - extra arguments using the standard :term:`C` variable argument - list mechanism. - - -.. c:function:: mps_res_t mps_arena_create_v(mps_arena_t *arena_o, mps_arena_class_t arena_class, va_list args) - - .. deprecated:: starting with version 1.112. - - Use :c:func:`mps_arena_create_k` instead: the :term:`keyword - arguments` interface is more reliable and produces better - error messages. - - An alternative to :c:func:`mps_arena_create_k` that takes its - extra arguments using the standard :term:`C` ``va_list`` - mechanism. - - .. c:function:: void mps_arena_destroy(mps_arena_t arena) Destroy an :term:`arena`. @@ -165,6 +139,21 @@ Client arenas * :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`) is its size. + It also accepts two 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 + will use out of the provided chunk (or chunks, if the arena is + extended). See :c:func:`mps_arena_commit_limit` for details. The + default commit limit is the maximum value of the + :c:type:`size_t` type. + + * :c:macro:`MPS_KEY_ARENA_GRAIN_SIZE` (type :c:type:`size_t`, + default 8192) is the granularity with which the arena will + manage memory internally. It must be a power of 2. Larger + granularity reduces overheads, but increases + :term:`fragmentation` and :term:`retention`. + For example:: MPS_ARGS_BEGIN(args) { @@ -184,15 +173,6 @@ Client arenas Client arenas have no mechanism for returning unused memory. - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_arena_create`, pass the size and base - address like this:: - - mps_res_t mps_arena_create(mps_arena_t *arena_o, - mps_arena_class_t mps_arena_class_cl, - size_t size, mps_addr_t base) - .. c:function:: mps_res_t mps_arena_extend(mps_arena_t arena, mps_addr_t base, size_t size) @@ -233,18 +213,26 @@ Virtual memory arenas more efficient. When creating a virtual memory arena, :c:func:`mps_arena_create_k` - requires one :term:`keyword argument`: - - * :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`). is the - initial amount of virtual address space, in :term:`bytes (1)`, - that the arena will reserve (this space is initially reserved so - that the arena can subsequently use it without interference from - other parts of the program, but most of it is not committed, so - it doesn't require any RAM or backing store). The arena may - allocate more virtual address space beyond this initial - reservation as and when it deems it necessary. The MPS is most - efficient if you reserve an address space that is several times - larger than your peak memory usage. + accepts four optional :term:`keyword arguments` on all platforms: + + * :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`, default + 256 :term:`megabytes`) is the initial amount of virtual address + space, in :term:`bytes (1)`, that the arena will reserve (this + space is initially reserved so that the arena can subsequently + use it without interference from other parts of the program, but + most of it is not committed, so it doesn't require any RAM or + backing store). The arena may allocate more virtual address + space beyond this initial reservation as and when it deems it + necessary. The MPS is most efficient if you reserve an address + space that is several times larger than your peak memory usage. + + If you specify a value for :c:macro:`MPS_KEY_ARENA_SIZE` that's + too small for the virtual memory arena, then the MPS rounds it + up to the minimum and continues. The minimum size for the + virtual memory arena is :c:macro:`MPS_WORD_WIDTH` × + :c:macro:`MPS_KEY_ARENA_GRAIN_SIZE` bytes. For example, on a + 64-bit platform with a 4 :term:`kilobyte` page size, this is + 256\ :term:`kilobytes`. .. note:: @@ -252,12 +240,35 @@ Virtual memory arenas more times it has to extend its address space, the less efficient garbage collection will become. - An optional :term:`keyword argument` may be passed, but is - only used on the Windows operating system: + * :c:macro:`MPS_KEY_COMMIT_LIMIT` (type :c:type:`size_t`) is + the maximum amount of main memory, in :term:`bytes (1)`, that + the MPS will obtain from the operating system. See + :c:func:`mps_arena_commit_limit` for details. The default commit + limit is the maximum value of the :c:type:`size_t` type. + + * :c:macro:`MPS_KEY_ARENA_GRAIN_SIZE` (type :c:type:`size_t`) is + the granularity with which the arena will manage memory + internally. It must be a power of 2. If not provided, the + operating system's page size is used. Larger granularity reduces + overheads, but increases :term:`fragmentation` and + :term:`retention`. + + If you specify a value of :c:macro:`MPS_KEY_ARENA_GRAIN_SIZE` + that's smaller than the operating system page size, the MPS + rounds it up to the page size and continues. + + * :c:macro:`MPS_KEY_SPARE_COMMIT_LIMIT` (type + :c:type:`size_t`, default 0) is the spare commit limit in + :term:`bytes (1)`. See :c:func:`mps_arena_spare_commit_limit` + for details. - * :c:macro:`MPS_KEY_VMW3_TOP_DOWN` (type :c:type:`mps_bool_t`). If - true, the arena will allocate address space starting at the - highest possible address and working downwards through memory. + A fifth optional :term:`keyword argument` may be passed, but it + only has any effect on the Windows operating system: + + * :c:macro:`MPS_KEY_VMW3_TOP_DOWN` (type :c:type:`mps_bool_t`, + default false). If true, the arena will allocate address space + starting at the highest possible address and working downwards + through memory. .. note:: @@ -273,25 +284,17 @@ Virtual memory arenas If the MPS fails to allocate memory for the internal arena structures, :c:func:`mps_arena_create_k` returns - :c:macro:`MPS_RES_MEMORY`. Either ``size`` was far too small or - the operating system refused to provide enough memory. + :c:macro:`MPS_RES_MEMORY`. Either :c:macro:`MPS_KEY_ARENA_SIZE` + was far too small or the operating system refused to provide + enough memory. For example:: MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, size); - res = mps_arena_create_k(&arena, mps_arena_class_cl(), args); + res = mps_arena_create_k(&arena, mps_arena_class_vm(), args); } MPS_ARGS_END(args); - .. deprecated:: starting with version 1.112. - - When using :c:func:`mps_arena_create`, pass the size like - this:: - - mps_res_t mps_arena_create(mps_arena_t *arena_o, - mps_arena_class_t arena_class_vm(), - size_t size) - .. index:: single: arena; properties @@ -314,10 +317,42 @@ Arena properties ``arena`` is the arena to return the commit limit for. - Returns the commit limit in :term:`bytes (1)`. The commit limit - controls how much memory the MPS can obtain from the operating - system, and can be changed by calling - :c:func:`mps_arena_commit_limit_set`. + Returns the commit limit in :term:`bytes (1)`. + + For a :term:`client arena`, this this the maximum amount of + memory, in :term:`bytes (1)`, that the MPS will use out of the + chunks provided by the client to the arena. + + For a :term:`virtual memory arena`, this is the maximum amount of + memory that the MPS will map to RAM via the operating system's + virtual memory interface. + + The commit limit can be set by passing the + :c:macro:`MPS_KEY_COMMIT_LIMIT` :term:`keyword argument` to + :c:func:`mps_arena_create_k`. It can be changed by calling + :c:func:`mps_arena_commit_limit_set`. The + commit limit cannot be set to a value that is lower than the + number of bytes that the MPS is using. If an attempt is made to + set the commit limit to a value greater than or equal to that + returned by :c:func:`mps_arena_committed` then it will succeed. If + an attempt is made to set the commit limit to a value less than + that returned by :c:func:`mps_arena_committed` then it will + succeed only if the amount committed by the MPS can be reduced by + reducing the amount of spare committed memory; in such a case the + spare committed memory will be reduced appropriately and the + attempt will succeed. + + .. note:: + + The commit limit puts a limit on all memory committed by the + MPS. The :term:`spare committed memory` (that is, memory + committed by the MPS but not currently in use, neither by the + :term:`client program`, or by the MPS itself) can be limited + separately; see :c:func:`mps_arena_spare_committed`. Note that + "spare committed" memory is subject to both limits; there + cannot be more spare committed memory than the spare commit + limit, and there can't be so much spare committed memory that + there is more committed memory than the commit limit. .. c:function:: mps_res_t mps_arena_commit_limit_set(mps_arena_t arena, size_t limit) @@ -331,29 +366,7 @@ Arena properties Returns :c:macro:`MPS_RES_OK` if successful, or another :term:`result code` if not. - If successful, the commit limit for ``arena`` is set to ``limit``. The - commit limit controls how much memory the MPS will obtain from the - operating system. The commit limit cannot be set to a value that - is lower than the number of bytes that the MPS is using. If an - attempt is made to set the commit limit to a value greater than or - equal to that returned by :c:func:`mps_arena_committed` then it - will succeed. If an attempt is made to set the commit limit to a - value less than that returned by :c:func:`mps_arena_committed` - then it will succeed only if the amount committed by the MPS can - be reduced by reducing the amount of spare committed memory; in - such a case the spare committed memory will be reduced - appropriately and the attempt will succeed. - - .. note:: - - :c:func:`mps_arena_commit_limit_set` puts a limit on all - memory committed by the MPS. The :term:`spare committed - memory` can be limited separately with - :c:func:`mps_arena_spare_commit_limit_set`. Note that "spare - committed" memory is subject to both limits; there cannot be - more spare committed memory than the spare commit limit, and - there can't be so much spare committed memory that there is - more committed memory than the commit limit. + See :c:func:`mps_arena_spare_commit_limit` for details. .. c:function:: size_t mps_arena_committed(mps_arena_t arena) @@ -363,9 +376,18 @@ Arena properties ``arena`` is the arena. - Returns the total amount of memory that has been committed to RAM + Returns the total amount of memory that has been committed for use by the MPS, in :term:`bytes (1)`. + For a :term:`virtual memory arena`, this is the amount of memory + mapped to RAM by the operating system's virtual memory interface. + + For a :term:`client arena`, this is the amount of memory marked as + in use in the arena's page tables. This is not particularly + meaningful by itself, but it corresponds to the amount of mapped + memory that the MPS would use if switched to a virtual memory + arena. + The committed memory is generally larger than the sum of the sizes of the allocated :term:`blocks`. The reasons for this are: @@ -392,11 +414,11 @@ Arena properties state>`). If it is called when the arena is in the unclamped state then the value may change after this function returns. A possible use might be to call it just after :c:func:`mps_arena_collect` to - (over-)estimate the size of the heap. + estimate the size of the heap. If you want to know how much memory the MPS is using then you're - probably interested in the value ``mps_arena_committed() - - mps_arena_spare_committed()``. + probably interested in the value :c:func:`mps_arena_committed` − + :c:func:`mps_arena_spare_committed`. The amount of committed memory can be limited with the function :c:func:`mps_arena_commit_limit`. @@ -419,12 +441,12 @@ Arena properties .. note:: - For a client arena, the reserved address may be lower than the - sum of the :c:macro:`MPS_KEY_ARENA_SIZE` keyword argument - passed to :c:func:`mps_arena_create_k` and the ``size`` - arguments passed to :c:func:`mps_arena_extend`, because the - arena may be unable to use the whole of each chunk for reasons - of alignment. + For a :term:`client arena`, the reserved address space may be + lower than the sum of the :c:macro:`MPS_KEY_ARENA_SIZE` + keyword argument passed to :c:func:`mps_arena_create_k` and + the ``size`` arguments passed to :c:func:`mps_arena_extend`, + because the arena may be unable to use the whole of each chunk + for reasons of alignment. .. c:function:: size_t mps_arena_spare_commit_limit(mps_arena_t arena) @@ -435,32 +457,20 @@ Arena properties ``arena`` is the arena to return the spare commit limit for. Returns the spare commit limit in :term:`bytes (1)`. The spare - commit limit can be changed by calling - :c:func:`mps_arena_spare_commit_limit_set`. - - -.. c:function:: void mps_arena_spare_commit_limit_set(mps_arena_t arena, size_t limit) - - Change the :term:`spare commit limit` for an :term:`arena`. - - ``arena`` is the arena to change the spare commit limit for. - - ``limit`` is the new spare commit limit in :term:`bytes (1)`. - - The spare commit limit is the maximum amount of :term:`spare - committed memory` the MPS is allowed to have. Setting it to a - value lower than the current amount of spare committed memory - causes spare committed memory to be uncommitted so as to bring the - value under the limit. In particular, setting it to 0 will mean - that the MPS will have no spare committed memory. - - Non-virtual-memory arena classes (for example, a :term:`client - arena`) do not have spare committed memory. For these arenas, this - function functions sets a value but has no other effect. - - Initially the spare commit limit is a configuration-dependent - value. The value of the limit can be retrieved by the function - :c:func:`mps_arena_spare_commit_limit`. + commit limit is the maximum amount of :term:`spare committed + memory` (that is, memory committed by the MPS but not currently in + use, neither by the :term:`client program`, or by the MPS itself) + the MPS is allowed to have. + + The spare commit limit can be set by passing the + :c:macro:`MPS_KEY_SPARE_COMMIT_LIMIT` :term:`keyword + argument` to :c:func:`mps_arena_create_k`. It can be changed + by calling :c:func:`mps_arena_spare_commit_limit_set`. + Setting it to a value lower than + the current amount of spare committed memory causes spare + committed memory to be uncommitted so as to bring the value under + the limit. In particular, setting it to 0 will mean that the MPS + will have no spare committed memory. .. c:function:: size_t mps_arena_spare_committed(mps_arena_t arena) @@ -482,12 +492,35 @@ Arena properties memory by :c:func:`mps_arena_committed` and is restricted by :c:func:`mps_arena_commit_limit`. - The amount of "spare committed" memory can be limited by calling - :c:func:`mps_arena_spare_commit_limit_set`, and the value of that - limit can be retrieved with - :c:func:`mps_arena_spare_commit_limit`. This is analogous to the - functions for limiting the amount of :term:`committed ` - memory. + The amount of "spare committed" memory can be limited passing the + :c:macro:`MPS_KEY_SPARE_COMMIT_LIMIT` :term:`keyword + argument` to :c:func:`mps_arena_create_k` or by calling + :c:func:`mps_arena_spare_commit_limit_set`. The value of the limit can be + retrieved with :c:func:`mps_arena_spare_commit_limit`. This is + analogous to the functions for limiting the amount of + :term:`committed ` memory. + + .. note:: + + :term:`Client arenas` do not use spare committed memory, and + so this function always returns 0. + + +.. c:function:: void mps_arena_spare_commit_limit_set(mps_arena_t arena, size_t limit) + + Change the :term:`spare commit limit` for an :term:`arena`. + + ``arena`` is the arena to change the spare commit limit for. + + ``limit`` is the new spare commit limit in :term:`bytes (1)`. + + Non-virtual-memory arena classes (for example, a :term:`client + arena`) do not have spare committed memory. For these arenas, this + function sets a value but has no other effect. + + Initially the spare commit limit is a configuration-dependent + value. The value of the limit can be retrieved by the function + :c:func:`mps_arena_spare_commit_limit`. .. index:: @@ -505,7 +538,7 @@ An arena is always in one of three states. In the *unclamped state*, garbage collection may take place, objects may move in memory, references may be updated, :term:`location dependencies` may become stale, virtual memory may - be requested from or return to the operating system, and other + be requested from or returned to the operating system, and other kinds of background activity may occur. This is the normal state. #. .. index:: @@ -530,19 +563,19 @@ An arena is always in one of three states. Here's a summary: -======================================== ================================== ============================= =========================== -State unclamped clamped parked -======================================== ================================== ============================= =========================== -Collections may be running? yes yes no -New collections may start? yes no no -Objects may move? yes no no -Location dependencies may become stale? yes no no -Memory may be returned to the OS? yes no no -Functions that leave arena in this state :c:func:`mps_arena_create`, :c:func:`mps_arena_clamp`, :c:func:`mps_arena_park`, - :c:func:`mps_arena_release`, :c:func:`mps_arena_step` :c:func:`mps_arena_collect` - :c:func:`mps_arena_start_collect`, - :c:func:`mps_arena_step` -======================================== ================================== ============================= =========================== +============================================ ================================== ============================= =========================== +State unclamped clamped parked +============================================ ================================== ============================= =========================== +Collections may be running? yes yes no +New collections may start? yes no no +Objects may move? yes no no +Location dependencies may become stale? yes no no +Memory may be returned to the OS? yes no no +Functions that leave the arena in this state :c:func:`mps_arena_create_k`, :c:func:`mps_arena_clamp`, :c:func:`mps_arena_park`, + :c:func:`mps_arena_release`, :c:func:`mps_arena_step` :c:func:`mps_arena_collect` + :c:func:`mps_arena_start_collect`, + :c:func:`mps_arena_step` +============================================ ================================== ============================= =========================== The clamped and parked states are important when introspecting and debugging. If you are examining the contents of the heap, you don't @@ -555,7 +588,7 @@ before inspecting memory, and:: (gdb) print mps_arena_release(arena) -afterward. +afterwards. The results of introspection functions like :c:func:`mps_arena_has_addr` only remain valid while the arena remains @@ -691,24 +724,7 @@ provides a function, :c:func:`mps_arena_step`, for making use of idle time to make memory management progress. Here's an example illustrating the use of this function in a program's -event loop. When the program is idle (there are no client actions to -perform), it requests that the MPS spend up to 10 milliseconds on -incremental work, by calling ``mps_arena_step(arena, 0.010, -0.0)``. When this returns false to indicate that there is no more work -to do, the program blocks on the client for two seconds: if this times -out, it predicts that the user will remain idle for at least a further -second, so it calls ``mps_arena_step(arena, 0.010, 100.0)`` to tell -that it's a good time to start a collection taking up to 10 ms × 100 -= 1 second, but not to pause for more than 10 ms. - -The program remains responsive: the MPS doesn't take control for more -than a few milliseconds at a time (at most 10). But at the same time, -major collection work can get done at times when the program would -otherwise be idle. Of course the numbers here are only for -illustration and should be chosen based on the requirements of the -application. - -:: +event loop. :: for (;;) { /* event loop */ for (;;) { @@ -728,6 +744,23 @@ application. } } +When the program is idle (there are no client actions to perform), it +requests that the MPS spend up to 10 milliseconds on incremental work, +by calling ``mps_arena_step(arena, 0.010, 0.0)``. When this returns +false to indicate that there is no more work to do, the program blocks +on the client for two seconds: if this times out, it predicts that the +user will remain idle for at least a further second, so it calls +``mps_arena_step(arena, 0.010, 100.0)`` to tell that it's a good time +to start a collection taking up to 10 ms × 100 = 1 second, but not to +pause for more than 10 ms. + +The program remains responsive: the MPS doesn't take control for more +than a few milliseconds at a time (at most 10). But at the same time, +major collection work can get done at times when the program would +otherwise be idle. Of course the numbers here are only for +illustration; they should be chosen based on the requirements of the +application. + .. c:function:: mps_bool_t mps_arena_step(mps_arena_t arena, double interval, double multiplier) @@ -811,114 +844,3 @@ Arena introspection return storage to the operating system). For reliable results call this function and interpret the result while the arena is in the :term:`parked state`. - - -.. index:: - pair: arena; protection - -Protection interface --------------------- - -.. c:function:: void mps_arena_expose(mps_arena_t arena) - - .. deprecated:: starting with version 1.111. - - Ensure that the MPS is not protecting any :term:`page` in the - :term:`arena` with a :term:`read barrier` or :term:`write - barrier`. - - ``mps_arena`` is the arena to expose. - - This is expected to only be useful for debugging. The arena is - left in the :term:`clamped state`. - - Since barriers are used during a collection, calling this function - has the same effect as calling :c:func:`mps_arena_park`: all - collections are run to completion, and the arena is clamped so - that no new collections begin. The MPS also uses barriers to - maintain :term:`remembered sets`, so calling this - function will effectively destroy the remembered sets and any - optimization gains from them. - - Calling this function is time-consuming: any active collections - will be run to completion; and the next collection will have to - recompute all the remembered sets by scanning the entire arena. - - The recomputation of the remembered sets can be avoided by calling - :c:func:`mps_arena_unsafe_expose_remember_protection` instead of - :c:func:`mps_arena_expose`, and by calling - :c:func:`mps_arena_unsafe_restore_protection` before calling - :c:func:`mps_arena_release`. Those functions have unsafe aspects - and place restrictions on what the :term:`client program` can do - (basically no exposed data can be changed). - - -.. c:function:: void mps_arena_unsafe_expose_remember_protection(mps_arena_t arena) - - .. deprecated:: starting with version 1.111. - - Ensure that the MPS is not protecting any :term:`page` in the - :term:`arena` with a :term:`read barrier` or :term:`write - barrier`. In addition, request the MPS to remember some parts of its - internal state so that they can be restored later. - - ``mps_arena`` is the arena to expose. - - This function is the same as :c:func:`mps_arena_expose`, but - additionally causes the MPS to remember its protection state. The - remembered protection state can optionally be restored later by - calling the :c:func:`mps_arena_unsafe_restore_protection` function. - This is an optimization that avoids the MPS having to recompute - all the remembered sets by scanning the entire arena. - - However, restoring the remembered protections is only safe if the - contents of the exposed pages have not been changed; therefore - this function should only be used if you do not intend to change - the pages, and the remembered protection must only be restored if - the pages have not been changed. - - The MPS will only remember the protection state if resources - (memory) are available. If memory is low then only some or - possibly none of the protection state will be remembered, with a - corresponding necessity to recompute it later. The MPS provides no - mechanism for the :term:`client program` to determine whether the - MPS has in fact remembered the protection state. - - The remembered protection state, if any, is discarded after - calling :c:func:`mps_arena_unsafe_restore_protection`, or as soon - as the arena leaves the :term:`clamped state` by calling - :c:func:`mps_arena_release`. - - -.. c:function:: void mps_arena_unsafe_restore_protection(mps_arena_t arena) - - .. deprecated:: starting with version 1.111. - - Restore the remembered protection state for an :term:`arena`. - - ``mps_arena`` is the arena to restore the protection state for. - - This function restores the protection state that the MPS has - remembered when the :term:`client program` called - :c:func:`mps_arena_unsafe_expose_remember_protection`. The purpose - of remembering and restoring the protection state is to avoid the - need for the MPS to recompute all the :term:`remembered sets` by scanning the entire arena, that occurs when - :c:func:`mps_arena_expose` is used, and which causes the next - :term:`garbage collection` to be slow. - - The client program must not change the exposed data between the - call to :c:func:`mps_arena_unsafe_expose_remember_protection` and - :c:func:`mps_arena_unsafe_restore_protection`. If the client - program has changed the exposed data then - :c:func:`mps_arena_unsafe_restore_protection` must not be called: - in this case simply call :c:func:`mps_arena_release`. - - Calling this function does not release the arena from the clamped - state: :c:func:`mps_arena_release` must be called to continue - normal collections. - - Calling this function causes the MPS to forget the remember - protection state; as a consequence the same remembered state - cannot be restored more than once. - - diff --git a/manual/source/topic/cache.rst b/manual/source/topic/cache.rst index fc523161a2..f95b0a7a16 100644 --- a/manual/source/topic/cache.rst +++ b/manual/source/topic/cache.rst @@ -152,8 +152,7 @@ Cache interface :c:macro:`MPS_RES_COMMIT_LIMIT` when it fails to allocate memory for the internal cache structure. Returns :c:macro:`MPS_RES_LIMIT` if you ask for too many size classes: in this case, combine some - small adjacent classes. Returns :c:macro:`MPS_RES_PARAM` if the - pool doesn't support segregated allocation caches. + small adjacent classes. After this function returns, the array of size classes pointed to be ``classes`` is no longer needed and may be discarded. The @@ -170,9 +169,9 @@ Cache interface The size classes are described by an array of element type :c:type:`mps_sac_class_s`. This array is used to initialize the - segregated allocation cache, and is not needed - after:c:func:`mps_sac_create` returns. The following constraints - apply to the array: + segregated allocation cache, and is not needed after + :c:func:`mps_sac_create` returns. The following constraints apply + to the array: * You must specify at least one size class. diff --git a/manual/source/topic/collection.rst b/manual/source/topic/collection.rst index de5629e52d..d911172f41 100644 --- a/manual/source/topic/collection.rst +++ b/manual/source/topic/collection.rst @@ -40,26 +40,27 @@ and die together, then it is efficient for them to share a chain. Typically blocks are allocated in the first generation in the chain, the :term:`nursery generation` (though you can change this using the -:c:macro:`MPS_KEY_GEN` keyword argument to :c:func:`mps_pool_create`), -and each time a block survives one collection then it is -:term:`promoted ` to the next generation. Thus a generation -contains a set of blocks of similar ages. +:c:macro:`MPS_KEY_GEN` keyword argument to +:c:func:`mps_pool_create_k`), and each time a block survives one +collection then it is :term:`promoted ` to the next +generation. Thus a generation contains a set of blocks of similar +ages. By default, all pools in an arena share the same generation chain ("the arena's default generation chain"), but if this doesn't meet your requirements, then when creating an automatically managed pool, you can choose which chain it should use by passing the :c:macro:`MPS_KEY_CHAIN` keyword argument to -:c:func:`mps_pool_create`. +:c:func:`mps_pool_create_k`. Create a generation chain by preparing an array of :c:type:`mps_gen_param_s` structures giving the *capacity* (in kilobytes) and *predicted mortality* (between 0 and 1) of each generation, and passing them to :c:func:`mps_chain_create`. -When the size of the generation exceeds the capacity, the MPS will be -prepared to start collecting the generation. See -:ref:`topic-collection-schedule` below. +When the *new size* of a generation exceeds its capacity, the MPS will +be prepared to start collecting the chain to which the generation +belongs. See :ref:`topic-collection-schedule` below. For example:: @@ -134,8 +135,12 @@ For example:: ``chain`` is the generation chain. - It is an error to destroy a generation chain if there exists a - :term:`pool` using the chain. The pool must be destroyed first. + It is an error to destroy a generation chain if there is a garbage + collection in progress on the chain, or if there are any + :term:`pools` using the chain. Before calling this function, the + arena should be parked (by calling :c:func:`mps_arena_park`) to + ensure that there are no collections in progress, and pools using + the chain must be destroyed. .. index:: @@ -163,20 +168,19 @@ size* of each generation (other than the topmost) is the same as its total size, but in pools like :ref:`pool-ams` where survivors do not get promoted, the two sizes can be different. -The first generation in a pool's chain is the :term:`nursery space`. -When the nursery's *new size* exceeds its capacity, the MPS considers -collecting the pool. (How long it takes to get around to it depends on -which other collections on other pools are in progress.) +When a generation's *new size* exceeds its capacity, the MPS considers +collecting the chain to which the generation belongs. (How long it +takes to get around to it depends on which other collections are in +progress.) .. note:: - You can affect the decision as to when to collect the nursery - space by using the :ref:`ramp allocation pattern - `. + You can affect the decision as to when to collect the chain by + using the :ref:`ramp allocation pattern `. -If the MPS decides to collect a pool at all, all generations are -collected below the first generation whose *new size* is less than its -capacity. +If the MPS decides to collect a chain, all generations are collected +up to, and including, the highest generation whose *new size* exceeds +its capacity. In pools such as :ref:`pool-amc`, blocks in generation *g* that survive collection get promoted to generation *g*\+1. If the last diff --git a/manual/source/topic/debugging.rst b/manual/source/topic/debugging.rst index 70494aa20c..61025e3136 100644 --- a/manual/source/topic/debugging.rst +++ b/manual/source/topic/debugging.rst @@ -50,9 +50,9 @@ debugging: for the pattern at any time by calling :c:func:`mps_pool_check_free_space`. -The :term:`client program` specifies templates for both of these -features via the :c:type:`mps_pool_debug_option_s` structure. This -allows it to specify patterns: +The :term:`client program` may optionally specify templates for both +of these features via the :c:type:`mps_pool_debug_option_s` structure. +This allows it to specify patterns: * that mimic illegal data values; @@ -66,8 +66,8 @@ allows it to specify patterns: For example:: mps_pool_debug_option_s debug_options = { - (const void *)"postpost", 8, - (const void *)"freefree", 8, + "fencepost", 9, + "free", 4, }; mps_pool_t pool; mps_res_t res; @@ -81,7 +81,7 @@ For example:: .. c:type:: mps_pool_debug_option_s - The type of the structure passed as the + The type of the structure passed as the value for the optional :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` keyword argument to :c:func:`mps_pool_create_k` when creating a debugging :term:`pool class`. :: @@ -104,10 +104,6 @@ For example:: ``free_size`` is the :term:`size` of ``free_template`` in bytes, or zero if the debugging pool should not splat free space. - Both ``fence_size`` and ``free_size`` must be a multiple of the - :term:`alignment` of the :term:`pool`, and also a multiple of the - alignment of the pool's :term:`object format` if it has one. - The debugging pool will copy the ``fence_size`` bytes pointed to by ``fence_template`` in a repeating pattern onto each fencepost during allocation, and it will copy the bytes pointed to by @@ -118,6 +114,13 @@ For example:: pieces smaller than the given size, for example to pad out part of a block that was left unused because of alignment requirements. + If the client omits to pass the + :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` keyword argument to + :c:func:`mps_pool_create_k`, then the fencepost template consists + of the four bytes ``50 4F 53 54`` (``POST`` in ASCII), and the + free space template consists of the four bytes ``46 52 45 45`` + (``FREE`` in ASCII). + .. c:function:: void mps_pool_check_fenceposts(mps_pool_t pool) diff --git a/manual/source/topic/deprecated.rst b/manual/source/topic/deprecated.rst new file mode 100644 index 0000000000..dfd4d8d74c --- /dev/null +++ b/manual/source/topic/deprecated.rst @@ -0,0 +1,745 @@ +.. index:: + single: deprecated interfaces + +.. _topic-deprecated: + +Deprecated interfaces +===================== + +This chapter documents the public symbols in the MPS interface that +are now deprecated. These symbols may be removed in any future release +(see :ref:`topic-interface-support` for details). If you are using one +of these symbols, then you should update your code to use the +supported interface. + +.. note:: + + If you are relying on a deprecated interface, and there is no + supported alternative, please :ref:`contact us `. It + makes a difference if we know that someone is using a feature. + + +.. index:: + single: deprecated interfaces; in version 1.115 + +Deprecated in version 1.115 +........................... + +.. c:type:: typedef mps_pool_class_t mps_class_t + + .. deprecated:: + + The former name for :c:type:`mps_pool_class_t`, chosen when + pools were the only objects in the MPS that belonged to + classes. + + +.. c:function:: size_t mps_mv_free_size(mps_pool_t pool) + + .. deprecated:: + + Use the generic function :c:func:`mps_pool_free_size` instead. + + Return the total amount of free space in an MV pool. + + ``pool`` is the MV pool. + + Returns the total free space in the pool, in :term:`bytes (1)`. + + +.. c:function:: size_t mps_mv_size(mps_pool_t pool) + + .. deprecated:: + + Use the generic function :c:func:`mps_pool_total_size` + instead. + + Return the total size of an MV pool. + + ``pool`` is the MV pool. + + Returns the total size of the pool, in :term:`bytes (1)`. This + is the sum of allocated space and free space. + + +.. c:function:: size_t mps_mvff_free_size(mps_pool_t pool) + + .. deprecated:: + + Use the generic function :c:func:`mps_pool_free_size` instead. + + Return the total amount of free space in an MVFF pool. + + ``pool`` is the MVFF pool. + + Returns the total free space in the pool, in :term:`bytes (1)`. + + +.. c:function:: size_t mps_mvff_size(mps_pool_t pool) + + .. deprecated:: + + Use the generic function :c:func:`mps_pool_total_size` + instead. + + Return the total size of an MVFF pool. + + ``pool`` is the MVFF pool. + + Returns the total size of the pool, in :term:`bytes (1)`. This + is the sum of allocated space and free space. + + +.. c:function:: size_t mps_mvt_free_size(mps_pool_t pool) + + .. deprecated:: + + Use the generic function :c:func:`mps_pool_free_size` instead. + + Return the total amount of free space in an MVT pool. + + ``pool`` is the MVT pool. + + Returns the total free space in the pool, in :term:`bytes (1)`. + + +.. c:function:: size_t mps_mvt_size(mps_pool_t pool) + + .. deprecated:: + + Use the generic function :c:func:`mps_pool_total_size` + instead. + + Return the total size of an MVT pool. + + ``pool`` is the MVT pool. + + Returns the total size of the pool, in :term:`bytes (1)`. This + is the sum of allocated space and free space. + + +.. index:: + single: deprecated interfaces; in version 1.113 + +Deprecated in version 1.113 +........................... + +.. c:function:: MPS_ARGS_DONE(args) + + .. deprecated:: + + Formerly this was used to finalize a list of :term:`keyword + arguments` before passing it to a function. It is no longer + needed. + + +.. index:: + single: deprecated interfaces; in version 1.112 + +Deprecated in version 1.112 +........................... + +.. c:function:: mps_res_t mps_arena_create(mps_arena_t *arena_o, mps_arena_class_t arena_class, ...) + + .. deprecated:: + + Use :c:func:`mps_arena_create_k` instead. + + An alternative to :c:func:`mps_arena_create_k` that takes its + extra arguments using the standard :term:`C` variable argument + list mechanism. + + When creating an arena of class :c:func:`mps_arena_class_cl`, pass + the values for the keyword arguments :c:macro:`MPS_KEY_ARENA_SIZE` + and :c:macro:`MPS_KEY_ARENA_CL_BASE` like this:: + + mps_res_t mps_arena_create(mps_arena_t *arena_o, + mps_arena_class_t mps_arena_class_cl(), + size_t arena_size, + mps_addr_t cl_base) + + When creating an arena of class :c:func:`mps_arena_class_vm`, pass + the value for the keyword argument :c:macro:`MPS_KEY_ARENA_SIZE` + like this:: + + mps_res_t mps_arena_create(mps_arena_t *arena_o, + mps_arena_class_t mps_arena_class_vm(), + size_t arena_size) + + +.. c:function:: mps_res_t mps_arena_create_v(mps_arena_t *arena_o, mps_arena_class_t arena_class, va_list args) + + .. deprecated:: + + Use :c:func:`mps_arena_create_k` instead. + + An alternative to :c:func:`mps_arena_create_k` that takes its + extra arguments using the standard :term:`C` ``va_list`` + mechanism. See :c:func:`mps_arena_create` for details of which + arguments to pass for the different arena classes. + + +.. c:function:: mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, mps_pool_class_t pool_class, ...) + + .. deprecated:: + + Use :c:func:`mps_pool_create_k` instead. + + An alternative to :c:func:`mps_pool_create_k` that takes its + extra arguments using the standard :term:`C` variable argument + list mechanism. + + When creating a pool of class :c:func:`mps_class_amc` or + :c:func:`mps_class_amcz`, pass the values for the keyword + arguments :c:macro:`MPS_KEY_FORMAT` and :c:macro:`MPS_KEY_CHAIN` + like this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_amc(), + mps_fmt_t format, + mps_chain_t chain) + + When creating a pool of class :c:func:`mps_class_ams`, pass the + values for the keyword arguments :c:macro:`MPS_KEY_FORMAT`, + :c:macro:`MPS_KEY_CHAIN` and ambiguous flag + :c:macro:`MPS_KEY_AMS_SUPPORT_AMBIGUOUS` like this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_ams(), + mps_fmt_t format, + mps_chain_t chain, + mps_bool_t ams_support_ambiguous) + + When creating a pool of class :c:func:`mps_class_ams_debug`, pass + the values for the keyword arguments + :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS`, :c:macro:`MPS_KEY_FORMAT`, + :c:macro:`MPS_KEY_CHAIN` and + :c:macro:`MPS_KEY_AMS_SUPPORT_AMBIGUOUS` like this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_ams_debug(), + mps_pool_debug_option_s *pool_debug_options, + mps_fmt_t format, + mps_chain_t chain, + mps_bool_t ams_support_ambiguous) + + When creating a pool of class :c:func:`mps_class_awl`, pass the + values for the keyword arguments :c:macro:`MPS_KEY_FORMAT` and + :c:macro:`MPS_KEY_AWL_FIND_DEPENDENT` like this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_awl(), + mps_fmt_t format, + mps_awl_find_dependent_t awl_find_dependent) + + When creating a pool of class :c:func:`mps_class_lo`, pass the + value for the keyword argument :c:macro:`MPS_KEY_FORMAT` like + this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_lo(), + mps_fmt_t format) + + When creating a pool of class :c:func:`mps_class_mfs`, pass the + values for the keyword arguments :c:macro:`MPS_KEY_EXTEND_BY` and + :c:macro:`MPS_KEY_MFS_UNIT_SIZE` like this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_mfs(), + size_t extend_by, + size_t unit_size) + + When creating a pool of class :c:func:`mps_class_mv`, pass the + values for the keyword arguments :c:macro:`MPS_KEY_EXTEND_BY`, + :c:macro:`MPS_KEY_MEAN_SIZE`, and :c:macro:`MPS_KEY_MAX_SIZE` like + this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_mv(), + size_t extend_by, + size_t mean_size, + size_t max_size) + + When creating a pool of class :c:func:`mps_class_mv_debug`, pass + the values for the keyword arguments + :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS`, + :c:macro:`MPS_KEY_EXTEND_BY`, :c:macro:`MPS_KEY_MEAN_SIZE` and + :c:macro:`MPS_KEY_MAX_SIZE` like this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_mv_debug(), + mps_pool_debug_option_s *pool_debug_options, + size_t extend_by, + size_t mean_size, + size_t max_size) + + When creating a pool of class :c:func:`mps_class_mvff`, pass the + values for the keyword arguments :c:macro:`MPS_KEY_EXTEND_BY`, + :c:macro:`MPS_KEY_MEAN_SIZE`, :c:macro:`MPS_KEY_ALIGN`, + :c:macro:`MPS_KEY_MVFF_SLOT_HIGH`, + :c:macro:`MPS_KEY_MVFF_ARENA_HIGH` and + :c:macro:`MPS_KEY_MVFF_FIRST_FIT` like this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_mvff(), + size_t extend_by, + size_t mean_size, + mps_align_t align, + mps_bool_t mvff_slot_high, + mps_bool_t mvff_arena_high, + mps_bool_t mvff_first_fit) + + When creating a pool of class :c:func:`mps_class_mvff_debug`, pass + the values for the keyword arguments + :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS`, + :c:macro:`MPS_KEY_EXTEND_BY`, :c:macro:`MPS_KEY_MEAN_SIZE`, + :c:macro:`MPS_KEY_ALIGN`, :c:macro:`MPS_KEY_MVFF_SLOT_HIGH`, + :c:macro:`MPS_KEY_MVFF_ARENA_HIGH`, and + :c:macro:`MPS_KEY_MVFF_FIRST_FIT` like this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_mvff_debug(), + mps_pool_debug_option_s *pool_debug_options, + size_t extend_by, + size_t mean_size, + mps_align_t align, + mps_bool_t mvff_slot_high, + mps_bool_t mvff_arena_high, + mps_bool_t mvff_first_fit) + + When creating a pool of class :c:func:`mps_class_mvt`, pass the + values for the keyword arguments :c:macro:`MPS_KEY_MIN_SIZE`, + :c:macro:`MPS_KEY_MEAN_SIZE`, :c:macro:`MPS_KEY_MAX_SIZE`, + :c:macro:`MPS_KEY_MVT_RESERVE_DEPTH` and + :c:macro:`MPS_KEY_MVT_FRAG_LIMIT` like this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_mvt(), + size_t min_size, + size_t mean_size, + size_t max_size, + mps_word_t mvt_reserve_depth, + mps_word_t mvt_frag_limit) + + .. note:: + + The ``mvt_frag_limit`` is a percentage from 0 to 100 + inclusive when passed to :c:func:`mps_pool_create`, not a + double from 0.0 to 1.0 as in :c:func:`mps_pool_create_k`. + + When creating a pool of class :c:func:`mps_class_snc`, pass the + value for the keyword argument :c:macro:`MPS_KEY_FORMAT` like + this:: + + mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, + mps_pool_class_t mps_class_snc(), + mps_fmt_t format) + + +.. c:function:: mps_res_t mps_pool_create_v(mps_pool_t *pool_o, mps_arena_t arena, mps_pool_class_t pool_class, va_list args) + + .. deprecated:: + + Use :c:func:`mps_pool_create_k` instead. + + An alternative to :c:func:`mps_pool_create_k` that takes its extra + arguments using the standard :term:`C` ``va_list`` mechanism. See + :c:func:`mps_pool_create` for details of which arguments to pass + for the different pool classes. + + +.. c:function:: mps_res_t mps_ap_create(mps_ap_t *ap_o, mps_pool_t pool, ...) + + .. deprecated:: + + Use :c:func:`mps_ap_create_k` instead. + + An alternative to :c:func:`mps_ap_create_k` that takes its extra + arguments using the standard :term:`C` variable argument list + mechanism. + + When creating an allocation point on a pool of class + :c:func:`mps_class_ams`, :c:func:`mps_class_ams_debug`, + :c:func:`mps_class_awl` or :c:func:`mps_class_snc`, pass the + keyword argument :c:macro:`MPS_KEY_RANK` like this:: + + mps_res_t mps_ap_create(mps_ap_t *ap_o, mps_pool_t pool, + mps_rank_t rank) + + +.. c:function:: mps_res_t mps_ap_create_v(mps_ap_t *ap_o, mps_pool_t pool, va_list args) + + .. deprecated:: + + Use :c:func:`mps_ap_create_k` instead. + + An alternative to :c:func:`mps_ap_create_k` that takes its extra + arguments using the standard :term:`C` ``va_list`` mechanism. See + :c:func:`mps_ap_create` for details of which arguments to pass + for the different pool classes. + + +.. c:type:: mps_fmt_A_s + + .. deprecated:: + + Use :c:func:`mps_fmt_create_k` instead. + + The type of the structure used to create an :term:`object format` + of variant A. :: + + typedef struct mps_fmt_A_s { + mps_align_t align; + mps_fmt_scan_t scan; + mps_fmt_skip_t skip; + mps_fmt_copy_t copy; + mps_fmt_fwd_t fwd; + mps_fmt_isfwd_t isfwd; + mps_fmt_pad_t pad; + } mps_fmt_A_s; + + The fields of this structure correspond to the keyword arguments + to :c:func:`mps_fmt_create_k`, except for ``copy``, which is not + used. In older versions of the MPS this was a *copy method* + that copied objects belonging to this format. + + +.. c:function:: mps_res_t mps_fmt_create_A(mps_fmt_t *fmt_o, mps_arena_t arena, mps_fmt_A_s *fmt_A) + + .. deprecated:: + + Use :c:func:`mps_fmt_create_k` instead. + + Create an :term:`object format` based on a description of an + object format of variant A. + + +.. c:type:: mps_fmt_B_s + + .. deprecated:: + + Use :c:func:`mps_fmt_create_k` instead. + + The type of the structure used to create an :term:`object format` + of variant B. :: + + typedef struct mps_fmt_B_s { + mps_align_t align; + mps_fmt_scan_t scan; + mps_fmt_skip_t skip; + mps_fmt_copy_t copy; + mps_fmt_fwd_t fwd; + mps_fmt_isfwd_t isfwd; + mps_fmt_pad_t pad; + mps_fmt_class_t mps_class; + } mps_fmt_B_s; + + Variant B is the same as variant A except for the addition of the + ``mps_class`` method. See :c:type:`mps_fmt_A_s`. + + +.. c:function:: mps_res_t mps_fmt_create_B(mps_fmt_t *fmt_o, mps_arena_t arena, mps_fmt_B_s *fmt_B) + + .. deprecated:: + + Use :c:func:`mps_fmt_create_k` instead. + + Create an :term:`object format` based on a description of an + object format of variant B. + + +.. c:type:: mps_fmt_auto_header_s + + .. deprecated:: + + Use :c:func:`mps_fmt_create_k` instead. + + The type of the structure used to create an :term:`object format` + of variant auto-header. :: + + typedef struct mps_fmt_auto_header_s { + mps_align_t align; + mps_fmt_scan_t scan; + mps_fmt_skip_t skip; + mps_fmt_fwd_t fwd; + mps_fmt_isfwd_t isfwd; + mps_fmt_pad_t pad; + size_t mps_headerSize; + } mps_fmt_auto_header_s; + + Variant auto-header is the same as variant A except for the + removal of the unused ``copy`` method, and the addition of the + ``mps_headerSize`` field. See :c:type:`mps_fmt_A_s`. + + +.. c:function:: mps_res_t mps_fmt_create_auto_header(mps_fmt_t *fmt_o, mps_arena_t arena, mps_fmt_auto_header_s *fmt_ah) + + .. deprecated:: + + Use :c:func:`mps_fmt_create_k` instead. + + Create an :term:`object format` based on a description of an + object format of variant auto-header. + + +.. c:type:: mps_fmt_fixed_s + + .. deprecated:: + + Use :c:func:`mps_fmt_create_k` instead. + + The type of the structure used to create an :term:`object format` + of variant fixed. :: + + typedef struct mps_fmt_fixed_s { + mps_align_t align; + mps_fmt_scan_t scan; + mps_fmt_fwd_t fwd; + mps_fmt_isfwd_t isfwd; + mps_fmt_pad_t pad; + } mps_fmt_fixed_s; + + Variant fixed is the same as variant A except for the removal of + the unused ``copy`` method, and the lack of a ``skip`` method + (this is not needed because the objects are fixed in size). See + :c:type:`mps_fmt_A_s`. + + +.. c:function:: mps_res_t mps_fmt_create_fixed(mps_fmt_t *fmt_o, mps_arena_t arena, mps_fmt_fixed_s *fmt_fixed) + + .. deprecated:: + + Use :c:func:`mps_fmt_create_k` instead. + + Create an :term:`object format` based on a description of an + object format of variant fixed. + + +.. index:: + single: deprecated interfaces; in version 1.111 + +Deprecated in version 1.111 +........................... + +.. c:function:: mps_res_t mps_fix(mps_ss_t ss, mps_addr_t *ref_io) + + .. deprecated:: + + Use :c:func:`MPS_FIX1` and :c:func:`MPS_FIX2` instead. + + :term:`Fix` a :term:`reference`. + + This is a function equivalent to:: + + MPS_SCAN_BEGIN(ss); + res = MPS_FIX12(ss, ref_io); + MPS_SCAN_END(ss); + return res; + + Because :term:`scanning ` is an operation on the + :term:`critical path`, we recommend that you use + :c:func:`MPS_FIX12` (or :c:func:`MPS_FIX1` and :c:func:`MPS_FIX2`) + to ensure that the "stage 1 fix" is inlined. + + .. note:: + + If you call this between :c:func:`MPS_SCAN_BEGIN` and + :c:func:`MPS_SCAN_END`, you must use :c:func:`MPS_FIX_CALL` to + ensure that the scan state is passed correctly. + + +.. c:function:: mps_word_t mps_telemetry_control(mps_word_t reset_mask, mps_word_t flip_mask) + + .. deprecated:: + + Use :c:func:`mps_telemetry_get`, + :c:func:`mps_telemetry_reset`, and :c:func:`mps_telemetry_set` + instead. + + Update and return the :term:`telemetry filter`. + + ``reset_mask`` is a :term:`bitmask` indicating the bits in the + telemetry filter that should be reset. + + ``flip_mask`` is a bitmask indicating the bits in the telemetry + filter whose value should be flipped after the resetting. + + Returns the previous value of the telemetry filter, prior to the + reset and the flip. + + The parameters ``reset_mask`` and ``flip_mask`` allow the + specification of any binary operation on the filter control. For + typical operations, the parameters should be set as follows: + + ============ ============== ============= + Operation ``reset_mask`` ``flip_mask`` + ============ ============== ============= + ``set(M)`` ``M`` ``M`` + ------------ -------------- ------------- + ``reset(M)`` ``M`` ``0`` + ------------ -------------- ------------- + ``flip(M)`` ``0`` ``M`` + ------------ -------------- ------------- + ``read()`` ``0`` ``0`` + ============ ============== ============= + + +.. c:function:: void mps_tramp(void **r_o, mps_tramp_t f, void *p, size_t s) + + .. deprecated:: + + The MPS trampoline is no longer required on any operating + system supported by the MPS. + + Call a function via the MPS trampoline. + + ``r_o`` points to a location that will store the result of calling + ``f``. + + ``f`` is the function to call. + + ``p`` and ``s`` are arguments that will be passed to ``f`` each + time it is called. This is intended to make it easy to pass, for + example, an array and its size as parameters. + + The MPS relies on :term:`barriers (1)` to protect memory + that is in an inconsistent state. On some operating systems, + barrier hits generate exceptions that have to be caught by a + handler that is on the stack. On these operating systems, any code + that uses memory managed by the MPS must be called from inside + such an exception handler, that is, inside a call to + :c:func:`mps_tramp`. + + If you have multiple threads that run code that uses memory + managed by the MPS, each thread must execute such code inside a + call to :c:func:`mps_tramp`. + + +.. index:: + single: trampoline + +.. c:type:: void *(*mps_tramp_t)(void *p, size_t s) + + .. deprecated:: + + The MPS trampoline is no longer required on any operating + system supported by the MPS. + + The type of a function called by :c:func:`mps_tramp`. + + ``p`` and ``s`` are the corresponding arguments that were passed + to :c:func:`mps_tramp`. + + +.. c:function:: void mps_arena_expose(mps_arena_t arena) + + .. deprecated:: + + If you need access to protected memory for debugging, + :ref:`contact us `. + + Ensure that the MPS is not protecting any :term:`page` in the + :term:`arena` with a :term:`read barrier` or :term:`write + barrier`. + + ``arena`` is the arena to expose. + + This is expected to only be useful for debugging. The arena is + left in the :term:`clamped state`. + + Since barriers are used during a collection, calling this function + has the same effect as calling :c:func:`mps_arena_park`: all + collections are run to completion, and the arena is clamped so + that no new collections begin. The MPS also uses barriers to + maintain :term:`remembered sets`, so calling this + function will effectively destroy the remembered sets and any + optimization gains from them. + + Calling this function is time-consuming: any active collections + will be run to completion; and the next collection will have to + recompute all the remembered sets by scanning the entire arena. + + The recomputation of the remembered sets can be avoided by calling + :c:func:`mps_arena_unsafe_expose_remember_protection` instead of + :c:func:`mps_arena_expose`, and by calling + :c:func:`mps_arena_unsafe_restore_protection` before calling + :c:func:`mps_arena_release`. Those functions have unsafe aspects + and place restrictions on what the :term:`client program` can do + (basically no exposed data can be changed). + + +.. c:function:: void mps_arena_unsafe_expose_remember_protection(mps_arena_t arena) + + .. deprecated:: + + If you need access to protected memory for debugging, + :ref:`contact us `. + + Ensure that the MPS is not protecting any :term:`page` in the + :term:`arena` with a :term:`read barrier` or :term:`write + barrier`. In addition, request the MPS to remember some parts of its + internal state so that they can be restored later. + + ``arena`` is the arena to expose. + + This function is the same as :c:func:`mps_arena_expose`, but + additionally causes the MPS to remember its protection state. The + remembered protection state can optionally be restored later by + calling the :c:func:`mps_arena_unsafe_restore_protection` function. + This is an optimization that avoids the MPS having to recompute + all the remembered sets by scanning the entire arena. + + However, restoring the remembered protections is only safe if the + contents of the exposed pages have not been changed; therefore + this function should only be used if you do not intend to change + the pages, and the remembered protection must only be restored if + the pages have not been changed. + + The MPS will only remember the protection state if resources + (memory) are available. If memory is low then only some or + possibly none of the protection state will be remembered, with a + corresponding necessity to recompute it later. The MPS provides no + mechanism for the :term:`client program` to determine whether the + MPS has in fact remembered the protection state. + + The remembered protection state, if any, is discarded after + calling :c:func:`mps_arena_unsafe_restore_protection`, or as soon + as the arena leaves the :term:`clamped state` by calling + :c:func:`mps_arena_release`. + + +.. c:function:: void mps_arena_unsafe_restore_protection(mps_arena_t arena) + + .. deprecated:: + + If you need access to protected memory for debugging, + :ref:`contact us `. + + Restore the remembered protection state for an :term:`arena`. + + ``arena`` is the arena to restore the protection state for. + + This function restores the protection state that the MPS has + remembered when the :term:`client program` called + :c:func:`mps_arena_unsafe_expose_remember_protection`. The purpose + of remembering and restoring the protection state is to avoid the + need for the MPS to recompute all the :term:`remembered sets` by + scanning the entire arena, that occurs when + :c:func:`mps_arena_expose` is used, and which causes the next + :term:`garbage collection` to be slow. + + The client program must not change the exposed data between the + call to :c:func:`mps_arena_unsafe_expose_remember_protection` and + :c:func:`mps_arena_unsafe_restore_protection`. If the client + program has changed the exposed data then + :c:func:`mps_arena_unsafe_restore_protection` must not be called: + in this case simply call :c:func:`mps_arena_release`. + + Calling this function does not release the arena from the clamped + state: :c:func:`mps_arena_release` must be called to continue + normal collections. + + Calling this function causes the MPS to forget the remembered + protection state; as a consequence the same remembered state + cannot be restored more than once. + diff --git a/manual/source/topic/error.rst b/manual/source/topic/error.rst index 97ec2f7321..7ae2900fed 100644 --- a/manual/source/topic/error.rst +++ b/manual/source/topic/error.rst @@ -8,12 +8,18 @@ Error handing ============= Operations in the Memory Pool System that might fail return a -:term:`result code` of type :c:type:`mps_res_t`, rather than a -"special value" of the return type. +:term:`result code` of type :c:type:`mps_res_t`. Success is always +indicated by the result code :c:macro:`MPS_RES_OK`, which is defined +to be zero. Other result codes indicate failure, and are non-zero. The +MPS never uses a "special value" of some other type to indicate +failure (such as returning ``NULL`` for a pointer result, or −1 for a +size result). -Success is always indicated by the result code :c:macro:`MPS_RES_OK`, -which is defined to be zero. Other result codes indicate failure, and -are non-zero. +.. note:: + + The MPS does not throw or catch exceptions. (This is necessary + for the MPS to be portable to systems that have only a + :term:`freestanding` implementation of the C language.) The modular nature of the MPS means that it is not usually possible for a function description to list the possible error codes that it @@ -70,31 +76,28 @@ Result codes A :term:`result code` indicating that an operation could not be completed as requested without exceeding the :term:`commit limit`. - You need to deallocate something to make more space, or increase + You need to deallocate something or allow the :term:`garbage + collector` to reclaim something to make more space, or increase the commit limit by calling :c:func:`mps_arena_commit_limit_set`. .. c:macro:: MPS_RES_FAIL A :term:`result code` indicating that something went wrong that - does not fall under the description of any other result code. The - exact meaning depends on the function that returned this result - code. + does not fall under the description of any other result code. .. c:macro:: MPS_RES_IO A :term:`result code` indicating that an input/output error - occurred. The exact meaning depends on the function that returned - this result code. + occurred in the :term:`telemetry` system. .. c:macro:: MPS_RES_LIMIT A :term:`result code` indicating that an operation could not be completed as requested because of an internal limitation of the - MPS. The exact meaning depends on the function that returned this - result code. + MPS. .. c:macro:: MPS_RES_MEMORY @@ -103,7 +106,7 @@ Result codes completed because there wasn't enough memory available. You need to deallocate something or allow the :term:`garbage - collector` to reclaim something to free enough memory, or expand + collector` to reclaim something to free enough memory, or extend the :term:`arena` (if you're using an arena for which that does not happen automatically). @@ -134,27 +137,23 @@ Result codes A :term:`result code` indicating that an operation could not be completed as requested because an invalid parameter was passed to - the operation. The exact meaning depends on the function that - returned this result code. + the operation. .. c:macro:: MPS_RES_RESOURCE A :term:`result code` indicating that an operation could not be completed as requested because the MPS could not obtain a needed - resource. The resource in question depends on the operation. + resource. It can be returned when the MPS runs out of + :term:`address space`. If this happens, you need to reclaim memory + within your process (as for the result code + :c:macro:`MPS_RES_MEMORY`). Two special cases have their own result codes: when the MPS runs out of committed memory, it returns :c:macro:`MPS_RES_MEMORY`, and when it cannot proceed without exceeding the :term:`commit limit`, it returns :c:macro:`MPS_RES_COMMIT_LIMIT`. - This result code can be returned when the MPS runs out of - :term:`virtual memory`. If this happens, you need to reclaim - memory within your process (as for the result code - :c:macro:`MPS_RES_MEMORY`), or terminate other processes running - on the same machine. - .. c:macro:: MPS_RES_UNIMPL @@ -232,12 +231,18 @@ assertion that is listed here but for which you discovered a different cause), please :ref:`let us know ` so that we can improve this documentation. -``arenavm.c: BTIsResRange(vmChunk->pageTableMapped, 0, chunk->pageTablePages)`` +``arg.c: MPS_KEY_...`` - The client program called :c:func:`mps_arena_destroy` without - having destroyed all pools in that arena first. (The assertion is - from the virtual memory manager which is checking that all pages - have been unmapped.) + A required :term:`keyword argument` was omitted from a call to + :c:func:`mps_ap_create_k`, :c:func:`mps_arena_create_k`, + :c:func:`mps_fmt_create_k`, or :c:func:`mps_pool_create_k`. + + +``buffer.c: BufferIsReady(buffer)`` + + The client program called :c:func:`mps_reserve` twice on the same + :term:`allocation point` without calling :c:func:`mps_commit`. See + :ref:`topic-allocation-point-protocol`. ``dbgpool.c: fencepost check on free`` @@ -260,13 +265,63 @@ this documentation. :c:type:`mps_fmt_t` for this argument. +``global.c: RingIsSingle(&arena->chainRing)`` + + The client program called :c:func:`mps_arena_destroy` without + destroying all the :term:`generation chains` belonging to the + arena. It is necessary to call :c:func:`mps_chain_destroy` first. + + +``global.c: RingIsSingle(&arena->formatRing)`` + + The client program called :c:func:`mps_arena_destroy` without + destroying all the :term:`object formats` belonging to the arena. + It is necessary to call :c:func:`mps_fmt_destroy` first. + + +``global.c: RingIsSingle(&arena->rootRing)`` + + The client program called :c:func:`mps_arena_destroy` without + destroying all the :term:`roots` belonging to the arena. + It is necessary to call :c:func:`mps_root_destroy` first. + + +``global.c: RingIsSingle(&arena->threadRing)`` + + The client program called :c:func:`mps_arena_destroy` without + deregistering all the :term:`threads` belonging to the arena. + It is necessary to call :c:func:`mps_thread_dereg` first. + + +``global.c: RingLength(&arenaGlobals->poolRing) == 5`` + + The client program called :c:func:`mps_arena_destroy` without + destroying all the :term:`pools` belonging to the arena. + It is necessary to call :c:func:`mps_pool_destroy` first. + + +``global.c: PoolHasAttr(pool, AttrGC)`` + + The client program called :c:func:`mps_finalize` on a reference + that does not belong to an :term:`automatically managed ` :term:`pool`. + + ``lockix.c: res == 0`` ``lockw3.c: lock->claims == 0`` The client program has made a re-entrant call into the MPS. Look - at the backtrace to see what it was. Common culprits are - :term:`format methods` and :term:`stepper functions`. + at the backtrace to see what it was. Common culprits are signal + handlers, assertion handlers, :term:`format methods`, and + :term:`stepper functions`. + + +``locus.c: chain->activeTraces == TraceSetEMPTY`` + + The client program called :c:func:`mps_chain_destroy`, but there + was a garbage collection in progress on that chain. Park the arena + before destroying the chain, by calling :c:func:`mps_arena_park`. ``mpsi.c: SizeIsAligned(size, BufferPool(buf)->alignment)`` @@ -276,21 +331,6 @@ this documentation. alignment required by the pool's :term:`object format`. -``pool.c: (pool->class->attr & AttrALLOC) != 0`` - - The client program called :c:func:`mps_alloc` on a pool that does - not support this form of allocation. Use an :term:`allocation - point` instead. - - -``poolams.c: !AMS_IS_INVALID_COLOUR(seg, i)`` - - The client program failed to :term:`fix` a reference to an object - in an :ref:`pool-ams` pool, violating the :term:`tri-colour - invariant` that the MPS depends on for the correctness of its - :term:`incremental garbage collection`. - - ``poolams.c: AMS_ALLOCED(seg, i)`` The client program tried to :term:`fix` a :term:`reference` to a @@ -301,18 +341,55 @@ this documentation. condition? +``poolsnc.c: foundSeg`` + + The client program passed an incorrect ``frame`` argument to + :c:func:`mps_ap_frame_pop`. This argument must be the result from + a previous call to :c:func:`mps_ap_frame_push` on the same + allocation point. + + +``seg.c: gcseg->buffer == NULL`` + + The client program destroyed pool without first destroying all the + allocation points created on that pool. The allocation points must + be destroyed first. + + +``trace.c: ss->rank < RankEXACT`` + + The client program destroyed a pool containing objects registered + for finalization, and then continued to run the garbage collector. + See :ref:`topic-finalization-cautions` under + :ref:`topic-finalization`, which says, "You must destroy these + pools by following the ‘safe tear-down’ procedure described under + :c:func:`mps_pool_destroy`." + + +``trace.c: RefSetSub(ScanStateUnfixedSummary(ss), SegSummary(seg))`` + + The client program's :term:`scan method` failed to update a + reference to an object that moved. See + :ref:`topic-scanning-protocol`, which says, "If :c:func:`MPS_FIX2` + returns :c:macro:`MPS_RES_OK`, it may have updated the reference. + Make sure that the updated reference is stored back to the region + being scanned." + + .. index:: single: error handling; varieties single: variety +.. _topic-error-variety: + Varieties --------- -The MPS has three behaviours with respect to internal checking and -:ref:`telemetry `, which need to be selected at -compile time, by defining one of the following preprocessor -constants. If none is specified then :c:macro:`CONFIG_VAR_HOT` is the -default. +The MPS has three *varieties* which have different levels of internal +checking and :ref:`telemetry `. The variety can be +selected at compile time, by defining one of the following +preprocessor constants. If none is specified then +:c:macro:`CONFIG_VAR_HOT` is the default. .. index:: @@ -321,7 +398,7 @@ default. .. c:macro:: CONFIG_VAR_COOL - The cool variety is intended for development and testing. + The *cool variety* is intended for development and testing. All functions check the consistency of their data structures and may assert, including functions on the :term:`critical path`. @@ -339,7 +416,7 @@ default. .. c:macro:: CONFIG_VAR_HOT - The hot variety is intended for production and deployment. + The *hot variety* is intended for production and deployment. Some functions check the consistency of their data structures and may assert, namely those not on the :term:`critical path`. However, @@ -356,7 +433,7 @@ default. .. c:macro:: CONFIG_VAR_RASH - The rash variety is intended for mature integrations, or for + The *rash variety* is intended for mature integrations, or for developers who like living dangerously. No functions check the consistency of their data structures and diff --git a/manual/source/topic/finalization.rst b/manual/source/topic/finalization.rst index 0f1d1d908e..7edadb9e3c 100644 --- a/manual/source/topic/finalization.rst +++ b/manual/source/topic/finalization.rst @@ -51,7 +51,7 @@ the block was allocated. to do the finalization. In such an implementation, the client program's finalization code may end up running concurrently with other code that accesses the underlying resource, and so access to - the resource need to be guarded with a lock, but then an unlucky + the resource needs to be guarded with a lock, but then an unlucky scheduling of finalization can result in deadlock. See :ref:`Boehm (2002) ` for a detailed discussion of this issue. @@ -85,6 +85,15 @@ calling :c:func:`mps_message_discard`. will cause it to be finalized again should all strong references disappear again. +.. note:: + + Calling :c:func:`mps_message_discard` does not reclaim the space + occupied by the finalized block (that happens at the next + collection, if the block is found to be dead at that point), and + so the block must remain validly formatted (:term:`scannable `, :term:`skippable `, and so on). It might + make sense to replace it with a :term:`padding object`. + See :ref:`topic-message` for details of the message mechanism. @@ -123,12 +132,12 @@ Cautions finalization. The MPS does not finalize a block until it determines that the block is finalizable, which may require a full garbage collection in the worst case, and such a collection may - not :ref:`scheduled ` for some time. Or - the block may never become finalizable because it is incorrectly - determined to be reachable due to an :term:`ambiguous reference` - pointing to it. Or the block may never become finalizable because - it remains reachable through a reference, even if that reference - might never be used. + not be :ref:`scheduled ` for some time. + Or the block may never become finalizable because it is + incorrectly determined to be reachable due to an :term:`ambiguous + reference` pointing to it. Or the block may never become + finalizable because it remains reachable through a reference, even + if that reference might never be used. #. Even when blocks are finalized in a reasonably timely fashion, the client needs to process the finalization messages in time to avoid @@ -157,12 +166,13 @@ Cautions prompt release of scarce resources. For example, Scheme provides the ``(with-input-from-file)`` procedure which specifies that the created port has :term:`dynamic extent` (and so can be closed as - soon as the procedure exits). + soon as the procedure exits, even if it is still reachable). #. The MPS does not finalize objects in the context of :c:func:`mps_arena_destroy` or :c:func:`mps_pool_destroy`. - :c:func:`mps_pool_destroy` should therefore not be invoked on pools - containing objects registered for finalization. + Moreover, if you have pools containing objects registered for + finalization, you must destroy these pools by following the “safe + tear-down” procedure described under :c:func:`mps_pool_destroy`. .. note:: @@ -180,13 +190,8 @@ Cautions .. note:: - You can safely destroy pools containing objects registered for - finalization if you follow the "safe tear-down" procedure - described under :c:func:`mps_pool_destroy`, but the objects do - not get finalized. - - The only reliable way to ensure that all finalizable object - gets finalized is to maintain a table of :term:`weak + The only reliable way to ensure that all finalizable objects + are finalized is to maintain a table of :term:`weak references (1)` to all such objects. The weak references don't prevent the objects from being finalized, but you can iterate over the list at an appropriate point and finalize any @@ -216,10 +221,8 @@ Finalization interface :term:`result code` if not. This function registers the block pointed to by ``*ref_p`` for - finalization. This block must have been allocated from a - :term:`pool` in ``arena``. Violations of this constraint may not - be checked by the MPS, and may be unsafe, causing the MPS to crash - in undefined ways. + finalization. This block must have been allocated from an + automatically managed :term:`pool` in ``arena``. .. note:: @@ -247,6 +250,13 @@ Finalization interface avoid placing the restriction on the :term:`client program` that the C call stack be a :term:`root`. + .. warning:: + + Definalization is not yet efficient: the current + implementation just loops over all finalized objects. If you + need efficient definalization, please :ref:`contact us + `. + .. index:: pair: finalization; message diff --git a/manual/source/topic/format.rst b/manual/source/topic/format.rst index 91ac7ae646..7db5c8a117 100644 --- a/manual/source/topic/format.rst +++ b/manual/source/topic/format.rst @@ -179,6 +179,20 @@ There are some cautions to be observed when using in-band headers: #. Not all :term:`pool classes` support objects with in-band headers. See the documentation for the pool class. +.. note:: + + A :term:`client program` that allocates objects with + :term:`in-band headers` has to make a choice about how to + represent references to those objects. It can represent them using + :term:`base pointers` (which is convenient for allocation, since + :c:func:`mps_reserve` returns a base pointer, but requires + decoding when scanning) or using :term:`client pointers` (which is + convenient for scanning, since the :term:`scan method` takes a + client pointer, but requires encoding on allocation). Either + approach will work, but :term:`client pointers` are normally the + better choice, since scanning is normally more + performance-critical than allocation. + .. index:: pair: object format; cautions @@ -218,6 +232,13 @@ Cautions #. Format methods must be re-entrant. +#. Format methods must use no more than 64 words of stack space. + + This restriction is necessary to avoid stack overflow in the MPS; + see :ref:`design-sp` for details. If your application has format + methods that need more stack space than this, :ref:`contact us + `. + #. Format methods must not: a. call library code; @@ -379,6 +400,11 @@ Format methods indicate references within the objects by calling :c:func:`MPS_FIX1` and :c:func:`MPS_FIX2`. + If the object format is capable of creating forwarding objects or + padding objects, the scan method must be able to scan these + objects. (In the case of the forwarding object, the scan method + should not fix the pointer to the new location.) + .. seealso:: :ref:`topic-scanning`. @@ -401,6 +427,10 @@ Format methods In either case, the result is the sum of ``addr`` and the size of the block containing the object. + If the object format is capable of creating forwarding objects or + padding objects, the skip method must be able to skip these + objects. + A skip method is not allowed to fail. .. note:: @@ -468,22 +498,27 @@ Object format introspection Each :term:`pool class` determines for which objects the stepper function is called. Typically, all validly formatted objects are - visited. During a :term:`trace` this will in general be only the - :term:`black` objects, though the :ref:`pool-lo` pool, for - example, will walk all objects since they are validly formatted - whether they are black or :term:`white`. :term:`Padding objects` - may be visited at the pool class's discretion: the :term:`client - program` should handle this case. - - .. seealso:: - - :ref:`topic-arena`. + visited. :term:`Padding objects` may be visited at the pool + class's discretion: the stepper function must handle this + case. .. note:: This function is intended for heap analysis, tuning, and debugging, not for frequent use in production. + .. warning:: + + If a garbage collection is currently in progress (that is, if + the arena is in the :term:`clamped ` or + :term:`unclamped state`), then only objects that are known to + be currently valid are visited. + + For the most reliable results, ensure the arena is in the + :term:`parked state` by calling :c:func:`mps_arena_park` + before calling this function (and release it by calling + :c:func:`mps_arena_release` afterwards, if desired). + .. c:type:: void (*mps_formatted_objects_stepper_t)(mps_addr_t addr, mps_fmt_t fmt, mps_pool_t pool, void *p, size_t s) @@ -514,141 +549,3 @@ Object format introspection c. memory not managed by the MPS; It must not access other memory managed by the MPS. - - .. seealso:: - - :ref:`topic-arena`. - - -Obsolete interface ------------------- - -.. deprecated:: starting with version 1.112. - - Use :c:func:`mps_ap_create_k` instead: the :term:`keyword - arguments` interface is more flexible and easier to understand. - -Formerly the only way to create object formats was to describe the -format in the form of a *format variant structure*. - -There are four format variants. - -* Variant A (:c:type:`mps_fmt_A_s`): for objects without - :term:`in-band headers`. - -* Variant B (:c:type:`mps_fmt_B_s`): as variant A, but with the - addition of a class method. - -* Variant auto-header (:c:type:`mps_fmt_auto_header_s`): for objects - with :term:`in-band headers`. - -* Variant fixed (:c:type:`mps_fmt_fixed_s`): for fixed-size objects. - -The client program creates an object format by construct a format -variant structure and then calling the appropriate ``mps_fmt_create_`` -function for the variant. The variant structure can then be disposed -of. - - -.. c:type:: mps_fmt_A_s - - The type of the structure used to create an :term:`object format` - of variant A. :: - - typedef struct mps_fmt_A_s { - mps_align_t align; - mps_fmt_scan_t scan; - mps_fmt_skip_t skip; - mps_fmt_copy_t copy; - mps_fmt_fwd_t fwd; - mps_fmt_isfwd_t isfwd; - mps_fmt_pad_t pad; - } mps_fmt_A_s; - - The fields of this structure correspond to the keyword arguments - to :c:func:`mps_fmt_create_k`, except for ``copy``, which is not - used. In older versions of the MPS this was a :term:`copy method` - that copied objects belonging to this format. - - -.. c:function:: mps_res_t mps_fmt_create_A(mps_fmt_t *fmt_o, mps_arena_t arena, mps_fmt_A_s *fmt_A) - - Create an :term:`object format` based on a description of an - object format of variant A. - - -.. c:type:: mps_fmt_B_s - - The type of the structure used to create an :term:`object format` - of variant B. :: - - typedef struct mps_fmt_B_s { - mps_align_t align; - mps_fmt_scan_t scan; - mps_fmt_skip_t skip; - mps_fmt_copy_t copy; - mps_fmt_fwd_t fwd; - mps_fmt_isfwd_t isfwd; - mps_fmt_pad_t pad; - mps_fmt_class_t mps_class; - } mps_fmt_B_s; - - Variant B is the same as variant A except for the addition of the - ``mps_class`` method. See :c:type:`mps_fmt_A_s`. - - -.. c:function:: mps_res_t mps_fmt_create_B(mps_fmt_t *fmt_o, mps_arena_t arena, mps_fmt_B_s *fmt_B) - - Create an :term:`object format` based on a description of an - object format of variant B. - - -.. c:type:: mps_fmt_auto_header_s - - The type of the structure used to create an :term:`object format` - of variant auto-header. :: - - typedef struct mps_fmt_auto_header_s { - mps_align_t align; - mps_fmt_scan_t scan; - mps_fmt_skip_t skip; - mps_fmt_fwd_t fwd; - mps_fmt_isfwd_t isfwd; - mps_fmt_pad_t pad; - size_t mps_headerSize; - } mps_fmt_auto_header_s; - - Variant auto-header is the same as variant A except for the - removal of the unused ``copy`` method, and the addition of the - ``mps_headerSize`` field. See :c:type:`mps_fmt_A_s`. - - -.. c:function:: mps_res_t mps_fmt_create_auto_header(mps_fmt_t *fmt_o, mps_arena_t arena, mps_fmt_auto_header_s *fmt_ah) - - Create an :term:`object format` based on a description of an - object format of variant auto-header. - - -.. c:type:: mps_fmt_fixed_s - - The type of the structure used to create an :term:`object format` - of variant fixed. :: - - typedef struct mps_fmt_fixed_s { - mps_align_t align; - mps_fmt_scan_t scan; - mps_fmt_fwd_t fwd; - mps_fmt_isfwd_t isfwd; - mps_fmt_pad_t pad; - } mps_fmt_fixed_s; - - Variant fixed is the same as variant A except for the removal of - the unused ``copy`` method, and the lack of a ``skip`` method - (this is not needed because the objects are fixed in size). See - :c:type:`mps_fmt_A_s`. - - -.. c:function:: mps_res_t mps_fmt_create_fixed(mps_fmt_t *fmt_o, mps_arena_t arena, mps_fmt_fixed_s *fmt_fixed) - - Create an :term:`object format` based on a description of an - object format of variant fixed. diff --git a/manual/source/topic/index.rst b/manual/source/topic/index.rst index e4e036409e..3f2cb2863b 100644 --- a/manual/source/topic/index.rst +++ b/manual/source/topic/index.rst @@ -28,3 +28,6 @@ Reference weak plinth platform + porting + deprecated + diff --git a/manual/source/topic/interface.rst b/manual/source/topic/interface.rst index eb3d0dcfec..e771518183 100644 --- a/manual/source/topic/interface.rst +++ b/manual/source/topic/interface.rst @@ -35,6 +35,10 @@ Support policy a version in which the symbol (or reliance on some of its behaviour) is deprecated. + Symbols may be deprecated in their old place in the reference + manual, or they may be moved to the :ref:`topic-deprecated` + chapter. + .. note:: If you are relying on a feature and you see that it's @@ -194,7 +198,7 @@ out parameter, like this:: res = mps_alloc((mps_addr_t *)&fp, pool, sizeof(struct foo)); This is known as :term:`type punning`, and its behaviour is not -defined in ANSI/ISO Standard C. See :ref:`ISO/IEC 9899:1990 ` +defined in ANSI/ISO Standard C. See :ref:`ISO/IEC 9899:1990 ` §6.3.2.3, which defines the conversion of a pointer from one type to another: the behaviour of this cast is not covered by any of the cases in the standard. @@ -204,12 +208,13 @@ Instead, we recommend this approach:: mps_addr_t p; struct foo *fp; res = mps_alloc(&p, pool, sizeof(struct foo)); - if(res) /* handle error case */; - fp = (struct foo *)p; + if (res != MPS_RES_OK) + /* handle error case */; + fp = p; -This is portable because conversion from ``void *`` to any other -:term:`object pointer` type is defined by :ref:`ISO/IEC 9899:1990 -` §6.3.2.3.1. +This has defined behaviour because conversion from ``void *`` to any +other :term:`object pointer` type is defined by :ref:`ISO/IEC +9899:1990 ` §6.3.2.3.1. .. index:: @@ -219,14 +224,14 @@ Macros ------ #. For function-like macros, the MPS follows the same convention as - the Standard C library. To quote :ref:`ISO/IEC 9899:1990 ` + the Standard C library. To quote :ref:`ISO/IEC 9899:1990 ` §7.1.7: Any function declared in a header may additionally be implemented as a macro defined in the header, so a library function should not be declared explicitly if its header is included. Any macro definition of a function can be suppressed - locally be enclosing the name of the function in parentheses, + locally by enclosing the name of the function in parentheses, because the name is then not followed by the left parenthesis that indicates expansion of a macro function name. [...] Any invocation of a library function that is implemented as a diff --git a/manual/source/topic/keyword.rst b/manual/source/topic/keyword.rst index 5f804a6384..ddbfbfd57a 100644 --- a/manual/source/topic/keyword.rst +++ b/manual/source/topic/keyword.rst @@ -36,9 +36,8 @@ help with forming keyword argument lists:: } MPS_ARGS_END(args); The argument array must not be ``NULL``, and must end with -:c:macro:`MPS_KEY_ARGS_END`. If you don't want to pass any arguments, you can -either call the equivalent function that does not take keyword arguments -(named without the ``_k``) or pass :c:macro:`mps_args_none`. +:c:macro:`MPS_KEY_ARGS_END`. If you don't want to pass any arguments, +you can pass :c:macro:`mps_args_none`. When a function that takes keyword arguments returns, the keyword argument array has been *modified* to remove any arguments that have @@ -83,40 +82,45 @@ now :c:macro:`MPS_KEY_ARGS_END`. The type of :term:`keyword argument` keys. Must take one of the following values: - ======================================== ====================================================== ========================================================== - Keyword Type & field in ``arg.val`` See - ======================================== ====================================================== ========================================================== - :c:macro:`MPS_KEY_ARGS_END` *none* *see above* - :c:macro:`MPS_KEY_ALIGN` :c:type:`mps_align_t` ``align`` :c:func:`mps_class_mvff`, :c:func:`mps_class_mvt` - :c:macro:`MPS_KEY_AMS_SUPPORT_AMBIGUOUS` :c:type:`mps_bool_t` ``b`` :c:func:`mps_class_ams` - :c:macro:`MPS_KEY_ARENA_CL_BASE` :c:type:`mps_addr_t` ``addr`` :c:func:`mps_arena_class_cl` - :c:macro:`MPS_KEY_ARENA_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_arena_class_vm`, :c:func:`mps_arena_class_cl` - :c:macro:`MPS_KEY_AWL_FIND_DEPENDENT` ``void *(*)(void *)`` ``addr_method`` :c:func:`mps_class_awl` - :c:macro:`MPS_KEY_CHAIN` :c:type:`mps_chain_t` ``chain`` :c:func:`mps_class_amc`, :c:func:`mps_class_amcz`, :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_lo` - :c:macro:`MPS_KEY_EXTEND_BY` :c:type:`size_t` ``size`` :c:func:`mps_class_mfs`, :c:func:`mps_class_mv`, :c:func:`mps_class_mvff` - :c:macro:`MPS_KEY_FMT_ALIGN` :c:type:`mps_align_t` ``align`` :c:func:`mps_fmt_create_k` - :c:macro:`MPS_KEY_FMT_CLASS` :c:type:`mps_fmt_class_t` ``fmt_class`` :c:func:`mps_fmt_create_k` - :c:macro:`MPS_KEY_FMT_FWD` :c:type:`mps_fmt_fwd_t` ``fmt_fwd`` :c:func:`mps_fmt_create_k` - :c:macro:`MPS_KEY_FMT_HEADER_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_fmt_create_k` - :c:macro:`MPS_KEY_FMT_ISFWD` :c:type:`mps_fmt_isfwd_t` ``fmt_isfwd`` :c:func:`mps_fmt_create_k` - :c:macro:`MPS_KEY_FMT_PAD` :c:type:`mps_fmt_pad_t` ``fmt_pad`` :c:func:`mps_fmt_create_k` - :c:macro:`MPS_KEY_FMT_SCAN` :c:type:`mps_fmt_scan_t` ``fmt_scan`` :c:func:`mps_fmt_create_k` - :c:macro:`MPS_KEY_FMT_SKIP` :c:type:`mps_fmt_skip_t` ``fmt_skip`` :c:func:`mps_fmt_create_k` - :c:macro:`MPS_KEY_FORMAT` :c:type:`mps_fmt_t` ``format`` :c:func:`mps_class_amc`, :c:func:`mps_class_amcz`, :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_lo` , :c:func:`mps_class_snc` - :c:macro:`MPS_KEY_GEN` :c:type:`unsigned` ``u`` :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_lo` - :c:macro:`MPS_KEY_MAX_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_class_mv` - :c:macro:`MPS_KEY_MEAN_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_class_mv`, :c:func:`mps_class_mvt`, :c:func:`mps_class_mvff` - :c:macro:`MPS_KEY_MFS_UNIT_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_class_mfs` - :c:macro:`MPS_KEY_MIN_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_class_mvt` - :c:macro:`MPS_KEY_MVFF_ARENA_HIGH` :c:type:`mps_bool_t` ``b`` :c:func:`mps_class_mvff` - :c:macro:`MPS_KEY_MVFF_FIRST_FIT` :c:type:`mps_bool_t` ``b`` :c:func:`mps_class_mvff` - :c:macro:`MPS_KEY_MVFF_SLOT_HIGH` :c:type:`mps_bool_t` ``b`` :c:func:`mps_class_mvff` - :c:macro:`MPS_KEY_MVT_FRAG_LIMIT` :c:type:`mps_count_t` ``count`` :c:func:`mps_class_mvt` - :c:macro:`MPS_KEY_MVT_RESERVE_DEPTH` :c:type:`mps_count_t` ``count`` :c:func:`mps_class_mvt` - :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` ``mps_pool_debug_options_s *`` ``pool_debug_options`` :c:func:`mps_class_ams_debug`, :c:func:`mps_class_mv_debug`, :c:func:`mps_class_mvff_debug` - :c:macro:`MPS_KEY_RANK` :c:type:`mps_rank_t` ``rank`` :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_snc` - :c:macro:`MPS_KEY_VMW3_TOP_DOWN` :c:type:`mps_bool_t` ``b`` :c:func:`mps_arena_class_vm` - ======================================== ====================================================== ========================================================== + ======================================== ========================================================= ========================================================== + Keyword Type & field in ``arg.val`` See + ======================================== ========================================================= ========================================================== + :c:macro:`MPS_KEY_ARGS_END` *none* *see above* + :c:macro:`MPS_KEY_ALIGN` :c:type:`mps_align_t` ``align`` :c:func:`mps_class_mv`, :c:func:`mps_class_mvff`, :c:func:`mps_class_mvt` + :c:macro:`MPS_KEY_AMS_SUPPORT_AMBIGUOUS` :c:type:`mps_bool_t` ``b`` :c:func:`mps_class_ams` + :c:macro:`MPS_KEY_ARENA_CL_BASE` :c:type:`mps_addr_t` ``addr`` :c:func:`mps_arena_class_cl` + :c:macro:`MPS_KEY_ARENA_GRAIN_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_arena_class_vm`, :c:func:`mps_arena_class_cl` + :c:macro:`MPS_KEY_ARENA_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_arena_class_vm`, :c:func:`mps_arena_class_cl` + :c:macro:`MPS_KEY_AWL_FIND_DEPENDENT` ``void *(*)(void *)`` ``addr_method`` :c:func:`mps_class_awl` + :c:macro:`MPS_KEY_CHAIN` :c:type:`mps_chain_t` ``chain`` :c:func:`mps_class_amc`, :c:func:`mps_class_amcz`, :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_lo` + :c:macro:`MPS_KEY_COMMIT_LIMIT` :c:type:`size_t` ``size`` :c:func:`mps_arena_class_vm`, :c:func:`mps_arena_class_cl` + :c:macro:`MPS_KEY_EXTEND_BY` :c:type:`size_t` ``size`` :c:func:`mps_class_amc`, :c:func:`mps_class_amcz`, :c:func:`mps_class_mfs`, :c:func:`mps_class_mv`, :c:func:`mps_class_mvff` + :c:macro:`MPS_KEY_FMT_ALIGN` :c:type:`mps_align_t` ``align`` :c:func:`mps_fmt_create_k` + :c:macro:`MPS_KEY_FMT_CLASS` :c:type:`mps_fmt_class_t` ``fmt_class`` :c:func:`mps_fmt_create_k` + :c:macro:`MPS_KEY_FMT_FWD` :c:type:`mps_fmt_fwd_t` ``fmt_fwd`` :c:func:`mps_fmt_create_k` + :c:macro:`MPS_KEY_FMT_HEADER_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_fmt_create_k` + :c:macro:`MPS_KEY_FMT_ISFWD` :c:type:`mps_fmt_isfwd_t` ``fmt_isfwd`` :c:func:`mps_fmt_create_k` + :c:macro:`MPS_KEY_FMT_PAD` :c:type:`mps_fmt_pad_t` ``fmt_pad`` :c:func:`mps_fmt_create_k` + :c:macro:`MPS_KEY_FMT_SCAN` :c:type:`mps_fmt_scan_t` ``fmt_scan`` :c:func:`mps_fmt_create_k` + :c:macro:`MPS_KEY_FMT_SKIP` :c:type:`mps_fmt_skip_t` ``fmt_skip`` :c:func:`mps_fmt_create_k` + :c:macro:`MPS_KEY_FORMAT` :c:type:`mps_fmt_t` ``format`` :c:func:`mps_class_amc`, :c:func:`mps_class_amcz`, :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_lo` , :c:func:`mps_class_snc` + :c:macro:`MPS_KEY_GEN` :c:type:`unsigned` ``u`` :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_lo` + :c:macro:`MPS_KEY_INTERIOR` :c:type:`mps_bool_t` ``b`` :c:func:`mps_class_amc`, :c:func:`mps_class_amcz` + :c:macro:`MPS_KEY_MAX_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_class_mv` + :c:macro:`MPS_KEY_MEAN_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_class_mv`, :c:func:`mps_class_mvt`, :c:func:`mps_class_mvff` + :c:macro:`MPS_KEY_MFS_UNIT_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_class_mfs` + :c:macro:`MPS_KEY_MIN_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_class_mvt` + :c:macro:`MPS_KEY_MVFF_ARENA_HIGH` :c:type:`mps_bool_t` ``b`` :c:func:`mps_class_mvff` + :c:macro:`MPS_KEY_MVFF_FIRST_FIT` :c:type:`mps_bool_t` ``b`` :c:func:`mps_class_mvff` + :c:macro:`MPS_KEY_MVFF_SLOT_HIGH` :c:type:`mps_bool_t` ``b`` :c:func:`mps_class_mvff` + :c:macro:`MPS_KEY_MVT_FRAG_LIMIT` :c:type:`mps_word_t` ``count`` :c:func:`mps_class_mvt` + :c:macro:`MPS_KEY_MVT_RESERVE_DEPTH` :c:type:`mps_word_t` ``count`` :c:func:`mps_class_mvt` + :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` :c:type:`mps_pool_debug_option_s` ``*pool_debug_options`` :c:func:`mps_class_ams_debug`, :c:func:`mps_class_mv_debug`, :c:func:`mps_class_mvff_debug` + :c:macro:`MPS_KEY_RANK` :c:type:`mps_rank_t` ``rank`` :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_snc` + :c:macro:`MPS_KEY_SPARE` :c:type:`double` ``d`` :c:func:`mps_class_mvff` + :c:macro:`MPS_KEY_SPARE_COMMIT_LIMIT` :c:type:`size_t` ``size`` :c:func:`mps_arena_class_vm` + :c:macro:`MPS_KEY_VMW3_TOP_DOWN` :c:type:`mps_bool_t` ``b`` :c:func:`mps_arena_class_vm` + ======================================== ========================================================= ========================================================== .. c:function:: MPS_ARGS_BEGIN(args) @@ -190,11 +194,3 @@ now :c:macro:`MPS_KEY_ARGS_END`. ``args`` is the name of array that contains the keyword arguments. It must match the argument to the preceding call to :c:func:`MPS_ARGS_BEGIN`. - - -.. c:function:: MPS_ARGS_DONE(args) - - .. deprecated:: starting with version 1.113. - - Formerly this was used to finalize a list of keyword arguments - before passing it to a function. It is no longer needed. diff --git a/manual/source/topic/location.rst b/manual/source/topic/location.rst index bcee6e6e75..fa928756a6 100644 --- a/manual/source/topic/location.rst +++ b/manual/source/topic/location.rst @@ -161,19 +161,22 @@ At this point you must: For example, in the case of a hash table you should rehash based on the new locations of the blocks. +In the toy Scheme interpreter this behaviour is encapsulated into ``table_find``: + .. code-block:: c - :emphasize-lines: 6 + :emphasize-lines: 7 - static obj_t table_ref(obj_t tbl, obj_t key) + static struct bucket_s *table_find(obj_t tbl, obj_t buckets, obj_t key, int add) { - struct bucket_s *b = buckets_find(tbl, tbl->table.buckets, key, NULL); - if (b && b->key != NULL && b->key != obj_deleted) - return b->value; - if (mps_ld_isstale(&tbl->table.ld, arena, key)) { + struct bucket_s *b; + assert(TYPE(tbl) == TYPE_TABLE); + b = buckets_find(tbl, tbl->table.buckets, key, add); + if ((b == NULL || b->key == NULL || b->key == obj_deleted) + && mps_ld_isstale(&tbl->table.ld, arena, key)) + { b = table_rehash(tbl, tbl->table.buckets->buckets.length, key); - if (b) return b->value; } - return NULL; + return b; } After :c:func:`mps_ld_isstale` has returned true, and you've rehashed @@ -190,6 +193,17 @@ back to a non-address-based version of the computation: here, since it might as well find the bucket containing ``key`` at the same time and return it. +.. warning:: + + Don't forget to check for staleness when setting a key in a table. + If the key is stale then it would be a mistake to add it to the + table as then the key will be present twice, at the positions + given by the hash of its old and new addresses, thus violating the + invariant of the hash table (that a key appears at most once). + + Similarly, staleness must be tested when deleting a key from a + table. + .. index:: pair: location dependency; thread safety diff --git a/manual/source/topic/pattern.rst b/manual/source/topic/pattern.rst index 4d798c00b7..f460e019fb 100644 --- a/manual/source/topic/pattern.rst +++ b/manual/source/topic/pattern.rst @@ -110,10 +110,22 @@ cutoff and decline in the live size. This pattern is useful if you are building a structure that involves temporarily allocating much more memory than will fit into your -:term:`nursery generation`. The ramp allocation pattern causes the -collection of the nursery generation to be deferred until the ramp +:term:`nursery generation`. By applying the ramp allocation pattern, +the collection of that generation can be deferred until the ramp allocation is over. +In detail: if the ramp allocation pattern is applied to an +:term:`allocation point`, then allocation on that AP is ignored by the +MPS when it is deciding whether to schedule a collection of the chain +containing the generation into which the AP is allocating. See :ref:`topic-collection-schedule`. + +.. note:: + + This does not prevent the generation from being collected + altogether: there may be other APs allocating into the generation, + or the MPS may have to collect the generation in order to avoid + running out of memory. + .. note:: Ramp allocation is only supported by :ref:`pool-amc`. diff --git a/manual/source/topic/platform.rst b/manual/source/topic/platform.rst index 0fbc4d5989..e74aa34cde 100644 --- a/manual/source/topic/platform.rst +++ b/manual/source/topic/platform.rst @@ -56,6 +56,8 @@ and compiler are supported. .. index:: single: platform; interface +.. _topic-platform-interface: + Platform interface ------------------ @@ -300,6 +302,7 @@ Formerly supported compiler toolchains: ``gp`` GCC with profiling ``MPS_BUILD_GP`` ``lc`` LCC ``MPS_BUILD_LC`` ``mw`` Metrowerks CodeWarrior ``MPS_BUILD_MW`` +``pc`` Pelles C ``MPS_BUILD_PC`` ``sc`` SunPro C ``MPS_BUILD_SC`` ====== ======================================= ================ @@ -348,7 +351,9 @@ Platform Status ``w3almv`` *Not supported* ``w3i3m9`` *Not supported* ``w3i3mv`` Supported +``w3i3pc`` *Not supported* ``w3i6mv`` Supported +``w3i6pc`` *Not supported* ``w3ppmv`` *Not supported* ``xci3gc`` *Not supported* ``xci3ll`` Supported diff --git a/manual/source/topic/plinth.rst b/manual/source/topic/plinth.rst index a7e94d21a0..f771f6c1c3 100644 --- a/manual/source/topic/plinth.rst +++ b/manual/source/topic/plinth.rst @@ -22,8 +22,8 @@ The :dfn:`plinth` is a program module that provides the MPS with the support it needs from the execution environment. The MPS uses the plinth instead of (say) the Standard C Library because: #. The MPS is designed to be portable to systems that have only a - *conforming freestanding implementation* of the C language: that - is, systems which potentially lack some of the facilities of the + :term:`freestanding` implementation of the C language: that is, + systems which potentially lack some of the facilities of the Standard C Library, such as standard I/O. The plinth provides a way to map MPS requirements to the facilities provided on the platform, whatever they are. @@ -39,22 +39,33 @@ facilities is included with the MPS, and this is good enough for most applications. There are many reasons why you might want to write your own plinth. -You may be targeting a *freestanding environment* such as an embedded -system. You might need to write the telemetry stream to a system -logging facility, or transmit it over a serial port or network -connection. Or you might need to direct debugging output to a -convenient window in the user interface. +You may be targeting an embedded system with only a +:term:`freestanding` implementation of the C language. You might need +to write the telemetry stream to a system logging facility, or +transmit it over a serial port or network connection. Or you might +need to direct debugging output to a convenient window in the user +interface. The plinth is divided into two parts: -#. The :ref:`topic-plinth-io` enables the MPS to write binary messages - to an output stream. +#. The :ref:`topic-plinth-io` provides general-purpose I/O + functionality. It is used to output a :term:`telemetry stream` of + events to assist with debugging and profiling. #. The :ref:`topic-plinth-lib` provides miscellaneous functionality that would be available via the Standard C Library on a hosted platform, including functions for reporting errors and accessing a processor clock. +The functions in the plinth module may be called in the context of a +signal handler for a protection fault (or equivalent), so they must +not access memory that is managed by the MPS, and they need to take +into account the restrictions imposed by the operating system. (See +"`Defining Signal Handlers`_" in the GNU C Library Reference Manual +for useful advice.) + +.. _Defining Signal Handlers: http://www.gnu.org/software/libc/manual/html_node/Defining-Handlers.html + .. c:macro:: CONFIG_PLINTH_NONE @@ -85,7 +96,7 @@ I/O module .. c:type:: mps_io_t - The type of the internal state of the I/O module. + The type of an I/O stream. This is an alias for a pointer to the incomplete structure :c:type:`mps_io_s`, which the :term:`plinth` may define if it @@ -100,19 +111,20 @@ I/O module .. c:function:: mps_res_t mps_io_create(mps_io_t *io_o) - A :term:`plinth` function for setting up the I/O module. + A :term:`plinth` function for creating an I/O stream for the + :term:`telemetry stream`. - ``io_o`` points to a location which the plinth may update with a - pointer to its internal state, if any. - - Returns :c:macro:`MPS_RES_OK` if successful. + ``io_o`` points to a location suitable for storing a pointer to an + I/O stream. - The MPS calls this function to set up the I/O module, for example - if there are events in the :term:`telemetry stream` that need to - be output. + If successful, the function must update this location with a + suitable pointer for the telemetry stream and return + :c:macro:`MPS_RES_OK`. Otherwise, it must return some other + :term:`result code`. - A typical plinth will use it to open a file for writing, or to - connect to the system logging interface. + The MPS calls this function to create the I/O stream for telemetry + output. A typical plinth will use it to open a file for writing, + or to connect to the system logging interface. .. note:: @@ -123,11 +135,10 @@ I/O module .. c:function:: void mps_io_destroy(mps_io_t io) - A :term:`plinth` function for tearing down the I/O module. + A :term:`plinth` function for destroying an I/O stream. - ``io`` is the value that the plinth wrote to ``io_o`` when the MPS - called :c:func:`mps_io_create`. If the plinth wrote no value, this - parameter is undefined. + ``io`` is a pointer to the I/O stream to be destroyed. It was + previously created by a call to :c:func:`mps_io_create`. After calling this function, the MPS guarantees not to use the value ``io`` again. @@ -140,11 +151,9 @@ I/O module .. c:function:: mps_res_t mps_io_write(mps_io_t io, void *buf, size_t size) - A :term:`plinth` function for writing data via the I/O module. + A :term:`plinth` function for writing data to an I/O stream. - ``io`` is the value that the plinth wrote to ``io_o`` when the MPS - called :c:func:`mps_io_create`. If the plinth wrote no value, this - parameter is undefined. + ``io`` is the I/O stream. ``buf`` points to the data to write. @@ -160,11 +169,9 @@ I/O module .. c:function:: mps_res_t mps_io_flush(mps_io_t io) - A :term:`plinth` function for flushing the I/O module. + A :term:`plinth` function for flushing an I/O stream. - ``io`` is the value that the plinth wrote to ``io_o`` when the MPS - called :c:func:`mps_io_create`. If the plinth wrote no value, this - parameter is undefined. + ``io`` is the I/O stream. Returns :c:macro:`MPS_RES_OK` if successful. @@ -247,12 +254,19 @@ Library module .. note:: In the ANSI Library module, ``mpsliban.c``, this reports the - failure using ``fprintf(stderr, "...%s...", message)`` and, in - the :term:`cool` :term:`variety`, terminates the program by - calling :c:func:`abort`. You can change this behaviour with + failure by calling ``fprintf(stderr, "...%s...", message)``, + flushes the :term:`telemetry stream` by calling + :c:func:`mps_telemetry_flush`, and, in the :term:`cool` + :term:`variety`, terminates the program by calling + :c:func:`abort`. You can change this behaviour with :c:func:`mps_lib_assert_fail_install`. For a discussion of the default behaviour, see :ref:`topic-error-assertion-handling`. + .. warning:: + + This function must not call any function in MPS, and it must + not access memory managed by the MPS. + .. c:function:: extern mps_lib_assert_fail_t mps_lib_assert_fail_install(mps_lib_assert_fail_t handler) This function customises the behaviour of the default assertion handler @@ -268,6 +282,11 @@ Library module Returns the previously installed handler. + .. warning:: + + The installed assertion handler must not call any function in + MPS, and it must not access memory managed by the MPS. + .. c:type:: typedef void (*mps_lib_assert_fail_t)(const char *, unsigned, const char *) The type of assertion handlers passed to and returned by @@ -296,7 +315,7 @@ Library module This function is intended to have the same semantics as the :c:func:`fputc` function of the ANSI C Standard (:ref:`ISO/IEC - 9899:1990 ` §7.11.7.3). + 9899:1990 ` §7.11.7.3). .. note:: @@ -314,7 +333,7 @@ Library module This function is intended to have the same semantics as the :c:func:`fputs` function of the ANSI C Standard (:ref:`ISO/IEC - 9899:1990 ` §7.11.7.4). + 9899:1990 ` §7.11.7.4). Return a non-negative integer if successful, or :c:func:`mps_lib_get_EOF` if not. @@ -383,7 +402,7 @@ Library module This function is intended to have the same semantics as the :c:func:`memcmp` function of the ANSI C Standard (:ref:`ISO/IEC - 9899:1990 ` §7.11.4.1). + 9899:1990 ` §7.11.4.1). .. note:: @@ -406,7 +425,7 @@ Library module This function is intended to have the same semantics as the :c:func:`memcpy` function of the ANSI C Standard (:ref:`ISO/IEC - 9899:1990 ` §7.11.2.1). + 9899:1990 ` §7.11.2.1). The MPS never passes overlapping blocks to :c:func:`mps_lib_memcpy`. @@ -432,7 +451,7 @@ Library module This function is intended to have the same semantics as the :c:func:`memset` function of the ANSI C Standard (:ref:`ISO/IEC - 9899:1990 ` §7.11.6.1). + 9899:1990 ` §7.11.6.1). .. note:: diff --git a/manual/source/topic/pool.rst b/manual/source/topic/pool.rst index 5fe096f9ae..d1ffe56d45 100644 --- a/manual/source/topic/pool.rst +++ b/manual/source/topic/pool.rst @@ -19,7 +19,7 @@ making it available for allocation. :c:func:`mps_alloc` or via an :term:`allocation point`. -.. c:function:: mps_res_t mps_pool_create_k(mps_pool_t *pool_o, mps_arena_t arena, mps_class_t class, mps_arg_s args[]) +.. c:function:: mps_res_t mps_pool_create_k(mps_pool_t *pool_o, mps_arena_t arena, mps_pool_class_t pool_class, mps_arg_s args[]) Create a :term:`pool` in an :term:`arena`. @@ -28,7 +28,7 @@ making it available for allocation. ``arena`` is the arena in which to create the pool. - ``class`` is the :term:`pool class` of the new pool. + ``pool_class`` is the :term:`pool class` of the new pool. ``args`` are :term:`keyword arguments` specific to the pool class. See the documentation for the pool class. @@ -40,31 +40,6 @@ making it available for allocation. :c:func:`mps_pool_destroy`. -.. c:function:: mps_res_t mps_pool_create(mps_pool_t *pool_o, mps_arena_t arena, mps_class_t class, ...) - - .. deprecated:: starting with version 1.112. - - Use :c:func:`mps_pool_create_k` instead: the :term:`keyword - arguments` interface is more reliable and produces better - error messages. - - An alternative to :c:func:`mps_pool_create_k` that takes its - extra arguments using the standard :term:`C` variable argument - list mechanism. - - -.. c:function:: mps_res_t mps_pool_create_v(mps_pool_t *pool_o, mps_arena_t arena, mps_class_t class, va_list args) - - .. deprecated:: starting with version 1.112. - - Use :c:func:`mps_pool_create_k` instead: the :term:`keyword - arguments` interface is more reliable and produces better - error messages. - - An alternative to :c:func:`mps_pool_create_k` that takes its extra - arguments using the standard :term:`C` ``va_list`` mechanism. - - .. c:function:: void mps_pool_destroy(mps_pool_t pool) Destroy a :term:`pool`. @@ -83,12 +58,12 @@ making it available for allocation. .. warning:: - It is not safe to destroy an :term:`automatically managed - ` pool if it contains any objects + It is not safe to carry on running the :term:`garbage + collector` after destroying an :term:`automatically managed + ` pool that contains any objects that are :term:`reachable` from your roots, or any objects that have been registered for :term:`finalization` but not yet - finalized, and then to carry on running the :term:`garbage - collector`. + finalized. Our recommended approach is to destroy automatically managed pools just before destroying the arena, and then only while @@ -119,15 +94,10 @@ return a block of memory to the pool) and others are See the :ref:`pool` for a list of pool classes. -.. c:type:: mps_class_t +.. c:type:: mps_pool_class_t The type of :term:`pool classes`. - .. note:: - - This should really have been called ``mps_pool_class_t`` but - it is too late to change it now. - .. index:: pair: pool; introspection @@ -135,6 +105,31 @@ See the :ref:`pool` for a list of pool classes. Pool introspection ------------------ +.. c:function:: size_t mps_pool_total_size(mps_pool_t pool) + + Return the total memory allocated from the arena and managed by + the pool. + + ``pool`` is the pool. + + The result includes memory in use by the client program, memory + that's available for use by the client program, and memory + that's lost to fragmentation. It does not include memory used by + the pool's internal control structures. + + +.. c:function:: size_t mps_pool_free_size(mps_pool_t pool) + + Return the free memory: memory managed by the pool but not in use + by the client program. + + ``pool`` is the pool. + + The result includes memory that's available for use by the client + program, and memory that's lost to fragmentation. It does not + include memory used by the pool's internal control structures. + + .. c:function:: mps_bool_t mps_addr_pool(mps_pool_t *pool_o, mps_arena_t arena, mps_addr_t addr) Determine the :term:`pool` to which an address belongs. diff --git a/manual/source/topic/porting.rst b/manual/source/topic/porting.rst new file mode 100644 index 0000000000..26c663f58f --- /dev/null +++ b/manual/source/topic/porting.rst @@ -0,0 +1,314 @@ +.. _topic-porting: + +Porting the MPS +=============== + +This chapter lists the steps involved in porting the MPS to a new +operating system, processor architecture, or compiler. It assumes that +you are familiar with :ref:`guide-build` and the :ref:`topic-platform` +chapter. + + +Platform code +------------- + +Pick two-character codes for the new platform's operating system, +processor architecture, and compiler toolchain, as described under +:ref:`topic-platform`, and concatenate them to get a six-character +platform code "``osarct``". + + +Functional modules +------------------ + +The MPS requires platform-specific implementations of the functional +modules in the list below. You'll probably find that it's unnecessary +to port them all: unless the new platform is very exotic, some of the +existing implementations ought to be usable. In most cases there is a +generic ("ANSI") implementation of the module, that uses only the +features of the Standard C Library. These generic implementations are +partially functional or non-functional, but can be used as a starting +point for a new port if none of the existing implementations is +usable. + +#. The **lock** module provides binary locks that ensure that only a + single :term:`thread` may be running with a lock held, and + recursive locks, where the same thread may safely take the lock + again without deadlocking. + + See :ref:`design-lock` for the design, and ``lock.h`` for the + interface. There are implementations for Linux in ``lockli.c``, + POSIX in ``lockix.c``, and Windows in ``lockw3.c``. + + There is a generic implementation in ``lockan.c``, which cannot + actually take any locks and so only works for a single thread. + +#. The **memory protection** module applies :term:`protection` to + areas of :term:`memory (2)`, ensuring that attempts to read or + write from those areas cause :term:`protection faults`, and + implements the means for the MPS to catch and handle these faults. + + See :ref:`design-prot` for the design, and ``prot.h`` for the + interface. There are implementations for POSIX in ``protix.c`` plus + ``protsgix.c``, Linux in ``protli.c``, Windows in ``protw3.c``, and + OS X using Mach in ``protxc.c``. + + There is a generic implementation in ``protan.c``, which can't + provide memory protection, so it forces memory to be scanned until + that there is no further need to protect it. This means it can't + support incremental collection, and has no control over pause + times. + +#. The **protection mutator context** module figures out what the + :term:`mutator` was doing when it caused a :term:`protection + fault`, so that access to a protected region of memory can be + handled, or when a thread was suspended, so that its + :term:`registers` and :term:`control stack` can be scanned. + + See :ref:`design-prmc` for the design, and ``prot.h`` for the + interface. There are implementations on Unix, Windows, and OS X for + IA-32 and x86-64. + + There is a generic implementation in ``prmcan.c``, which can't + provide these features, and so only supports a single thread. + +#. The **stack probe** module checks that there is enough space on the + :term:`control stack` for the MPS to complete any operation that it + might start. The purpose is to provoke a stack overflow exception, + if necessary, before taking the arena lock. + + See :ref:`design-sp` for the design, and ``sp.h`` for the + interface. There are implementations on Windows on IA-32 in + ``spi3w3.c`` and x86-64 in ``spi6w3.c``. + + There is a generic implementation in ``span.c``, which can't + provide this feature, and so is only suitable for use with a client + program that does not handle stack overflow faults, or does not + call into the MPS from the handler. + +#. The **stack and register scanning** module :term:`scans` the + :term:`registers` and :term:`control stack` of a thread. + + See :ref:`design-ss` for the design, and ``ss.h`` for the + interface. There are implementations for POSIX on IA-32 in + ``ssixi3.c`` and x86-64 in ``ssixi6.c``, and for Windows with + Microsoft Visual C/C++ on IA-32 in ``ssw3i3mv.c`` and x86-64 in + ``ssw3i6mv.c``. + + There is a generic implementation in ``ssan.c``, which calls + :c:func:`setjmp` to spill the registers and scans the whole jump + buffer, thus overscanning compared to a platform-specific + implementation. + +#. The **thread manager** module suspends and resumes :term:`threads`, + so that the MPS can gain exclusive access to :term:`memory (2)`, + and so that it can scan the :term:`registers` and :term:`control + stack` of suspended threads. + + See :ref:`design-thread-manager` for the design, and ``th.h`` for + the interface. There are implementations for POSIX in ``thix.c`` + plus ``pthrdext.c``, OS X using Mach in ``thxc.c``, Windows in + ``thw3.c``. + + There is a generic implementation in ``than.c``, which necessarily + only supports a single thread. + +#. The **virtual mapping** module reserves :term:`address space` from + the operating system (and returns it), and :term:`maps ` + address space to :term:`main memory` (and unmaps it). + + See :ref:`design-vm` for the design, and ``vm.h`` for the + interface. There are implementations for POSIX in ``vmix.c``, and + Windows in ``vmw3.c``. There is a generic implementation in + ``vman.c``, which fakes virtual memory by calling :c:func:`malloc`. + + +Platform detection +------------------ + +The new platform must be detected in ``mpstd.h`` and preprocessor +constants like :c:macro:`MPS_WORD_WIDTH` defined. See +:ref:`design-config` for the design of this header, and +:ref:`topic-platform-interface` for the list of preprocessor constants +that may need to be defined. For example:: + + /* "Predefined Macros" from "Visual Studio 2010" on MSDN + * . + * Note that Win32 includes 64-bit Windows! + * We use the same alignment as MS malloc: 16, which is used for XMM + * operations. + * See MSDN -> x64 Software Conventions -> Overview of x64 Calling Conventions + * + */ + + #elif defined(_MSC_VER) && defined(_WIN32) && defined(_WIN64) && defined(_M_X64) && !defined(__POCC__) + #if defined(CONFIG_PF_STRING) && ! defined(CONFIG_PF_W3I6MV) + #error "specified CONFIG_PF_... inconsistent with detected w3i6mv" + #endif + #define MPS_PF_W3I6MV + #define MPS_PF_STRING "w3i6mv" + #define MPS_OS_W3 + #define MPS_ARCH_I6 + #define MPS_BUILD_MV + #define MPS_T_WORD unsigned __int64 + #define MPS_T_ULONGEST unsigned __int64 + #define MPS_WORD_WIDTH 64 + #define MPS_WORD_SHIFT 6 + #define MPS_PF_ALIGN 16 + +The comment should justify the platform test (with reference to +documentation or to the output of a command like ``gcc -E -dM``), and +explain any unusual definitions. For example, here we need to explain +the choice of 16 bytes for :c:macro:`MPS_PF_ALIGN`, since normally a +64-bit platform requires 8-byte :term:`alignment`. + + +Platform configuration +---------------------- + +The new platform may be configured, if necessary, in ``config.h``. See +:ref:`design-config` for the design of this header. Avoid +platform-specific configuration if possible, to reduce the risk of +errors being introduced on one platform and not detected when other +platforms are tested. + + +Module selection +---------------- + +In ``mps.c``, add a section for the new platform. This must test the +platform constant ``MPS_PF_OSARCT`` that is now defined in +``mpstd.h``, and then include all the module sources for the platform. +For example:: + + /* Linux on 64-bit Intel with GCC or Clang */ + + #elif defined(MPS_PF_LII6GC) || defined(MPS_PF_LII6LL) + + #include "lockli.c" /* Linux locks */ + #include "thix.c" /* Posix threading */ + #include "pthrdext.c" /* Posix thread extensions */ + #include "vmix.c" /* Posix virtual memory */ + #include "protix.c" /* Posix protection */ + #include "protli.c" /* Linux protection */ + #include "proti6.c" /* 64-bit Intel mutator context */ + #include "prmci6li.c" /* 64-bit Intel for Linux mutator context */ + #include "span.c" /* generic stack probe */ + #include "ssixi6.c" /* Posix on 64-bit Intel stack scan */ + + +Makefile +-------- + +Add a makefile even if you expect to use an integrated development +environment (IDE) like Visual Studio or Xcode. Makefiles make it +easier to carry out continuous integration and delivery, and are less +likely to stop working because of incompatibilities between IDE +versions. + +On Unix platforms, the makefile must be named ``osarct.gmk``, and must +define ``PFM`` to be the platform code, ``MPMPF`` to be the list of +platform modules (the same files included by ``mps.c``), and ``LIBS`` +to be the linker options for any libraries required by the test cases. +Then it must include the compiler-specific makefile and ``comm.gmk``. +For example, ``lii6ll.gmk`` looks like this:: + + PFM = lii6ll + + MPMPF = \ + lockli.c \ + prmci6li.c \ + proti6.c \ + protix.c \ + protli.c \ + pthrdext.c \ + span.c \ + ssixi6.c \ + thix.c \ + vmix.c + + LIBS = -lm -lpthread + + include ll.gmk + include comm.gmk + +If the platform needs specific compilation options, then define +``PFMDEFS`` accordingly, but avoid this if at all possible. We +recommend in :ref:`guide-build` that users compile the MPS using a +simple command like ``cc -c mps.c``, and we suggest that they can +improve performance by compiling the MPS and their object format in +the same compilation unit. These steps would be more complicated if +the MPS required particular compilation options. + +On Windows, the makefile must be named ``osarct.nmk``, and must define +``PFM`` to be the platform code, and ``MPMPF`` to be the list of +platform modules (the same files included by ``mps.c``) in square +brackets. Then it must include ``commpre.nmk``, the compiler-specific +makefile and ``commpost.nmk``. For example, ``w3i6mv.nmk`` looks like +this:: + + PFM = w3i6mv + + MPMPF = \ + [lockw3] \ + [mpsiw3] \ + [prmci6w3] \ + [proti6] \ + [protw3] \ + [spw3i6] \ + [ssw3i6mv] \ + [thw3] \ + [thw3i6] \ + [vmw3] + + !INCLUDE commpre.nmk + !INCLUDE mv.nmk + !INCLUDE commpost.nmk + + + +Porting strategy +---------------- + +Start the port by selecting existing implementations of the functional +modules, using the generic implementations where nothing else will do. +Then check that the "smoke tests" pass, by running:: + + make -f osarct.gmk testrun # Unix + nmake /f osarct.nmk testrun # Windows + +Most or all of the test cases should pass at this point. If you're +using the generic threading implementation, then the multi-threaded +test cases are expected to fail. If you're using the generic lock +implementation, then the lock utilization test case ``lockut`` is +expected to fail. If you're using the generic memory protection +implementation, all the tests that rely on incremental collection are +expected to fail. See ``tool/testcases.txt`` for a database of test +cases and the configurations in which they are expected to pass. + +Now that there is a working system to build on, porting the necessary +modules to the new platform can be done incrementally. It's a good +idea to measure the performance as you go along (for example, using +the ``gcbench`` benchmark) to check that the new memory protection +module is effective. + + +Update the documentation +------------------------ + +These sections of the manual should be updated to mention the new +platform: + +- :ref:`guide-build` +- :ref:`topic-platform` + +In addition, if aspects of the port were especially tricky, then +consider writing a design document (see :ref:`design`) justifying the +implementation. + + +Contribute +---------- + +Consider contributing the new platform to the MPS. See +:ref:`contributing`. diff --git a/manual/source/topic/root.rst b/manual/source/topic/root.rst index efc87fb8e4..4a1e06686a 100644 --- a/manual/source/topic/root.rst +++ b/manual/source/topic/root.rst @@ -242,8 +242,8 @@ allowing the MPS to detect whether they have changed. The type of :term:`root modes`. It should be zero (meaning neither constant or protectable), or - the sum of some subset of :c:macro:`MPS_RM_CONST` and - :c:macro:`MPS_RM_PROT`. + the sum of some of :c:macro:`MPS_RM_CONST`, + :c:macro:`MPS_RM_PROT`, and :c:macro:`MPS_RM_PROT_INNER`. .. c:macro:: MPS_RM_CONST @@ -271,7 +271,7 @@ allowing the MPS to detect whether they have changed. The :term:`root mode` for :term:`protectable roots`. This tells the MPS that it may place a :term:`barrier (1)` on any - :term:`page` which any part of the :term:`root` covers. No + :term:`page` containing any part of the :term:`root`. No :term:`format method` or :term:`scan method` (except for the one for this root) may write data in this root. They may read it. @@ -289,6 +289,14 @@ allowing the MPS to detect whether they have changed. wants the operating system to be able to access the root. Many operating systems can't cope with writing to protected pages. +.. c:macro:: MPS_RM_PROT_INNER + + The :term:`root mode` for :term:`protectable roots` whose inner + pages (only) may be protected. This mode must not be specified + unless :c:macro:`MPS_RM_PROT` is also specified. It tells the MPS + that it may not place a :term:`barrier (1)` on a :term:`page` + that's partly (but not wholly) covered by the :term:`root`. + .. index:: single: root; interface @@ -514,6 +522,28 @@ Root interface The registered root description persists until it is destroyed by calling :c:func:`mps_root_destroy`. + .. _topic-root-type-pun: + + .. warning:: + + The ``base`` argument has type ``mps_addr_t *`` (a typedef for + ``void **``) but the table of references most likely has some + other pointer type, ``my_object *`` say. It is tempting to + write:: + + mps_root_create_table(..., (mps_addr_t *)my_table, ...) + + but this is :term:`type punning`, and its behaviour is not + defined in ANSI/ISO Standard C. (GCC and Clang have a warning + flag ``-Wstrict-aliasing`` which detects some errors of this + form.) + + To ensure well-defined behaviour, the pointer must be + converted via ``void *`` (or via :c:type:`mps_addr_t`, which + is a typedef for ``void *``), like this:: + + mps_addr_t base = my_table; + mps_root_create_table(..., base, ...) .. c:function:: mps_res_t mps_root_create_table_masked(mps_root_t *root_o, mps_arena_t arena, mps_rank_t rank, mps_rm_t rm, mps_addr_t *base, size_t count, mps_word_t mask) @@ -558,13 +588,17 @@ Root interface mps_res_t res; mps_root_t root; + mps_addr_t base = symtab; res = mps_root_create_table_masked(&root, arena, mps_rank_exact(), (mps_rm_t)0, - symtab, symtab_size * 2, + base, symtab_size * 2, (mps_word_t)TAG_MASK); if (res != MPS_RES_OK) errror("can't create symtab root"); + .. warning:: + + See the warning for :c:func:`mps_root_create_table` above. .. c:function:: void mps_root_destroy(mps_root_t root) diff --git a/manual/source/topic/scanning.rst b/manual/source/topic/scanning.rst index 1dbb15399c..6b4c4a1d39 100644 --- a/manual/source/topic/scanning.rst +++ b/manual/source/topic/scanning.rst @@ -56,12 +56,12 @@ region to be scanned. They must carry out the following steps: a pointer to a location containing the reference. #. If :c:func:`MPS_FIX2` returns a :term:`result code` other than - :c:func:`MPS_RES_OK`, return this result code from the scanning + :c:macro:`MPS_RES_OK`, return this result code from the scanning function as soon as practicable. #. If :c:func:`MPS_FIX2` returns :c:macro:`MPS_RES_OK`, it may have - updated the reference. If necessary, make sure that the updated - reference is stored back to the region being scanned. + updated the reference. Make sure that the updated reference is + stored back into the region being scanned. #. Call the macro :c:func:`MPS_SCAN_END` on the scan state. @@ -180,7 +180,7 @@ so that it cannot move. You could use this fact to optimize the scan by avoiding the need to reassemble and store the updated reference after calling -:c:func:`MPS_FIX2` +:c:func:`MPS_FIX2`. .. note:: @@ -361,17 +361,15 @@ Scanning interface .. c:function:: MPS_FIX_CALL(ss, call) - Call a function from within a :term:`scan method`, between - :c:func:`MPS_SCAN_BEGIN` and :c:func:`MPS_SCAN_END`, passing - the :term:`scan state` correctly. + Call a function to do some scanning, from within a :term:`scan + method`, between :c:func:`MPS_SCAN_BEGIN` and + :c:func:`MPS_SCAN_END`, passing the :term:`scan state` correctly. ``ss`` is the scan state that was passed to the scan method. ``call`` is an expression containing a function call where ``ss`` is one of the arguments. - Returns the result of evaluating the expression ``call``. - Between :c:func:`MPS_SCAN_BEGIN` and :c:func:`MPS_SCAN_END`, the scan state is in a special state, and must not be passed to a function. If you really need to do so, for example because you @@ -379,6 +377,9 @@ Scanning interface must wrap the call with :c:func:`MPS_FIX_CALL` to ensure that the scan state is passed correctly. + The function being called must use :c:func:`MPS_SCAN_BEGIN` and + :c:func:`MPS_SCAN_END` appropriately. + In example below, the scan method ``obj_scan`` fixes the object's ``left`` and ``right`` references, but delegates the scanning of references inside the object's ``data`` member to the function @@ -392,12 +393,14 @@ Scanning interface mps_res_t res; MPS_SCAN_BEGIN(ss) { for (obj = base; obj < limit; obj++) { - if (MPS_FIX12(ss, &obj->left) != MPS_RES_OK) + res = MPS_FIX12(ss, &obj->left); + if (res != MPS_RES_OK) return res; MPS_FIX_CALL(ss, res = data_scan(ss, &obj->data)); if (res != MPS_RES_OK) return res; - if (MPS_FIX12(ss, &obj->right) != MPS_RES_OK) + res = MPS_FIX12(ss, &obj->right); + if (res != MPS_RES_OK) return res; } } MPS_SCAN_END(ss); @@ -406,9 +409,11 @@ Scanning interface .. warning:: - Use of :c:func:`MPS_FIX_CALL` is best avoided, as it forces - values out of registers. The gains in simplicity of the code - need to be measured against the loss in performance. + Use of :c:func:`MPS_FIX_CALL` is best avoided, as it may + force values out of registers (depending on compiler + optimisations such as inlining). The gains in simplicity of + the code ought to be measured against the loss in + performance. .. index:: @@ -463,15 +468,18 @@ Fixing interface :term:`Fix` a :term:`reference`. - ``ss`` is the :term:`scan state` that was passed to the scan method. + ``ss`` is the :term:`scan state` that was passed to the + :term:`scan method`. ``ref_io`` points to the reference. - Returns :c:macro:`MPS_RES_OK` if successful: in this case the - reference may have been updated, and the scan method must continue - to scan the :term:`block`. If it returns any other result, the - :term:`scan method` must return that result as soon as possible, - without fixing any further references. + Returns :c:macro:`MPS_RES_OK` if successful. In this case the + reference may have been updated, and so the scan method must store + the updated reference back to the region being scanned. The scan + method must continue to scan the :term:`block`. + + If it returns any other result, the scan method must return that + result as soon as possible, without fixing any further references. This macro must only be used within a :term:`scan method`, between :c:func:`MPS_SCAN_BEGIN` and :c:func:`MPS_SCAN_END`. @@ -493,29 +501,3 @@ Fixing interface In the case where the scan method does not need to do anything between :c:func:`MPS_FIX1` and :c:func:`MPS_FIX2`, you can use the convenience macro :c:func:`MPS_FIX12`. - - -.. c:function:: mps_res_t mps_fix(mps_ss_t ss, mps_addr_t *ref_io) - - .. deprecated:: starting with version 1.111. - - Use :c:func:`MPS_FIX1` and :c:func:`MPS_FIX2` instead. - - :term:`Fix` a :term:`reference`. - - This is a function equivalent to:: - - MPS_SCAN_BEGIN(ss); - MPS_FIX12(ss, ref_io); - MPS_SCAN_END(ss); - - Because :term:`scanning ` is an operation on the - :term:`critical path`, we recommend that you use - :c:func:`MPS_FIX12` (or :c:func:`MPS_FIX1` and :c:func:`MPS_FIX2`) - to ensure that the "stage 1 fix" is inlined. - - .. note:: - - If you call this between :c:func:`MPS_SCAN_BEGIN` and - :c:func:`MPS_SCAN_END`, you must use :c:func:`MPS_FIX_CALL` to - ensure that the scan state is passed correctly. diff --git a/manual/source/topic/telemetry.rst b/manual/source/topic/telemetry.rst index f33de3516c..e25e71e4c5 100644 --- a/manual/source/topic/telemetry.rst +++ b/manual/source/topic/telemetry.rst @@ -68,7 +68,7 @@ The telemetry system relies on three utility programs: SQLite database for further analysis. You must build and install these programs as described in -:ref:`guide-build`. Thee programs are described in more detail below. +:ref:`guide-build`. These programs are described in more detail below. .. index:: @@ -110,7 +110,7 @@ The MPS writes the telemetry to the log in an encoded form for speed. It can be decoded using the :ref:`mpseventcnv ` and :ref:`mpseventtxt ` programs:: - (gdb) shell mpseventcnv | mpseventtxt | sort > mpsio.txt + (gdb) shell mpseventcnv | sort | mpseventtxt > mpsio.txt The ``sort`` is useful because the events are not necessarily written to the telemetry file in time order, but each event starts with a @@ -388,41 +388,6 @@ further analysis by running :program:`mpseventsql`. Telemetry interface ------------------- -.. c:function:: mps_word_t mps_telemetry_control(mps_word_t reset_mask, mps_word_t flip_mask) - - .. deprecated:: starting with version 1.111. - - Use :c:func:`mps_telemetry_get`, :c:func:`mps_telemetry_reset`, - and :c:func:`mps_telemetry_set` instead. - - Update and return the :term:`telemetry filter`. - - ``reset_mask`` is a :term:`bitmask` indicating the bits in the - telemetry filter that should be reset. - - ``flip_mask`` is a bitmask indicating the bits in the telemetry - filter whose value should be flipped after the resetting. - - Returns the previous value of the telemetry filter, prior to the - reset and the flip. - - The parameters ``reset_mask`` and ``flip_mask`` allow the - specification of any binary operation on the filter control. For - typical operations, the parameters should be set as follows: - - ============ ============== ============= - Operation ``reset_mask`` ``flip_mask`` - ============ ============== ============= - ``set(M)`` ``M`` ``M`` - ------------ -------------- ------------- - ``reset(M)`` ``M`` ``0`` - ------------ -------------- ------------- - ``flip(M)`` ``0`` ``M`` - ------------ -------------- ------------- - ``read()`` ``0`` ``0`` - ============ ============== ============= - - .. c:function:: void mps_telemetry_flush(void) Flush the internal event buffers into the :term:`telemetry stream`. @@ -431,8 +396,7 @@ Telemetry interface stream itself. This ensures that even the latest events are now properly recorded, should the :term:`client program` terminate (uncontrollably as a result of a bug, for example) or some - interactive tool require access to the telemetry stream. You could - even try calling this from a debugger after a problem. + interactive tool require access to the telemetry stream. .. note:: @@ -548,3 +512,26 @@ used in queries, for example: If the ``User`` event category is not turned on in the :term:`telemetry filter` (via :c:func:`mps_telemetry_control`) then calling this function has no effect. + + +.. index:: + pair: telemetry; customizing + +Customizing the telemetry system +-------------------------------- + +If you need the telemetry system to support features not described +here (for example, you need to transmit telemetry data over a network +rather than writing it to a file on the local filesystem) then you may +be able to do so by providing your own implementation of the +:ref:`topic-plinth-io`. + +When it first needs to output telemetry, the MPS call the plinth +function :c:func:`mps_io_create` to create an I/O stream. It then +calls :c:func:`mps_io_write` to write binary data to the stream +and :c:func:`mps_io_flush` to flush the stream in response to +:c:func:`mps_telemetry_flush`. By providing your own implementations +of these functions, you can direct the telemetry stream wherever you +like. + +See :ref:`topic-plinth` for details. diff --git a/manual/source/topic/thread.rst b/manual/source/topic/thread.rst index fe16b1e450..9417c473b0 100644 --- a/manual/source/topic/thread.rst +++ b/manual/source/topic/thread.rst @@ -100,6 +100,7 @@ Signal and exception handling issues for co-operating: if you are in this situation, please :ref:`contact us `. + .. index:: single: thread; interface @@ -142,6 +143,9 @@ Thread interface It is recommended that all threads be registered with all arenas. + It is an error if a thread terminates while it is registered. The + client program must call :c:func:`mps_thread_dereg` first. + .. c:function:: void mps_thread_dereg(mps_thr_t thr) @@ -164,47 +168,3 @@ Thread interface It is recommended that threads be deregistered only when they are just about to exit. - - -.. c:function:: void mps_tramp(void **r_o, mps_tramp_t f, void *p, size_t s) - - .. deprecated:: starting with version 1.111. - - Call a function via the MPS trampoline. - - ``r_o`` points to a location that will store the result of calling - ``f``. - - ``f`` is the function to call. - - ``p`` and ``s`` are arguments that will be passed to ``f`` each - time it is called. This is intended to make it easy to pass, for - example, an array and its size as parameters. - - The MPS relies on :term:`barriers (1)` to protect memory - that is in an inconsistent state. On some operating systems, - barrier hits generate exceptions that have to be caught by a - handler that is on the stack. On these operating systems, any code - that uses memory managed by the MPS must be called from inside - such an exception handler, that is, inside a call to - :c:func:`mps_tramp`. - - If you have multiple threads that run code that uses memory - managed by the MPS, each thread must execute such code inside a - call to :c:func:`mps_tramp`. - - Starting with version 1.111, this is not required on any operating - system supported by the MPS. - - -.. index:: - single: trampoline - -.. c:type:: void *(*mps_tramp_t)(void *p, size_t s) - - .. deprecated:: starting with version 1.111. - - The type of a function called by :c:func:`mps_tramp`. - - ``p`` and ``s`` are the corresponding arguments that were passed - to :c:func:`mps_tramp`. diff --git a/procedure/branch-merge.rst b/procedure/branch-merge.rst index 232a5a925b..a29a6daa19 100644 --- a/procedure/branch-merge.rst +++ b/procedure/branch-merge.rst @@ -17,11 +17,34 @@ This document contains procedures and checklists for branching and merging durin .. _release-build: release-build -2. Creating a development branch --------------------------------- +2. Pre-branch checklist +----------------------- -#. If you are creating a development branch to fix a problem, make - sure that there's a job recording the problem. +#. Have you created a job recording the problem you are planning to + solve on the development branch? + + +3. Creating a development branch (automated procedure) +------------------------------------------------------ + +Run the script ``tool/branch``, passing the options: + +* ``-P mps`` — project name +* ``-p PARENT`` — parent branch: for example ``master`` or ``custom/cet/main`` +* ``-C CHANGELEVEL`` — changelevel at which to make the branch +* ``-t TASK`` — task name: for example ``lii6ll`` +* ``-d "DESCRIPTION"`` — the description of the branch +* ``-y`` — yes, really create the branch + +If omitted, the project and parent branch are deduced from the current +directory, and the changelevel defaults to the most recent change on +the parent branch. So a typical invocation looks like this:: + + tool/branch -p master -t lii6ll -d "Adding new supported platform lii6ll (job003596)." -y + + +4. Creating a development branch (manual procedure) +--------------------------------------------------- #. Create a branch specification. For example:: @@ -43,18 +66,9 @@ This document contains procedures and checklists for branching and merging durin View: //info.ravenbrook.com/project/mps/custom/cet/main/... //info.ravenbrook.com/project/mps/branch/2013-11-04/cet-i6-stack-probe/... -#. If you're just going to work on a subset of the sources, then just - map the files you need:: - - View: - //info.ravenbrook.com/project/mps/custom/cet/main/code/... //info.ravenbrook.com/project/mps/branch/2013-07-02/cet-ap-key/code/... - -#. Ensure that the branch is mapped in your client specification. +#. Populate the branch:: -#. Integrate and submit:: - - p4 integrate -b mps/branch/2013-08-21/lii6ll - p4 submit -d "Branching to add new supported platform lii6ll (job003596)." + p4 populate -b mps/branch/2013-08-21/lii6ll -d "Branching to add new supported platform lii6ll (job003596)." #. Edit the index of branches:: @@ -63,17 +77,20 @@ This document contains procedures and checklists for branching and merging durin and add an entry to the "Active branches" section. -3. Pre-merge checklist +5. Pre-merge checklist ---------------------- #. Have you solved the problem? #. Is there an automated test case? -#. Does the test suite pass? +#. Does the test suite pass on all supported platforms? #. If there are interface changes, is there documentation? +#. If the changes are significant and user-visible, have you updated + the release notes (``master/manual/source/release.rst``)? + #. If this is work on a customer-specific branch, and you've added new customer-specific interfaces, have you ensured that these interfaces are separated from the public interfaces? @@ -86,7 +103,7 @@ This document contains procedures and checklists for branching and merging durin #. Has there been a code review? -4. Merging a development branch +6. Merging a development branch ------------------------------- #. Do a catch-up merge from the master sources (or the appropriate @@ -103,8 +120,8 @@ This document contains procedures and checklists for branching and merging durin p4 submit -d "Catch-up merge from the master sources to $BRANCH" -#. Update the branch on other platforms and check that the test suite - passes. +#. Update the branch on all supported platforms and check that the + test suite passes. #. Backward merge into the master sources (or the appropriate customer-specific mainline):: @@ -132,9 +149,6 @@ This document contains procedures and checklists for branching and merging durin "Active branches" to "Dormant branches" section and linking the change in which the branch was merged. -#. If the change is significant and user-visible, update the release - notes (``master/manual/source/release.rst``). - A. References @@ -146,6 +160,7 @@ B. Document History ========== ===== ================================================== 2014-01-09 GDR_ Created. +2014-03-19 GDR_ Describe automated procedure. ========== ===== ================================================== .. _GDR: mailto:gdr@ravenbrook.com diff --git a/procedure/release-build.rst b/procedure/release-build.rst index 5308804548..cf07cee062 100644 --- a/procedure/release-build.rst +++ b/procedure/release-build.rst @@ -33,7 +33,7 @@ All relative paths are relative to .. _version-create: version-create -#. Make sure that you have rights to push to the ``mps-temporary`` +#. Make sure that you have rights to push to the ``mps`` repository on GitHub. If not, follow the `Becoming a Ravenbrook team member procedure `_ first. @@ -47,39 +47,28 @@ All relative paths are relative to 1.111.0), where *VERSION* is the number of the version you’re releasing, and *N* is the first unused release number (starting at zero). Look in the index of releases (``release/index.html``) for - existing release numbers for your version:: + existing release numbers for your version. :: VERSION=A.BBB RELEASE=$VERSION.N -#. Ensure that ``version/$VERSION/readme.txt`` contains an up-to-date - description of the release you intend to build and the correct - release name. +#. Check that the macro ``MPS_RELEASE`` in ``code/version.c`` has the + correct value. -#. Ensure that ``version/$VERSION/manual/source/release.rst`` contains - a section with an up-to-date description of significant - user-visible changes since the previous release. +#. Check that ``readme.txt`` contains an up-to-date description of the + release you intend to build. For example, is the list of supported + platforms still correct? -#. In ``version/$VERSION/code/version.c``, set ``MPS_RELEASE`` to the - correct value (see the rules in the comments), and check strings that - contain copyright dates, etc. - -#. In ``version/$VERSION/configure.ac`` edit the second argument of - ``AC_INIT`` to be ``[release $RELEASE]``, then open - ``version/$VERSION/configure`` for edit and run ``autoreconf -vif`` - to bring the configure script up to date. - -#. Submit ``readme.txt``, ``manual/source/release.rst``, - ``version.c``, ``configure.ac`` and ``configure`` (if changed) to - Perforce:: - - p4 submit -d "Updated files preparatory to release $RELEASE." +#. Check that ``manual/source/release.rst`` contains a section with an + up-to-date description of significant user-visible changes since + the previous release. #. Determine the *CHANGELEVEL* at which you’re going to make the - release. This will usually be the latest submitted changelevel on the - version branch; to get it, use ``p4 changes -m 1``:: + release. This will usually be the latest submitted changelevel on + the branch from which you are making the release; to get it, use + ``p4 changes -m 1``:: - CHANGELEVEL=$(p4 changes -m 1 version/$VERSION/... | cut -d' ' -f2) + CHANGELEVEL=$(p4 changes -m 1 ... | cut -d' ' -f2) 4. Pre-release testing @@ -113,9 +102,31 @@ All relative paths are relative to On other platforms they are as shown above. +#. Check that there are no performance regressions by comparing the + benchmarks (``djbench`` and ``gcbench``) for the last release and + this one. + + +5. Making the release (automated procedure) +------------------------------------------- + +Run the script ``tool/release``, passing the options: + +* ``-P mps`` — project name +* ``-b BRANCH`` — branch to make the release from: for example ``version/1.113`` +* ``-C CHANGELEVEL`` — changelevel at which to make the release +* ``-d "DESCRIPTION"`` — description of the release +* ``-y`` — yes, really make the release + +If omitted, the project and branch are deduced from the current +directory, and the changelevel defaults to the most recent change on +the branch. A typical invocation looks like this:: -5. MPS Kit Tarball and Zip file -------------------------------- + tool/release -b version/1.113 -d "Improved interface to generation chains." -y + + +6. Making the release (manual procedure) +---------------------------------------- .. note:: @@ -177,10 +188,6 @@ On a Unix (including OS X) machine: p4 -c $CLIENT client -d $CLIENT rm -rf /tmp/$CLIENT - -6. Registering the release --------------------------- - #. Edit the index of releases (``release/index.html``) and add the release to the table, in a manner consistent with previous releases. @@ -191,17 +198,13 @@ On a Unix (including OS X) machine: #. Edit the main MPS Project index page (``index.rst``), updating the "Download the latest release" link. -#. Submit these changes to Perforce: +#. Submit these changes to Perforce:: p4 submit -d "MPS: registered release $RELEASE." -#. Integrate the changes you made on the version branch back to the - master sources, ignoring changes that don't apply to the master - sources:: - p4 integrate -r -b $BRANCH - p4 resolve - p4 submit -d "Merging updates preparatory to release $RELEASE." +7. Registering the release +-------------------------- #. Visit the `project updater `__, @@ -215,7 +218,12 @@ On a Unix (including OS X) machine: Memory Pool System Kit release $RELEASE. See . END - git push --tags git@github.com:Ravenbrook/mps-temporary.git + git push --tags git@github.com:Ravenbrook/mps.git + +#. Go to the `list of releases on Github + `__ and + select "Draft a new release". Select the tag you just pushed, and + set the title and description to match the other releases. #. Inform the project manager and staff by e-mail to mps-staff@ravenbrook.com. @@ -232,8 +240,6 @@ A. References Ravenbrook Limited; 2008-10-16; http://info.ravenbrook.com/mail/2008/10/16/13-08-20/0.txt -.. [Sphinx] "Sphinx: Python document generator"; http://sphinx-doc.org/ - B. Document History ------------------- @@ -253,6 +259,7 @@ B. Document History 2012‑09‑24 RB_ Make sure ZIP files contain files with Windows line endings. Use a fresh Perforce client to avoid any possibility of a clash with working files. Different archive name for custom variants. 2013-03-20 GDR_ Ensure that manual HTML is up to date before making a release. 2014-01-13 GDR_ Make procedure less error-prone by giving exact sequence of commands (where possible) based on experience of release 1.112.0. +2016-01-28 RB_ Git repository renamed from mps-temporary to mps. ========== ===== ========================================================== .. _RB: mailto:rb@ravenbrook.com diff --git a/procedure/version-create.rst b/procedure/version-create.rst index e829ded777..3f743639fd 100644 --- a/procedure/version-create.rst +++ b/procedure/version-create.rst @@ -69,38 +69,49 @@ evolution. A version has these parts: --------------------------------------- -3.1. Prerequisites -~~~~~~~~~~~~~~~~~~ +3.1. Pre-branch checklist +~~~~~~~~~~~~~~~~~~~~~~~~~ -#. Make sure that you are an authenticated Git Fusion user (follow the - git-fusion_ procedure if not). +#. Are you an authenticated Git Fusion user? If not, follow the + git-fusion_ procedure. .. _git-fusion: /procedure/git-fusion -#. Make sure that the sources for the version you are about to create, - for the table of versions, and for the table of Git Fusion pushes, - are mapped in your Perforce client:: +#. Does ``code/version.c`` in the master sources contain the correct + value for the ``MPS_RELEASE`` macro? It should be the name of the + first release from the version you are about to create. If it is + wrong, correct and submit it. - //info.ravenbrook.com/project/mps/version/$VERSION/... - //info.ravenbrook.com/project/mps/branch/index.html - //info.ravenbrook.com/infosys/robots/git-fusion/etc/pushes +3.2. Automated procedure +~~~~~~~~~~~~~~~~~~~~~~~~ -3.2. Create the version branch -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Run the script ``tool/branch``, passing the options: + +* ``-P mps`` — project name +* ``-p master`` — parent branch +* ``-C CHANGELEVEL`` — changelevel at which to make the branch +* ``-v`` — request a version branch +* ``-d "DESCRIPTION"`` — description of the branch +* ``-y`` — yes, really create the branch + +If omitted, the project and parent branch are deduced from the current +directory, and the changelevel defaults to the most recent change on +the parent branch. A typical invocation looks like this:: -#. Update the following files in the master sources that contain a - version number, so that they refer to the version that you are - about to create:: + tool/branch -p master -v -d "Improved interface to generation chains." -y - code/version.c - Submit these files to Perforce. +3.3. Manual procedure +~~~~~~~~~~~~~~~~~~~~~ - (If there are other files that need updating, update this procedure - and add them here. But it is better to parse the information out of - ``code/version.c`` so that the version number is mentioned just - once. See for example ``manual/source/conf.py``.) +#. Make sure that the sources for the version you are about to create, + for the table of versions, and for the table of Git Fusion pushes, + are mapped in your Perforce client:: + + //info.ravenbrook.com/project/mps/version/$VERSION/... + //info.ravenbrook.com/project/mps/branch/index.html + //info.ravenbrook.com/infosys/robots/git-fusion/etc/pushes #. Create the version branch specification by running:: @@ -128,29 +139,9 @@ evolution. A version has these parts: Note the latest change that was in before the integrate. -#. Edit ``configure.ac`` on the version branch, replacing ``[master]`` - with ``[version A.BBB]``, and then submit it:: - - p4 submit -d "Update 'master' to 'version $VERSION'" - - (If there are other files that need updating, update this procedure - and add them here. But it is better to organize the sources so that - this is not necessary.) - -#. Do an empty integrate of this change back on to the masters, so - Perforce knows that it's not wanted:: - - p4 integrate -r -b $BRANCH - p4 resolve -ay - p4 submit -d "Ignoring update of 'master' to 'version $VERSION' from version branch" - - -3.3. Register the new version branch -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - #. Update the `table of versions `_. -#. Make a client specification that can be used by the `git-fusion robot `_ to sync the version: +#. Make a client specification that can be used by the `git-fusion robot `_ to sync the version:: p4 client -i <`_: +#. Add an entry to the `list of repositories to push to GitHub `_:: PUSHES=$(p4 have //info.ravenbrook.com/infosys/robots/git-fusion/etc/pushes | cut -d' ' -f3) p4 edit $PUSHES - printf "mps-version-$VERSION\tgit@github.com:Ravenbrook/mps-temporary.git\tversion/$VERSION" >> $PUSHES + printf "mps-version-$VERSION\tgit@github.com:Ravenbrook/mps.git\tversion/$VERSION" >> $PUSHES p4 submit -d "Arranging for MPS version $VERSION to be pushed to GitHub by Git Fusion" $PUSHES @@ -186,6 +177,8 @@ B. Document History 2010-11-06 RHSK_ Correctly format example of p4 branch -o mps/version/1.105 2014-01-13 GDR_ Make procedure less error-prone by giving exact sequence of commands (where possible) based on experience of version 1.112. 2014-01-14 GDR_ Step for adding to Git Fusion. +2014-03-19 GDR_ Describe automated procedure. +2016-01-28 RB_ Git repository renamed from mps-temporary to mps. ========== ===== ======================================================== .. _GDR: mailto:gdr@ravenbrook.com diff --git a/readme.txt b/readme.txt index 778d696a49..2959f6eb7b 100644 --- a/readme.txt +++ b/readme.txt @@ -72,27 +72,29 @@ Supported target platforms The MPS is currently supported for deployment on: -- Windows XP or later on IA-32 and x86-64, using Microsoft Visual C/C++; +- Windows XP or later, on IA-32 and x86-64, using Microsoft Visual C/C++; -- Linux (Ubuntu 11 and RHEL 6.3 known good, otherwise YMMV) on IA-32 - using GCC and on x86-64 using GCC or Clang/LLVM; +- Linux 2.4 or later, on IA-32 using GCC and on x86-64 using GCC or + Clang/LLVM; - FreeBSD 7 or later, on IA-32 and x86-64, using GCC; -- Mac OS X 10.4 or later, on IA-32 and x86-64, using Clang/LLVM. - -The MPS will *not* work in a multi-threaded 32-bit application on 64-bit -Windows 7. This is due to a serious fault in Microsoft's WOW64 emulator -that we are powerless to correct. It is due to be fixed in Windows 8. -(See `WOW64 bug: GetThreadContext() may return stale contents -`__.) +- OS X 10.4 or later, on IA-32 and x86-64, using Clang/LLVM. The MPS is highly portable and has run on many other processors and operating systems in the past (see `Building the MPS `__). Most of the MPS is written in very pure ANSI C and compiles without warnings on anything. +.. warning:: + + If you are running a multi-threaded 32-bit application on 64-bit + Windows 7 via the WOW64 emulator, then you must install this + hotfix from Microsoft: + ``__. See + ``__ + for a description of the problem. + Getting help ------------ @@ -129,6 +131,7 @@ Document History 2012-09-05 RB_ Considerably reduced ready for version 1.110. Now brought to you in glorious reStructuredText. 2014-01-13 GDR_ Updated supported platforms. +2014-07-04 GDR_ Link to hotfix for WOW64 bug. ========== ===== ====================================================== .. _GDR: mailto:gdr@ravenbrook.com diff --git a/test/README b/test/README new file mode 100644 index 0000000000..90edaf8857 --- /dev/null +++ b/test/README @@ -0,0 +1,29 @@ +$Id$ + +This is the Memory Management QA test harness. To use it you need +perl 5 (or higher). Go "perl qa help" for help, "perl qa options" +to see what version of the harness you have (or look at the +file "test/version"). + + +Testing on unix +--------------- + +From the test directory:: + + PLATFORM=lii6ll # substitute your platform + CODE=../code # code directory of the branch you are testing + 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 + qa runset testsets/passing + +Each test case is compiled in its turn to the file +``test/obj/$(uname -s)_$(uname -r)_$(uname -p)__unix/tmp_test`` +so you can debug it with:: + + lldb test/obj/$(uname -s)_$(uname -r)_$(uname -p)__unix/tmp_test + +Or ``gdb`` instead of ``lldb``. MMQA sets its own assertion handler, +so you'll probably want to set a breakpoint on mmqa_assert_handler. diff --git a/test/argerr/0.c b/test/argerr/0.c index 2a794641e3..12f4411e8a 100644 --- a/test/argerr/0.c +++ b/test/argerr/0.c @@ -4,6 +4,10 @@ TEST_HEADER summary = create an arena with a NULL arena_t language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_arena_o != NULL END_HEADER */ diff --git a/test/argerr/1.c b/test/argerr/1.c index 89b761e7ae..75ab07dc84 100644 --- a/test/argerr/1.c +++ b/test/argerr/1.c @@ -4,6 +4,8 @@ TEST_HEADER summary = create an arena with an unaligned arena_t language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/10.c b/test/argerr/10.c index 6fdc46c733..1519c3e417 100644 --- a/test/argerr/10.c +++ b/test/argerr/10.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null 1st arg to pool_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_pool_o != NULL END_HEADER */ diff --git a/test/argerr/100.c b/test/argerr/100.c index 9fc5706ce3..6b7bf5a547 100644 --- a/test/argerr/100.c +++ b/test/argerr/100.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null &root_t for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_root_o != NULL END_HEADER */ diff --git a/test/argerr/101.c b/test/argerr/101.c index 7352da4194..3115e6fddd 100644 --- a/test/argerr/101.c +++ b/test/argerr/101.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED &root_t for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/102.c b/test/argerr/102.c index 2aad60a800..b65d6a037d 100644 --- a/test/argerr/102.c +++ b/test/argerr/102.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null arena for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/argerr/103.c b/test/argerr/103.c index 47bd022b7c..588292e8c3 100644 --- a/test/argerr/103.c +++ b/test/argerr/103.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED arena for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/104.c b/test/argerr/104.c index 588c3ea224..0c758172d4 100644 --- a/test/argerr/104.c +++ b/test/argerr/104.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = MIN-1 rank for mps_root_create_table + summary = -1 rank for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ @@ -24,7 +28,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); cdie(mps_root_create_table(&root, arena, - MPS_RANK_MIN-1, 0, a, sizeof a), + -1, 0, a, sizeof a), "root create"); } diff --git a/test/argerr/105.c b/test/argerr/105.c index 4c6d33fd9d..0dd479590a 100644 --- a/test/argerr/105.c +++ b/test/argerr/105.c @@ -4,6 +4,10 @@ TEST_HEADER summary = MAX+1 rank for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ @@ -24,7 +28,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); cdie(mps_root_create_table(&root, arena, - MPS_RANK_MAX+1, 0, a, sizeof a), + 8, 0, a, sizeof a), "root create"); } diff --git a/test/argerr/106.c b/test/argerr/106.c index 389518d1bc..791edb2f0e 100644 --- a/test/argerr/106.c +++ b/test/argerr/106.c @@ -4,6 +4,10 @@ TEST_HEADER summary = highbit set rank for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ diff --git a/test/argerr/107.c b/test/argerr/107.c index f72cb6bee5..ebc6b8cc72 100644 --- a/test/argerr/107.c +++ b/test/argerr/107.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = MIN-1 root mode for mps_root_create_table + summary = -1 root mode for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = (mode & (RootModeCONSTANT | RootModePROTECTABLE | RootModePROTECTABLE_INNER)) == mode END_HEADER */ @@ -24,7 +28,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); cdie(mps_root_create_table(&root, arena, - mps_rank_ambig(), MPS_RM_MIN-1, a, sizeof a), + mps_rank_ambig(), -1, a, sizeof a), "root create"); } diff --git a/test/argerr/108.c b/test/argerr/108.c index ffc6165a1e..d9a3013625 100644 --- a/test/argerr/108.c +++ b/test/argerr/108.c @@ -4,6 +4,10 @@ TEST_HEADER summary = MAX+1 root mode for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = (mode & (RootModeCONSTANT | RootModePROTECTABLE | RootModePROTECTABLE_INNER)) == mode END_HEADER */ @@ -24,7 +28,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); cdie(mps_root_create_table(&root, arena, - mps_rank_ambig(), MPS_RM_MAX+1, a, sizeof a), + mps_rank_ambig(), 8, a, sizeof a), "root create"); } diff --git a/test/argerr/109.c b/test/argerr/109.c index d85b96ba03..7a43ad5077 100644 --- a/test/argerr/109.c +++ b/test/argerr/109.c @@ -4,6 +4,10 @@ TEST_HEADER summary = highbit set root mode for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = (mode & (RootModeCONSTANT | RootModePROTECTABLE | RootModePROTECTABLE_INNER)) == mode END_HEADER */ diff --git a/test/argerr/11.c b/test/argerr/11.c index 7dcb8e9472..f04946c5f4 100644 --- a/test/argerr/11.c +++ b/test/argerr/11.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED 1st arg to pool_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -24,7 +26,6 @@ static void test(void) mps_arena_t arena; mps_thr_t thread; mps_root_t root; - mps_chain_t chain; mps_fmt_t format; diff --git a/test/argerr/110.c b/test/argerr/110.c index 9bbcdc5200..9509182913 100644 --- a/test/argerr/110.c +++ b/test/argerr/110.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL base for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = base != NULL END_HEADER */ diff --git a/test/argerr/111.c b/test/argerr/111.c index cba3b7feda..6f60b0a326 100644 --- a/test/argerr/111.c +++ b/test/argerr/111.c @@ -4,6 +4,10 @@ TEST_HEADER summary = UNALIGNED base for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = AddrIsAligned(base, sizeof(Word)) END_HEADER */ diff --git a/test/argerr/112.c b/test/argerr/112.c index 26dc870414..9fdb3dc999 100644 --- a/test/argerr/112.c +++ b/test/argerr/112.c @@ -4,6 +4,10 @@ TEST_HEADER summary = zero size for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = size > 0 END_HEADER */ diff --git a/test/argerr/114.c b/test/argerr/114.c index 454838d4ad..e814ded99d 100644 --- a/test/argerr/114.c +++ b/test/argerr/114.c @@ -4,6 +4,10 @@ TEST_HEADER summary = negative size for mps_root_create_table language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = base < limit END_HEADER */ diff --git a/test/argerr/115.c b/test/argerr/115.c index e1378afe83..7c32ebd661 100644 --- a/test/argerr/115.c +++ b/test/argerr/115.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null &root_t for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_root_o != NULL END_HEADER */ diff --git a/test/argerr/116.c b/test/argerr/116.c index ead7686d12..5173a55549 100644 --- a/test/argerr/116.c +++ b/test/argerr/116.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED &root_t for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/117.c b/test/argerr/117.c index 338d3c5974..83b9afcfd5 100644 --- a/test/argerr/117.c +++ b/test/argerr/117.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL arena for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/argerr/118.c b/test/argerr/118.c index 2064345cc0..27f1cb0ee2 100644 --- a/test/argerr/118.c +++ b/test/argerr/118.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED arena for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/119.c b/test/argerr/119.c index 23a62e0674..0b0f1d9892 100644 --- a/test/argerr/119.c +++ b/test/argerr/119.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = MIN-1 rank for mps_root_create_fmt + summary = -1 rank for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ @@ -29,7 +33,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_root_create_fmt(&root, arena, MPS_RANK_MIN-1, 0, + cdie(mps_root_create_fmt(&root, arena, -1, 0, fmtscan, a, &a[32]), "root create"); diff --git a/test/argerr/12.c b/test/argerr/12.c index 2e896e8def..efb4f8acba 100644 --- a/test/argerr/12.c +++ b/test/argerr/12.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null 2nd arg to pool_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/argerr/120.c b/test/argerr/120.c index 81ff92bfff..26049da615 100644 --- a/test/argerr/120.c +++ b/test/argerr/120.c @@ -4,6 +4,10 @@ TEST_HEADER summary = MAX+1 rank for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ @@ -29,7 +33,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_root_create_fmt(&root, arena, MPS_RANK_MAX+1, 0, + cdie(mps_root_create_fmt(&root, arena, 4, 0, fmtscan, a, &a[32]), "root create"); diff --git a/test/argerr/121.c b/test/argerr/121.c index 13ae451b2a..b19ca32b78 100644 --- a/test/argerr/121.c +++ b/test/argerr/121.c @@ -4,6 +4,10 @@ TEST_HEADER summary = highbit set rank for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ diff --git a/test/argerr/122.c b/test/argerr/122.c index 0ed85e64bd..bbe91a830a 100644 --- a/test/argerr/122.c +++ b/test/argerr/122.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = MIN-1 root mode for mps_root_create_fmt + summary = -1 root mode for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = (mode & (RootModeCONSTANT | RootModePROTECTABLE | RootModePROTECTABLE_INNER)) == mode END_HEADER */ @@ -29,7 +33,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_root_create_fmt(&root, arena, mps_rank_ambig(), MPS_RM_MIN-1, + cdie(mps_root_create_fmt(&root, arena, mps_rank_ambig(), -1, fmtscan, a, &a[32]), "root create"); diff --git a/test/argerr/123.c b/test/argerr/123.c index 278df8131a..fb39e9e35b 100644 --- a/test/argerr/123.c +++ b/test/argerr/123.c @@ -4,6 +4,10 @@ TEST_HEADER summary = MAX+1 root mode for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = (mode & (RootModeCONSTANT | RootModePROTECTABLE | RootModePROTECTABLE_INNER)) == mode END_HEADER */ @@ -29,7 +33,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_root_create_fmt(&root, arena, mps_rank_ambig(), MPS_RM_MAX+1, + cdie(mps_root_create_fmt(&root, arena, mps_rank_ambig(), 8, fmtscan, a, &a[32]), "root create"); diff --git a/test/argerr/124.c b/test/argerr/124.c index 5154f119ed..31534bb65d 100644 --- a/test/argerr/124.c +++ b/test/argerr/124.c @@ -4,6 +4,10 @@ TEST_HEADER summary = highbit set root mode for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = (mode & (RootModeCONSTANT | RootModePROTECTABLE | RootModePROTECTABLE_INNER)) == mode END_HEADER */ diff --git a/test/argerr/125.c b/test/argerr/125.c index f83d832875..61b66ebbc6 100644 --- a/test/argerr/125.c +++ b/test/argerr/125.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null base for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = base != 0 END_HEADER */ diff --git a/test/argerr/127.c b/test/argerr/127.c index 2245b1fb8b..be27cfc5b7 100644 --- a/test/argerr/127.c +++ b/test/argerr/127.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL limit for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = base < limit END_HEADER */ diff --git a/test/argerr/129.c b/test/argerr/129.c index 25c743cc75..0a1091e1dd 100644 --- a/test/argerr/129.c +++ b/test/argerr/129.c @@ -4,6 +4,10 @@ TEST_HEADER summary = limit < base for mps_root_create_fmt language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = base < limit END_HEADER */ diff --git a/test/argerr/13.c b/test/argerr/13.c index ea5282db6e..5e705b8ba0 100644 --- a/test/argerr/13.c +++ b/test/argerr/13.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED 2nd arg to pool_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/130.c b/test/argerr/130.c index f2eb78d311..cabeee1a04 100644 --- a/test/argerr/130.c +++ b/test/argerr/130.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null &root_t for mps_root_create_reg language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_root_o != NULL END_HEADER */ diff --git a/test/argerr/131.c b/test/argerr/131.c index eedfebb393..362ca31c8c 100644 --- a/test/argerr/131.c +++ b/test/argerr/131.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED &root_t for mps_root_create_reg language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/132.c b/test/argerr/132.c index f5e3edef73..150fbbec17 100644 --- a/test/argerr/132.c +++ b/test/argerr/132.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null arena for mps_root_create_reg language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/argerr/133.c b/test/argerr/133.c index 45aa48e5f3..49509529ef 100644 --- a/test/argerr/133.c +++ b/test/argerr/133.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED arena for mps_root_create_reg language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/134.c b/test/argerr/134.c index 24bf2b9d4a..e0e34a4240 100644 --- a/test/argerr/134.c +++ b/test/argerr/134.c @@ -4,6 +4,10 @@ TEST_HEADER summary = < AMBIG rank for mps_root_create_reg (with stack scan ambig) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = rank == mps_rank_ambig() END_HEADER */ diff --git a/test/argerr/135.c b/test/argerr/135.c index da983b14af..5f5edbf7fe 100644 --- a/test/argerr/135.c +++ b/test/argerr/135.c @@ -4,6 +4,10 @@ TEST_HEADER summary = > AMBIG rank for mps_root_create_reg (with stack scan ambig) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = rank == mps_rank_ambig() END_HEADER */ diff --git a/test/argerr/136.c b/test/argerr/136.c index 2d48f95154..5bbfcc8a50 100644 --- a/test/argerr/136.c +++ b/test/argerr/136.c @@ -4,6 +4,10 @@ TEST_HEADER summary = highbit set rank for mps_root_create_reg (with stack scan ambig) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = rank == mps_rank_ambig() END_HEADER */ diff --git a/test/argerr/137.c b/test/argerr/137.c index feb18be335..0d98b56a58 100644 --- a/test/argerr/137.c +++ b/test/argerr/137.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = MIN-1 root mode for mps_root_create_reg (with stack scan ambig) + summary = -1 root mode for mps_root_create_reg (with stack scan ambig) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_rm == (mps_rm_t)0 END_HEADER */ @@ -23,7 +27,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); cdie(mps_root_create_reg(&root, arena, mps_rank_ambig(), - MPS_RM_MIN-1, + -1, thread, mps_stack_scan_ambig, stackpointer, 0), "root create"); diff --git a/test/argerr/138.c b/test/argerr/138.c index 39db49adc4..472b37c607 100644 --- a/test/argerr/138.c +++ b/test/argerr/138.c @@ -4,6 +4,10 @@ TEST_HEADER summary = MAX+1 root mode for mps_root_create_reg (with stack scan ambig) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_rm == (mps_rm_t)0 END_HEADER */ @@ -23,7 +27,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); cdie(mps_root_create_reg(&root, arena, mps_rank_ambig(), - MPS_RM_MAX+1, + 8, thread, mps_stack_scan_ambig, stackpointer, 0), "root create"); diff --git a/test/argerr/139.c b/test/argerr/139.c index eba8a5d91d..a87a6f340f 100644 --- a/test/argerr/139.c +++ b/test/argerr/139.c @@ -4,6 +4,10 @@ TEST_HEADER summary = highbit set root mode for mps_root_create_reg (with stack scan ambig) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_rm == (mps_rm_t)0 END_HEADER */ diff --git a/test/argerr/14.c b/test/argerr/14.c index 72b9fc053e..7cef120f73 100644 --- a/test/argerr/14.c +++ b/test/argerr/14.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL 3rd arg to pool_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(PoolClass, pool_class) END_HEADER */ diff --git a/test/argerr/140.c b/test/argerr/140.c index 34aa7d7e9b..419105bc2a 100644 --- a/test/argerr/140.c +++ b/test/argerr/140.c @@ -4,6 +4,9 @@ TEST_HEADER summary = null thread for mps_root_create_reg language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertcond = SigCheck Thread: thread END_HEADER */ diff --git a/test/argerr/141.c b/test/argerr/141.c index 327e0d3a87..b00f4bc9fb 100644 --- a/test/argerr/141.c +++ b/test/argerr/141.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED thread for mps_root_create_reg language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/142.c b/test/argerr/142.c index f48f42f09c..1c077ba710 100644 --- a/test/argerr/142.c +++ b/test/argerr/142.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null stackpointer for mps_root_create_reg language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = reg_scan_p != NULL END_HEADER */ diff --git a/test/argerr/143.c b/test/argerr/143.c index 045b8d8989..9099a6c3c1 100644 --- a/test/argerr/143.c +++ b/test/argerr/143.c @@ -4,6 +4,10 @@ TEST_HEADER summary = UNALIGNED stackpointer for mps_root_create_reg language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = AddrIsAligned(reg_scan_p, sizeof(Word)) END_HEADER */ diff --git a/test/argerr/144.c b/test/argerr/144.c index 85957f36a7..3556c45a96 100644 --- a/test/argerr/144.c +++ b/test/argerr/144.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null root_t for mps_root_destroy language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = TESTT(Root, root) END_HEADER */ diff --git a/test/argerr/145.c b/test/argerr/145.c index c8883bb590..8795d32856 100644 --- a/test/argerr/145.c +++ b/test/argerr/145.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED root_t for mps_root_destroy language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/146.c b/test/argerr/146.c index 84df17aa0e..c6d465671e 100644 --- a/test/argerr/146.c +++ b/test/argerr/146.c @@ -3,10 +3,14 @@ TEST_HEADER id = $Id$ summary = null scan state to fix (function) language = c - link = testlib.o newfmt.o + link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ +#include + #include "testlib.h" #include "mpscamc.h" #include "arg.h" @@ -15,7 +19,6 @@ END_HEADER */ #include "mps.h" -#include "newfmt.h" /* tags */ @@ -76,9 +79,7 @@ static int freeze=0; typedef int tag; -/* typedef union mycell mycell; - (it's in the header) -*/ +typedef union mycell mycell; struct padsingle {tag tag;}; @@ -450,7 +451,7 @@ static void test(void) mps_ap_t ap; int j; - mycell *a; + mycell *a, *b; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -469,7 +470,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); @@ -482,9 +483,20 @@ static void test(void) for (j=1; j<1000; j++) { - allocone(ap, 1000); + b = allocone(ap, 1000); + setref(b, 0, a); + a = b; } + mps_arena_collect(arena); + + mps_ap_destroy(ap); + mps_pool_destroy(pool); + mps_chain_destroy(chain); + mps_fmt_destroy(format); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); } int main(void) diff --git a/test/argerr/147.c b/test/argerr/147.c index 6b49ca0291..0b11507cc8 100644 --- a/test/argerr/147.c +++ b/test/argerr/147.c @@ -3,10 +3,14 @@ TEST_HEADER id = $Id$ summary = unaligned scan state to fix (function) language = c - link = testlib.o newfmt.o + link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ +#include + #include "testlib.h" #include "mpscamc.h" #include "arg.h" @@ -15,7 +19,6 @@ END_HEADER */ #include "mps.h" -#include "newfmt.h" /* tags */ @@ -76,9 +79,7 @@ static int freeze=0; typedef int tag; -/* typedef union mycell mycell; - (it's in the header) -*/ +typedef union mycell mycell; struct padsingle {tag tag;}; @@ -450,7 +451,7 @@ static void test(void) mps_ap_t ap; int j; - mycell *a; + mycell *a, *b; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -469,7 +470,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); @@ -482,7 +483,9 @@ static void test(void) for (j=1; j<1000; j++) { - allocone(ap, 1000); + b = allocone(ap, 1000); + setref(b, 0, a); + a = b; } } diff --git a/test/argerr/148.c b/test/argerr/148.c index 32c12d95d2..814429c1cd 100644 --- a/test/argerr/148.c +++ b/test/argerr/148.c @@ -3,10 +3,14 @@ TEST_HEADER id = $Id$ summary = null addr to fix (function) language = c - link = testlib.o newfmt.o + link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ +#include + #include "testlib.h" #include "mpscamc.h" #include "arg.h" @@ -15,7 +19,6 @@ END_HEADER */ #include "mps.h" -#include "newfmt.h" /* tags */ @@ -76,9 +79,7 @@ static int freeze=0; typedef int tag; -/* typedef union mycell mycell; - (it's in the header) -*/ +typedef union mycell mycell; struct padsingle {tag tag;}; @@ -450,7 +451,7 @@ static void test(void) mps_ap_t ap; int j; - mycell *a; + mycell *a, *b; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -469,7 +470,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); @@ -482,7 +483,9 @@ static void test(void) for (j=1; j<1000; j++) { - allocone(ap, 1000); + b = allocone(ap, 1000); + setref(b, 0, a); + a = b; } } diff --git a/test/argerr/149.c b/test/argerr/149.c index 167357319b..c86d9ce398 100644 --- a/test/argerr/149.c +++ b/test/argerr/149.c @@ -3,10 +3,12 @@ TEST_HEADER id = $Id$ summary = pointer to null addr to fix (function) language = c - link = testlib.o newfmt.o + link = testlib.o END_HEADER */ +#include + #include "testlib.h" #include "mpscamc.h" #include "arg.h" @@ -15,7 +17,6 @@ END_HEADER */ #include "mps.h" -#include "newfmt.h" /* tags */ @@ -76,9 +77,7 @@ static int freeze=0; typedef int tag; -/* typedef union mycell mycell; - (it's in the header) -*/ +typedef union mycell mycell; struct padsingle {tag tag;}; @@ -451,7 +450,7 @@ static void test(void) mps_ap_t ap; int j; - mycell *a; + mycell *a, *b; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -470,7 +469,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); @@ -483,7 +482,9 @@ static void test(void) for (j=1; j<1000; j++) { - allocone(ap, 1000); + b = allocone(ap, 1000); + setref(b, 0, a); + a = b; } } diff --git a/test/argerr/15.c b/test/argerr/15.c index e12c20b3ef..e2eeb83051 100644 --- a/test/argerr/15.c +++ b/test/argerr/15.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED 3rd arg to pool_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/150.c b/test/argerr/150.c index 9884ad87be..88d751f5d4 100644 --- a/test/argerr/150.c +++ b/test/argerr/150.c @@ -3,10 +3,12 @@ TEST_HEADER id = $Id$ summary = unaligned addr to fix (function) language = c - link = testlib.o newfmt.o + link = testlib.o END_HEADER */ +#include + #include "testlib.h" #include "mpscamc.h" #include "arg.h" @@ -15,7 +17,6 @@ END_HEADER */ #include "mps.h" -#include "newfmt.h" /* tags */ @@ -76,9 +77,7 @@ static int freeze=0; typedef int tag; -/* typedef union mycell mycell; - (it's in the header) -*/ +typedef union mycell mycell; struct padsingle {tag tag;}; @@ -450,7 +449,7 @@ static void test(void) mps_ap_t ap; int j; - mycell *a; + mycell *a, *b; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -469,7 +468,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); @@ -482,7 +481,9 @@ static void test(void) for (j=1; j<1000; j++) { - allocone(ap, 1000); + b = allocone(ap, 1000); + setref(b, 0, a); + a = b; } } diff --git a/test/argerr/151.c b/test/argerr/151.c index a310ab9afe..19ed2169a9 100644 --- a/test/argerr/151.c +++ b/test/argerr/151.c @@ -3,10 +3,12 @@ TEST_HEADER id = $Id$ summary = pointer to unaligned addr to fix (function) language = c - link = testlib.o newfmt.o + link = testlib.o END_HEADER */ +#include + #include "testlib.h" #include "mpscamc.h" #include "arg.h" @@ -15,7 +17,6 @@ END_HEADER */ #include "mps.h" -#include "newfmt.h" /* tags */ @@ -76,9 +77,7 @@ static int freeze=0; typedef int tag; -/* typedef union mycell mycell; - (it's in the header) -*/ +typedef union mycell mycell; struct padsingle {tag tag;}; @@ -451,7 +450,7 @@ static void test(void) mps_ap_t ap; int j; - mycell *a; + mycell *a, *b; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -470,7 +469,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); @@ -483,7 +482,9 @@ static void test(void) for (j=1; j<1000; j++) { - allocone(ap, 1000); + b = allocone(ap, 1000); + setref(b, 0, a); + a = b; } } diff --git a/test/argerr/152.c b/test/argerr/152.c index 28a9b70fca..18d0feb933 100644 --- a/test/argerr/152.c +++ b/test/argerr/152.c @@ -35,7 +35,6 @@ static void test(void) cdie(mps_alloc(&a, pool, mysize), "alloc"); mps_free(pool, (mps_addr_t) ((char *)a+8), mysize); - error("free"); mps_pool_destroy(pool); } diff --git a/test/argerr/153.c b/test/argerr/153.c index d7c76b1b40..b6d1d6791f 100644 --- a/test/argerr/153.c +++ b/test/argerr/153.c @@ -1,9 +1,12 @@ /* TEST_HEADER id = $Id$ - summary = -1 as third argument to mps_alloc (MV) + summary = very large number as third argument to mps_alloc (MV) language = c link = testlib.o +OUTPUT_SPEC + error = true + errtext = alloc: RESOURCE END_HEADER */ @@ -20,7 +23,7 @@ static void test(void) { cdie(mps_pool_create(&pool, arena, mps_class_mv(), 1024*32, 1024*16, 1024*256), "pool"); - cdie(mps_alloc(&q, pool, (size_t) -1), "alloc"); + cdie(mps_alloc(&q, pool, (size_t) -100 * mmqaArenaSIZE), "alloc"); mps_pool_destroy(pool); mps_arena_destroy(arena); @@ -28,6 +31,5 @@ static void test(void) { int main(void) { easy_tramp(test); - pass(); return 0; } diff --git a/test/argerr/154.c b/test/argerr/154.c index 702aa21196..58bfb6f50e 100644 --- a/test/argerr/154.c +++ b/test/argerr/154.c @@ -4,6 +4,10 @@ TEST_HEADER summary = size = -MPS_PF_ALIGN to mps_reserve language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= locus.c + assertcond = size > 0 END_HEADER */ @@ -51,7 +55,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/16.c b/test/argerr/16.c index 9b3501ecc3..fc7289dfa4 100644 --- a/test/argerr/16.c +++ b/test/argerr/16.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL 4th arg to pool_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= arg.c + assertcond = SigCheck Format: arg->val.format END_HEADER */ diff --git a/test/argerr/17.c b/test/argerr/17.c index d82fab6b1c..6c509d28bd 100644 --- a/test/argerr/17.c +++ b/test/argerr/17.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED 4th arg to pool_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -25,7 +27,6 @@ static void test(void) mps_pool_t pool; mps_thr_t thread; mps_root_t root; - mps_chain_t chain; mps_fmt_t format; diff --git a/test/argerr/18.c b/test/argerr/18.c index 432a20f984..1e1da958b7 100644 --- a/test/argerr/18.c +++ b/test/argerr/18.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL arg to pool_destroy language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ @@ -46,7 +50,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/19.c b/test/argerr/19.c index 5cc02b4e6b..b6048163a8 100644 --- a/test/argerr/19.c +++ b/test/argerr/19.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED arg to pool_destroy language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -25,7 +27,6 @@ static void test(void) mps_pool_t pool; mps_thr_t thread; mps_root_t root; - mps_chain_t chain; mps_fmt_t format; @@ -46,7 +47,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/2.c b/test/argerr/2.c index c4589032b7..0d7b912541 100644 --- a/test/argerr/2.c +++ b/test/argerr/2.c @@ -4,6 +4,10 @@ TEST_HEADER summary = destroy an arena with an null arena_t language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/argerr/20.c b/test/argerr/20.c index 91ba4eecd1..356aa7f0bd 100644 --- a/test/argerr/20.c +++ b/test/argerr/20.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = NULL 1st arg to mps_alloc + summary = NULL 1st arg to mps_alloc (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = p_o != NULL END_HEADER */ @@ -20,7 +24,6 @@ static void test(void) mps_thr_t thread; size_t mysize; - mps_addr_t a; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); diff --git a/test/argerr/21.c b/test/argerr/21.c index a4864dee93..24ac122270 100644 --- a/test/argerr/21.c +++ b/test/argerr/21.c @@ -1,9 +1,11 @@ /* TEST_HEADER id = $Id$ - summary = UNALIGNED 1st arg to mps_alloc + summary = UNALIGNED 1st arg to mps_alloc (MFS) language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -20,7 +22,6 @@ static void test(void) mps_thr_t thread; size_t mysize; - mps_addr_t a; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); diff --git a/test/argerr/22.c b/test/argerr/22.c index 153d472632..eef081c491 100644 --- a/test/argerr/22.c +++ b/test/argerr/22.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = NULL 2nd arg to mps_alloc + summary = NULL 2nd arg to mps_alloc (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ diff --git a/test/argerr/23.c b/test/argerr/23.c index d0446ab6f4..eea18e3944 100644 --- a/test/argerr/23.c +++ b/test/argerr/23.c @@ -1,9 +1,11 @@ /* TEST_HEADER id = $Id$ - summary = UNALIGNED 2nd arg to mps_alloc + summary = UNALIGNED 2nd arg to mps_alloc (MFS) language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/24.c b/test/argerr/24.c index 577930c930..2418039edf 100644 --- a/test/argerr/24.c +++ b/test/argerr/24.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = zero 3rd arg to mps_alloc + summary = zero 3rd arg to mps_alloc (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = size > 0 END_HEADER */ diff --git a/test/argerr/25.c b/test/argerr/25.c index 9bce2b69bf..e1d8c93a71 100644 --- a/test/argerr/25.c +++ b/test/argerr/25.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = high bit set 3rd arg to mps_alloc + summary = high bit set 3rd arg to mps_alloc (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolmfs.c + assertcond = size == mfs->unroundedUnitSize END_HEADER */ diff --git a/test/argerr/26.c b/test/argerr/26.c index 4e61584b1f..3329cc38c5 100644 --- a/test/argerr/26.c +++ b/test/argerr/26.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = wrong 3rd arg to mps_alloc + summary = wrong 3rd arg to mps_alloc (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolmfs.c + assertcond = size == mfs->unroundedUnitSize END_HEADER */ diff --git a/test/argerr/27.c b/test/argerr/27.c index ee8bb2179e..d0b5b5de68 100644 --- a/test/argerr/27.c +++ b/test/argerr/27.c @@ -1,9 +1,11 @@ /* TEST_HEADER id = $Id$ - summary = unaligned 1st arg to mps_free + summary = unaligned 1st arg to mps_free (MFS) language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/28.c b/test/argerr/28.c index 114456e3f2..46969d09bd 100644 --- a/test/argerr/28.c +++ b/test/argerr/28.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = NULL 1st arg to mps_free + summary = NULL 1st arg to mps_free (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ diff --git a/test/argerr/29.c b/test/argerr/29.c index e77bb2dca7..d453b65c61 100644 --- a/test/argerr/29.c +++ b/test/argerr/29.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = unaligned 2nd arg to mps_free + summary = unaligned 2nd arg to mps_free (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= pool.c + assertcond = AddrIsAligned(old, pool->alignment) END_HEADER */ diff --git a/test/argerr/3.c b/test/argerr/3.c index 11ff90c716..18fe333316 100644 --- a/test/argerr/3.c +++ b/test/argerr/3.c @@ -4,6 +4,8 @@ TEST_HEADER summary = destroy an arena with an unaligned arena_t language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/30.c b/test/argerr/30.c index b8bf2d616a..812ea8e9e7 100644 --- a/test/argerr/30.c +++ b/test/argerr/30.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = NULL 2nd arg to mps_free + summary = NULL 2nd arg to mps_free (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= pool.c + assertcond = old != NULL END_HEADER */ diff --git a/test/argerr/31.c b/test/argerr/31.c index 5fbbf7e961..c09f9d7c5d 100644 --- a/test/argerr/31.c +++ b/test/argerr/31.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = zero 3rd arg to mps_free + summary = zero 3rd arg to mps_free (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = size > 0 END_HEADER */ diff --git a/test/argerr/32.c b/test/argerr/32.c index a483ea5f11..4f97ee61cc 100644 --- a/test/argerr/32.c +++ b/test/argerr/32.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = wrong 3rd arg to mps_free + summary = wrong 3rd arg to mps_free (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolmfs.c + assertcond = size == mfs->unroundedUnitSize END_HEADER */ diff --git a/test/argerr/33.c b/test/argerr/33.c index 0862b911bf..0401b1c5e5 100644 --- a/test/argerr/33.c +++ b/test/argerr/33.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = highbit set 3rd arg to mps_free + summary = highbit set 3rd arg to mps_free (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= pool.c + assertcond = (alignedLimit) <= _ch->limit END_HEADER */ diff --git a/test/argerr/34.c b/test/argerr/34.c index 63b2d34d1a..51c58ee0ab 100644 --- a/test/argerr/34.c +++ b/test/argerr/34.c @@ -4,6 +4,9 @@ TEST_HEADER summary = high bit set 3rd arg to mps_alloc (MV) language = c link = testlib.o +OUTPUT_SPEC + error = true + errtext = allocation failed (correct): RESOURCE END_HEADER */ diff --git a/test/argerr/35.c b/test/argerr/35.c index 1f98093047..218721759b 100644 --- a/test/argerr/35.c +++ b/test/argerr/35.c @@ -4,6 +4,10 @@ TEST_HEADER summary = unaligned addr_t to free (MV) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= pool.c + assertcond = AddrIsAligned(old, pool->alignment) END_HEADER */ diff --git a/test/argerr/36.c b/test/argerr/36.c index 8704bc1dc2..ac35d796ca 100644 --- a/test/argerr/36.c +++ b/test/argerr/36.c @@ -4,6 +4,10 @@ TEST_HEADER summary = wrong size_t to free (MV) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= dbgpool.c + assertcond = tag->size == size END_HEADER */ @@ -25,11 +29,8 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie( - mps_pool_create( - &pool, arena, mps_class_mv(), - (size_t) 4096, (size_t) 32, (size_t) 64*1024), - "create pool"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mv_debug(), mps_args_none), + "create pool"); die(mps_alloc(&a, pool, 8), "alloc a"); diff --git a/test/argerr/37.c b/test/argerr/37.c index 56a897614c..ffcd86a265 100644 --- a/test/argerr/37.c +++ b/test/argerr/37.c @@ -4,6 +4,10 @@ TEST_HEADER summary = wrong size_t to free (MV) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= pool.c + assertcond = (alignedLimit) <= _ch->limit END_HEADER */ diff --git a/test/argerr/38.c b/test/argerr/38.c index c599b300e8..3da121b91d 100644 --- a/test/argerr/38.c +++ b/test/argerr/38.c @@ -4,6 +4,10 @@ TEST_HEADER summary = zero extendBy for pool_create (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolmfs.c + assertcond = extendBy > 0 END_HEADER */ diff --git a/test/argerr/39.c b/test/argerr/39.c index 99c4c3d994..abea09f623 100644 --- a/test/argerr/39.c +++ b/test/argerr/39.c @@ -4,6 +4,10 @@ TEST_HEADER summary = zero unitSize for pool_create (MFS) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolmfs.c + assertcond = unitSize > 0 END_HEADER */ diff --git a/test/argerr/4.c b/test/argerr/4.c index a237ff67bb..80f17177eb 100644 --- a/test/argerr/4.c +++ b/test/argerr/4.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL 1st arg to fmt_create_A language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_fmt_o != NULL END_HEADER */ @@ -17,7 +21,6 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_pool_t pool; mps_thr_t thread; mps_root_t root; diff --git a/test/argerr/40.c b/test/argerr/40.c index 4e498b29cb..b087cd8f82 100644 --- a/test/argerr/40.c +++ b/test/argerr/40.c @@ -38,5 +38,6 @@ int main(void) stackpointer=&m; /* hack to get stack pointer */ easy_tramp(test); + pass(); return 0; } diff --git a/test/argerr/41.c b/test/argerr/41.c index 692e31180b..8aa1520aa4 100644 --- a/test/argerr/41.c +++ b/test/argerr/41.c @@ -4,6 +4,10 @@ TEST_HEADER summary = zero extendBy for pool_create (MV) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolmv.c + assertcond = extendBy > 0 END_HEADER */ diff --git a/test/argerr/42.c b/test/argerr/42.c index ec001f8a48..cfc5acac79 100644 --- a/test/argerr/42.c +++ b/test/argerr/42.c @@ -4,6 +4,10 @@ TEST_HEADER summary = zero avgSize for pool_create (MV) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolmv.c + assertcond = avgSize > 0 END_HEADER */ diff --git a/test/argerr/43.c b/test/argerr/43.c index 1850d11b0e..7e68227aaa 100644 --- a/test/argerr/43.c +++ b/test/argerr/43.c @@ -4,6 +4,10 @@ TEST_HEADER summary = zero maxSize for pool_create (MV) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolmv.c + assertcond = maxSize > 0 END_HEADER */ diff --git a/test/argerr/44.c b/test/argerr/44.c index c7b5954bc2..f1c752aada 100644 --- a/test/argerr/44.c +++ b/test/argerr/44.c @@ -4,6 +4,10 @@ TEST_HEADER summary = extendBy > maxSize for pool_create (MV) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolmv.c + assertcond = extendBy <= maxSize END_HEADER */ diff --git a/test/argerr/45.c b/test/argerr/45.c index 8c07ea42b7..c93fc3bbf7 100644 --- a/test/argerr/45.c +++ b/test/argerr/45.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null ap_t to mps_ap_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_ap_o != NULL END_HEADER */ @@ -46,7 +50,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/46.c b/test/argerr/46.c index 2c9e869124..29079884ed 100644 --- a/test/argerr/46.c +++ b/test/argerr/46.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED ap_t to mps_ap_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -46,7 +48,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/47.c b/test/argerr/47.c index 35d878d716..ede9b9a0da 100644 --- a/test/argerr/47.c +++ b/test/argerr/47.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL pool to mps_ap_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ @@ -47,7 +51,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/48.c b/test/argerr/48.c index 8c1870bd92..a938b8371c 100644 --- a/test/argerr/48.c +++ b/test/argerr/48.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED pool to mps_ap_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -47,7 +49,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/49.c b/test/argerr/49.c index 34a1b2bc87..d75e9c258c 100644 --- a/test/argerr/49.c +++ b/test/argerr/49.c @@ -4,11 +4,15 @@ TEST_HEADER summary = too small rank to mps_ap_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ #include "testlib.h" -#include "mpscamc.h" +#include "mpscawl.h" #include "newfmt.h" #include "arg.h" @@ -47,12 +51,12 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( - mps_pool_create(&pool, arena, mps_class_amc(), format, chain), + cdie( + mps_pool_create(&pool, arena, mps_class_awl(), format, chain), "create pool"); cdie( - mps_ap_create(&ap, pool, MPS_RANK_MIN-1), + mps_ap_create(&ap, pool, -1), "create ap"); } diff --git a/test/argerr/5.c b/test/argerr/5.c index 2e145b665a..a13072e130 100644 --- a/test/argerr/5.c +++ b/test/argerr/5.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED 1st arg to fmt_create_A language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -17,7 +19,6 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_pool_t pool; mps_thr_t thread; mps_root_t root; diff --git a/test/argerr/50.c b/test/argerr/50.c index 3fa9e2334b..d4e700aaf6 100644 --- a/test/argerr/50.c +++ b/test/argerr/50.c @@ -4,11 +4,15 @@ TEST_HEADER summary = too big rank to mps_ap_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ #include "testlib.h" -#include "mpscamc.h" +#include "mpscawl.h" #include "newfmt.h" #include "arg.h" @@ -47,12 +51,12 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( - mps_pool_create(&pool, arena, mps_class_amc(), format, chain), + cdie( + mps_pool_create(&pool, arena, mps_class_awl(), format, chain), "create pool"); cdie( - mps_ap_create(&ap, pool, MPS_RANK_MAX+1), + mps_ap_create(&ap, pool, 5), "create ap"); } diff --git a/test/argerr/51.c b/test/argerr/51.c index 28c8947b97..e4a901fbb8 100644 --- a/test/argerr/51.c +++ b/test/argerr/51.c @@ -4,11 +4,15 @@ TEST_HEADER summary = highbit set rank to mps_ap_create language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ #include "testlib.h" -#include "mpscamc.h" +#include "mpscawl.h" #include "newfmt.h" #include "arg.h" @@ -47,8 +51,8 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( - mps_pool_create(&pool, arena, mps_class_amc(), format, chain), + cdie( + mps_pool_create(&pool, arena, mps_class_awl(), format, chain), "create pool"); cdie( diff --git a/test/argerr/52.c b/test/argerr/52.c index ad2ab39090..a8a9c41936 100644 --- a/test/argerr/52.c +++ b/test/argerr/52.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL ap to mps_ap_destroy language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_ap != NULL END_HEADER */ @@ -47,7 +51,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/53.c b/test/argerr/53.c index f021eb4d7a..fa60735e63 100644 --- a/test/argerr/53.c +++ b/test/argerr/53.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED ap to mps_ap_destroy language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -47,7 +49,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/54.c b/test/argerr/54.c index 885ab8d88b..f983a82527 100644 --- a/test/argerr/54.c +++ b/test/argerr/54.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null addr_t to mps_reserve language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = p_o != NULL END_HEADER */ @@ -50,7 +54,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/55.c b/test/argerr/55.c index 4b27c30b95..087ad3d95e 100644 --- a/test/argerr/55.c +++ b/test/argerr/55.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED addr_t to mps_reserve language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -50,7 +52,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/56.c b/test/argerr/56.c index bfb2068d54..51bf29cc98 100644 --- a/test/argerr/56.c +++ b/test/argerr/56.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL ap to mps_reserve language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_ap != NULL END_HEADER */ @@ -51,7 +55,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/57.c b/test/argerr/57.c index 06d0b9c3c4..773fa1fbb5 100644 --- a/test/argerr/57.c +++ b/test/argerr/57.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED ap to mps_reserve language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -51,7 +53,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/58.c b/test/argerr/58.c index 48856c03e7..530b559827 100644 --- a/test/argerr/58.c +++ b/test/argerr/58.c @@ -4,6 +4,10 @@ TEST_HEADER summary = zero size to mps_reserve language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = size > 0 END_HEADER */ @@ -51,7 +55,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/59.c b/test/argerr/59.c index 23cef90813..6dd044fe64 100644 --- a/test/argerr/59.c +++ b/test/argerr/59.c @@ -4,6 +4,9 @@ TEST_HEADER summary = highbit set size to mps_reserve language = c link = testlib.o newfmt.o +OUTPUT_SPEC + error = true + errtext = reserve: RESOURCE END_HEADER */ @@ -51,7 +54,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/6.c b/test/argerr/6.c index 28ecc9cc89..8c3848aaba 100644 --- a/test/argerr/6.c +++ b/test/argerr/6.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL 2nd arg to fmt_create_A language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ @@ -17,7 +21,6 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_pool_t pool; mps_thr_t thread; mps_root_t root; diff --git a/test/argerr/60.c b/test/argerr/60.c index f3c2f205cc..92cbcd5f04 100644 --- a/test/argerr/60.c +++ b/test/argerr/60.c @@ -4,6 +4,10 @@ TEST_HEADER summary = unaligned size to mps_reserve language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = SizeIsAligned(size, BufferPool(buf)->alignment) END_HEADER */ @@ -51,7 +55,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/61.c b/test/argerr/61.c index 3cb4d68fc4..a1d5c81b59 100644 --- a/test/argerr/61.c +++ b/test/argerr/61.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null ap to mps_commit language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_ap != NULL END_HEADER */ @@ -51,7 +55,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/62.c b/test/argerr/62.c index fab0dec321..9cb1ef1e6d 100644 --- a/test/argerr/62.c +++ b/test/argerr/62.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED ap to mps_commit language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -51,7 +53,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/63.c b/test/argerr/63.c index db42bc9199..41a2cf52f9 100644 --- a/test/argerr/63.c +++ b/test/argerr/63.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL addr to mps_commit language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = p != NULL END_HEADER */ @@ -51,7 +55,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/64.c b/test/argerr/64.c index 0434b253b7..4743c21d06 100644 --- a/test/argerr/64.c +++ b/test/argerr/64.c @@ -4,6 +4,10 @@ TEST_HEADER summary = UNALIGNED addr to mps_commit language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = p == mps_ap->init END_HEADER */ @@ -51,7 +55,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/65.c b/test/argerr/65.c index 4218ce52c4..65be30d9aa 100644 --- a/test/argerr/65.c +++ b/test/argerr/65.c @@ -4,6 +4,10 @@ TEST_HEADER summary = zero size to mps_commit language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = size > 0 END_HEADER */ @@ -51,7 +55,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/66.c b/test/argerr/66.c index 1e3ffaa6ba..9272b6c135 100644 --- a/test/argerr/66.c +++ b/test/argerr/66.c @@ -4,6 +4,10 @@ TEST_HEADER summary = highbit set size to mps_commit language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = PointerAdd(mps_ap->init, size) == mps_ap->alloc END_HEADER */ @@ -51,7 +55,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/67.c b/test/argerr/67.c index 65008d8fbe..b77eaa35d9 100644 --- a/test/argerr/67.c +++ b/test/argerr/67.c @@ -4,6 +4,10 @@ TEST_HEADER summary = unaligned size to mps_commit language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = PointerAdd(mps_ap->init, size) == mps_ap->alloc END_HEADER */ @@ -51,7 +55,7 @@ static void test(void) cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - ddie( + cdie( mps_pool_create(&pool, arena, mps_class_amc(), format, chain), "create pool"); diff --git a/test/argerr/68.c b/test/argerr/68.c index e8fb2d4231..e56f54fb11 100644 --- a/test/argerr/68.c +++ b/test/argerr/68.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null thr_t to thread_reg language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_thr_o != NULL END_HEADER */ diff --git a/test/argerr/69.c b/test/argerr/69.c index 09234b096d..28e9d44c11 100644 --- a/test/argerr/69.c +++ b/test/argerr/69.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED thr_t to thread_reg language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/7.c b/test/argerr/7.c index d05484bc8f..cc22fcb59e 100644 --- a/test/argerr/7.c +++ b/test/argerr/7.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED 2nd arg to fmt_create_A language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -17,10 +19,8 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_pool_t pool; mps_thr_t thread; mps_root_t root; - mps_fmt_t format; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); diff --git a/test/argerr/70.c b/test/argerr/70.c index b4ceea08e2..acdbfd0592 100644 --- a/test/argerr/70.c +++ b/test/argerr/70.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null arena_t to thread_reg language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/argerr/71.c b/test/argerr/71.c index ea51447b3b..f3f8878b6d 100644 --- a/test/argerr/71.c +++ b/test/argerr/71.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED arena_t to thread_reg language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/72.c b/test/argerr/72.c index e800ced685..2cb4585b67 100644 --- a/test/argerr/72.c +++ b/test/argerr/72.c @@ -4,6 +4,9 @@ TEST_HEADER summary = null thr_t to thread_dereg language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertcond = SigCheck Thread: thread END_HEADER */ diff --git a/test/argerr/73.c b/test/argerr/73.c index 4b6200ce29..552dc0c7c5 100644 --- a/test/argerr/73.c +++ b/test/argerr/73.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED thr_t to thread_dereg language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/74.c b/test/argerr/74.c index fe0695527c..b14aa1782c 100644 --- a/test/argerr/74.c +++ b/test/argerr/74.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null ld for ld_reset language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ld.c + assertcond = ld != NULL END_HEADER */ diff --git a/test/argerr/75.c b/test/argerr/75.c index d45e2d9877..dae670ff32 100644 --- a/test/argerr/75.c +++ b/test/argerr/75.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED ld for ld_reset language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/76.c b/test/argerr/76.c index 6bd331c7b3..4eb0bf682c 100644 --- a/test/argerr/76.c +++ b/test/argerr/76.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null arena for ld_reset language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/argerr/77.c b/test/argerr/77.c index 3df9bde1b6..1e618cd922 100644 --- a/test/argerr/77.c +++ b/test/argerr/77.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED arena for ld_reset language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/78.c b/test/argerr/78.c index cca2105c37..21c148bad3 100644 --- a/test/argerr/78.c +++ b/test/argerr/78.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null ld for ld_add language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ld.c + assertcond = ld != NULL END_HEADER */ diff --git a/test/argerr/79.c b/test/argerr/79.c index dd26fe1474..19ef79d88b 100644 --- a/test/argerr/79.c +++ b/test/argerr/79.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED ld for ld_add language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/8.c b/test/argerr/8.c index feaa9302a8..1313e74ef8 100644 --- a/test/argerr/8.c +++ b/test/argerr/8.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL arg to fmt_destroy language = c link = testlib.o newfmt.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Format, format) END_HEADER */ @@ -17,7 +21,6 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_pool_t pool; mps_thr_t thread; mps_root_t root; diff --git a/test/argerr/80.c b/test/argerr/80.c index 754fa802a4..fe18238e2d 100644 --- a/test/argerr/80.c +++ b/test/argerr/80.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null arena for ld_add language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ld.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/argerr/81.c b/test/argerr/81.c index 96cab4fde0..869a5b0cb1 100644 --- a/test/argerr/81.c +++ b/test/argerr/81.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED arena for ld_add language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/82.c b/test/argerr/82.c index 85bdda23f2..ee7c9c18d2 100644 --- a/test/argerr/82.c +++ b/test/argerr/82.c @@ -17,7 +17,6 @@ static void test(void) mps_arena_t arena; mps_ld_s ld; mps_thr_t thread; - mps_addr_t p; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); diff --git a/test/argerr/83.c b/test/argerr/83.c index 6a9e9d9a73..dbf8ceaed9 100644 --- a/test/argerr/83.c +++ b/test/argerr/83.c @@ -17,7 +17,6 @@ static void test(void) mps_arena_t arena; mps_ld_s ld; mps_thr_t thread; - mps_addr_t p; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); diff --git a/test/argerr/84.c b/test/argerr/84.c index f93e0744b5..d720daf13b 100644 --- a/test/argerr/84.c +++ b/test/argerr/84.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null ld for is_stale language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ld.c + assertcond = ld != NULL END_HEADER */ diff --git a/test/argerr/85.c b/test/argerr/85.c index c361fc1278..2d0a69d472 100644 --- a/test/argerr/85.c +++ b/test/argerr/85.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED ld for is_stale language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/86.c b/test/argerr/86.c index 475677f288..886d3120d1 100644 --- a/test/argerr/86.c +++ b/test/argerr/86.c @@ -4,6 +4,10 @@ TEST_HEADER summary = NULL arena for is_stale language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ld.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/argerr/87.c b/test/argerr/87.c index a30ad1ea09..3321e301c0 100644 --- a/test/argerr/87.c +++ b/test/argerr/87.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED arena for is_stale language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/9.c b/test/argerr/9.c index 69a4cd1a2a..59910608ef 100644 --- a/test/argerr/9.c +++ b/test/argerr/9.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED arg to fmt_destroy language = c link = testlib.o newfmt.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -17,10 +19,8 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_pool_t pool; mps_thr_t thread; mps_root_t root; - mps_fmt_t format; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); diff --git a/test/argerr/90.c b/test/argerr/90.c index aceef36def..b6a38156b4 100644 --- a/test/argerr/90.c +++ b/test/argerr/90.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null &root_t for mps_root_create language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_root_o != NULL END_HEADER */ diff --git a/test/argerr/91.c b/test/argerr/91.c index 3e4062d2e2..8264b1fe5d 100644 --- a/test/argerr/91.c +++ b/test/argerr/91.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED &root_t for mps_root_create language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/92.c b/test/argerr/92.c index f3240466c5..0a669653e9 100644 --- a/test/argerr/92.c +++ b/test/argerr/92.c @@ -4,6 +4,10 @@ TEST_HEADER summary = null arena for mps_root_create language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/argerr/93.c b/test/argerr/93.c index c675c96d1a..6dc8d383f0 100644 --- a/test/argerr/93.c +++ b/test/argerr/93.c @@ -4,6 +4,8 @@ TEST_HEADER summary = UNALIGNED arena for mps_root_create language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/argerr/94.c b/test/argerr/94.c index fb216241f1..cd308af5e5 100644 --- a/test/argerr/94.c +++ b/test/argerr/94.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = MPS_RANK_MIN-1 rank for mps_root_create + summary = -1 rank for mps_root_create language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ @@ -27,7 +31,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_root_create(&root, arena, MPS_RANK_MIN-1, 0, + cdie(mps_root_create(&root, arena, -1, 0, rootscan, NULL, 0), "root create"); diff --git a/test/argerr/95.c b/test/argerr/95.c index 0fb5f8e8f2..478a9dfd78 100644 --- a/test/argerr/95.c +++ b/test/argerr/95.c @@ -4,6 +4,10 @@ TEST_HEADER summary = MPS_RANK_MAX+1 rank for mps_root_create language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ @@ -27,7 +31,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_root_create(&root, arena, MPS_RANK_MAX+1, 0, + cdie(mps_root_create(&root, arena, 4, 0, rootscan, NULL, 0), "root create"); diff --git a/test/argerr/96.c b/test/argerr/96.c index 8a4799f3ed..adff8cc6a0 100644 --- a/test/argerr/96.c +++ b/test/argerr/96.c @@ -4,6 +4,10 @@ TEST_HEADER summary = highbit set rank for mps_root_create language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ref.c + assertcond = rank < RankLIMIT END_HEADER */ diff --git a/test/argerr/97.c b/test/argerr/97.c index 174d225468..5c10435c3c 100644 --- a/test/argerr/97.c +++ b/test/argerr/97.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = MIN-1 root mode for mps_root_create + summary = -1 root mode for mps_root_create language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_rm == (mps_rm_t)0 END_HEADER */ @@ -27,7 +31,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_root_create(&root, arena, mps_rank_ambig(), MPS_RM_MIN-1, + cdie(mps_root_create(&root, arena, mps_rank_ambig(), -1, rootscan, NULL, 0), "root create"); diff --git a/test/argerr/98.c b/test/argerr/98.c index 29498e21a2..35160ef047 100644 --- a/test/argerr/98.c +++ b/test/argerr/98.c @@ -4,6 +4,10 @@ TEST_HEADER summary = MAX+1 root mode for mps_root_create language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_rm == (mps_rm_t)0 END_HEADER */ @@ -27,7 +31,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_root_create(&root, arena, mps_rank_ambig(), MPS_RM_MAX+1, + cdie(mps_root_create(&root, arena, mps_rank_ambig(), 8, rootscan, NULL, 0), "root create"); diff --git a/test/argerr/99.c b/test/argerr/99.c index 2862da2808..9eece2c92b 100644 --- a/test/argerr/99.c +++ b/test/argerr/99.c @@ -4,6 +4,10 @@ TEST_HEADER summary = highbit set root mode for mps_root_create language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_rm == (mps_rm_t)0 END_HEADER */ diff --git a/test/conerr/0.c b/test/conerr/0.c index 8023ed2ff4..a7bd9a8256 100644 --- a/test/conerr/0.c +++ b/test/conerr/0.c @@ -4,6 +4,8 @@ TEST_HEADER summary = create an arena and then destroy it, twice! language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/conerr/1.c b/test/conerr/1.c index c7ddc8845b..2758c0385d 100644 --- a/test/conerr/1.c +++ b/test/conerr/1.c @@ -4,6 +4,8 @@ TEST_HEADER summary = destroy an arena without creating it language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -11,7 +13,7 @@ END_HEADER static void test(void) { - mps_arena_t arena; + mps_arena_t arena = (mps_arena_t)1; mps_arena_destroy(arena); comment("Destroy arena."); diff --git a/test/conerr/10.c b/test/conerr/10.c index a78add272b..ff28d34649 100644 --- a/test/conerr/10.c +++ b/test/conerr/10.c @@ -4,44 +4,23 @@ TEST_HEADER summary = destroy a format though uncreated language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Format, format) END_HEADER */ #include "testlib.h" #include "mpscmv.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return object; -} - static void test(void) { mps_arena_t arena; - mps_fmt_t format; - mps_fmt_A_s fmtA; - + mps_fmt_t format = (mps_fmt_t)&format; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - -/* cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); -*/ - mps_fmt_destroy(format); comment("Destroyed format."); diff --git a/test/conerr/11.c b/test/conerr/11.c index b39d8a1697..f3fe41f91c 100644 --- a/test/conerr/11.c +++ b/test/conerr/11.c @@ -4,42 +4,24 @@ TEST_HEADER summary = destroy a format twice language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Format, format) END_HEADER */ #include "testlib.h" #include "mpscmv.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return object; -} - static void test(void) { mps_arena_t arena; mps_fmt_t format; - mps_fmt_A_s fmtA; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); mps_fmt_destroy(format); comment("Destroyed format."); diff --git a/test/conerr/12.c b/test/conerr/12.c index eb4a48768f..a8f932114a 100644 --- a/test/conerr/12.c +++ b/test/conerr/12.c @@ -10,40 +10,17 @@ END_HEADER #include "testlib.h" #include "mpsclo.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return object; -} - static void test(void) { mps_arena_t arena; mps_fmt_t format; - mps_fmt_A_s fmtA; mps_pool_t pool; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); - - cdie( - mps_pool_create(&pool, arena, mps_class_lo(), format), - "create pool"); + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); + + cdie(mps_pool_create(&pool, arena, mps_class_lo(), format), "create pool"); mps_fmt_destroy(format); comment("Destroyed format."); diff --git a/test/conerr/13.c b/test/conerr/13.c index 89b93d50d7..b3712a99e4 100644 --- a/test/conerr/13.c +++ b/test/conerr/13.c @@ -4,6 +4,10 @@ TEST_HEADER summary = create a pool in an uncreated arena language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/conerr/14.c b/test/conerr/14.c index 380bbb8b5f..44cd5ea7f9 100644 --- a/test/conerr/14.c +++ b/test/conerr/14.c @@ -4,6 +4,8 @@ TEST_HEADER summary = create a pool in a destroyed arena language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/conerr/15.c b/test/conerr/15.c index 91ea53d6a2..c4114d5b99 100644 --- a/test/conerr/15.c +++ b/test/conerr/15.c @@ -4,6 +4,8 @@ TEST_HEADER summary = destroy an uncreated pool language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -13,7 +15,7 @@ END_HEADER static void test(void) { mps_arena_t arena; - mps_pool_t pool; + mps_pool_t pool = (mps_pool_t)1; size_t extendBy; size_t avgSize; size_t maxSize; diff --git a/test/conerr/16.c b/test/conerr/16.c index ca8e3c8415..5043f96a55 100644 --- a/test/conerr/16.c +++ b/test/conerr/16.c @@ -4,6 +4,10 @@ TEST_HEADER summary = destroy a pool twice language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ diff --git a/test/conerr/17.c b/test/conerr/17.c index f18a7f52f5..7eaaa0f554 100644 --- a/test/conerr/17.c +++ b/test/conerr/17.c @@ -1,56 +1,33 @@ /* TEST_HEADER id = $Id$ - summary = destroy a pool though containining an AP + summary = destroy a pool though containing an AP language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ring.c + assertcond = ring->next == ring END_HEADER */ #include "testlib.h" #include "mpsclo.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return *(mps_addr_t *)object; -} - - static void test(void) { mps_arena_t arena; mps_pool_t pool; - mps_fmt_t format; - mps_fmt_A_s fmtA; mps_ap_t ap; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); - cdie( - mps_pool_create(&pool, arena, mps_class_lo(), format), - "create pool"); + cdie(mps_pool_create(&pool, arena, mps_class_lo(), format), "create pool"); - cdie( - mps_ap_create(&ap, pool, mps_rank_exact()), - "create ap"); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); /* mps_ap_destroy(ap); diff --git a/test/conerr/18.c b/test/conerr/18.c index 51814888a9..4d13dede9b 100644 --- a/test/conerr/18.c +++ b/test/conerr/18.c @@ -4,45 +4,29 @@ TEST_HEADER summary = create a pool with a format in the wrong arena language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poollo.c + assertcond = FormatArena(pool->format) == arena END_HEADER */ #include "testlib.h" #include "mpsclo.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return *(mps_addr_t *)object; -} - - static void test(void) { - mps_arena_t arena; + mps_arena_t arena0; mps_arena_t arena1; mps_pool_t pool; mps_fmt_t format; - mps_fmt_A_s fmtA; - - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena 0"); - cdie(mps_arena_create(&arena1), "create arena 1"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; + cdie(mps_arena_create(&arena0, mps_arena_class_vm(), mmqaArenaSIZE), "create arena 0"); + cdie(mps_arena_create(&arena1, mps_arena_class_vm(), mmqaArenaSIZE), "create arena 1"); cdie( - mps_fmt_create_A(&format, arena, &fmtA), + mps_fmt_create_k(&format, arena0, mps_args_none), "create format in arena 0"); cdie( @@ -55,7 +39,7 @@ static void test(void) mps_fmt_destroy(format); comment("Destroyed format."); - mps_arena_destroy(arena); + mps_arena_destroy(arena0); comment("Destroyed arena 0."); mps_arena_destroy(arena1); comment("Destroyed arena 1."); diff --git a/test/conerr/19.c b/test/conerr/19.c index dd80558e7c..3c8211c23d 100644 --- a/test/conerr/19.c +++ b/test/conerr/19.c @@ -4,6 +4,8 @@ TEST_HEADER summary = alloc in an uncreated pool language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -13,7 +15,7 @@ END_HEADER static void test(void) { mps_arena_t arena; - mps_pool_t pool; + mps_pool_t pool = (mps_pool_t)1; size_t extendBy; size_t avgSize; size_t maxSize; diff --git a/test/conerr/2.c b/test/conerr/2.c index d42ae589b0..78738d5bc8 100644 --- a/test/conerr/2.c +++ b/test/conerr/2.c @@ -4,6 +4,10 @@ TEST_HEADER summary = destroy an arena which isn't an arena language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ diff --git a/test/conerr/20.c b/test/conerr/20.c index 57519568bf..dcb6565353 100644 --- a/test/conerr/20.c +++ b/test/conerr/20.c @@ -4,6 +4,10 @@ TEST_HEADER summary = alloc in an destroyed pool language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ diff --git a/test/conerr/21.c b/test/conerr/21.c index f6a4f887bc..9d389fc952 100644 --- a/test/conerr/21.c +++ b/test/conerr/21.c @@ -4,6 +4,10 @@ TEST_HEADER summary = free in a destroyed pool language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ diff --git a/test/conerr/22.c b/test/conerr/22.c index 23054c73d6..fb02000362 100644 --- a/test/conerr/22.c +++ b/test/conerr/22.c @@ -4,6 +4,10 @@ TEST_HEADER summary = free though not allocated language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= tract.c + assertcond = found END_HEADER */ @@ -18,7 +22,7 @@ static void test(void) size_t avgSize; size_t maxSize; - mps_addr_t obj; + mps_addr_t obj = (mps_addr_t)MPS_PF_ALIGN; extendBy = (size_t) 4096; avgSize = (size_t) 32; diff --git a/test/conerr/23.c b/test/conerr/23.c index 8c8a8c89f9..c654115419 100644 --- a/test/conerr/23.c +++ b/test/conerr/23.c @@ -4,6 +4,10 @@ TEST_HEADER summary = free though not allocated language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= tract.c + assertcond = found END_HEADER */ diff --git a/test/conerr/24.c b/test/conerr/24.c index 3146d7853b..39ff8890bf 100644 --- a/test/conerr/24.c +++ b/test/conerr/24.c @@ -4,42 +4,27 @@ TEST_HEADER summary = alloc in pool not supporting alloc language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolabs.c + assertcond = unreachable code END_HEADER */ #include "testlib.h" #include "mpsclo.h" -static void zilch(void) -{ -} - -static mps_addr_t myskip(mps_addr_t object) -{ - return object; -} - static void test(void) { mps_arena_t arena; mps_pool_t pool; mps_fmt_t format; - mps_fmt_A_s fmtA; - mps_addr_t obj; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - cdie( - mps_fmt_create_A(&format, arena, &fmtA), + mps_fmt_create_k(&format, arena, mps_args_none), "create format"); cdie( diff --git a/test/conerr/25.c b/test/conerr/25.c index 9ea16e45f2..480b382b8f 100644 --- a/test/conerr/25.c +++ b/test/conerr/25.c @@ -1,54 +1,37 @@ /* TEST_HEADER id = $Id$ - summary = free in pool not supporting alloc (n.b. this means no alloc at the moment) + summary = free in pool not supporting free language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolabs.c + assertcond = unreachable code END_HEADER */ #include "testlib.h" #include "mpsclo.h" -static void zilch(void) -{ -} - -static mps_addr_t myskip(mps_addr_t object) -{ - return object; -} - static void test(void) { mps_arena_t arena; mps_pool_t pool; mps_fmt_t format; - mps_fmt_A_s fmtA; - + mps_ap_t ap; mps_addr_t obj; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); + cdie(mps_pool_create(&pool, arena, mps_class_lo(), format), "create pool"); - cdie( - mps_pool_create(&pool, arena, mps_class_lo(), format), - "create pool"); + cdie(mps_ap_create(&ap, pool), "create ap"); -/* - cdie(mps_alloc(&obj, pool, 152), "allocate"); -*/ + cdie(mps_reserve(&obj, ap, 152), "reserve"); + mps_commit(ap, &obj, 152); mps_free(pool, obj, 152); comment("Freed."); diff --git a/test/conerr/26.c b/test/conerr/26.c index 36a1fcc11a..8fcabc4435 100644 --- a/test/conerr/26.c +++ b/test/conerr/26.c @@ -4,6 +4,10 @@ TEST_HEADER summary = free in the wrong pool language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= pool.c + assertcond = PoolHasRange(pool, old, AddrAdd(old, size)) END_HEADER */ @@ -13,33 +17,21 @@ END_HEADER static void test(void) { mps_arena_t arena; - mps_pool_t pool; + mps_pool_t pool0; mps_pool_t pool1; - size_t extendBy; - size_t avgSize; - size_t maxSize; - mps_addr_t obj; - extendBy = (size_t) 4096; - avgSize = (size_t) 32; - maxSize = (size_t) 65536; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie( - mps_pool_create(&pool, arena, mps_class_mv(), - extendBy, avgSize, maxSize), - "create pool 0"); + cdie(mps_pool_create_k(&pool0, arena, mps_class_mv(), mps_args_none), + "create pool 0"); - cdie( - mps_pool_create(&pool1, arena, mps_class_mv(), - extendBy, avgSize, maxSize), - "create pool 1"); + cdie(mps_pool_create_k(&pool1, arena, mps_class_mv(), mps_args_none), + "create pool 1"); - cdie(mps_alloc(&obj, pool, 152), "allocate in 0"); + cdie(mps_alloc(&obj, pool0, 152), "allocate in 0"); - mps_free(pool, obj, 512); + mps_free(pool1, obj, 512); comment("Freed in 1."); } diff --git a/test/conerr/27.c b/test/conerr/27.c index ed48ec9e54..07cbf6b215 100644 --- a/test/conerr/27.c +++ b/test/conerr/27.c @@ -4,6 +4,10 @@ TEST_HEADER summary = free in the wrong pool (and a destroyed pool at that!) language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ @@ -13,39 +17,27 @@ END_HEADER static void test(void) { mps_arena_t arena; - mps_pool_t pool; + mps_pool_t pool0; mps_pool_t pool1; - size_t extendBy; - size_t avgSize; - size_t maxSize; - mps_addr_t obj; - extendBy = (size_t) 4096; - avgSize = (size_t) 32; - maxSize = (size_t) 65536; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie( - mps_pool_create(&pool, arena, mps_class_mv(), - extendBy, avgSize, maxSize), - "create pool 0"); + cdie(mps_pool_create_k(&pool0, arena, mps_class_mv(), mps_args_none), + "create pool 0"); - cdie( - mps_pool_create(&pool1, arena, mps_class_mv(), - extendBy, avgSize, maxSize), - "create pool 1"); + cdie(mps_pool_create_k(&pool1, arena, mps_class_mv(), mps_args_none), + "create pool 1"); - cdie(mps_alloc(&obj, pool, 152), "allocate in 0"); + cdie(mps_alloc(&obj, pool0, 152), "allocate in 0"); mps_pool_destroy(pool1); comment("Pool 1 destroyed."); - mps_free(pool, obj, 512); + mps_free(pool1, obj, 512); comment("Freed in 1."); - mps_pool_destroy(pool); + mps_pool_destroy(pool0); comment("Pool 0 destroyed."); } diff --git a/test/conerr/28.c b/test/conerr/28.c index 2b80e77713..d45c2f1248 100644 --- a/test/conerr/28.c +++ b/test/conerr/28.c @@ -4,55 +4,26 @@ TEST_HEADER summary = create an AP in an uncreated pool language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ #include "testlib.h" #include "mpsclo.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return *(mps_addr_t *)object; -} - - static void test(void) { mps_arena_t arena; - mps_pool_t pool; - + mps_pool_t pool = (mps_pool_t)1; mps_fmt_t format; - mps_fmt_A_s fmtA; mps_ap_t ap; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); - -/* - cdie( - mps_pool_create(&pool, arena, mps_class_lo(), format), - "create pool"); -*/ + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); - cdie( - mps_ap_create(&ap, pool, mps_rank_exact()), - "create ap"); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); mps_ap_destroy(ap); comment("Destroyed ap."); diff --git a/test/conerr/29.c b/test/conerr/29.c index 767bef3737..915581cacc 100644 --- a/test/conerr/29.c +++ b/test/conerr/29.c @@ -4,56 +4,33 @@ TEST_HEADER summary = create an AP in a destroyed pool language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ #include "testlib.h" #include "mpsclo.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return *(mps_addr_t *)object; -} - - static void test(void) { mps_arena_t arena; mps_pool_t pool; - mps_fmt_t format; - mps_fmt_A_s fmtA; mps_ap_t ap; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); - cdie( - mps_pool_create(&pool, arena, mps_class_lo(), format), - "create pool"); + cdie(mps_pool_create(&pool, arena, mps_class_lo(), format), "create pool"); mps_pool_destroy(pool); comment("Destroyed pool."); - cdie( - mps_ap_create(&ap, pool, mps_rank_exact()), - "create ap"); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); mps_ap_destroy(ap); comment("Destroyed ap."); diff --git a/test/conerr/3.c b/test/conerr/3.c index e9ff274dfe..dae5b20200 100644 --- a/test/conerr/3.c +++ b/test/conerr/3.c @@ -1,19 +1,25 @@ /* TEST_HEADER id = $Id$ - summary = destroy an arena which isn't an arena, with a pointer in + summary = destroy an arena which isn't an arena language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ +#include "mpmst.h" #include "testlib.h" static void test(void) { + char buf[sizeof(ArenaStruct)]; mps_arena_t arena; - arena = &arena; + arena = (void *)buf; mps_arena_destroy(arena); comment("Destroy arena."); } diff --git a/test/conerr/30.c b/test/conerr/30.c index 9a8e6593a3..79daebca18 100644 --- a/test/conerr/30.c +++ b/test/conerr/30.c @@ -1,58 +1,29 @@ /* TEST_HEADER id = $Id$ - summary = create an AP in a destroyed pool + summary = destroy an uncreated AP language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ #include "testlib.h" #include "mpsclo.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return *(mps_addr_t *)object; -} - - static void test(void) { mps_arena_t arena; mps_pool_t pool; - mps_fmt_t format; - mps_fmt_A_s fmtA; - mps_ap_t ap; + mps_ap_t ap = (mps_ap_t)1; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); - - cdie( - mps_pool_create(&pool, arena, mps_class_lo(), format), - "create pool"); - -/* - cdie( - mps_ap_create(&ap, pool, mps_rank_exact()), - "create ap"); -*/ + cdie(mps_pool_create(&pool, arena, mps_class_lo(), format), "create pool"); mps_ap_destroy(ap); comment("Destroyed ap."); diff --git a/test/conerr/31.c b/test/conerr/31.c index 8b4f95197a..dca4cc3cab 100644 --- a/test/conerr/31.c +++ b/test/conerr/31.c @@ -4,53 +4,30 @@ TEST_HEADER summary = destroy an AP twice language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Buffer, buf) END_HEADER */ #include "testlib.h" #include "mpsclo.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return *(mps_addr_t *)object; -} - - static void test(void) { mps_arena_t arena; mps_pool_t pool; - mps_fmt_t format; - mps_fmt_A_s fmtA; mps_ap_t ap; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); - cdie( - mps_pool_create(&pool, arena, mps_class_lo(), format), - "create pool"); + cdie(mps_pool_create(&pool, arena, mps_class_lo(), format), "create pool"); - cdie( - mps_ap_create(&ap, pool, mps_rank_exact()), - "create ap"); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); mps_ap_destroy(ap); comment("Destroyed ap."); diff --git a/test/conerr/32.c b/test/conerr/32.c index 3e1d77f861..7ee92a781b 100644 --- a/test/conerr/32.c +++ b/test/conerr/32.c @@ -4,36 +4,27 @@ TEST_HEADER summary = create AP in a pool that doesn't support it language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolabs.c + assertcond = unreachable code END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmfs.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - size_t extendBy; - size_t avgSize; - size_t maxSize; mps_ap_t ap; - mps_addr_t obj; - - extendBy = (size_t) 4096; - avgSize = (size_t) 32; - maxSize = (size_t) 65536; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie( - mps_pool_create(&pool, arena, mps_class_mv(), - extendBy, avgSize, maxSize), - "create pool"); + cdie(mps_pool_create(&pool, arena, mps_class_mfs(), 64), "create pool"); - cdie(mps_ap_create(&ap, pool, mps_rank_exact()), - "create ap"); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); mps_pool_destroy(pool); comment("Destroyed pool"); diff --git a/test/conerr/33.c b/test/conerr/33.c index 8a9ca7fedc..3a3cb2c128 100644 --- a/test/conerr/33.c +++ b/test/conerr/33.c @@ -8,57 +8,29 @@ END_HEADER */ #include "testlib.h" -#include "mpscamc.h" - -#define genCOUNT (3) - -static mps_gen_param_s testChain[genCOUNT] = { - { 6000, 0.90 }, { 8000, 0.65 }, { 16000, 0.50 } }; - -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return *(mps_addr_t *)object; -} - +#include "mpscams.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - - mps_chain_t chain; mps_fmt_t format; - mps_fmt_A_s fmtA; mps_ap_t ap; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 4; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + cdie(mps_pool_create_k(&pool, arena, mps_class_ams(), args), + "create pool"); + } MPS_ARGS_END(args); - cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - - cdie( - mps_pool_create(&pool, arena, mps_class_amc(), format, chain), - "create pool"); - - cdie( - mps_ap_create(&ap, pool, mps_rank_ambig()), - "create ap"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_RANK, mps_rank_weak()); + cdie(mps_ap_create_k(&ap, pool, args), "create ap"); + } MPS_ARGS_END(args); mps_ap_destroy(ap); comment("Destroyed ap."); @@ -69,9 +41,6 @@ static void test(void) mps_fmt_destroy(format); comment("Destroyed format."); - mps_chain_destroy(chain); - comment("Destroyed chain."); - mps_arena_destroy(arena); comment("Destroyed arena."); } diff --git a/test/conerr/33a.c b/test/conerr/33a.c index 371ecb6d71..3add3105d5 100644 --- a/test/conerr/33a.c +++ b/test/conerr/33a.c @@ -8,64 +8,34 @@ END_HEADER */ #include "testlib.h" -#include "mpscamc.h" - -#define genCOUNT (3) - -static mps_gen_param_s testChain[genCOUNT] = { - { 6000, 0.90 }, { 8000, 0.65 }, { 16000, 0.50 } }; - -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return *(mps_addr_t *)object; -} - +#include "mpscams.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - - mps_chain_t chain; mps_fmt_t format; - mps_fmt_A_s fmtA; mps_ap_t ap; mps_addr_t p; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 4; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + cdie(mps_pool_create_k(&pool, arena, mps_class_ams(), args), + "create pool"); + } MPS_ARGS_END(args); - cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_RANK, mps_rank_weak()); + cdie(mps_ap_create_k(&ap, pool, args), "create ap"); + } MPS_ARGS_END(args); - cdie( - mps_pool_create(&pool, arena, mps_class_amc(), format, chain), - "create pool"); - - cdie( - mps_ap_create(&ap, pool, mps_rank_ambig()), - "create ap"); - -do - { + do { cdie(mps_reserve(&p, ap, 0x100), "Reserve: "); - } - while (!mps_commit(ap, p, 0x100)); + } while (!mps_commit(ap, p, 0x100)); comment("Committed."); mps_ap_destroy(ap); @@ -77,9 +47,6 @@ do mps_fmt_destroy(format); comment("Destroyed format."); - mps_chain_destroy(chain); - comment("Destroyed chain."); - mps_arena_destroy(arena); comment("Destroyed arena."); } diff --git a/test/conerr/34.c b/test/conerr/34.c index 3908e22b90..4b6e526eba 100644 --- a/test/conerr/34.c +++ b/test/conerr/34.c @@ -4,6 +4,8 @@ TEST_HEADER summary = allocate in uncreated AP language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -27,7 +29,7 @@ static void test(void) mps_chain_t chain; mps_fmt_t format; - mps_ap_t ap; + mps_ap_t ap = (mps_ap_t)1; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); diff --git a/test/conerr/35.c b/test/conerr/35.c index da5efe3510..eb25e49f83 100644 --- a/test/conerr/35.c +++ b/test/conerr/35.c @@ -4,6 +4,10 @@ TEST_HEADER summary = allocate in destroyed AP language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Buffer, buf) END_HEADER */ diff --git a/test/conerr/36.c b/test/conerr/36.c index 96d66d92e8..60e68f8943 100644 --- a/test/conerr/36.c +++ b/test/conerr/36.c @@ -4,6 +4,10 @@ TEST_HEADER summary = destroy AP between reserve and commit language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= buffer.c + assertcond = BufferIsReady(buffer) END_HEADER */ diff --git a/test/conerr/37.c b/test/conerr/37.c index 0fcf40c4d7..659a213976 100644 --- a/test/conerr/37.c +++ b/test/conerr/37.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = reserve and commit with different address + summary = reserve and commit (macros) with different address language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = p == mps_ap->init END_HEADER */ diff --git a/test/conerr/37f.c b/test/conerr/37f.c index 268d9939e2..530af933fc 100644 --- a/test/conerr/37f.c +++ b/test/conerr/37f.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = reserve and commit with different address + summary = reserve and commit (functions) with different address language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = p == mps_ap->init END_HEADER */ @@ -14,6 +18,11 @@ END_HEADER #undef mps_reserve #undef mps_commit +#define genCOUNT (3) + +static mps_gen_param_s testChain[genCOUNT] = { + { 6000, 0.90 }, { 8000, 0.65 }, { 16000, 0.50 } }; + void *stackpointer; static void test(void) diff --git a/test/conerr/38.c b/test/conerr/38.c index 42cc13ba86..cbaf71de7d 100644 --- a/test/conerr/38.c +++ b/test/conerr/38.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = reserve and commit with different sizes + summary = reserve and commit (macros) with different sizes language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = PointerAdd(mps_ap->init, size) == mps_ap->alloc END_HEADER */ diff --git a/test/conerr/38f.c b/test/conerr/38f.c index 504ebfcaa6..eb8974dd32 100644 --- a/test/conerr/38f.c +++ b/test/conerr/38f.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = reserve and commit with different sizes + summary = reserve and commit (functions) with different sizes language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = PointerAdd(mps_ap->init, size) == mps_ap->alloc END_HEADER */ @@ -14,6 +18,11 @@ END_HEADER #undef mps_reserve #undef mps_commit +#define genCOUNT (3) + +static mps_gen_param_s testChain[genCOUNT] = { + { 6000, 0.90 }, { 8000, 0.65 }, { 16000, 0.50 } }; + void *stackpointer; static void test(void) diff --git a/test/conerr/39.c b/test/conerr/39.c index 386cfdc8c4..c2e4b82e3b 100644 --- a/test/conerr/39.c +++ b/test/conerr/39.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = commit without reserving + summary = commit (macro) without reserving language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = p == mps_ap->init END_HEADER */ @@ -28,7 +32,7 @@ static void test(void) mps_chain_t chain; mps_fmt_t format; mps_ap_t ap; - mps_addr_t p; + mps_addr_t p = (mps_addr_t)1; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -53,7 +57,7 @@ static void test(void) mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); - allocone(ap, 0, NULL, NULL, 0x20); + allocone(ap, 0, NULL, NULL, 0x100); do { diff --git a/test/conerr/39f.c b/test/conerr/39f.c index d6cd0d0bc3..326481c090 100644 --- a/test/conerr/39f.c +++ b/test/conerr/39f.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = commit without reserving + summary = commit (function) without reserving language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = p == mps_ap->init END_HEADER */ @@ -14,6 +18,11 @@ END_HEADER #undef mps_reserve #undef mps_commit +#define genCOUNT (3) + +static mps_gen_param_s testChain[genCOUNT] = { + { 6000, 0.90 }, { 8000, 0.65 }, { 16000, 0.50 } }; + void *stackpointer; static void test(void) @@ -26,7 +35,7 @@ static void test(void) mps_chain_t chain; mps_fmt_t format; mps_ap_t ap; - mps_addr_t p; + mps_addr_t p = (mps_addr_t)1; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -51,7 +60,7 @@ static void test(void) mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); - allocone(ap, 0, NULL, NULL, 0x20); + allocone(ap, 0, NULL, NULL, 0x100); do { diff --git a/test/conerr/4.c b/test/conerr/4.c index 5f1532d9ef..38451258bd 100644 --- a/test/conerr/4.c +++ b/test/conerr/4.c @@ -4,6 +4,10 @@ TEST_HEADER summary = destroy an arena which contains a pool language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = RingLength(&arenaGlobals->poolRing) == 5 END_HEADER */ diff --git a/test/conerr/40.c b/test/conerr/40.c index e0794006f4..516b477808 100644 --- a/test/conerr/40.c +++ b/test/conerr/40.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = reserve twice without committing + summary = reserve (macro) twice without committing language = c link = myfmt.o testlib.o END_HEADER diff --git a/test/conerr/40f.c b/test/conerr/40f.c index f6f4e59e57..6f9d85dde6 100644 --- a/test/conerr/40f.c +++ b/test/conerr/40f.c @@ -1,9 +1,13 @@ /* TEST_HEADER id = $Id$ - summary = reserve twice without committing + summary = reserve (function) twice without committing language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= mpsi.c + assertcond = mps_ap->init == mps_ap->alloc END_HEADER */ @@ -14,6 +18,11 @@ END_HEADER #undef mps_reserve #undef mps_commit +#define genCOUNT (3) + +static mps_gen_param_s testChain[genCOUNT] = { + { 6000, 0.90 }, { 8000, 0.65 }, { 16000, 0.50 } }; + void *stackpointer; static void test(void) diff --git a/test/conerr/41.c b/test/conerr/41.c index 7360f25954..44c03b77c4 100644 --- a/test/conerr/41.c +++ b/test/conerr/41.c @@ -4,6 +4,8 @@ TEST_HEADER summary = create root in uncreated arena language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -15,7 +17,7 @@ void *stackpointer; static void test(void) { - mps_arena_t arena; + mps_arena_t arena = (mps_arena_t)1; mps_root_t root; mps_addr_t roottable[10]; diff --git a/test/conerr/42.c b/test/conerr/42.c index dd19baf61f..c6c453a9de 100644 --- a/test/conerr/42.c +++ b/test/conerr/42.c @@ -4,6 +4,8 @@ TEST_HEADER summary = create root in destroyed arena language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/conerr/43.c b/test/conerr/43.c index e2ca92c55b..1532e2f9a8 100644 --- a/test/conerr/43.c +++ b/test/conerr/43.c @@ -4,6 +4,8 @@ TEST_HEADER summary = destroy root though uncreated language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -16,7 +18,7 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_root_t root; + mps_root_t root = (mps_root_t)1; mps_addr_t roottable[10]; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); diff --git a/test/conerr/44.c b/test/conerr/44.c index 3ca0129b18..5b7df5e1a0 100644 --- a/test/conerr/44.c +++ b/test/conerr/44.c @@ -4,6 +4,10 @@ TEST_HEADER summary = destroy root twice language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= root.c + assertcond = TESTT(Root, root) END_HEADER */ diff --git a/test/conerr/44a.c b/test/conerr/44a.c index 193ab4b350..6a430ae5e7 100644 --- a/test/conerr/44a.c +++ b/test/conerr/44a.c @@ -4,6 +4,8 @@ TEST_HEADER summary = create register root without registering thread language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -16,7 +18,7 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_thr_t thread; + mps_thr_t thread = (mps_thr_t)1; mps_root_t root; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); diff --git a/test/conerr/45.c b/test/conerr/45.c index cd85bffaaf..82b73e3287 100644 --- a/test/conerr/45.c +++ b/test/conerr/45.c @@ -4,6 +4,8 @@ TEST_HEADER summary = register thread in uncreated arena language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -15,7 +17,7 @@ void *stackpointer; static void test(void) { - mps_arena_t arena; + mps_arena_t arena = (mps_arena_t)1; mps_thr_t thread; /* diff --git a/test/conerr/46.c b/test/conerr/46.c index d4d495c742..7c375bfe92 100644 --- a/test/conerr/46.c +++ b/test/conerr/46.c @@ -4,6 +4,8 @@ TEST_HEADER summary = register thread in destroyed arena language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/conerr/47.c b/test/conerr/47.c index 851692f0e0..93bdda8f99 100644 --- a/test/conerr/47.c +++ b/test/conerr/47.c @@ -4,6 +4,8 @@ TEST_HEADER summary = deregister thread without registering it first language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -16,7 +18,7 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_thr_t thread; + mps_thr_t thread = (mps_thr_t)1; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); diff --git a/test/conerr/48.c b/test/conerr/48.c index 82d02b9f34..98fd71caf3 100644 --- a/test/conerr/48.c +++ b/test/conerr/48.c @@ -4,6 +4,9 @@ TEST_HEADER summary = deregister thread twice language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertcond = SigCheck Thread: thread END_HEADER */ diff --git a/test/conerr/5.c b/test/conerr/5.c index aaac5408d4..09a1893f31 100644 --- a/test/conerr/5.c +++ b/test/conerr/5.c @@ -4,22 +4,16 @@ TEST_HEADER summary = destroy an arena which contains a format language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = RingIsSingle(&arena->formatRing) END_HEADER */ #include "testlib.h" #include "mpscmv.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return object; -} - static void test(void) { mps_arena_t arena; @@ -27,18 +21,8 @@ static void test(void) mps_fmt_A_s fmtA; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); mps_arena_destroy(arena); comment("Destroy arena."); diff --git a/test/conerr/50.c b/test/conerr/50.c index 8873492071..586600afea 100644 --- a/test/conerr/50.c +++ b/test/conerr/50.c @@ -4,6 +4,8 @@ TEST_HEADER summary = reset ld in uncreated arena language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ @@ -13,7 +15,7 @@ END_HEADER static void test(void) { - mps_arena_t arena; + mps_arena_t arena = (mps_arena_t)1; mps_ld_s ld; /* diff --git a/test/conerr/51.c b/test/conerr/51.c index 719a4f27d2..53e89493d0 100644 --- a/test/conerr/51.c +++ b/test/conerr/51.c @@ -4,6 +4,8 @@ TEST_HEADER summary = reset ld in destroyed arena language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/conerr/52.c b/test/conerr/52.c index 83e3d3eece..a8d2425e0b 100644 --- a/test/conerr/52.c +++ b/test/conerr/52.c @@ -4,6 +4,8 @@ TEST_HEADER summary = reset ld again, in destroyed arena language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/conerr/53.c b/test/conerr/53.c index 1ee5eda761..0cd7a08ac3 100644 --- a/test/conerr/53.c +++ b/test/conerr/53.c @@ -4,6 +4,10 @@ TEST_HEADER summary = add to ld without resetting language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ld.c + assertcond = ld->_epoch <= arena->epoch END_HEADER */ diff --git a/test/conerr/54.c b/test/conerr/54.c index 81e8ceff4d..994a5f73b7 100644 --- a/test/conerr/54.c +++ b/test/conerr/54.c @@ -4,6 +4,10 @@ TEST_HEADER summary = is_stale without resetting language = c link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= ld.c + assertcond = ld->_epoch <= arena->epoch END_HEADER */ diff --git a/test/conerr/55.c b/test/conerr/55.c index 8f544925a4..9f32ccdcad 100644 --- a/test/conerr/55.c +++ b/test/conerr/55.c @@ -4,6 +4,8 @@ TEST_HEADER summary = add to ld in destroyed arena language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/conerr/56.c b/test/conerr/56.c index 6d0abdd7e0..332bec7747 100644 --- a/test/conerr/56.c +++ b/test/conerr/56.c @@ -4,6 +4,8 @@ TEST_HEADER summary = isstale in destroyed arena language = c link = myfmt.o testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ diff --git a/test/conerr/57.c b/test/conerr/57.c index d8ac6b44b2..7e6707d46e 100644 --- a/test/conerr/57.c +++ b/test/conerr/57.c @@ -13,20 +13,23 @@ END_HEADER static void test(void) { - mps_arena_t arena; + mps_arena_t arena0; mps_arena_t arena1; mps_ld_s ld; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie(mps_arena_create(&arena1), "create arena 1"); + cdie(mps_arena_create(&arena0, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); + cdie(mps_arena_create(&arena1, mps_arena_class_vm(), mmqaArenaSIZE), "create arena 1"); - mps_ld_reset(&ld, arena); + mps_ld_reset(&ld, arena0); comment("Reset ld."); - mps_ld_add(&ld, arena1, &arena); + mps_ld_add(&ld, arena1, &arena0); comment("Added to ld."); - mps_arena_destroy(arena); + mps_arena_destroy(arena0); + comment("Destroyed arena."); + + mps_arena_destroy(arena1); comment("Destroyed arena."); } diff --git a/test/conerr/58.c b/test/conerr/58.c index 47be4a6ab6..7feeb5ea76 100644 --- a/test/conerr/58.c +++ b/test/conerr/58.c @@ -13,19 +13,22 @@ END_HEADER static void test(void) { - mps_arena_t arena; + mps_arena_t arena0; mps_arena_t arena1; mps_ld_s ld; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie(mps_arena_create(&arena1), "create arena 1"); + cdie(mps_arena_create(&arena0, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); + cdie(mps_arena_create(&arena1, mps_arena_class_vm(), mmqaArenaSIZE), "create arena 1"); - mps_ld_reset(&ld, arena); + mps_ld_reset(&ld, arena0); comment("Reset ld."); - report("isstale", "%d", mps_ld_isstale(&ld, arena1, &arena)); + report("isstale", "%d", mps_ld_isstale(&ld, arena1, &arena0)); - mps_arena_destroy(arena); + mps_arena_destroy(arena0); + comment("Destroyed arena."); + + mps_arena_destroy(arena1); comment("Destroyed arena."); } diff --git a/test/conerr/59.c b/test/conerr/59.c index 8412f4daf7..347780adce 100644 --- a/test/conerr/59.c +++ b/test/conerr/59.c @@ -4,6 +4,10 @@ TEST_HEADER summary = free though not allocated language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolmv.c + assertcond = unreachable code END_HEADER */ diff --git a/test/conerr/6.c b/test/conerr/6.c index 7e68baa74a..bfc417aa9f 100644 --- a/test/conerr/6.c +++ b/test/conerr/6.c @@ -4,6 +4,10 @@ TEST_HEADER summary = destroy an arena which contains a root language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = RingIsSingle(&arenaGlobals->rootRing) END_HEADER */ @@ -27,6 +31,7 @@ static void test(void) mps_stack_scan_ambig, stackpointer, 0), "create root"); + mps_thread_dereg(thread); mps_arena_destroy(arena); comment("Destroy arena."); } diff --git a/test/conerr/7.c b/test/conerr/7.c index f1dd36c6f2..7c5bdab31a 100644 --- a/test/conerr/7.c +++ b/test/conerr/7.c @@ -4,6 +4,10 @@ TEST_HEADER summary = destroy an arena which contains a thread language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = RingIsSingle(&arena->threadRing) END_HEADER */ diff --git a/test/conerr/8.c b/test/conerr/8.c index 57836544ec..ba37375ea9 100644 --- a/test/conerr/8.c +++ b/test/conerr/8.c @@ -4,43 +4,24 @@ TEST_HEADER summary = create a format in an uncreated arena language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = TESTT(Arena, arena) END_HEADER */ #include "testlib.h" #include "mpscmv.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return object; -} - static void test(void) { mps_arena_t arena; mps_fmt_t format; - mps_fmt_A_s fmtA; arena=malloc(64); - /* cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - */ - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); mps_arena_destroy(arena); comment("Destroy arena."); diff --git a/test/conerr/9.c b/test/conerr/9.c index 9ef4cf1e0e..47779320f8 100644 --- a/test/conerr/9.c +++ b/test/conerr/9.c @@ -4,45 +4,25 @@ TEST_HEADER summary = create a format in a destroyed arena language = c link = testlib.o +OUTPUT_SPEC + abort = true END_HEADER */ #include "testlib.h" #include "mpscmv.h" -static void zilch(void) -{ -} - - -static mps_addr_t myskip(mps_addr_t object) -{ - return object; -} - static void test(void) { mps_arena_t arena; mps_fmt_t format; - mps_fmt_A_s fmtA; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - fmtA.align = (mps_align_t) 1; - fmtA.scan = &zilch; - fmtA.skip = &myskip; - fmtA.copy = &zilch; - fmtA.fwd = &zilch; - fmtA.isfwd = &zilch; - fmtA.pad = &zilch; - mps_arena_destroy(arena); comment("Destroy arena."); - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); + cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); } diff --git a/test/function/10.c b/test/function/10.c index 912901881a..7a29d6c8c2 100644 --- a/test/function/10.c +++ b/test/function/10.c @@ -10,6 +10,7 @@ END_HEADER #include "testlib.h" #include "mpscamc.h" +#define OBJSIZE (1u << 20) #define genCOUNT (3) static mps_gen_param_s testChain[genCOUNT] = { @@ -19,14 +20,12 @@ void *stackpointer; static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) { - MPS_SCAN_BEGIN(ss) - MPS_SCAN_END(ss); return MPS_RES_OK; } static mps_addr_t myskip(mps_addr_t object) { - return (mps_addr_t) ((char *) object + 1); + return (mps_addr_t) ((char *) object + OBJSIZE); } static void mycopy(mps_addr_t object, mps_addr_t to) @@ -99,12 +98,13 @@ static void test(void) for(i=0; i<1000; i++) { do - { die(mps_reserve(&p, ap, 1024*1024), "Reserve: "); + { die(mps_reserve(&p, ap, OBJSIZE), "Reserve: "); } - while (!mps_commit(ap, p, 1024*1024)); + while (!mps_commit(ap, p, OBJSIZE)); comment("%i megabytes allocated", i); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_fmt_destroy(format); diff --git a/test/function/103.c b/test/function/103.c index c8c2722516..07a77bf247 100644 --- a/test/function/103.c +++ b/test/function/103.c @@ -32,7 +32,9 @@ static void fillup(void) mps_addr_t a; char *b; - mps_pool_create(&poolmv, arena, mps_class_mv(), 64, 64, 64); + die(mps_pool_create(&poolmv, arena, mps_class_mv(), + (size_t)64, (size_t)64, (size_t)64), + "mps_pool_create"); size=1024ul*1024ul; while (size) { while (mps_alloc(&a, poolmv, size)==MPS_RES_OK) { @@ -136,6 +138,7 @@ static void test(void) mps_arena_collect(arena); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/104.c b/test/function/104.c index b267b9b07f..6faba1afab 100644 --- a/test/function/104.c +++ b/test/function/104.c @@ -213,6 +213,7 @@ static void test(void) comment("ok"); } + mps_arena_park(arena); mps_ap_destroy(apamc); mps_ap_destroy(aplo); mps_ap_destroy(apawl); diff --git a/test/function/105.c b/test/function/105.c index 888a0f74fa..c3bf5c69a1 100644 --- a/test/function/105.c +++ b/test/function/105.c @@ -67,6 +67,7 @@ static void test(void) b = allocone(apamc, 1, mps_rank_exact()); a = allocone(apweak, 1, mps_rank_weak()); + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_ap_destroy(apweak); diff --git a/test/function/106.c b/test/function/106.c index 8f751cfaeb..5f6811bbfa 100644 --- a/test/function/106.c +++ b/test/function/106.c @@ -69,7 +69,7 @@ static void test(void) mps_fmt_t format; mps_chain_t chain; - locell *a,*b,*c; + locell *a; int i; alloclocomments = 0; @@ -94,14 +94,15 @@ static void test(void) "create ap"); a = string_ch("Hello there"); - b = string_ch("Wibble wobble foo"); - c = string_ch("Ba "); + (void)string_ch("Wibble wobble foo"); + (void)string_ch("Ba "); for (i=0; i<10000; i++) { a = conc(string_ch("B"), a); - c = conc(string_ch("Hello there"), string_ch(" folks!")); + (void)conc(string_ch("Hello there"), string_ch(" folks!")); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/107.c b/test/function/107.c index 0a83d04a15..9727322b1e 100644 --- a/test/function/107.c +++ b/test/function/107.c @@ -69,7 +69,7 @@ static void test(void) mps_fmt_t format; mps_chain_t chain; - locell *a,*b,*c,*z; + locell *a; int i; alloclocomments = 0; @@ -94,15 +94,16 @@ static void test(void) "create ap"); a = string_ch("Hello there"); - b = string_ch("Wibble wobble foo"); - c = string_ch("Ba "); + (void)string_ch("Wibble wobble foo"); + (void)string_ch("Ba "); for (i=0; i<10000; i++) { a = conc(string_ch("B"), a); - c = conc(string_ch("Hello there"), string_ch(" folks!")); - z = alloclo(ap, 0x4000); + (void)conc(string_ch("Hello there"), string_ch(" folks!")); + (void)alloclo(ap, 0x4000); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/108.c b/test/function/108.c index 10631e5eb5..80cea58c1a 100644 --- a/test/function/108.c +++ b/test/function/108.c @@ -34,7 +34,7 @@ static void test(void) mps_chain_t chain; mps_ap_t apamc, aplo; - mycell *a[100], *b; + mycell *a[100]; int i; int j; @@ -79,9 +79,10 @@ static void test(void) z = ranint(5); comment("setting %i (%p) %i", k, a[k], z); setref(a[k], z, a[j]); - b = allocdumb(apamc, 0x400*64, 0); + (void)allocdumb(apamc, 0x400*64, 0); } + mps_arena_park(arena); mps_ap_destroy(aplo); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/109.c b/test/function/109.c index 0fccb52398..bccb5cfec7 100644 --- a/test/function/109.c +++ b/test/function/109.c @@ -267,6 +267,7 @@ static void test(void) report("count2", "%d", final_count); + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_ap_destroy(aplo); diff --git a/test/function/11.c b/test/function/11.c index 286b06a385..9f08936d14 100644 --- a/test/function/11.c +++ b/test/function/11.c @@ -74,9 +74,10 @@ static void test(void) b = c; } - comment("%d: %x", j, (int) a); + comment("%d: %p", j, a); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/110.c b/test/function/110.c index e61536040d..bbf5aa0dc6 100644 --- a/test/function/110.c +++ b/test/function/110.c @@ -262,6 +262,7 @@ static void test(void) finalpoll(&z, FINAL_DISCARD); } + mps_arena_park(arena); mps_root_destroy(root0); mps_root_destroy(root1); comment("Destroyed roots."); @@ -280,6 +281,7 @@ static void test(void) report("count2", "%d", final_count); + mps_arena_park(arena); mps_pool_destroy(poolamc); mps_pool_destroy(poolawl); mps_pool_destroy(poollo); diff --git a/test/function/111.c b/test/function/111.c index f7544f8291..04765b741e 100644 --- a/test/function/111.c +++ b/test/function/111.c @@ -161,6 +161,7 @@ static void test(void) /* throw them all away and collect everything */ + comment("b = %p", b); /* suppress compiler warning about unused b */ a = NULL; b = NULL; c = NULL; @@ -193,6 +194,7 @@ static void test(void) /* now to test leaving messages open for a long time! */ + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_ap_destroy(aplo); diff --git a/test/function/112.c b/test/function/112.c index 6626add3d9..14ed49da3b 100644 --- a/test/function/112.c +++ b/test/function/112.c @@ -33,8 +33,6 @@ static void test(void) { mps_chain_t chain; mps_ap_t aplo; - mycell *a; - long int j; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t)1024*1024*30), @@ -62,11 +60,12 @@ static void test(void) { /* alloc lots in an LO pool; it should be collected away */ for(j=0; j<1000; j++) { - a = allocdumb(aplo, 1024ul*1024, mps_rank_exact()); + (void)allocdumb(aplo, 1024ul*1024, mps_rank_exact()); } /* (total allocated is 1000 M) */ + mps_arena_park(arena); mps_root_destroy(root0); mps_root_destroy(root1); comment("Destroyed roots."); diff --git a/test/function/113.c b/test/function/113.c index 83a42fb170..a897c7fb60 100644 --- a/test/function/113.c +++ b/test/function/113.c @@ -73,9 +73,9 @@ static void test(void) b = allocone(apamc, 1, mps_rank_exact()); - for (j=1; j<100; j++) + for (j=1; j<=10; j++) { - comment("%i of 100.", j); + comment("%i of 10.", j); a = allocone(apamc, 5, mps_rank_exact()); b = a; c = a; @@ -100,6 +100,7 @@ static void test(void) } } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); comment("Destroyed aps."); diff --git a/test/function/114.c b/test/function/114.c index 6510796a9b..c273f30cd5 100644 --- a/test/function/114.c +++ b/test/function/114.c @@ -73,9 +73,9 @@ static void test(void) b = allocone(apamc, 1, mps_rank_exact()); - for (j=1; j<100; j++) + for (j=1; j<=10; j++) { - comment("%i of 100.", j); + comment("%i of 10.", j); a = allocone(apamc, 5, mps_rank_exact()); b = a; c = a; @@ -100,6 +100,7 @@ static void test(void) } } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); comment("Destroyed aps."); diff --git a/test/function/116.c b/test/function/116.c index b065af6103..8c87bae445 100644 --- a/test/function/116.c +++ b/test/function/116.c @@ -42,6 +42,7 @@ static void test(void) cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*30)), "create arena"); + die(mps_arena_commit_limit_set(arena, 1ul << 20), "commit_limit_set"); cdie(mps_thread_reg(&thread, arena), "register thread"); @@ -96,6 +97,7 @@ static void test(void) report("postdie", "%s", err_text(res)); } + mps_arena_park(arena); mps_ap_destroy(ap2); comment("Destroyed ap."); diff --git a/test/function/118.c b/test/function/118.c index 4185a586dd..a77ec4f8f8 100644 --- a/test/function/118.c +++ b/test/function/118.c @@ -127,6 +127,7 @@ static void test(void) /* now simulate rest of commit */ (void)(busy_ap->limit != 0 || mps_ap_trip(busy_ap, busy_init, objSIZE)); + mps_arena_park(arena); mps_ap_destroy(busy_ap); mps_ap_destroy(ap); mps_pool_destroy(pool); diff --git a/test/function/12.c b/test/function/12.c index 9ac936dc76..b1f8ad9f17 100644 --- a/test/function/12.c +++ b/test/function/12.c @@ -63,6 +63,9 @@ static void test(void) mycell *cells; int h,i,j,k,l; mycell *pobj; + + mycell *ambig[NAPS]; + size_t bytes; size_t alignment; mps_addr_t q; @@ -106,6 +109,9 @@ static void test(void) comment("%i of 100", h); for(j=0; j<1000; j++) { + if (j == 500) { + mps_arena_collect(arena); + } i = ranint(NAPS); switch (ap_state[i]) { @@ -144,6 +150,7 @@ static void test(void) break; case 2: commentif(BLAH, "%i: begin commit %li", i, p[i]->data.id); + ambig[i] = p[i]; ap[i]->init = ap[i]->alloc; ap_state[i] = 3; break; @@ -156,12 +163,14 @@ static void test(void) commentif(BLAH, "%i -> %i", i, l); } ap_state[i] = 0; + ambig[i] = NULL; break; } } checkfrom(cells); } + comment("ambig[i] = %p", ambig[i]); /* stop compiler optimizing ambig away */ comment("Finished main loop"); for (i=0; i= 0; i--) { + for (i--; i >= 0; i--) { mps_pool_destroy(pools[i]); } diff --git a/test/function/121.c b/test/function/121.c index 263fa70559..c5780e23ee 100644 --- a/test/function/121.c +++ b/test/function/121.c @@ -11,50 +11,56 @@ END_HEADER #include "testlib.h" #include "mpsavm.h" -#include "mpscmv.h" - - -void *stackpointer; +#include "mpsacl.h" mps_arena_t arena; -mps_thr_t thread; -mps_pool_t pool; -mps_pool_t pools[100]; +static char buffer[1024 * 1024]; static void test(void) { + mps_res_t res, prev_res = MPS_RES_OK; int i; - for (i = 64; i >= 0; i--) { - mps_res_t res; + + /* VM arenas round up small sizes and so creation must succeed. */ + for (i = 1024; i >= 0; i -= i/17 + 1) { + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 1024 * i); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), + "mps_arena_create"); + } MPS_ARGS_END(args); + mps_arena_destroy(arena); + } - comment("Trying arena of %d kB.", i); - res = mps_arena_create(&arena, mps_arena_class_vm(), (size_t)(1024*i)); + /* Client arenas have to work within the memory they are given and + * so must fail at some point. */ + for (i = 1024; i >= 0; i -= i/17 + 1) { + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_CL_BASE, buffer); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 1024 * i); + res = mps_arena_create_k(&arena, mps_arena_class_cl(), args); + } MPS_ARGS_END(args); if (res == MPS_RES_OK) { - res = mps_thread_reg(&thread, arena); - if (res == MPS_RES_OK) { - mps_thread_dereg(thread); - } else { - if (res != MPS_RES_MEMORY) { - error("Wrong error code, %d, for mps_thread_reg.", res); - } + if (prev_res != MPS_RES_OK) { + error("Success with smaller size."); } mps_arena_destroy(arena); } else { - report_res("arena_create", res); if (res != MPS_RES_MEMORY) { + report_res("arena_create", res); error("Wrong error code."); } } + prev_res = res; + } + if (res != MPS_RES_MEMORY) { + error("Wrong error code."); } } int main(void) { - void *m; - stackpointer=&m; /* hack to get stack pointer */ - easy_tramp(test); pass(); return 0; diff --git a/test/function/122.c b/test/function/122.c index 6f1465289c..2e26c497be 100644 --- a/test/function/122.c +++ b/test/function/122.c @@ -84,7 +84,7 @@ mycell *a[4], *b[4]; static void test(void) { mps_chain_t chain; - mycell *w, *x, *y; + mycell *x; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) 1024*1024*30), "create arena"); @@ -133,7 +133,7 @@ static void test(void) die(allocrdumb(&a[0], aplo, 64, mps_rank_exact()), "alloc"); die(allocrdumb(&a[1], apamc, 64, mps_rank_exact()), "alloc"); die(allocrdumb(&a[3], apawl, 64, mps_rank_exact()), "alloc"); - a[2] = (mycell *)((int)a[3] | 4); + a[2] = (mycell *)((mps_word_t)a[3] | 4); die(allocrdumb(&b[0], aplo, 64, mps_rank_exact()), "alloc"); die(allocrdumb(&b[1], apamc, 64, mps_rank_exact()), "alloc"); @@ -148,14 +148,15 @@ static void test(void) mps_stack_scan_ambig, stackpointer, 0), "create stack root"); x = allocdumb(apamc, 64, mps_rank_exact()); - y = allocdumb(apamc, 64, mps_rank_exact()); - w = allocdumb(apamc, 64, mps_rank_exact()); + (void)allocdumb(apamc, 64, mps_rank_exact()); + (void)allocdumb(apamc, 64, mps_rank_exact()); rootcount = 0; speccount = 0; walkroots(x); report("count2", "%ld", rootcount); report("countspec", "%ld", speccount); + mps_arena_park(arena); mps_ap_destroy(apamc); mps_ap_destroy(aplo); mps_ap_destroy(apawl); diff --git a/test/function/123.c b/test/function/123.c index 280f05760c..4166f30f68 100644 --- a/test/function/123.c +++ b/test/function/123.c @@ -95,6 +95,7 @@ static void test(void) setref(a, 0, b); } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); comment("Destroyed aps."); diff --git a/test/function/124.c b/test/function/124.c index 5b84bea864..cb4620b49d 100644 --- a/test/function/124.c +++ b/test/function/124.c @@ -30,7 +30,7 @@ static mps_gen_param_s testChain[genCOUNT] = { #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (1000000ul) +#define ITERATIONS (100000ul) #define RAMP_INTERFACE /* @@ -137,6 +137,7 @@ static void test(void) } } + mps_arena_park(arena); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); mps_chain_destroy(chain); diff --git a/test/function/125.c b/test/function/125.c index d457f9adc9..1ca2f61fe4 100644 --- a/test/function/125.c +++ b/test/function/125.c @@ -79,6 +79,7 @@ static void test(void) mps_arena_collect(arena); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/126.c b/test/function/126.c index 988205232f..5611da4412 100644 --- a/test/function/126.c +++ b/test/function/126.c @@ -64,13 +64,13 @@ static void test(void) mps_arena_reserved(arena), mps_arena_committed(arena)); b = allocdumb(ap, 1024ul*1024ul*40, mps_rank_exact()); - comment("alloc 40 MB"); + comment("alloc 40 MB at %p", b); comment("reserved %ld, committed %ld", mps_arena_reserved(arena), mps_arena_committed(arena)); b = allocdumb(ap, 1024ul*1024ul*40, mps_rank_exact()); - comment("alloc 80 MB"); + comment("alloc 80 MB at %p", b); comment("reserved %ld, committed %ld", mps_arena_reserved(arena), mps_arena_committed(arena)); @@ -81,6 +81,7 @@ static void test(void) comment("reserved %ld, committed %ld", mps_arena_reserved(arena), mps_arena_committed(arena)); + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/127.c b/test/function/127.c index afdb013e7c..55c6796198 100644 --- a/test/function/127.c +++ b/test/function/127.c @@ -24,7 +24,7 @@ END_HEADER #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (1000000ul) +#define ITERATIONS (100000ul) /* #define RAMP_INTERFACE @@ -137,6 +137,7 @@ static void test(void) { } } + mps_arena_park(arena); mps_ap_destroy(apamc); comment("Destroyed ap."); diff --git a/test/function/128.c b/test/function/128.c index bfb406a23a..6414e41d97 100644 --- a/test/function/128.c +++ b/test/function/128.c @@ -24,7 +24,7 @@ END_HEADER #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (1000000ul) +#define ITERATIONS (100000ul) /* #define RAMP_INTERFACE @@ -137,6 +137,7 @@ static void test(void) { } } + mps_arena_park(arena); mps_ap_destroy(apamc); comment("Destroyed ap."); diff --git a/test/function/129.c b/test/function/129.c index dd59be650f..94512cf6e1 100644 --- a/test/function/129.c +++ b/test/function/129.c @@ -24,7 +24,7 @@ END_HEADER #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (1000000ul) +#define ITERATIONS (100000ul) #define RAMP_INTERFACE /* @@ -136,6 +136,7 @@ static void test(void) { } } + mps_arena_park(arena); mps_ap_destroy(apamc); comment("Destroyed ap."); diff --git a/test/function/12p.c b/test/function/12p.c index f748b8eddb..db389e7f14 100644 --- a/test/function/12p.c +++ b/test/function/12p.c @@ -196,6 +196,7 @@ cells = allocone(ap[0], NCELLS); mps_ap_destroy(ap[i]); } + mps_arena_park(arena); mps_pool_destroy(pool); comment("Destroyed pool."); diff --git a/test/function/13.c b/test/function/13.c index cd7662a809..aa5ca31b3e 100644 --- a/test/function/13.c +++ b/test/function/13.c @@ -192,6 +192,7 @@ cells = allocone(ap[0], NCELLS); mps_ap_destroy(ap[i]); } + mps_arena_park(arena); mps_pool_destroy(pool); comment("Destroyed pool."); diff --git a/test/function/130.c b/test/function/130.c index 1a4a6da3fe..f871d5944f 100644 --- a/test/function/130.c +++ b/test/function/130.c @@ -19,8 +19,6 @@ static mps_gen_param_s testChain[genCOUNT] = { { 6000, 0.90 }, { 8000, 0.65 }, { 16000, 0.50 } }; -void *stackpointer; - mps_pool_t poolmv; mps_arena_t arena; @@ -28,28 +26,18 @@ mps_arena_t arena; static void test(void) { mps_pool_t pool; - mps_thr_t thread; - mps_root_t root; - mps_fmt_t format; mps_chain_t chain; mps_ap_t ap, ap2; - - mycell *a, *b; - + mycell *a[2]; mps_res_t res; int i; /* create an arena that can't grow beyond 30 M */ cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*30)), "create arena"); - mps_arena_commit_limit_set(arena, (size_t) (1024*1024*40)); - - cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_root_create_reg(&root, arena, mps_rank_ambig(), 0, thread, - mps_stack_scan_ambig, stackpointer, 0), - "create root"); + die(mps_arena_commit_limit_set(arena, 1u << 20), "commit_limit_set"); cdie(mps_fmt_create_A(&format, arena, &fmtA), "create format"); @@ -64,12 +52,14 @@ static void test(void) /* allocate until full */ i = 0; - b = NULL; + a[0] = a[1] = NULL; + cdie(mps_root_create_table(&root, arena, mps_rank_ambig(), 0, (void *)&a, 2), + "create root"); - while (allocrone(&a, ap, 128, mps_rank_exact()) == MPS_RES_OK) { + while (allocrone(&a[0], ap, 128, mps_rank_exact()) == MPS_RES_OK) { i++; - setref(a, 0, b); - b = a; + setref(a[0], 0, a[1]); + a[1] = a[0]; } comment("%d objs allocated.", i); @@ -81,7 +71,7 @@ static void test(void) mps_ap_destroy(ap); for (i = 0; i < 10; i++) { - res = allocrone(&a, ap2, 128, mps_rank_exact()); + res = allocrone(&a[0], ap2, 128, mps_rank_exact()); report("predie", "%s", err_text(res)); } @@ -90,17 +80,17 @@ static void test(void) mps_root_destroy(root); for (i = 0; i < 10; i++) { - res = allocrone(&a, ap2, 128, mps_rank_exact()); + res = allocrone(&a[0], ap2, 128, mps_rank_exact()); report("postdie", "%s", err_text(res)); } - die(allocrone(&a, ap2, 128, mps_rank_exact()), "alloc failed"); + die(allocrone(&a[0], ap2, 128, mps_rank_exact()), "alloc failed"); + mps_arena_park(arena); mps_ap_destroy(ap2); mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(format); - mps_thread_dereg(thread); mps_arena_destroy(arena); comment("Destroyed arena."); } @@ -108,9 +98,6 @@ static void test(void) int main(void) { - void *m; - stackpointer=&m; /* hack to get stack pointer */ - easy_tramp(test); pass(); return 0; diff --git a/test/function/131.c b/test/function/131.c index 5256efeb14..0b0ae7be02 100644 --- a/test/function/131.c +++ b/test/function/131.c @@ -24,8 +24,6 @@ static mps_gen_param_s testChain[genCOUNT] = { { 6000, 0.90 }, { 8000, 0.65 }, { 16000, 0.50 } }; -void *stackpointer; - mps_pool_t poolmv; mps_arena_t arena; @@ -33,28 +31,18 @@ mps_arena_t arena; static void test(void) { mps_pool_t pool; - mps_thr_t thread; - mps_root_t root; - mps_fmt_t format; mps_chain_t chain; mps_ap_t ap, ap2; - - mycell *a, *b; - + mycell *a[2]; mps_res_t res; int i; - /* create an arena that can't grow beyond 30 M */ - cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*40)), + /* create an arena that can't grow beyond 1 M */ + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*4)), "create arena"); - mps_arena_commit_limit_set(arena, (size_t) (1024*1024*30)); - - cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_root_create_reg(&root, arena, mps_rank_ambig(), 0, thread, - mps_stack_scan_ambig, stackpointer, 0), - "create root"); + mps_arena_commit_limit_set(arena, (size_t) (1024*1024*1)); cdie( mps_fmt_create_A(&format, arena, &fmtA), @@ -71,12 +59,14 @@ static void test(void) /* allocate until full */ i = 0; - b = NULL; + a[0] = a[1] = NULL; + cdie(mps_root_create_table(&root, arena, mps_rank_ambig(), 0, (void *)&a, 2), + "create root"); - while (allocrone(&a, ap, 128, mps_rank_exact()) == MPS_RES_OK) { + while (allocrone(&a[0], ap, 128, mps_rank_exact()) == MPS_RES_OK) { i++; - setref(a, 0, b); - b = a; + setref(a[0], 0, a[1]); + a[1] = a[0]; } comment("%d objs allocated.", i); @@ -88,7 +78,7 @@ static void test(void) mps_ap_destroy(ap); for (i = 0; i < 10; i++) { - res = allocrone(&a, ap2, 128, mps_rank_exact()); + res = allocrone(&a[0], ap2, 128, mps_rank_exact()); report("predie", "%s", err_text(res)); } @@ -97,15 +87,15 @@ static void test(void) mps_root_destroy(root); for (i = 0; i < 10; i++) { - res = allocrone(&a, ap2, 128, mps_rank_exact()); + res = allocrone(&a[0], ap2, 128, mps_rank_exact()); report("postdie", "%s", err_text(res)); } + mps_arena_park(arena); mps_ap_destroy(ap2); mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(format); - mps_thread_dereg(thread); mps_arena_destroy(arena); comment("Destroyed arena."); } @@ -113,9 +103,6 @@ static void test(void) int main(void) { - void *m; - stackpointer=&m; /* hack to get stack pointer */ - easy_tramp(test); pass(); return 0; diff --git a/test/function/132.c b/test/function/132.c index f7cae603ce..b69e6cbcec 100644 --- a/test/function/132.c +++ b/test/function/132.c @@ -23,8 +23,8 @@ OUTPUT_SPEC spill5 <= 0 grow5 = 0 avail5 > 1500000 - allocfail2 > 10000 - failres2 = MEMORY + allocfail2 > 5000 + failres2 = COMMIT_LIMIT shrink6 > 1000000 spill6 <= 0 completed = yes @@ -166,6 +166,7 @@ static void test(void) report("spill6", "%d", commit6-mps_arena_commit_limit(arena)); report("shrink6", "%d", avail5-avail6); + mps_arena_park(arena); mps_root_destroy(root); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/133.c b/test/function/133.c index 0556d5cc9d..1f717b1166 100644 --- a/test/function/133.c +++ b/test/function/133.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o rankfmt.o OUTPUT_SPEC - allocfail3 > 8000 + allocfail3 > 3000 failres3 = COMMIT_LIMIT spill8 <= 0 spill9 <= 0 @@ -120,6 +120,7 @@ static void test(void) { /* destroy everything remaining */ + mps_arena_park(arena); mps_ap_destroy(apamc); comment("Destroyed ap."); diff --git a/test/function/134.c b/test/function/134.c index 178894d731..5eee39e5c4 100644 --- a/test/function/134.c +++ b/test/function/134.c @@ -24,7 +24,7 @@ END_HEADER #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (1000000ul) +#define ITERATIONS (100000ul) #define RAMP_INTERFACE /* @@ -137,6 +137,7 @@ static void test(void) { } } + mps_arena_park(arena); mps_ap_destroy(apamc); comment("Destroyed ap."); diff --git a/test/function/136.c b/test/function/136.c index 61c8d8dd5c..b76a2fcf59 100644 --- a/test/function/136.c +++ b/test/function/136.c @@ -1,18 +1,21 @@ /* TEST_HEADER id = $Id$ - summary = MVFF low-memory test; reusing arena in other pool + summary = MVFF low-memory test; failover of CBS to freelist language = c link = testlib.o +OUTPUT_SPEC + limit < 160000 END_HEADER */ /* Purpose: - * This is a grey-box test intended to expose problems in the - * interaction between MVFF and CBS, whereby MVFF can't return - * segments to the arena when CBS can't allocate control blocks. * - * This problem is believed to occur in release.epcore.anchovy.1. + * This tests that the MVFF can continue to return blocks to the arena + * even if its CBS can no longer allocate control blocks (by failing + * over to use the freelist). + * + * This failed to work in release.epcore.anchovy.1. * * * Strategy: @@ -57,14 +60,30 @@ static void do_test(size_t extendBy, size_t avgSize, size_t align, largeObjectSize = extendBy; smallObjectSize = align; - die(mps_pool_create(&pool, arena, mps_class_mvff(), - extendBy, avgSize, align, slotHigh, arenaHigh, firstFit), - "create MVFF pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, extendBy); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, avgSize); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, arenaHigh); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, slotHigh); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, firstFit); + /* Set SPARE to 0 as we want this pool to return memory to the + arena as soon as it is freed so we can allocate it elsewhere. */ + MPS_ARGS_ADD(args, MPS_KEY_SPARE, 0.0); + die(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), + "create MVFF pool"); + } MPS_ARGS_END(args); die(mps_pool_create(&pool2, arena, mps_class_mv(), extendBy, avgSize, /* maxSize */ extendBy), "create MV pool"); + /* Allocate one small object in pool2 so that its block and span + pools get some initial memory. */ + res = mps_alloc(&p, pool2, 8); + asserts(res == MPS_RES_OK, + "Couldn't allocate one object of size %lu in second pool", + (unsigned long)8); + /* First we allocate large objects until we run out of memory. */ for(i = 0; i < MAXLARGEOBJECTS; i++) { res = mps_alloc(&p, pool, largeObjectSize); @@ -76,8 +95,10 @@ static void do_test(size_t extendBy, size_t avgSize, size_t align, asserts(res != MPS_RES_OK, "Unexpectedly managed to create %lu objects of size %lu", MAXLARGEOBJECTS, largeObjectSize); - asserts(nLargeObjects > 0, "Couldn't create even one object of size %lu", - largeObjectSize); + if (nLargeObjects < 2) { + /* Need two large objects for the rest of the test to work */ + goto done; + } /* Then we free one to make sure we can allocate some small objects */ mps_free(pool, largeObjects[nLargeObjects - 1], largeObjectSize); @@ -107,7 +128,7 @@ static void do_test(size_t extendBy, size_t avgSize, size_t align, smallObjects[i] = (mps_addr_t)0; } - /* The CBS should be in emergency mode now. */ + /* MVFF should be failing over from the CBS to the freelist now. */ /* Then we free every other large object */ for(i = 0; i < nLargeObjects; i += 2) { @@ -121,6 +142,7 @@ static void do_test(size_t extendBy, size_t avgSize, size_t align, "Couldn't allocate one object of size %lu in second pool", (unsigned long)largeObjectSize); + done: mps_pool_destroy(pool); mps_pool_destroy(pool2); } @@ -137,9 +159,9 @@ static void test(void) "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - for (comlimit = 512 *1024; comlimit >= 64 * 1024; comlimit -= 4*1024) { + for (comlimit = 512 * 1024; comlimit >= 148 * 1024; comlimit -= 4*1024) { mps_arena_commit_limit_set(arena, comlimit); - report("limit", "%x", comlimit); + report("limit", "%d", comlimit); symm = ranint(8); slotHigh = (symm >> 2) & 1; arenaHigh = (symm >> 1) & 1; diff --git a/test/function/137.c b/test/function/137.c index 4366f0a507..715dae9b4c 100644 --- a/test/function/137.c +++ b/test/function/137.c @@ -47,16 +47,16 @@ static void test(void) { unsigned int i; unsigned long nLarge; - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); mps_arena_commit_limit_set(arena, COMLIMIT1); - die( - mps_pool_create(&pool, arena, mps_class_mvff(), - EXTENDBY, 8, 8, 0, 0, 1), - "create MVFF pool"); + die(mps_pool_create(&pool, arena, mps_class_mvff(), + (size_t)EXTENDBY, (size_t)8, (mps_align_t)8, + (mps_bool_t)0, (mps_bool_t)0, (mps_bool_t)1), + "create MVFF pool"); for (i = 0; i < NSMALL; i++) { die(mps_alloc(&smallObjects[i], pool, SMALLSIZE), "small alloc failed"); diff --git a/test/function/138.c b/test/function/138.c index 8c823c6c8a..d4cfa5d5d4 100644 --- a/test/function/138.c +++ b/test/function/138.c @@ -66,6 +66,7 @@ static void test(void) mps_arena_collect(arena); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/139.c b/test/function/139.c index 7e698b887d..3ba3c2a339 100644 --- a/test/function/139.c +++ b/test/function/139.c @@ -37,16 +37,16 @@ static void test(void) { unsigned int i; unsigned long nLarge; - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); mps_arena_commit_limit_set(arena, COMLIMIT1); - die( - mps_pool_create(&pool, arena, mps_class_mvff(), - EXTENDBY, 8, 8, 0, 0, 1), - "create MVFF pool"); + die(mps_pool_create(&pool, arena, mps_class_mvff(), + (size_t)EXTENDBY, (size_t)8, (mps_align_t)8, + (mps_bool_t)0, (mps_bool_t)0, (mps_bool_t)1), + "create MVFF pool"); for (i = 0; i < NSMALL; i++) { die(mps_alloc(&smallObjects[i], pool, SMALLSIZE), "small alloc failed"); diff --git a/test/function/14.c b/test/function/14.c index 9f0e9a4f26..e77bafeb65 100644 --- a/test/function/14.c +++ b/test/function/14.c @@ -98,6 +98,7 @@ static void test(void) comment("Finished"); + mps_arena_park(arena); mps_ap_destroy(apA); mps_ap_destroy(apB); diff --git a/test/function/144.c b/test/function/144.c index d83b354fd6..4343be93fe 100644 --- a/test/function/144.c +++ b/test/function/144.c @@ -25,14 +25,14 @@ static void test(void) { mps_addr_t a, b; char *c; - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - die( - mps_pool_create(&pool, arena, mps_class_mvff_debug(), &debugOpts, - 8192, 8, 8, 0, 0, 1), - "create MVFF pool"); + die(mps_pool_create(&pool, arena, mps_class_mvff_debug(), &debugOpts, + (size_t)8192, (size_t)8, (mps_align_t)8, + (mps_bool_t)0, (mps_bool_t)0, (mps_bool_t)1), + "create MVFF pool"); die(mps_alloc(&a, pool, 64), "alloc a"); die(mps_alloc(&b, pool, 64), "alloc b"); diff --git a/test/function/147.c b/test/function/147.c index 21c76bcdbd..1911f05a54 100644 --- a/test/function/147.c +++ b/test/function/147.c @@ -79,6 +79,7 @@ static void test(void) } } + mps_arena_park(arena); mps_ap_destroy(sap); comment("Destroyed ap."); diff --git a/test/function/148.c b/test/function/148.c index ee80c9169b..d78b963145 100644 --- a/test/function/148.c +++ b/test/function/148.c @@ -124,6 +124,7 @@ static void test(void) report("inc4", "%ld", (com2-com1)/BIGSIZE); + mps_arena_park(arena); mps_ap_destroy(ap); mps_ap_destroy(sap); mps_pool_destroy(pool); diff --git a/test/function/149.c b/test/function/149.c index a9d47dac8d..a26f510ebd 100644 --- a/test/function/149.c +++ b/test/function/149.c @@ -23,8 +23,8 @@ OUTPUT_SPEC spill5 <= 0 grow5 = 0 avail5 > 1500000 - allocfail2 > 10000 - failres2 = MEMORY + allocfail2 > 5000 + failres2 = COMMIT_LIMIT shrink6 > 1000000 spill6 <= 0 completed = yes @@ -168,6 +168,7 @@ static void test(void) { report("spill6", "%d", commit6-mps_arena_commit_limit(arena)); report("shrink6", "%d", avail5-avail6); + mps_arena_park(arena); mps_root_destroy(root); comment("Destroyed root."); diff --git a/test/function/15.c b/test/function/15.c index e84ad74313..cf04dd7554 100644 --- a/test/function/15.c +++ b/test/function/15.c @@ -54,6 +54,7 @@ static void test(void) allocdumb(ap, 1024*256); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); diff --git a/test/function/150.c b/test/function/150.c index 1ff43383d3..c8496f3d66 100644 --- a/test/function/150.c +++ b/test/function/150.c @@ -141,9 +141,6 @@ static void messagepoll(mycell **ref, int faction) } -#define clear(type, var) (*((volatile type*)&(var)) = NULL) - - static void test(void) { mps_pool_t poolamc, poolawl, poollo; @@ -198,13 +195,12 @@ static void test(void) /* register loads of objects for finalization (1000*4) */ a = allocone(apamc, 2, 1); - b = a; for (j=0; j<1000; j++) { - a = allocone(apamc, 2, mps_rank_exact()); + b = allocone(apamc, 2, mps_rank_exact()); c = allocone(apawl, 2, mps_rank_weak()); d = allocone(aplo, 2, mps_rank_exact()); /* rank irrelevant here! */ - mps_finalize(arena, (mps_addr_t*)&a); + mps_finalize(arena, (mps_addr_t*)&b); mps_finalize(arena, (mps_addr_t*)&c); mps_finalize(arena, (mps_addr_t*)&d); mps_finalize(arena, (mps_addr_t*)&d); @@ -212,16 +208,16 @@ static void test(void) setref(a, 0, b); setref(a, 1, c); setref(c, 1, d); - b = a; + a = b; } /* throw them all away and collect everything */ - clear(mycell*, a); - clear(mycell*, b); - clear(mycell*, c); - clear(mycell*, d); - clear(mycell*, exfmt_root); + a = NULL; + b = NULL; + c = NULL; + d = NULL; + exfmt_root = NULL; for (j=0; j<5; j++) { mps_arena_collect(arena); @@ -286,6 +282,7 @@ static void test(void) messagepoll(&z, FINAL_DISCARD); } + mps_arena_park(arena); mps_root_destroy(root0); mps_root_destroy(root1); comment("Destroyed roots."); @@ -304,6 +301,7 @@ static void test(void) report("count2", "%d", final_count); + mps_arena_park(arena); mps_pool_destroy(poolamc); mps_pool_destroy(poolawl); mps_pool_destroy(poollo); diff --git a/test/function/151.c b/test/function/151.c index c6bfb05de0..2bf3114857 100644 --- a/test/function/151.c +++ b/test/function/151.c @@ -25,7 +25,6 @@ static void test(void) mps_pool_t spool; mps_thr_t thread; mps_frame_t frame; - mycell *p; /* create an arena that can't grow beyond 30 M */ @@ -54,11 +53,12 @@ static void test(void) for (i=0; i < ITERATIONS; i++) { die(mps_ap_frame_push(&frame, sap), "push"); - p = allocdumb(sap, OBJSIZE, mps_rank_exact()); + (void)allocdumb(sap, OBJSIZE, mps_rank_exact()); die(mps_ap_frame_pop(sap, frame), "pop"); comment("%i of %i", i, ITERATIONS); } + mps_arena_park(arena); mps_ap_destroy(sap); comment("Destroyed ap."); diff --git a/test/function/152.c b/test/function/152.c index 784a40a2a1..1836637668 100644 --- a/test/function/152.c +++ b/test/function/152.c @@ -100,6 +100,7 @@ static void test(void) report("com", "%ld", com1); report("inc2", "%ld", (com1-com)/BIGSIZE); + mps_arena_park(arena); mps_ap_destroy(ap); mps_ap_destroy(sap); comment("Destroyed ap."); diff --git a/test/function/153.c b/test/function/153.c index 97dac39277..219d5f50f4 100644 --- a/test/function/153.c +++ b/test/function/153.c @@ -24,7 +24,6 @@ static void test(void) mps_pool_t spool; mps_thr_t thread; mps_frame_t frame; - mycell *p; /* create an arena that can't grow beyond 30 M */ @@ -53,11 +52,12 @@ static void test(void) for (i=0; i < ITERATIONS; i++) { die(mps_ap_frame_push(&frame, sap), "push"); - p = allocdumb(sap, OBJSIZE, mps_rank_exact()); + (void)allocdumb(sap, OBJSIZE, mps_rank_exact()); die(mps_ap_frame_pop(sap, frame), "pop"); comment("%i of %i", i, ITERATIONS); } + mps_arena_park(arena); mps_ap_destroy(sap); comment("Destroyed ap."); diff --git a/test/function/158.c b/test/function/158.c index 7331f82689..7f54823d1d 100644 --- a/test/function/158.c +++ b/test/function/158.c @@ -25,14 +25,14 @@ static void test(void) { mps_pool_t pool; mps_addr_t a; - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - die( - mps_pool_create(&pool, arena, mps_class_mvff_debug(), &debugOpts, - 8192, 8, 8, 1, 0, 0), - "create MVFF pool"); + die(mps_pool_create(&pool, arena, mps_class_mvff_debug(), &debugOpts, + (size_t)8192, (size_t)8, (mps_align_t)8, + (mps_bool_t)1, (mps_bool_t)0, (mps_bool_t)0), + "create MVFF pool"); die(mps_alloc(&a, pool, 64), "alloc a"); diff --git a/test/function/159.c b/test/function/159.c index c8c1843fc8..987824dd73 100644 --- a/test/function/159.c +++ b/test/function/159.c @@ -25,14 +25,14 @@ static void test(void) { mps_addr_t a; char * c; - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - die( - mps_pool_create(&pool, arena, mps_class_mvff_debug(), &debugOpts, - 8192, 8, 8, 0, 1, 0), - "create MVFF pool"); + die(mps_pool_create(&pool, arena, mps_class_mvff_debug(), &debugOpts, + (size_t)8192, (size_t)8, (mps_align_t)8, + (mps_bool_t)0, (mps_bool_t)1, (mps_bool_t)0), + "create MVFF pool"); die(mps_alloc(&a, pool, 63), "alloc a"); diff --git a/test/function/16.c b/test/function/16.c index d89940f654..f308820837 100644 --- a/test/function/16.c +++ b/test/function/16.c @@ -88,6 +88,7 @@ static void test(void) comment("Finished"); + mps_arena_park(arena); mps_ap_destroy(apA); mps_ap_destroy(apB); diff --git a/test/function/160.c b/test/function/160.c index 6406a5d124..af5b017e2a 100644 --- a/test/function/160.c +++ b/test/function/160.c @@ -25,14 +25,13 @@ static void test(void) { mps_addr_t a; char *c; - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - die( - mps_pool_create(&pool, arena, mps_class_mv_debug(), &debugOpts, - 8192, 8, 65536), - "create MVFF pool"); + die(mps_pool_create(&pool, arena, mps_class_mv_debug(), &debugOpts, + (size_t)8192, (size_t)8, (size_t)65536), + "create MV pool"); die(mps_alloc(&a, pool, 64), "alloc a"); diff --git a/test/function/161.c b/test/function/161.c index 1c4d06d6e3..23a0fb8be1 100644 --- a/test/function/161.c +++ b/test/function/161.c @@ -28,14 +28,13 @@ static void test(void) mps_pool_t pool; mps_addr_t a; - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - die( - mps_pool_create(&pool, arena, mps_class_mv_debug(), &debugOpts, - 8192, 8, 65536), - "create MVFF pool"); + die(mps_pool_create(&pool, arena, mps_class_mv_debug(), &debugOpts, + (size_t)8192, (size_t)8, (size_t)65536), + "create MVFF pool"); die(mps_alloc(&a, pool, 64), "alloc a"); diff --git a/test/function/162.c b/test/function/162.c index 1b93d1e9e5..8967b95b26 100644 --- a/test/function/162.c +++ b/test/function/162.c @@ -25,14 +25,13 @@ static void test(void) { mps_addr_t a; char * c; - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - die( - mps_pool_create(&pool, arena, mps_class_mv_debug(), &debugOpts, - 8192, 8, 65536), - "create MVFF pool"); + die(mps_pool_create(&pool, arena, mps_class_mv_debug(), &debugOpts, + (size_t)8192, (size_t)8, (size_t)65536), + "create MVFF pool"); die(mps_alloc(&a, pool, 63), "alloc a"); diff --git a/test/function/163.c b/test/function/163.c index e7f24197bf..9b735a076f 100644 --- a/test/function/163.c +++ b/test/function/163.c @@ -47,16 +47,16 @@ static void test(void) { unsigned int i; unsigned long nLarge; - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); mps_arena_commit_limit_set(arena, COMLIMIT1); - die( - mps_pool_create(&pool, arena, mps_class_mvff(), - EXTENDBY, 8, MPS_PF_ALIGN, 0, 0, 1), - "create MVFF pool"); + die(mps_pool_create(&pool, arena, mps_class_mvff(), + (size_t)EXTENDBY, (size_t)8, (mps_align_t)MPS_PF_ALIGN, + (mps_bool_t)0, (mps_bool_t)0, (mps_bool_t)1), + "create MVFF pool"); for (i = 0; i < NSMALL; i++) { die(mps_alloc(&smallObjects[i], pool, SMALLSIZE), "small alloc failed"); diff --git a/test/function/165.c b/test/function/165.c index d267c08a53..5bd3bda7a4 100644 --- a/test/function/165.c +++ b/test/function/165.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = simple spare_commit_limit test + summary = simple spare commit limit test language = c link = testlib.o rankfmt.o harness = 2.0 @@ -9,7 +9,7 @@ TEST_HEADER OUTPUT_SPEC reduce1 > 4000000 reduce2 <= 0 - reduce3 > 4000000 + reduce3 > 3000000 completed = yes END_HEADER */ @@ -18,9 +18,6 @@ END_HEADER #include "mpscmvff.h" #include "mpsavm.h" -#define MVFF_HI_PARMS EXTEND,AVGSIZE,MPS_PF_ALIGN,1,1,0 -#define MVFF_LO_PARMS EXTEND,AVGSIZE,MPS_PF_ALIGN,0,0,1 - mps_arena_t arena; #define MAXOBJS (10000) @@ -30,36 +27,42 @@ mps_addr_t sizes[MAXOBJS]; static void test(void) { - mps_pool_t poolhi, poollo; + mps_pool_t pool; mps_thr_t thread; unsigned long com0, com1, com2; -/* create a VM arena of 30MB */ - - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), (size_t) (1024*1024*40)), - "create arena"); +/* create a VM arena of 40MB with commit limit of 100MB, i.e. let the + arena do the limiting. */ -/* set the commit limit to 100MB, i.e. let the arena do the limiting */ - - mps_arena_commit_limit_set(arena, (size_t) (1024ul*1024ul*100ul)); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 1024*1024*40); + MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, 1024ul*1024ul*100ul); + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), args), + "create arena"); + } MPS_ARGS_END(args); cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie( - mps_pool_create(&poolhi, arena, mps_class_mvff(), MVFF_HI_PARMS), - "create high pool"); - - cdie( - mps_pool_create(&poollo, arena, mps_class_mvff(), MVFF_LO_PARMS), - "create low pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, EXTEND); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, AVGSIZE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, 0); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, 0); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, 1); + /* Set SPARE to 0 as we are testing arena hysteresis here and we + don't want MVFF hysteresis to get in the way. */ + MPS_ARGS_ADD(args, MPS_KEY_SPARE, 0.0); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), + "create low pool"); + } MPS_ARGS_END(args); /* Set the spare commit limit to 0MB */ mps_arena_spare_commit_limit_set(arena, (size_t) 0); - die(mps_alloc(&objs[0], poollo, BIGSIZE), "alloc"); + die(mps_alloc(&objs[0], pool, BIGSIZE), "alloc"); com0 = mps_arena_committed(arena); - mps_free(poollo, objs[0], BIGSIZE); + mps_free(pool, objs[0], BIGSIZE); com1 = mps_arena_committed(arena); /* the free should have reduced the total amount committed */ @@ -69,9 +72,9 @@ static void test(void) /* nb. size_t unsigned, therefore (size_t)-1 is the maximum limit */ mps_arena_spare_commit_limit_set(arena, (size_t)-1); - die(mps_alloc(&objs[0], poollo, BIGSIZE), "alloc"); + die(mps_alloc(&objs[0], pool, BIGSIZE), "alloc"); com0 = mps_arena_committed(arena); - mps_free(poollo, objs[0], BIGSIZE); + mps_free(pool, objs[0], BIGSIZE); com1 = mps_arena_committed(arena); /* This time the free shouldn't make any difference */ @@ -81,16 +84,10 @@ static void test(void) mps_arena_spare_commit_limit_set(arena, (size_t)(1024*1024)); com2 = mps_arena_committed(arena); report("reduce3", "%ld", com0-com2); - - - - - comment("Finishing off."); - mps_pool_destroy(poolhi); - mps_pool_destroy(poollo); + mps_pool_destroy(pool); comment("Destroyed pool."); mps_thread_dereg(thread); diff --git a/test/function/167.c b/test/function/167.c index aba5607ddb..c885866809 100644 --- a/test/function/167.c +++ b/test/function/167.c @@ -17,7 +17,6 @@ END_HEADER #include "mpsavm.h" #define MVFF_HI_PARMS EXTEND,AVGSIZE,MPS_PF_ALIGN,1,1,0 -#define MVFF_LO_PARMS EXTEND,AVGSIZE,MPS_PF_ALIGN,0,0,1 mps_arena_t arena; @@ -33,9 +32,9 @@ static void test(void) unsigned long com0, com1; -/* create a VM arena of 30MB */ +/* create a VM arena of 40MB */ - cdie(mps_arena_create(&arena, mps_arena_class_vmnz(), (size_t) (1024*1024*40)), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t)(1024*1024*40)), "create arena"); @@ -49,19 +48,28 @@ static void test(void) mps_pool_create(&poolhi, arena, mps_class_mvff(), MVFF_HI_PARMS), "create high pool"); - cdie( - mps_pool_create(&poollo, arena, mps_class_mvff(), MVFF_LO_PARMS), - "create low pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, EXTEND); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, AVGSIZE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, 0); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, 0); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, 1); + /* Set SPARE to 0 as we want this pool to return memory to the + arena as soon as it is freed so we can allocate it elsewhere. */ + MPS_ARGS_ADD(args, MPS_KEY_SPARE, 0.0); + cdie(mps_pool_create_k(&poollo, arena, mps_class_mvff(), args), + "create low pool"); + } MPS_ARGS_END(args); /* set the spare commit limit to something very big */ mps_arena_spare_commit_limit_set(arena, (size_t)-1); /* allocate a jolly big object, clamp the commit limit down, leaving - 64KB space, then free it */ + 128KB space, then free it */ die(mps_alloc(&objs[0], poollo, BIGSIZE), "alloc"); com0 = mps_arena_committed(arena); - mps_arena_commit_limit_set(arena, com0+(1024*64)); + mps_arena_commit_limit_set(arena, com0+(1024*128)); mps_free(poollo, objs[0], BIGSIZE); com1 = mps_arena_committed(arena); diff --git a/test/function/17.c b/test/function/17.c index f029f9a742..a8f665d3ab 100644 --- a/test/function/17.c +++ b/test/function/17.c @@ -47,6 +47,7 @@ static void test(void) pool1=pool; } + mps_arena_park(arena); mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(format); diff --git a/test/function/170.c b/test/function/170.c index 53ff1bfb58..28eb7028be 100644 --- a/test/function/170.c +++ b/test/function/170.c @@ -17,10 +17,6 @@ END_HEADER #include "mpsavm.h" -#define MVFF_HI_PARMS EXTEND,AVGSIZE,MPS_PF_ALIGN,1,1,0 -#define MVFF_LO_PARMS EXTEND,AVGSIZE,MPS_PF_ALIGN,0,0,1 - - enum { SPARE_EMPTY, SPARE_LESS, @@ -116,13 +112,27 @@ static void t_alloc(int spare, int spare_total, int commit, int obj_size) { /* create low and high pools */ - die( - mps_pool_create(&poolhi, arena, mps_class_mvff(), MVFF_HI_PARMS), - "create high pool"); - - die( - mps_pool_create(&poollo, arena, mps_class_mvff(), MVFF_LO_PARMS), - "create low pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, EXTEND); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, AVGSIZE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, 1); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, 1); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, 0); + MPS_ARGS_ADD(args, MPS_KEY_SPARE, 0.0); + die(mps_pool_create_k(&poolhi, arena, mps_class_mvff(), args), + "create high pool"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, EXTEND); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, AVGSIZE); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, 0); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, 0); + MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, 1); + MPS_ARGS_ADD(args, MPS_KEY_SPARE, 0.0); + die(mps_pool_create_k(&poollo, arena, mps_class_mvff(), args), + "create low pool"); + } MPS_ARGS_END(args); /* flush hysteresis fund, then set limit */ @@ -130,10 +140,12 @@ static void t_alloc(int spare, int spare_total, int commit, int obj_size) { mps_arena_spare_commit_limit_set(arena, SPARE_LIMIT); /* allocate something in each pool (to reduce risk of subsidiary - allocation being neede later */ + allocation being needed later) */ die(mps_alloc(&objlo, poollo, EXTEND), "low alloc"); + mps_free(poollo, objlo, EXTEND); die(mps_alloc(&objhi, poolhi, EXTEND), "high alloc"); + mps_free(poolhi, objhi, EXTEND); /* set up spare committed the way we want it */ @@ -181,7 +193,12 @@ static void t_alloc(int spare, int spare_total, int commit, int obj_size) { } if (res != res_expected) { - comment("Spare useful/total %i/%i. Limit %i. Size %i. Expected %s. Got %s", spare, spare_total, commit, obj_size, err_text(res_expected), err_text(res)); + comment("hisize=%lu losize=%lu\n" + "comsize=%lu comlimit=%lu\n" + "Expected %s. Got %s", + (unsigned long)hisize, (unsigned long)losize, + (unsigned long)comsize, (unsigned long)comlimit, + err_text(res_expected), err_text(res)); report("failed", "yes"); } @@ -196,7 +213,7 @@ static void test(void) /* create a VM arena of 100MB */ - cdie(mps_arena_create(&arena,mps_arena_class_vmnz(),(size_t)(1024*1024*100)), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t)(1024*1024*100)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); diff --git a/test/function/171.c b/test/function/171.c index b6a0037aa5..5be537d758 100644 --- a/test/function/171.c +++ b/test/function/171.c @@ -18,14 +18,14 @@ END_HEADER #define ARENALIMIT (ARENA) #define TABSIZE (50000) -#define ENTERRAMP (30000) -#define LEAVERAMP (100000) +#define ENTERRAMP (3000) +#define LEAVERAMP (10000) #define BACKSIZE (128) #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (1000000ul) +#define ITERATIONS (100000ul) #define RAMP_INTERFACE /* @@ -138,6 +138,7 @@ static void test(void) { } } + mps_arena_park(arena); mps_ap_destroy(apamc); comment("Destroyed ap."); diff --git a/test/function/18.c b/test/function/18.c index 21f2c38e69..45f1a0fe8c 100644 --- a/test/function/18.c +++ b/test/function/18.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o newfmt.o OUTPUT_SPEC - errtext = create pool: MEMORY + errtext = create AMC pool: COMMIT_LIMIT END_HEADER */ @@ -39,6 +39,7 @@ static void test(void) cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); + die(mps_arena_commit_limit_set(arena, 1ul << 30), "commit_limit_set"); die(mps_thread_reg(&thread, arena), "register thread"); die(mps_root_create_reg(&root, arena, mps_rank_ambig(), 0, thread, mps_stack_scan_ambig, stackpointer, 0), @@ -47,8 +48,8 @@ static void test(void) die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); die(mps_pool_create(&pool, arena, mps_class_mv(), - 1024*32, 1024*16, 1024*256), - "pool"); + (size_t)(1024*32), (size_t)(1024*16), (size_t)(1024*256)), + "create MV pool"); do { res = mps_alloc(&q, pool, 64*1024); @@ -59,7 +60,7 @@ static void test(void) while (1) { p++; die(mmqa_pool_create_chain(&pool, arena, mps_class_amc(), format, chain), - "create pool"); + "create AMC pool"); report("pool", "%i", p); } diff --git a/test/function/19.c b/test/function/19.c index 7727f45310..15fdecd901 100644 --- a/test/function/19.c +++ b/test/function/19.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o newfmt.o OUTPUT_SPEC - errtext = create ap: MEMORY + errtext = create ap: COMMIT_LIMIT END_HEADER */ @@ -40,6 +40,7 @@ static void test(void) cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); + die(mps_arena_commit_limit_set(arena, 1ul << 30), "commit_limit_set"); die(mps_thread_reg(&thread, arena), "register thread"); die(mps_root_create_reg(&root, arena, mps_rank_ambig(), 0, thread, mps_stack_scan_ambig, stackpointer, 0), @@ -48,14 +49,14 @@ static void test(void) die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); die(mps_pool_create(&pool, arena, mps_class_mv(), - 1024*32, 1024*16, 1024*256), - "pool"); + (size_t)(1024*32), (size_t)(1024*16), (size_t)(1024*256)), + "create MV pool"); while (mps_alloc(&q, pool, 64*1024)==MPS_RES_OK); p = 0; cdie(mmqa_pool_create_chain(&pool, arena, mps_class_amc(), format, chain), - "create pool"); + "create AMC pool"); while (1) { p++; diff --git a/test/function/2.c b/test/function/2.c index 2133f56f47..7c0c697491 100644 --- a/test/function/2.c +++ b/test/function/2.c @@ -82,6 +82,7 @@ static void test(void) b = b->ref[0]; } + mps_arena_park(arena); mps_ap_destroy(ap); comment("Destroyed ap."); diff --git a/test/function/20.c b/test/function/20.c index 5a95e31439..3cec79e7df 100644 --- a/test/function/20.c +++ b/test/function/20.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o newfmt.o OUTPUT_SPEC - errtext = create format: MEMORY + errtext = create format: COMMIT_LIMIT END_HEADER */ @@ -27,12 +27,14 @@ static void test(void) { int p; die(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create"); + die(mps_arena_commit_limit_set(arena, 1ul << 30), "commit_limit_set"); die(mps_thread_reg(&thread, arena), "register thread"); die(mps_root_create_reg(&root, arena, mps_rank_ambig(), 0, thread, mps_stack_scan_ambig, stackpointer, 0), "create root"); die(mps_pool_create(&pool, arena, mps_class_mv(), - 1024*32, 1024*16, 1024*256), "pool"); + (size_t)(1024*32), (size_t)(1024*16), (size_t)(1024*256)), + "create MV pool"); while (mps_alloc(&q, pool, 64*1024)==MPS_RES_OK); p=0; diff --git a/test/function/203.c b/test/function/203.c index bd3cf8d92e..207937d1d3 100644 --- a/test/function/203.c +++ b/test/function/203.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = new MV2 allocation test + summary = new MVT allocation test language = c link = testlib.o END_HEADER @@ -9,14 +9,10 @@ END_HEADER #include #include "testlib.h" -#include "mpscmv2.h" +#include "mpscmvt.h" #include "mpsavm.h" -#define MAXNUMBER 1000000 - -/* this shouldn't be necessary, but it's not provided anywhere */ - -typedef MPS_T_WORD mps_count_t; +#define MAXNUMBER 100000 void *stackpointer; mps_arena_t arena; @@ -42,7 +38,7 @@ static void setobj(mps_addr_t a, size_t size, unsigned char val) } } -static mps_res_t mv2_alloc(mps_addr_t *ref, mps_ap_t ap, size_t size) { +static mps_res_t mvt_alloc(mps_addr_t *ref, mps_ap_t ap, size_t size) { mps_res_t res; size = ((size+7)/8)*8; @@ -73,7 +69,7 @@ static int chkobj(mps_addr_t a, size_t size, unsigned char val) static void dt(int kind, size_t minSize, size_t avgSize, size_t maxSize, - mps_count_t depth, mps_count_t fragLimit, + mps_word_t depth, mps_word_t fragLimit, size_t mins, size_t maxs, int number, int iter) { mps_pool_t pool; @@ -89,9 +85,9 @@ static void dt(int kind, asserts(time0 != -1, "processor time not available"); die( - mps_pool_create(&pool, arena, mps_class_mv2(), + mps_pool_create(&pool, arena, mps_class_mvt(), minSize, avgSize, maxSize, depth, fragLimit), - "create MV2 pool"); + "create MVT pool"); die(mps_ap_create(&ap, pool, mps_rank_ambig()), "create ap"); @@ -104,7 +100,7 @@ static void dt(int kind, } else { - die(mv2_alloc(&queue[hd].addr, ap, size), "alloc"); + die(mvt_alloc(&queue[hd].addr, ap, size), "alloc"); setobj(queue[hd].addr, size, (unsigned char) (hd%256)); queue[hd].size = size; } @@ -136,12 +132,13 @@ static void dt(int kind, } else { - die(mv2_alloc(&queue[hd].addr, ap, size),"alloc"); + die(mvt_alloc(&queue[hd].addr, ap, size),"alloc"); setobj(queue[hd].addr, size, (unsigned char) (hd%256)); queue[hd].size = size; } } + mps_ap_destroy(ap); mps_pool_destroy(pool); time1=clock(); @@ -157,7 +154,7 @@ static void test(void) { mps_thr_t thread; size_t mins; - mps_count_t dep, frag; + mps_word_t dep, frag; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*100)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); @@ -170,34 +167,34 @@ static void test(void) comment("Frag: %i", frag); - dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, 1000); - dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, 100000); + dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, 100); + dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, 10000); - dt(DUMMY, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(SEQ, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(RAN, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(SEQGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(RANGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); + dt(DUMMY, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(SEQ, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(RAN, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(SEQGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(RANGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); - dt(DUMMY, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(SEQ, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(RAN, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(SEQGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(RANGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); + dt(DUMMY, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(SEQ, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(RAN, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(SEQGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(RANGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); - dt(DUMMY, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQ, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RAN, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RANGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); + dt(DUMMY, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQ, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RAN, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RANGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); /* try again using exceptional obj for anything over 16K */ - dt(DUMMY, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQ, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RAN, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RANGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); + dt(DUMMY, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQ, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RAN, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RANGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); } diff --git a/test/function/204.c b/test/function/204.c index 73e40029df..d1a0073108 100644 --- a/test/function/204.c +++ b/test/function/204.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = new MV2 allocation test, extra shallow + summary = new MVT allocation test, extra shallow language = c link = testlib.o END_HEADER @@ -9,15 +9,11 @@ END_HEADER #include #include "testlib.h" -#include "mpscmv2.h" +#include "mpscmvt.h" #include "mpsavm.h" #define MAXNUMBER 1000000 -/* this shouldn't be necessary, but it's not provided anywhere */ - -typedef MPS_T_WORD mps_count_t; - void *stackpointer; mps_arena_t arena; @@ -42,7 +38,7 @@ static void setobj(mps_addr_t a, size_t size, unsigned char val) } } -static mps_res_t mv2_alloc(mps_addr_t *ref, mps_ap_t ap, size_t size) { +static mps_res_t mvt_alloc(mps_addr_t *ref, mps_ap_t ap, size_t size) { mps_res_t res; size = ((size+7)/8)*8; @@ -73,7 +69,7 @@ static int chkobj(mps_addr_t a, size_t size, unsigned char val) static void dt(int kind, size_t minSize, size_t avgSize, size_t maxSize, - mps_count_t depth, mps_count_t fragLimit, + mps_word_t depth, mps_word_t fragLimit, size_t mins, size_t maxs, int number, int iter) { mps_pool_t pool; @@ -89,9 +85,9 @@ static void dt(int kind, asserts(time0 != -1, "processor time not available"); die( - mps_pool_create(&pool, arena, mps_class_mv2(), + mps_pool_create(&pool, arena, mps_class_mvt(), minSize, avgSize, maxSize, depth, fragLimit), - "create MV2 pool"); + "create MVT pool"); die(mps_ap_create(&ap, pool, mps_rank_ambig()), "create ap"); @@ -104,7 +100,7 @@ static void dt(int kind, } else { - die(mv2_alloc(&queue[hd].addr, ap, size), "alloc"); + die(mvt_alloc(&queue[hd].addr, ap, size), "alloc"); setobj(queue[hd].addr, size, (unsigned char) (hd%256)); queue[hd].size = size; } @@ -136,12 +132,13 @@ static void dt(int kind, } else { - die(mv2_alloc(&queue[hd].addr, ap, size),"alloc"); + die(mvt_alloc(&queue[hd].addr, ap, size),"alloc"); setobj(queue[hd].addr, size, (unsigned char) (hd%256)); queue[hd].size = size; } } + mps_ap_destroy(ap); mps_pool_destroy(pool); time1=clock(); @@ -157,7 +154,7 @@ static void test(void) { mps_thr_t thread; size_t mins; - mps_count_t dep, frag; + mps_word_t dep, frag; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*100)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); @@ -170,34 +167,34 @@ static void test(void) comment("Frag: %i", frag); - dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, 1000); - dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, 100000); + dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, 100); + dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, 10000); - dt(DUMMY, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(SEQ, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(RAN, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(SEQGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(RANGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); + dt(DUMMY, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(SEQ, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(RAN, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(SEQGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(RANGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); - dt(DUMMY, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(SEQ, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(RAN, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(SEQGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(RANGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); + dt(DUMMY, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(SEQ, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(RAN, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(SEQGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(RANGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); - dt(DUMMY, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQ, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RAN, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RANGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); + dt(DUMMY, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQ, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RAN, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RANGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); /* try again using exceptional obj for anything over 16K */ - dt(DUMMY, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQ, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RAN, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RANGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); + dt(DUMMY, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQ, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RAN, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RANGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); } diff --git a/test/function/205.c b/test/function/205.c index e7b4e7c812..d067d6a5ad 100644 --- a/test/function/205.c +++ b/test/function/205.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = new MV2 allocation test, extra deep + summary = new MVT allocation test, extra deep language = c link = testlib.o END_HEADER @@ -9,15 +9,11 @@ END_HEADER #include #include "testlib.h" -#include "mpscmv2.h" +#include "mpscmvt.h" #include "mpsavm.h" #define MAXNUMBER 1000000 -/* this shouldn't be necessary, but it's not provided anywhere */ - -typedef MPS_T_WORD mps_count_t; - void *stackpointer; mps_arena_t arena; @@ -42,7 +38,7 @@ static void setobj(mps_addr_t a, size_t size, unsigned char val) } } -static mps_res_t mv2_alloc(mps_addr_t *ref, mps_ap_t ap, size_t size) { +static mps_res_t mvt_alloc(mps_addr_t *ref, mps_ap_t ap, size_t size) { mps_res_t res; size = ((size+7)/8)*8; @@ -73,7 +69,7 @@ static int chkobj(mps_addr_t a, size_t size, unsigned char val) static void dt(int kind, size_t minSize, size_t avgSize, size_t maxSize, - mps_count_t depth, mps_count_t fragLimit, + mps_word_t depth, mps_word_t fragLimit, size_t mins, size_t maxs, int number, int iter) { mps_pool_t pool; @@ -89,9 +85,9 @@ static void dt(int kind, asserts(time0 != -1, "processor time not available"); die( - mps_pool_create(&pool, arena, mps_class_mv2(), + mps_pool_create(&pool, arena, mps_class_mvt(), minSize, avgSize, maxSize, depth, fragLimit), - "create MV2 pool"); + "create MVT pool"); die(mps_ap_create(&ap, pool, mps_rank_ambig()), "create ap"); @@ -104,7 +100,7 @@ static void dt(int kind, } else { - die(mv2_alloc(&queue[hd].addr, ap, size), "alloc"); + die(mvt_alloc(&queue[hd].addr, ap, size), "alloc"); setobj(queue[hd].addr, size, (unsigned char) (hd%256)); queue[hd].size = size; } @@ -136,12 +132,13 @@ static void dt(int kind, } else { - die(mv2_alloc(&queue[hd].addr, ap, size),"alloc"); + die(mvt_alloc(&queue[hd].addr, ap, size),"alloc"); setobj(queue[hd].addr, size, (unsigned char) (hd%256)); queue[hd].size = size; } } + mps_ap_destroy(ap); mps_pool_destroy(pool); time1=clock(); @@ -157,7 +154,7 @@ static void test(void) { mps_thr_t thread; size_t mins; - mps_count_t dep, frag; + mps_word_t dep, frag; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*100)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); @@ -170,34 +167,34 @@ static void test(void) comment("Frag: %i", frag); - dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, 1000); - dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, 100000); + dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, 100); + dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, 10000); - dt(DUMMY, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(SEQ, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(RAN, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(SEQGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); - dt(RANGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 1000000); + dt(DUMMY, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(SEQ, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(RAN, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(SEQGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); + dt(RANGAP, 8, 32, 64, dep, frag, 8, 64, 1000, 100000); - dt(DUMMY, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(SEQ, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(RAN, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(SEQGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); - dt(RANGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 1000000); + dt(DUMMY, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(SEQ, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(RAN, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(SEQGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); + dt(RANGAP, 100, 116, 132, dep, frag, 100, 132, 1000, 100000); - dt(DUMMY, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQ, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RAN, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RANGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 10000); + dt(DUMMY, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQ, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RAN, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RANGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, 1000); /* try again using exceptional obj for anything over 16K */ - dt(DUMMY, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQ, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RAN, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(SEQGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); - dt(RANGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 10000); + dt(DUMMY, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQ, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RAN, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(SEQGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); + dt(RANGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, 1000); } diff --git a/test/function/21.c b/test/function/21.c index d7e12e266f..7b493f6602 100644 --- a/test/function/21.c +++ b/test/function/21.c @@ -19,7 +19,8 @@ static void test(void) { die(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create"); die(mps_pool_create(&pool, arena, mps_class_mv(), - 1024*32, 1024*16, 1024*256), "pool"); + (size_t)(1024*32), (size_t)(1024*16), (size_t)(1024*256)), + "create MV pool"); for (p=0; p<2000; p++) { die(mps_alloc(&q, pool, 1024*1024), "alloc"); diff --git a/test/function/214.c b/test/function/214.c index d84e4b71be..018722786c 100644 --- a/test/function/214.c +++ b/test/function/214.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = MV2 greed test + summary = MVT greed test language = c link = testlib.o parameters = OBJECTS=1000 OBJSIZE=8192 DEPTH=2 FRAGLIMIT=50 @@ -9,19 +9,15 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv2.h" +#include "mpscmvt.h" #include "mpsavm.h" -/* this shouldn't be necessary, but it's not provided anywhere */ - -typedef MPS_T_WORD mps_count_t; - void *stackpointer; mps_arena_t arena; static mps_addr_t objs[OBJECTS]; -static mps_res_t mv2_alloc(mps_addr_t *ref, mps_ap_t ap, size_t size) { +static mps_res_t mvt_alloc(mps_addr_t *ref, mps_ap_t ap, size_t size) { mps_res_t res; size = ((size+7)/8)*8; @@ -43,20 +39,21 @@ static void test (void) { cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*100)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); die( - mps_pool_create(&pool, arena, mps_class_mv2(), - OBJSIZE, OBJSIZE, OBJSIZE, DEPTH, FRAGLIMIT), - "create MV2 pool"); + mps_pool_create(&pool, arena, mps_class_mvt(), + (size_t)OBJSIZE, (size_t)OBJSIZE, (size_t)OBJSIZE, + (mps_word_t)DEPTH, (mps_word_t)FRAGLIMIT), + "create MVT pool"); die(mps_ap_create(&ap, pool, mps_rank_ambig()), "create ap"); for (i = 0; i < OBJECTS; i++) { - die(mv2_alloc(&objs[i], ap, OBJSIZE), "alloc"); + die(mvt_alloc(&objs[i], ap, OBJSIZE), "alloc"); } report("size1", "%ld", mps_arena_committed(arena)); for (i = 0; i < OBJECTS; i+=2) { mps_free(pool, objs[i], OBJSIZE); - die(mv2_alloc(&objs[i], ap, OBJSIZE), "alloc"); + die(mvt_alloc(&objs[i], ap, OBJSIZE), "alloc"); } report("size2", "%ld", mps_arena_committed(arena)); diff --git a/test/function/215.c b/test/function/215.c index ad55e9df43..14ad7a1b80 100644 --- a/test/function/215.c +++ b/test/function/215.c @@ -150,6 +150,7 @@ static void test(void) { } } + mps_arena_park(arena); mps_ap_destroy(apamc); comment("Destroyed ap."); diff --git a/test/function/22.c b/test/function/22.c index cbdaa90989..8927643e49 100644 --- a/test/function/22.c +++ b/test/function/22.c @@ -19,7 +19,8 @@ static void test(void) { die(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create"); die(mps_pool_create(&pool, arena, mps_class_mv(), - 1024*32, 1024*16, 1024*256), "pool"); + (size_t)(1024*32), (size_t)(1024*16), (size_t)(1024*256)), + "create MV pool"); die(mps_alloc(&q, pool, 1024*1024), "alloc"); diff --git a/test/function/223.c b/test/function/223.c index 9340e44f45..6a402d1c83 100644 --- a/test/function/223.c +++ b/test/function/223.c @@ -24,7 +24,7 @@ END_HEADER #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (1000000ul) +#define ITERATIONS (100000ul) #define RAMP_INTERFACE /* @@ -97,7 +97,7 @@ static void test(void) { mps_ap_create(&apamc, poolamc, mps_rank_exact()), "create ap"); - mps_message_type_enable(arena, mps_message_type_collection_stats()); + mps_message_type_enable(arena, mps_message_type_gc()); inramp = 0; @@ -138,18 +138,18 @@ static void test(void) { rsize = 0; } } - if(mps_message_get(&message, arena, mps_message_type_collection_stats())) { + if(mps_message_get(&message, arena, mps_message_type_gc())) { unsigned long live, condemned, notCondemned; - live = mps_message_collection_stats_live_size(arena, message); - condemned = mps_message_collection_stats_condemned_size(arena, message); - notCondemned = - mps_message_collection_stats_not_condemned_size(arena, message); + live = mps_message_gc_live_size(arena, message); + condemned = mps_message_gc_condemned_size(arena, message); + notCondemned = mps_message_gc_not_condemned_size(arena, message); comment("Collection: live=%ld, condemned=%ld, not condemned = %ld", - live, condemned, notCondemned); + live, condemned, notCondemned); mps_message_discard(arena, message); } } + mps_arena_park(arena); mps_ap_destroy(apamc); comment("Destroyed ap."); diff --git a/test/function/224.c b/test/function/224.c index cd648209bf..ec828bd902 100644 --- a/test/function/224.c +++ b/test/function/224.c @@ -6,6 +6,8 @@ TEST_HEADER link = testlib.o harness = 2.5 parameters = EXTENDBY=65536 AVGSIZE=32 PROMISE=64 ITERATE=2000 +OUTPUT_SPEC + errtext = alloc: COMMIT_LIMIT END_HEADER This one is supposed to fail, telling us that MV is badly fragmented. @@ -16,7 +18,7 @@ This one is supposed to fail, telling us that MV is badly fragmented. #include "mpsavm.h" -#define VMNZSIZE ((size_t) 30*1024*1024) +#define VMSIZE ((size_t) 30*1024*1024) static void test(void) @@ -26,11 +28,11 @@ static void test(void) mps_addr_t q; int p; - die(mps_arena_create(&arena, mps_arena_class_vmnz(), VMNZSIZE), "create"); - die(mps_arena_commit_limit_set(arena, VMNZSIZE), "commit limit"); + die(mps_arena_create(&arena, mps_arena_class_vm(), VMSIZE), "create"); + die(mps_arena_commit_limit_set(arena, VMSIZE), "commit limit"); die(mps_pool_create(&pool, arena, mps_class_mv(), - EXTENDBY, AVGSIZE, EXTENDBY), + (size_t)EXTENDBY, (size_t)AVGSIZE, (size_t)EXTENDBY), "pool create"); for (p=0; psize = size; + return header + 1; +} + +static void xfree(void *p) +{ + if (p) { + header_u *header = ((header_u *)p) - 1; + mps_free(malloc_pool, header, header->size); + } +} + +static void test(void) +{ + mps_arena_t arena; + size_t i, j; + void *p[POINTERS] = {0}; + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), + "create arena"); + cdie(mps_pool_create_k(&malloc_pool, arena, mps_class_mvff(), mps_args_none), + "create pool"); + + for (i = 0; i < ITERATIONS; ++i) { + j = ranint(POINTERS); + xfree(p[j]); + p[j] = xmalloc(ranint(POINTERS)); + } + for (j = 0; j < POINTERS; ++j) { + xfree(p[j]); + } + asserts(mps_pool_free_size(malloc_pool) == mps_pool_total_size(malloc_pool), + "free size != total_size"); + + mps_pool_destroy(malloc_pool); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + pass(); + return 0; +} diff --git a/test/function/23.c b/test/function/23.c index 02f08029a6..2046aa56b9 100644 --- a/test/function/23.c +++ b/test/function/23.c @@ -19,7 +19,9 @@ END_HEADER #include "mpsavm.h" #include "newfmt.h" - +#define EXTEND_BY ((size_t)(1024*128)) +#define MEAN_SIZE ((size_t)(1024*64)) +#define MAX_SIZE ((size_t)(1024*1024)) #define genCOUNT (3) static mps_gen_param_s testChain[genCOUNT] = { @@ -66,7 +68,7 @@ static void test(void) comment("Sizes in megabytes:"); die(mps_pool_create(&poolMV, arena, mps_class_mv(), - 1024*128, 1024*64, 1024*1024), + EXTEND_BY, MEAN_SIZE, MAX_SIZE), "create MV pool"); i = 0; while ((r=mps_alloc(&p, poolMV, 1024*1024)) == 0) i++; @@ -76,7 +78,7 @@ static void test(void) mps_pool_destroy(poolMV); die(mps_pool_create(&poolMV, arena, mps_class_mv(), - 1024*128, 1024*64, 1024*1024), + EXTEND_BY, MEAN_SIZE, MAX_SIZE), "create MV pool"); i = 0; while ((r=mps_alloc(&p, poolMV, 1024*1024)) == 0) i++; @@ -88,7 +90,7 @@ static void test(void) a = allocdumb(ap, 1024*1024*30); /* allocate 30 M object */ die(mps_pool_create(&poolMV, arena, mps_class_mv(), - 1024*128, 1024*64, 1024*1024), + EXTEND_BY, MEAN_SIZE, MAX_SIZE), "create MV pool"); i=0; while ((r=mps_alloc(&p, poolMV, 1024*1024)) == 0) i++; @@ -103,6 +105,8 @@ static void test(void) r = mps_alloc(&p, poolMV, 1024*1024); report("refuse4", "%s", err_text(r)); } + + mps_arena_park(arena); mps_pool_destroy(poolMV); mps_ap_destroy(ap); diff --git a/test/function/231.c b/test/function/231.c new file mode 100644 index 0000000000..c8c29fa44e --- /dev/null +++ b/test/function/231.c @@ -0,0 +1,40 @@ +/* +TEST_HEADER + id = $Id$ + summary = create/configure arena with too-small commit limit + language = c + link = testlib.o +OUTPUT_SPEC + create1 = COMMIT_LIMIT + create2 = OK + configure = FAIL +END_HEADER +*/ + +#include "testlib.h" +#include "newfmt.h" + +static void test(void) +{ + mps_arena_t arena; + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, 16 * 1024); + report_res("create1", + mps_arena_create_k(&arena, mps_arena_class_vm(), args)); + } MPS_ARGS_END(args); + + report_res("create2", + mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none)); + + report_res("configure", mps_arena_commit_limit_set(arena, 16 * 1024)); + + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + pass(); + return 0; +} diff --git a/test/function/24.c b/test/function/24.c index a16cdcec6e..920d85862b 100644 --- a/test/function/24.c +++ b/test/function/24.c @@ -10,6 +10,8 @@ END_HEADER #include "testlib.h" #include "mpscamc.h" +#define OBJSIZE (10 * (1u << 20)) + #define genCOUNT (3) static mps_gen_param_s testChain[genCOUNT] = { @@ -19,15 +21,13 @@ void *stackpointer; static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) { - MPS_SCAN_BEGIN(ss) comment("Scan: %p", base); - MPS_SCAN_END(ss); return MPS_RES_OK; } static mps_addr_t myskip(mps_addr_t object) { - return (mps_addr_t) ((char *) object + 1); + return (mps_addr_t) ((char *) object + OBJSIZE); } static void mycopy(mps_addr_t object, mps_addr_t to) @@ -100,13 +100,14 @@ static void test(void) for(i=1; i<1000; i++) { do - { die(mps_reserve(&p, ap, 10*1024*1024), "Reserve: "); + { die(mps_reserve(&p, ap, OBJSIZE), "Reserve: "); } - while (!mps_commit(ap, p, 10*1024*1024)); + while (!mps_commit(ap, p, OBJSIZE)); comment("%i at %p", i, p); comment("%i objects of 10 megabytes each allocated", i); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_fmt_destroy(format); diff --git a/test/function/25.c b/test/function/25.c index 5ee683d2cc..5e4b2e37d7 100644 --- a/test/function/25.c +++ b/test/function/25.c @@ -58,7 +58,7 @@ static void test(void) { mps_fmt_t format; - locell *a,*b,*c; + locell *a; int i; alloclocomments = 0; @@ -86,14 +86,15 @@ static void test(void) { "create ap"); a = string_ch("Hello there"); - b = string_ch("Wibble wobble foo"); - c = string_ch("Ba "); + (void)string_ch("Wibble wobble foo"); + (void)string_ch("Ba "); for (i=0; i<10000; i++) { a = conc(string_ch("B"), a); - c = conc(string_ch("Hello there"), string_ch(" folks!")); + (void)conc(string_ch("Hello there"), string_ch(" folks!")); } + mps_arena_park(arena); mps_ap_destroy(ap); comment("Destroyed ap."); diff --git a/test/function/27.c b/test/function/27.c index f0c186f397..2f30b1a81c 100644 --- a/test/function/27.c +++ b/test/function/27.c @@ -70,8 +70,8 @@ static void test(void) } + mps_arena_park(arena); mps_ap_destroy(ap); - mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(format); diff --git a/test/function/28.c b/test/function/28.c index 2ebfcd55bd..5cbf8e803f 100644 --- a/test/function/28.c +++ b/test/function/28.c @@ -96,6 +96,7 @@ static void test(void) checkfrom(a); + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/29.c b/test/function/29.c index d3294f348b..32ac7c841a 100644 --- a/test/function/29.c +++ b/test/function/29.c @@ -57,7 +57,7 @@ static void test(void) { mps_fmt_t format; - locell *a,*b,*c,*z; + locell *a; int i; alloclocomments = 0; @@ -85,15 +85,16 @@ static void test(void) { "create ap"); a = string_ch("Hello there"); - b = string_ch("Wibble wobble foo"); - c = string_ch("Ba "); + (void)string_ch("Wibble wobble foo"); + (void)string_ch("Ba "); for (i=0; i<10000; i++) { a = conc(string_ch("B"), a); - c = conc(string_ch("Hello there"), string_ch(" folks!")); - z = alloclo(ap, 0x4000); + (void)conc(string_ch("Hello there"), string_ch(" folks!")); + (void)alloclo(ap, 0x4000); } + mps_arena_park(arena); mps_ap_destroy(ap); comment("Destroyed ap."); diff --git a/test/function/3.c b/test/function/3.c index ffc2a533b4..02400bd0e7 100644 --- a/test/function/3.c +++ b/test/function/3.c @@ -82,6 +82,7 @@ static void test(void) comment("%d: %x", j, (int) a); } + mps_arena_park(arena); mps_ap_destroy(ap); comment("Destroyed ap."); diff --git a/test/function/30.c b/test/function/30.c index 748cf8b56a..583429d8bc 100644 --- a/test/function/30.c +++ b/test/function/30.c @@ -85,6 +85,7 @@ static void test(void) checkfrom(a); + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_fmt_destroy(format); diff --git a/test/function/31.c b/test/function/31.c index 2221ed53e0..29be49f667 100644 --- a/test/function/31.c +++ b/test/function/31.c @@ -34,7 +34,7 @@ static void test(void) mps_chain_t chain; mps_ap_t apamc, apawl; - mycell *a, *b, *c; + mycell *b, *c; int i; @@ -70,7 +70,7 @@ static void test(void) mps_ap_create(&apamc, poolamc, mps_rank_exact()), "create ap"); - a = allocone(apawl, 1, 1); + (void)allocone(apawl, 1, 1); b = allocone(apawl, 1, 1); c = allocone(apawl, 1000, 1); @@ -83,6 +83,7 @@ static void test(void) comment("%d of 1000.", i); } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); comment("Destroyed aps."); diff --git a/test/function/32.c b/test/function/32.c index f3cb17f155..64f0253dcf 100644 --- a/test/function/32.c +++ b/test/function/32.c @@ -34,7 +34,7 @@ static void test(void) mps_chain_t chain; mps_ap_t apamc, apawl; - mycell *a[100], *b; + mycell *a[100]; int i; int j; @@ -80,9 +80,10 @@ static void test(void) z = ranint(5); comment("setting %i (%p) %i", k, a[k], z); setref(a[k], z, a[j]); - b = allocdumb(apamc, 0x400*64, 0); + (void)allocdumb(apamc, 0x400*64, 0); } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/33.c b/test/function/33.c index a883b7a767..361736b06b 100644 --- a/test/function/33.c +++ b/test/function/33.c @@ -33,7 +33,7 @@ static void test(void) mps_chain_t chain; mps_ap_t apamc, apawl; - mycell *a[100], *b; + mycell *a[100]; int i; int j; @@ -79,9 +79,10 @@ static void test(void) z = ranint(5); comment("setting %i (%p) %i", k, a[k], z); setref(a[k], z, a[j]); - b = allocdumb(apamc, 0x400*64, 0); + (void)allocdumb(apamc, 0x400*64, 0); } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/34.c b/test/function/34.c index de1ad4ab42..56a8b39165 100644 --- a/test/function/34.c +++ b/test/function/34.c @@ -34,7 +34,7 @@ static void test(void) mps_chain_t chain; mps_ap_t apamc, apawl; - mycell *a[100], *b; + mycell *a[100]; int i; int j; @@ -82,9 +82,10 @@ static void test(void) z = ranint(2); comment("setting %i (%p) %i", k, a[k], z); setref(a[k], z, a[j]); - b = allocdumb(apamc, 0x400*64, 0); + (void)allocdumb(apamc, 0x400*64, 0); } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/35.c b/test/function/35.c index a79ab52c99..42def83780 100644 --- a/test/function/35.c +++ b/test/function/35.c @@ -107,6 +107,7 @@ static void test(void) checkfrom(*a); + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/36.c b/test/function/36.c index 3d4482311a..609d9f9210 100644 --- a/test/function/36.c +++ b/test/function/36.c @@ -90,6 +90,7 @@ static void test(void) setref(a[j], z, a[k]); } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/37.c b/test/function/37.c index 275cd58dce..664e9a3dd6 100644 --- a/test/function/37.c +++ b/test/function/37.c @@ -109,6 +109,7 @@ static void test(void) checkfrom(*a); + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/38.c b/test/function/38.c index 409cc6a97b..e351286444 100644 --- a/test/function/38.c +++ b/test/function/38.c @@ -151,6 +151,7 @@ static void test(void) } } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolmv); diff --git a/test/function/39.c b/test/function/39.c index 5b27b4ec94..54b0c2ee38 100644 --- a/test/function/39.c +++ b/test/function/39.c @@ -35,7 +35,7 @@ static void test(void) mps_chain_t chain; mps_ap_t apamc, aplo; - mycell *a[100], *b; + mycell *a[100]; int i; int j; @@ -81,9 +81,10 @@ static void test(void) z = ranint(5); comment("setting %i (%p) %i", k, a[k], z); setref(a[k], z, a[j]); - b = allocdumb(apamc, 0x400*64, 0); + (void)allocdumb(apamc, 0x400*64, 0); } + mps_arena_park(arena); mps_ap_destroy(aplo); mps_ap_destroy(apamc); comment("Destroyed aps."); diff --git a/test/function/4.c b/test/function/4.c index 3294eb0a03..e730bc77ff 100644 --- a/test/function/4.c +++ b/test/function/4.c @@ -94,6 +94,7 @@ static void test(void) time0 = time1; } + mps_arena_park(arena); mps_ap_destroy(ap); comment("Destroyed ap."); diff --git a/test/function/40.c b/test/function/40.c index c57f8c62ba..de0e4dbe46 100644 --- a/test/function/40.c +++ b/test/function/40.c @@ -66,7 +66,7 @@ static void test(void) comment("%i of 10.", i); UC; z[i] = allocone(ap, 1, 1); - if (i % 8 == 0) { z[i] = (mycell *) ((int)z[i] + 4); } /* error to scan this! */ + if (i % 8 == 0) { z[i] = (mycell *) ((mps_word_t)z[i] + 4); } /* error to scan this! */ } for (i=0; i<1000; i++) { @@ -75,6 +75,7 @@ static void test(void) DC; DMC; + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/41.c b/test/function/41.c index b394e5f37f..8fcdb372a6 100644 --- a/test/function/41.c +++ b/test/function/41.c @@ -110,6 +110,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/42.c b/test/function/42.c index 093246cba1..b56a61f2a0 100644 --- a/test/function/42.c +++ b/test/function/42.c @@ -106,6 +106,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/43.c b/test/function/43.c index da73237b90..1b955f268b 100644 --- a/test/function/43.c +++ b/test/function/43.c @@ -114,6 +114,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(apweak); mps_ap_destroy(apexact); mps_ap_destroy(apamc); diff --git a/test/function/44.c b/test/function/44.c index 949fa92281..f38e36a06e 100644 --- a/test/function/44.c +++ b/test/function/44.c @@ -166,6 +166,7 @@ static void test(void) RC; } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/45.c b/test/function/45.c index 0442a4dbd3..50fdc5ca07 100644 --- a/test/function/45.c +++ b/test/function/45.c @@ -101,9 +101,9 @@ static void test(void) for(j=0; j<1000; j++) { if (j == 500) { - size0 = mps_arena_committed(arena); + size0 = mps_arena_committed(arena) - mps_arena_spare_committed(arena); mps_arena_collect(arena); - size1 = mps_arena_committed(arena); + size1 = mps_arena_committed(arena) - mps_arena_spare_committed(arena); asserts(((long) size1)-((long) size0) < 1024*1024, "Collection made arena bigger: %lu -> %lu", (unsigned long) size0, (unsigned long) size1); @@ -172,6 +172,7 @@ static void test(void) comment("Maximum size increase:"); report("mincr", "%ld", mdiff); + comment("ambig[i] = %p", ambig[i]); /* stop compiler optimizing ambig away */ comment("Finished main loop"); for (i=0; idata.size = OBJ_SIZE; (void) mps_commit(apA, p, OBJ_SIZE); + mps_arena_park(arena); mps_ap_destroy(apA); comment("Destroyed apA."); mps_ap_destroy(apB); diff --git a/test/function/50.c b/test/function/50.c index cae53e67e1..741f865eb9 100644 --- a/test/function/50.c +++ b/test/function/50.c @@ -263,6 +263,7 @@ static void test(void) finalpoll(&z, FINAL_DISCARD); } + mps_arena_park(arena); mps_root_destroy(root0); mps_root_destroy(root1); comment("Destroyed roots."); @@ -281,6 +282,7 @@ static void test(void) report("count2", "%d", final_count); + mps_arena_park(arena); mps_pool_destroy(poolamc); mps_pool_destroy(poolawl); mps_pool_destroy(poollo); diff --git a/test/function/51.c b/test/function/51.c index 7be804d666..bdf1a87ad7 100644 --- a/test/function/51.c +++ b/test/function/51.c @@ -14,6 +14,7 @@ END_HEADER #include "testlib.h" #include "mpscawl.h" #include "mpscamc.h" +#include "mpscams.h" #include "mpsclo.h" #include "mpsavm.h" #include "rankfmt.h" @@ -94,15 +95,15 @@ static void finalpoll(mycell **ref, int faction) static void test(void) { - mps_pool_t poolamc, poolawl, poollo; + mps_pool_t poolamc, poolamcz, poolams, poolawl, poollo; mps_thr_t thread; mps_root_t root0, root1; mps_fmt_t format; mps_chain_t chain; - mps_ap_t apamc, apawl, aplo; + mps_ap_t apamc, apamcz, apams, apawl, aplo; - mycell *a, *b, *c, *d, *z; + mycell *a, *b, *c, *d, *e, *z; long int i,j; @@ -126,20 +127,39 @@ static void test(void) die(mmqa_pool_create_chain(&poolamc, arena, mps_class_amc(), format, chain), "create pool"); - cdie(mps_pool_create(&poolawl, arena, mps_class_awl(), format, getassociated), - "create pool"); - - cdie(mps_pool_create(&poollo, arena, mps_class_lo(), format), - "create pool"); + die(mmqa_pool_create_chain(&poolamcz, arena, mps_class_amcz(), format, chain), + "create pool"); - cdie(mps_ap_create(&apawl, poolawl, mps_rank_weak()), - "create ap"); - - cdie(mps_ap_create(&apamc, poolamc, mps_rank_exact()), - "create ap"); - - cdie(mps_ap_create(&aplo, poollo, mps_rank_exact()), - "create ap"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + MPS_ARGS_ADD(args, MPS_KEY_GEN, 0); + cdie(mps_pool_create_k(&poolams, arena, mps_class_ams(), args), + "create pool"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + MPS_ARGS_ADD(args, MPS_KEY_GEN, 0); + MPS_ARGS_ADD(args, MPS_KEY_AWL_FIND_DEPENDENT, getassociated); + cdie(mps_pool_create_k(&poolawl, arena, mps_class_awl(), args), + "create pool"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + MPS_ARGS_ADD(args, MPS_KEY_GEN, 0); + cdie(mps_pool_create_k(&poollo, arena, mps_class_lo(), args), + "create pool"); + } MPS_ARGS_END(args); + + cdie(mps_ap_create(&apawl, poolawl, mps_rank_weak()), "create ap"); + cdie(mps_ap_create(&apamc, poolamc, mps_rank_exact()), "create ap"); + cdie(mps_ap_create(&apamcz, poolamcz, mps_rank_exact()), "create ap"); + cdie(mps_ap_create(&apams, poolams, mps_rank_exact()), "create ap"); + cdie(mps_ap_create(&aplo, poollo, mps_rank_exact()), "create ap"); mps_message_type_enable(arena, mps_message_type_finalization()); @@ -150,13 +170,17 @@ static void test(void) for (j=0; j<1000; j++) { a = allocone(apamc, 2, mps_rank_exact()); - c = allocone(apawl, 2, mps_rank_weak()); - d = allocone(aplo, 2, mps_rank_exact()); /* rank irrelevant here! */ + b = allocone(apamcz, 2, mps_rank_exact()); /* rank irrelevant here! */ + c = allocone(apams, 2, mps_rank_exact()); + d = allocone(apawl, 2, mps_rank_weak()); + e = allocone(aplo, 2, mps_rank_exact()); /* rank irrelevant here! */ mps_finalize(arena, (mps_addr_t*)&a); + mps_finalize(arena, (mps_addr_t*)&b); mps_finalize(arena, (mps_addr_t*)&c); mps_finalize(arena, (mps_addr_t*)&d); - mps_finalize(arena, (mps_addr_t*)&d); - final_count += 4; + mps_finalize(arena, (mps_addr_t*)&e); + mps_finalize(arena, (mps_addr_t*)&e); + final_count += 6; } /* throw them all away and collect everything */ @@ -176,10 +200,12 @@ static void test(void) while (final_count != 0 && i < 10) { finalpoll(&z, FINAL_DISCARD); - if (mps_message_poll(arena) == 0) { + if (final_count != 0 && mps_message_poll(arena) == 0) { i++; - a = allocdumb(apawl, 1024, mps_rank_weak()); a = allocdumb(apamc, 1024, mps_rank_exact()); + a = allocdumb(apamcz, 1024, mps_rank_exact()); + a = allocdumb(apams, 1024, mps_rank_exact()); + a = allocdumb(apawl, 1024, mps_rank_weak()); a = allocdumb(aplo, 1024, mps_rank_exact()); mps_arena_collect(arena); comment(" %i", final_count); @@ -193,12 +219,17 @@ static void test(void) /* now to test leaving messages open for a long time! */ - mps_ap_destroy(apawl); + mps_arena_park(arena); mps_ap_destroy(apamc); + mps_ap_destroy(apamcz); + mps_ap_destroy(apams); + mps_ap_destroy(apawl); mps_ap_destroy(aplo); comment("Destroyed aps."); mps_pool_destroy(poolamc); + mps_pool_destroy(poolamcz); + mps_pool_destroy(poolams); mps_pool_destroy(poolawl); mps_pool_destroy(poollo); comment("Destroyed pools."); diff --git a/test/function/52.c b/test/function/52.c index 4663f49e65..d0c757a305 100644 --- a/test/function/52.c +++ b/test/function/52.c @@ -87,6 +87,7 @@ static void test(void) time0 = time1; } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/53.c b/test/function/53.c index 4fc78b1b18..6012a05bef 100644 --- a/test/function/53.c +++ b/test/function/53.c @@ -103,6 +103,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(apamc); mps_ap_destroy(aplo); comment("Destroyed aps."); diff --git a/test/function/54.c b/test/function/54.c index cbf4044a0e..f2c712ac15 100644 --- a/test/function/54.c +++ b/test/function/54.c @@ -105,6 +105,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/55.c b/test/function/55.c index c8ed50f3c0..f34a2fb185 100644 --- a/test/function/55.c +++ b/test/function/55.c @@ -104,6 +104,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/56.c b/test/function/56.c index 87f9a29b97..697b2e04b6 100644 --- a/test/function/56.c +++ b/test/function/56.c @@ -102,6 +102,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/57.c b/test/function/57.c index 5e0177b10b..dbaab342bd 100644 --- a/test/function/57.c +++ b/test/function/57.c @@ -99,6 +99,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/6.c b/test/function/6.c index 6c761089d8..bbbe1217b3 100644 --- a/test/function/6.c +++ b/test/function/6.c @@ -61,7 +61,7 @@ static void test(void) for (j = 1; j < 100; j++) { comment("%i of 100", j); - p = allocdumb(ap, sizeof(mps_ld_s)); + p = allocdumb(ap, sizeof(mycell)); ld = (mps_ld_t) getdata(p); b = a; @@ -79,6 +79,8 @@ static void test(void) } } + mps_arena_park(arena); + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/60.c b/test/function/60.c index 51008852af..b075e0b938 100644 --- a/test/function/60.c +++ b/test/function/60.c @@ -68,11 +68,11 @@ static void test(void) mps_ap_create(&ap2, poolawl2, mps_rank_exact()), "create ap"); - for (j=1; j<100; j++) + for (j=1; j<=10; j++) { - comment("%i of 100.", j); + comment("%i of 10.", j); - for (i=1; i<10000; i++) + for (i=1; i<=1000; i++) { UC; a = allocone(ap1, 100, 1); @@ -85,6 +85,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(ap1); mps_ap_destroy(ap2); comment("Destroyed aps."); diff --git a/test/function/61.c b/test/function/61.c index 1d3df5e90e..9ccd97d77d 100644 --- a/test/function/61.c +++ b/test/function/61.c @@ -79,6 +79,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(ap1); mps_ap_destroy(ap2); mps_pool_destroy(poolamc1); diff --git a/test/function/62.c b/test/function/62.c index cc99e36c7c..1ea9ff432b 100644 --- a/test/function/62.c +++ b/test/function/62.c @@ -79,6 +79,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(ap1); mps_ap_destroy(ap2); mps_pool_destroy(poolamc1); diff --git a/test/function/63.c b/test/function/63.c index a46c993b3f..1e9b63693e 100644 --- a/test/function/63.c +++ b/test/function/63.c @@ -73,6 +73,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(ap1); mps_pool_destroy(poolamc1); mps_chain_destroy(chain); diff --git a/test/function/64.c b/test/function/64.c index 7e9bb633d9..390567b711 100644 --- a/test/function/64.c +++ b/test/function/64.c @@ -104,6 +104,7 @@ static void test(void) DMC; } + mps_arena_park(arena); mps_ap_destroy(apamc); mps_ap_destroy(aplo); mps_pool_destroy(poolamc); diff --git a/test/function/65.c b/test/function/65.c index 3300ca895f..3305d96cd7 100644 --- a/test/function/65.c +++ b/test/function/65.c @@ -179,6 +179,7 @@ static void test(void) mps_arena_release(arena); comment("released."); + mps_arena_park(arena); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); mps_chain_destroy(chain); diff --git a/test/function/66.c b/test/function/66.c index 069a4b9935..d395dbb363 100644 --- a/test/function/66.c +++ b/test/function/66.c @@ -155,6 +155,7 @@ static void test(void) { } } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); comment("Destroyed aps."); diff --git a/test/function/69.c b/test/function/69.c index c86abc37fe..bbc758b7f4 100644 --- a/test/function/69.c +++ b/test/function/69.c @@ -94,6 +94,7 @@ static void test(void) { mps_message_discard(arena, message); + mps_arena_park(arena); mps_root_destroy(root); mps_ap_destroy(ap); mps_pool_destroy(pool); diff --git a/test/function/72.c b/test/function/72.c index 2eb6b17852..6a761742cc 100644 --- a/test/function/72.c +++ b/test/function/72.c @@ -7,7 +7,7 @@ TEST_HEADER OUTPUT_SPEC assert = true assertfile P= trace.c - assertline = 963 + assertcond = ss->rank < RankEXACT END_HEADER */ @@ -80,6 +80,7 @@ static void test(void) fail(); + mps_arena_park(arena); mps_ap_destroy(ap); comment("Destroyed ap."); diff --git a/test/function/73.c b/test/function/73.c index 492aa6cd91..9d97209060 100644 --- a/test/function/73.c +++ b/test/function/73.c @@ -26,8 +26,6 @@ static void test(void) { mps_fmt_t format; mps_ap_t apawl; - mycell *a; - long int j; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t)1024*1024*30), @@ -55,11 +53,12 @@ static void test(void) { /* alloc lots in an AWL pool; it should be collected away */ for(j=0; j<1000; j++) { - a = allocdumb(apawl, 1024ul*1024, mps_rank_exact()); + (void)allocdumb(apawl, 1024ul*1024, mps_rank_exact()); } /* (total allocated is 1000 M) */ + mps_arena_park(arena); mps_root_destroy(root0); mps_root_destroy(root1); comment("Destroyed roots."); diff --git a/test/function/74.c b/test/function/74.c index 40f86f09e1..b92295fa3e 100644 --- a/test/function/74.c +++ b/test/function/74.c @@ -26,8 +26,6 @@ static void test(void) { mps_fmt_t format; mps_ap_t aplo; - mycell *a; - long int j; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t)1024*1024*30), @@ -55,11 +53,12 @@ static void test(void) { /* alloc lots in an LO pool; it should be collected away */ for(j=0; j<1000; j++) { - a = allocdumb(aplo, 1024ul*1024, mps_rank_exact()); + (void)allocdumb(aplo, 1024ul*1024, mps_rank_exact()); } /* (total allocated is 1000 M) */ + mps_arena_park(arena); mps_root_destroy(root0); mps_root_destroy(root1); comment("Destroyed roots."); diff --git a/test/function/75.c b/test/function/75.c index d6e4a38e87..289ebb2941 100644 --- a/test/function/75.c +++ b/test/function/75.c @@ -34,8 +34,6 @@ static void test(void) mps_chain_t chain; mps_ap_t apamc; - mycell *a; - long int j; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t)1024*1024*30), @@ -64,11 +62,12 @@ static void test(void) /* alloc lots in an AMC pool; it should be collected away */ for(j=0; j<1000; j++) { - a = allocdumb(apamc, 1024ul*1024, mps_rank_exact()); + (void)allocdumb(apamc, 1024ul*1024, mps_rank_exact()); } /* (total allocated is 1000 M) */ + mps_arena_park(arena); mps_root_destroy(root0); mps_root_destroy(root1); comment("Destroyed roots."); diff --git a/test/function/76.c b/test/function/76.c index 89120d6aa9..cd0a187540 100644 --- a/test/function/76.c +++ b/test/function/76.c @@ -97,6 +97,7 @@ static void test(void) /* throw them all away and collect everything */ + comment("b = %p", b); a = NULL; b = NULL; c = NULL; @@ -121,6 +122,7 @@ static void test(void) /* now to test leaving messages open for a long time! */ + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_ap_destroy(aplo); diff --git a/test/function/77.c b/test/function/77.c index 7a32973ecd..f57ea4bc2f 100644 --- a/test/function/77.c +++ b/test/function/77.c @@ -69,8 +69,8 @@ static void test(void) b = allocone(apamc, 1, mps_rank_exact()); - for (j=1; j<100; j++) { - comment("%i of 100.", j); + for (j=1; j<=10; j++) { + comment("%i of 10.", j); a = allocone(apamc, 5, mps_rank_exact()); b = a; c = a; @@ -94,6 +94,7 @@ static void test(void) } } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/78.c b/test/function/78.c index 75c371f739..331c0f2b2b 100644 --- a/test/function/78.c +++ b/test/function/78.c @@ -97,6 +97,7 @@ static void test(void) } } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/79.c b/test/function/79.c index 42163242b7..78857b4c3b 100644 --- a/test/function/79.c +++ b/test/function/79.c @@ -94,6 +94,7 @@ static void test(void) } } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/80.c b/test/function/80.c index bdc5843fcc..2d373206f7 100644 --- a/test/function/80.c +++ b/test/function/80.c @@ -94,6 +94,7 @@ static void test(void) } } + mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/function/81.c b/test/function/81.c index ccaad2111b..4443612896 100644 --- a/test/function/81.c +++ b/test/function/81.c @@ -78,6 +78,7 @@ static void test(void) mps_arena_collect(arena); + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/83.c b/test/function/83.c index 5524e4ebd2..baad036e36 100644 --- a/test/function/83.c +++ b/test/function/83.c @@ -107,6 +107,7 @@ static void test(void) report("d", "%p", d); + mps_arena_park(arena); mps_ap_destroy(ap1); mps_ap_destroy(ap2); mps_pool_destroy(pool1); diff --git a/test/function/9.c b/test/function/9.c index 9f205a1654..9b9f41b00f 100644 --- a/test/function/9.c +++ b/test/function/9.c @@ -33,8 +33,6 @@ static void test(void) mps_chain_t chain; mps_ap_t ap; - mycell *a; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -56,8 +54,9 @@ static void test(void) mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); - a = allocdumb(ap, 1024*1024*80); + (void)allocdumb(ap, 1024*1024*80); + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/96.c b/test/function/96.c index 3ccdb83849..6504674cf4 100644 --- a/test/function/96.c +++ b/test/function/96.c @@ -32,7 +32,9 @@ static void fillup(void) mps_addr_t a; char *b; - mps_pool_create(&poolmv, arena, mps_class_mv(), 64, 64, 64); + die(mps_pool_create(&poolmv, arena, mps_class_mv(), + (size_t)64, (size_t)64, (size_t)64), + "create MV pool"); size=1024ul*1024ul; while (size) { while (mps_alloc(&a, poolmv, size)==MPS_RES_OK) { @@ -116,7 +118,9 @@ static void test(void) for (j=0; j<1000*1024; j++) { res=allocrdumb(&a, ap, 1024, mps_rank_exact()); if (res == MPS_RES_OK) { - comment("%i ok", j); + if (j % 100000 == 0) { + comment("%i ok", j); + } } else { break; } @@ -128,6 +132,7 @@ static void test(void) mps_arena_collect(arena); } + mps_arena_park(arena); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); diff --git a/test/function/97.c b/test/function/97.c index cc7f49ef43..3e5f3b8d30 100644 --- a/test/function/97.c +++ b/test/function/97.c @@ -218,10 +218,12 @@ static void test(void) tracegraph(b[1]); tracegraph(b[2]); tracegraph(b[3]); - + + comment("f[0] = %p", f[0]); /* avoid compiler warning about used f */ comment("ok"); } + mps_arena_park(arena); mps_ap_destroy(apamc); mps_ap_destroy(aplo); mps_ap_destroy(apawl); diff --git a/test/function/99.c b/test/function/99.c index d2594f1a5e..939991c155 100644 --- a/test/function/99.c +++ b/test/function/99.c @@ -94,6 +94,7 @@ static void test(void) } } + mps_arena_park(arena); mps_ap_destroy(apamcz); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); diff --git a/test/test/README b/test/test/README deleted file mode 100644 index 78ac29bdbe..0000000000 --- a/test/test/README +++ /dev/null @@ -1,10 +0,0 @@ -$Id$ - -This is the Memory Management QA test harness. To use it you need -perl 5 (or higher). Go "perl qa help" for help, "perl qa options" -to see what version of the harness you have (or look at the -file "test/version"). - -Some brief instructions are in guide.mm-qa in MM Information; ask - (ext 3822) if you need help, want to complain, &c. - diff --git a/test/test/script/clib b/test/test/script/clib index eb331ac8c0..6552a20b3b 100644 --- a/test/test/script/clib +++ b/test/test/script/clib @@ -22,7 +22,7 @@ sub clib { while (defined($tlfile = )) { unless ($tlfile =~ /^%/) { - chop($tlfile); + chomp($tlfile); $tlfile = $testlib_dir."/".$tlfile; $tlobj = $tlfile; $tlobj =~ s/\.c/$obj_suffix/; @@ -326,7 +326,7 @@ sub readSymbols { } while () { - chop; + chomp; if (/#define MMQA_SYMBOL_(.*)$/) { $mps_symbols{$1} = 1; } elsif (/#define MMQA_DEFINED_(.*)$/) { @@ -340,7 +340,7 @@ sub readSymbols { } while () { - chop; + chomp; unless (/^%/) { $mps_assumed{$_} = 1; } @@ -361,8 +361,10 @@ sub listFileSymbols { die "Failed to open $infile.\n"; } while () { - while (s/((mps|MPS)_\w+)/ /) { - push @symbols, $1; + unless (/^\/\*/ .. /\*\/$/) { + while (/\b((mps|MPS)_\w+\b)/g) { + push @symbols, $1; + } } } close(IN); diff --git a/test/test/script/headread b/test/test/script/headread index e69b9636c4..794027763b 100644 --- a/test/test/script/headread +++ b/test/test/script/headread @@ -48,7 +48,7 @@ sub readheader { $line = $_; while (! /END_HEADER/) { defined($_=) || die "Couldn't find end of test header in $infile.\n"; - chop; + chomp; if ($line =~ /\\$/) { chop($line); $line = $line.$_; @@ -154,7 +154,7 @@ sub read_results { } else { die "Badly formatted result line in output:\n$_\n"; } - } elsif (/Abort trap|abnormal program termination|Segmentation fault|MPS ASSERTION FAILED/ ) { + } elsif (/Abort trap|abnormal program termination|Segmentation fault/) { # abort for other reason $real_output{"abort"} = "true"; } elsif (/^%/ || /^\s$/) { @@ -216,7 +216,7 @@ sub verdict { $testconclusion = "PASS"; $testconcreason = ""; - foreach $key (keys %spec_output) { + foreach $key (sort keys %spec_output) { $ope = $spec_rel{$key}; $spe = $spec_output{$key}; if (defined($real_output{$key})) { diff --git a/test/test/script/options b/test/test/script/options index 023b1f5c7a..5ef905a09f 100644 --- a/test/test/script/options +++ b/test/test/script/options @@ -26,7 +26,7 @@ sub platform_detect { local $os = `uname`; local $osrel = `uname -r`; local $processor = `uname -p`; - chop($os); chop($osrel); chop($processor); + chomp($os); chomp($osrel); chomp($processor); $platform_class = $os."_".$osrel."_".$processor; $platform_class =~ s/ /_/g; $platform_phylum = "unix"; diff --git a/test/test/script/platform b/test/test/script/platform index d2560ce5bc..88b3320db7 100644 --- a/test/test/script/platform +++ b/test/test/script/platform @@ -138,11 +138,8 @@ sub settings_macosx { $cc_command = "cc"; $cc_link = "$obj_dir/platform.o"; $cc_link_opts =~ s/-z muldefs//; - # See comments in impl.gmk.xcppgc and impl.h.osxc $cc_opts =~ s/-Wstrict-prototypes//; - $cc_opts =~ s/-ggdb3//; - $cc_opts .= " -fprofile-arcs -ftest-coverage"; - $cc_opts .= " -Wno-unused -Wno-long-long -D__inline__="; + $cc_opts .= " -Wno-unused -Wno-missing-prototypes"; $stdboth_red = ">&%s"; $preprocommand = "$cc_command $cc_preonly"; } diff --git a/test/test/script/runtest b/test/test/script/runtest index b540843eab..64269c4382 100644 --- a/test/test/script/runtest +++ b/test/test/script/runtest @@ -241,7 +241,7 @@ sub run_testset { } else { while () { unless (/(^%)|(^\s*$)/) { - chop; + chomp; &run_from_testset($_); } } diff --git a/test/test/testlib/fastfmt.c b/test/test/testlib/fastfmt.c index 17f3512bca..80beda1f4a 100644 --- a/test/test/testlib/fastfmt.c +++ b/test/test/testlib/fastfmt.c @@ -139,7 +139,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) { mycell *obj = base; mps_res_t res; - mps_addr_t p, q; + mps_addr_t p; switch (obj->tag & 0x3) { @@ -151,7 +151,6 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) /* make sure to fix the assoc pointer first */ p = obj->data.assoc; if (p != NULL) { - q = p; res = MPS_FIX(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; obj->data.assoc = p; @@ -162,7 +161,6 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) p = obj->data.ref[i].addr; if (p != NULL) { - q = p; res = MPS_FIX(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; diff --git a/test/test/testlib/myfmt.c b/test/test/testlib/myfmt.c index 1e56d85487..2dd95617a5 100644 --- a/test/test/testlib/myfmt.c +++ b/test/test/testlib/myfmt.c @@ -102,7 +102,7 @@ mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) if (obj->ref[0] != NULL) { - if (formatcomments) printf("Fix: %x.\n", (int) &(obj->ref[0])); + if (formatcomments) printf("Fix: %p.\n", (void*)&(obj->ref[0])); res = MPS_FIX(ss, (mps_addr_t *) &(obj->ref[0])); /* pun! */ if (res != MPS_RES_OK) { @@ -111,7 +111,7 @@ mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) } if (obj->ref[1] != NULL) { - if (formatcomments) printf("Fix: %x.\n", (int) &(obj->ref[1])); + if (formatcomments) printf("Fix: %p.\n", (void*)&(obj->ref[1])); res = MPS_FIX(ss, (mps_addr_t *) &(obj->ref[1])); /* pun! */ if (res != MPS_RES_OK) { @@ -154,7 +154,7 @@ void mycopy(mps_addr_t object, mps_addr_t to) /* mycell *toj = to; */ - if (formatcomments) printf("copy! %x -> %x\n", (int) object, (int) to); + if (formatcomments) printf("copy! %p -> %p\n", object, to); /* this line is bad, because the objects might overlap, and then C doesn't guarantee to do the right thing! diff --git a/test/test/testlib/testlib.c b/test/test/testlib/testlib.c index 1303ceaf50..4d43211a89 100644 --- a/test/test/testlib/testlib.c +++ b/test/test/testlib/testlib.c @@ -2,7 +2,6 @@ some useful functions for testing the MPS */ #include -#include #include #include #include @@ -552,18 +551,12 @@ void *TQElement(TimeQueue TQ) void *TQPop(TimeQueue TQ) { - void *ref; - void *nref; - unsigned long ntime; unsigned long i, c, s; asserts(!TQEmpty(TQ), "TQPop called on empty TimeQueue"); - ref = TQ->element[0].ref; TQ->used -= 1; s = TQ->used; - ntime = TQ->element[s].time; - nref = TQ->element[s].ref; i = 0; while (1) { c = (2*i)+1; diff --git a/test/test/testlib/testlib.h b/test/test/testlib/testlib.h index 9ea3a62d4b..095ad07555 100644 --- a/test/test/testlib/testlib.h +++ b/test/test/testlib/testlib.h @@ -6,6 +6,8 @@ test_lib.h #ifndef testlib_h #define testlib_h +#include + #include "mmqasym.h" #ifndef MMQA_HEADER_mps #error Header file mps.h not found diff --git a/test/testsets/argerr b/test/testsets/argerr new file mode 100644 index 0000000000..990040192b --- /dev/null +++ b/test/testsets/argerr @@ -0,0 +1,155 @@ +argerr/0.c +argerr/1.c +argerr/2.c +argerr/3.c +argerr/4.c +argerr/5.c +argerr/6.c +argerr/7.c +argerr/8.c +argerr/9.c +argerr/10.c +argerr/11.c +argerr/12.c +argerr/13.c +argerr/14.c +argerr/15.c +argerr/16.c +argerr/17.c +argerr/18.c +argerr/19.c +argerr/20.c +argerr/21.c +argerr/22.c +argerr/23.c +argerr/24.c +argerr/25.c +argerr/26.c +argerr/27.c +argerr/28.c +argerr/29.c +argerr/30.c +argerr/31.c +argerr/32.c +argerr/33.c +argerr/34.c +argerr/35.c +argerr/36.c +argerr/37.c +argerr/38.c +argerr/39.c +argerr/40.c +argerr/41.c +argerr/42.c +argerr/43.c +argerr/44.c +argerr/45.c +argerr/46.c +argerr/47.c +argerr/48.c +argerr/49.c +argerr/50.c +argerr/51.c +argerr/52.c +argerr/53.c +argerr/54.c +argerr/55.c +argerr/56.c +argerr/57.c +argerr/58.c +argerr/59.c +argerr/60.c +argerr/61.c +argerr/62.c +argerr/63.c +argerr/64.c +argerr/65.c +argerr/66.c +argerr/67.c +argerr/68.c +argerr/69.c +argerr/70.c +argerr/71.c +argerr/72.c +argerr/73.c +argerr/74.c +argerr/75.c +argerr/76.c +argerr/77.c +argerr/78.c +argerr/79.c +argerr/80.c +argerr/81.c +% argerr/82.c -- see +% argerr/83.c -- see +argerr/84.c +argerr/85.c +argerr/86.c +argerr/87.c +% argerr/88.c -- see +% argerr/89.c -- see +argerr/90.c +argerr/91.c +argerr/92.c +argerr/93.c +argerr/94.c +argerr/95.c +argerr/96.c +argerr/97.c +argerr/98.c +argerr/99.c +argerr/100.c +argerr/101.c +argerr/102.c +argerr/103.c +argerr/104.c +argerr/105.c +argerr/106.c +argerr/107.c +argerr/108.c +argerr/109.c +argerr/110.c +argerr/111.c +argerr/112.c +% argerr/113.c -- last argument to mps_root_create_table is count, not size +argerr/114.c +argerr/115.c +argerr/116.c +argerr/117.c +argerr/118.c +argerr/119.c +argerr/120.c +argerr/121.c +argerr/122.c +argerr/123.c +argerr/124.c +argerr/125.c +% argerr/126.c -- see +argerr/127.c +% argerr/128.c -- see +argerr/129.c +argerr/130.c +argerr/131.c +argerr/132.c +argerr/133.c +argerr/134.c +argerr/135.c +argerr/136.c +argerr/137.c +argerr/138.c +argerr/139.c +argerr/140.c +argerr/141.c +argerr/142.c +argerr/143.c +argerr/144.c +argerr/145.c +argerr/146.c +argerr/147.c +argerr/148.c +% argerr/149.c -- you're allowed to fix non-references +% argerr/150.c -- you're allowed to fix non-references +% argerr/151.c -- you're allowed to fix unaligned references +% argerr/152.c -- no way to check this +argerr/153.c +argerr/154.c diff --git a/test/testsets/conerr b/test/testsets/conerr new file mode 100644 index 0000000000..12966b681a --- /dev/null +++ b/test/testsets/conerr @@ -0,0 +1,65 @@ +conerr/0.c +conerr/1.c +conerr/2.c +conerr/3.c +conerr/4.c +conerr/5.c +conerr/6.c +conerr/7.c +conerr/8.c +conerr/9.c +conerr/10.c +conerr/11.c +% conerr/12.c -- job003889 +conerr/13.c +conerr/14.c +conerr/15.c +conerr/16.c +conerr/17.c +conerr/18.c +conerr/19.c +conerr/20.c +conerr/21.c +conerr/22.c +conerr/23.c +conerr/24.c +conerr/25.c +conerr/26.c +conerr/27.c +conerr/28.c +conerr/29.c +conerr/30.c +conerr/31.c +conerr/32.c +% conerr/33.c -- job003791 +conerr/34.c +conerr/35.c +conerr/36.c +% conerr/37.c -- reserve/commit macros don't check arguments +conerr/37f.c +% conerr/38.c -- reserve/commit macros don't check arguments +conerr/38f.c +% conerr/39.c -- reserve/commit macros don't check arguments +conerr/39f.c +% conerr/40.c -- reserve/commit macros don't check arguments +conerr/40f.c +conerr/41.c +conerr/42.c +conerr/43.c +conerr/44.c +conerr/44a.c +conerr/45.c +conerr/46.c +conerr/47.c +conerr/48.c +% conerr/49.c -- see design.mps.thread-manager.req.register.multi +conerr/50.c +conerr/51.c +conerr/52.c +conerr/53.c +conerr/54.c +conerr/55.c +conerr/56.c +% conerr/57.c -- see +% conerr/58.c -- see +conerr/59.c diff --git a/test/testsets/passing b/test/testsets/passing index 41421d3616..c6893a54c3 100644 --- a/test/testsets/passing +++ b/test/testsets/passing @@ -7,7 +7,7 @@ function/1.c % function/3.c -- interactive test, can't run unattended % function/4.c -- interactive test, can't run unattended function/5.c -% function/6.c -- job003494 +function/6.c function/7.c % function/8.c -- tries to exhaust memory by mps_arena_create function/9.c @@ -20,9 +20,9 @@ function/14.c function/15.c function/16.c function/17.c -% function/18.c -- tries to exhaust memory by mps_alloc -% function/19.c -- tries to exhaust memory by mps_alloc -% function/20.c -- tries to exhaust memory by mps_alloc +function/18.c +function/19.c +function/20.c function/21.c function/22.c % function/23.c -- interactive test, can't run unattended @@ -48,19 +48,19 @@ function/42.c function/43.c function/44.c function/45.c -% function/46.c -- report("result", "unknown"); @@@@ +function/46.c function/47.c function/48.c function/49.c function/50.c -% function/51.c -- would pass if we allowed iter = 4 @@@@ +function/51.c function/52.c function/53.c function/55.c function/56.c function/57.c % 58-59 -- no such test -% function/60.c -- slow +function/60.c function/61.c function/62.c function/63.c @@ -72,12 +72,12 @@ function/67.c function/69.c function/70.c function/71.c -% function/72.c -- testcase seems bogus +function/72.c function/73.c function/74.c function/75.c function/76.c -% function/77.c -- slow +function/77.c function/78.c function/79.c function/80.c @@ -87,7 +87,7 @@ function/83.c % 84-95 -- no such test function/96.c function/97.c -function/98.c +% function/98.c -- tries to exhaust memory by mps_arena_create function/99.c function/100.c function/101.c @@ -104,13 +104,13 @@ function/112.c function/113.c function/114.c % 115 -- no such test -% function/116.c -- tries to exhaust memory by mps_alloc +function/116.c function/117.c function/118.c function/119.c function/120.c -% function/121.c -- job003495 -% function/122.c -- job003496 +function/121.c +function/122.c function/123.c function/124.c function/125.c @@ -118,10 +118,10 @@ function/126.c function/127.c function/128.c function/129.c -% function/130.c -- tries to exhaust memory by mps_alloc -% function/131.c -- tries to exhaust memory by mps_alloc -% function/132.c -- failed on allocfail2: wanted > 10000, was 6840 @@@@ -% function/133.c -- failed on allocfail3: wanted > 8000, was 3060 @@@@ +% function/130.c -- job003789 +% function/131.c -- job003789 +% function/132.c -- job003869 +function/133.c function/134.c function/135.c function/136.c @@ -134,7 +134,7 @@ function/144.c % 145-146 -- no such test function/147.c % function/148.c -- failed on inc4: wanted = 1, was 0 @@@@ -% function/149.c -- failed on allocfail2: wanted > 10000, was 6858 @@@@ +function/149.c function/150.c function/151.c function/152.c @@ -151,19 +151,23 @@ function/165.c % 166 -- no such test function/167.c % 168-169 -- no such test -% function/170.c -- commit limit logic seems wrong @@@@ -% function/171.c -- job003495 +function/170.c +function/171.c function/200.c % 201-202 -- no such test -% function/203.c -- requires mps_count_t and mps_class_mv2 -% function/204.c -- requires mps_count_t and mps_class_mv2 -% function/205.c -- requires mps_count_t and mps_class_mv2 +function/203.c +function/204.c +function/205.c function/206.c function/207.c % 208-213 -- no such test -% function/214.c -- requires mps_count_t and mps_class_mv2 +function/214.c function/215.c -% function/223.c -- requires mps_message_type_collection_stats -% function/224.c -- COMMIT_LIMIT @@@@ +function/223.c +function/224.c % 225 -- no such test function/226.c +function/227.c +function/228.c +function/229.c +function/231.c diff --git a/tool/.p4ignore b/tool/.p4ignore new file mode 100644 index 0000000000..835fd9ec66 --- /dev/null +++ b/tool/.p4ignore @@ -0,0 +1 @@ +.test \ No newline at end of file diff --git a/tool/branch b/tool/branch new file mode 100755 index 0000000000..5bb9b21c3e --- /dev/null +++ b/tool/branch @@ -0,0 +1,345 @@ +#!/usr/bin/env python +# +# BRANCH -- CREATE VERSION OR TASK BRANCH +# Gareth Rees, Ravenbrook Limited, 2014-03-18 +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. +# +# +# 1. INTRODUCTION +# +# This script automates the process of branching the master sources +# (or customer mainline sources) of a project. It can create a version +# branch as described in [VERSION-CREATE], or a development (task) +# branch as described in [BRANCH-MERGE]. + + +from __future__ import unicode_literals +import argparse +from collections import deque +import datetime +import os +import re +import subprocess +import sys +import p4 +import uuid + +if sys.version_info < (3,): + from codecs import open + +class Error(Exception): pass + +DEPOT = '//info.ravenbrook.com' +PROJECT_RE = r'[a-z][a-z0-9.-]*' +PROJECT_FILESPEC_RE = r'{}/project/({})/?'.format(re.escape(DEPOT), PROJECT_RE) +CUSTOMER_RE = r'[a-z][a-z0-9.-]*' +PARENT_RE = r'master|custom/({})/main'.format(CUSTOMER_RE) +PARENT_FILESPEC_RE = r'{}({})(?:/|$)'.format(PROJECT_FILESPEC_RE, PARENT_RE) +TASK_RE = r'[a-zA-Z][a-zA-Z0-9._-]*' +TASK_BRANCH_RE = r'branch/(\d\d\d\d-\d\d-\d\d)/({})'.format(TASK_RE) +VERSION_RE = r'\d+\.\d+' +VERSION_BRANCH_RE = (r'(?:custom/({})/)?version/({})' + .format(CUSTOMER_RE, VERSION_RE)) +CHILD_RE = r'(?:{}|{})$'.format(TASK_BRANCH_RE, VERSION_BRANCH_RE) + +TASK_BRANCH_ENTRY = ''' + + {date}/{task} + Changes + {desc_html} + Diffs + + +''' + +VERSION_BRANCH_ENTRY = ''' + + {version} + None. + {parent}/...@{changelevel} + + {desc_html} + + + base
+ changelists + + + +''' + +# Git Fusion repos in which to register new branches. +GF_REPOS = ['mps', 'mps-public'] + +# Regular expression matching the view of the master codeline in Git Fusion +# configuration files. +GF_VIEW_RE = r'git-branch-name\s*=\s*master\s*view\s*=\s*(.*?)\n\n' + +# Template for the new entry in the Git Fusion configuration file +GF_ENTRY = ''' + +[{uuid}] +git-branch-name = {child} +view = {gf_view} +''' + + +def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument('-P', '--project', + help='Name of the project.') + parser.add_argument('-p', '--parent', + help='Name of the parent branch.') + parser.add_argument('-C', '--changelevel', type=int, + help='Changelevel at which to make the branch.') + parser.add_argument('-d', '--description', + help='Description of the branch (for the branch spec).') + parser.add_argument('-g', '--github', action='store_true', + help='Push this branch to GitHub.') + parser.add_argument('-y', '--yes', action='store_true', + help='Yes, really make the branch.') + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-c', '--child', + help='Name of the child branch.') + group.add_argument('-v', '--version', action='store_true', + help='Make the next version branch.') + group.add_argument('-t', '--task', + help='Name of the task branch.') + args = parser.parse_args(argv[1:]) + args.depot = DEPOT + args.date = datetime.date.today().strftime('%Y-%m-%d') + fmt = lambda s: s.format(**vars(args)) + + if not args.project: + # Deduce project from current directory. + filespec = next(p4.run('dirs', '.'))['dir'] + m = re.match(PROJECT_FILESPEC_RE, filespec) + if not m: + raise Error("Can't deduce project from current directory.") + args.project = m.group(1) + print(fmt("project={project}")) + + if not any(p4.run('dirs', fmt('{depot}/project/{project}'))): + raise Error(fmt("No such project: {project}")) + + if not args.parent: + # Deduce parent branch from current directory. + filespec = next(p4.run('dirs', '.'))['dir'] + m = re.match(PARENT_FILESPEC_RE, filespec) + if not m: + raise Error("Can't deduce parent branch from {}".format(filespec)) + if args.project != m.group(1): + raise Error("Specified project={} but current directory belongs " + "to project={}.".format(args.project, m.group(1))) + args.parent = m.group(2) + print(fmt("parent={parent}")) + + m = re.match(PARENT_RE, args.parent) + if not m: + raise Error("Invalid parent branch: must be master or custom/*/main.") + args.customer = m.group(1) + if not any(p4.run('dirs', fmt('{depot}/project/{project}/{parent}'))): + raise Error(fmt("No such branch: {parent}")) + + if not args.changelevel: + cmd = p4.run('changes', '-m', '1', fmt('{depot}/project/{project}/{parent}/...')) + args.changelevel = int(next(cmd)['change']) + print(fmt("changelevel={changelevel}")) + + if args.task: + if not re.match(TASK_RE, args.task): + raise Error(fmt("Invalid task: {task}")) + args.child = fmt('branch/{date}/{task}') + print(fmt("child={child}")) + elif args.version: + # Deduce version number from code/version.c. + f = fmt('{depot}/project/{project}/{parent}/code/version.c@{changelevel}') + m = re.search(r'^#define MPS_RELEASE "release/(\d+\.\d+)\.\d+"$', + p4.contents(f), re.M) + if not m: + raise Error("Failed to extract version from {}.".format(f)) + args.version = m.group(1) + if args.parent == 'master': + args.child = fmt('version/{version}') + else: + args.child = fmt('custom/{customer}/version/{version}') + print(fmt("child={child}")) + + m = re.match(CHILD_RE, args.child) + if not m: + raise Error(fmt("Invalid child: {child}")) + if not args.task and args.customer != m.group(3): + raise Error(fmt("Customer mismatch between {parent} and {child}.")) + args.date, args.task, _, args.version = m.groups() + + if not args.description: + args.description = fmt("Branching {parent} to {child}.") + print(fmt("description={description}")) + args.desc_html = re.sub(r'\b(job\d{6})\b', + fmt(r'\1'), + args.description) + + # Create the branch specification + args.branch = fmt('{project}/{child}') + branch_spec = dict(Branch=args.branch, + Description=args.description, + View0=fmt('{depot}/project/{project}/{parent}/... ' + '{depot}/project/{project}/{child}/...')) + print("view={}".format(branch_spec['View0'])) + have_branch = False + if any(p4.run('branches', '-E', args.branch)): + print(fmt("Branch spec {branch} already exists: skipping.")) + have_branch = True + elif args.yes: + print(fmt("Creating branch spec {branch}.")) + p4.run('branch', '-i').send(branch_spec).done() + have_branch = True + else: + print("--yes omitted: skipping branch creation.") + + # Populate the branch + if any(p4.run('dirs', fmt('{depot}/project/{project}/{child}'))): + print("Child branch already populated: skipping.") + else: + srcs = fmt('{depot}/project/{project}/{parent}/...@{changelevel}') + populate_args = ['populate', '-n', + '-b', args.branch, + '-d', fmt("Branching {parent} to {child}."), + '-s', srcs] + if args.yes: + print(fmt("Populating branch {branch}...")) + populate_args.remove('-n') + p4.do(*populate_args) + elif have_branch: + print("--yes omitted: populate -n ...") + p4.do(*populate_args) + else: + print("--yes omitted: skipping populate.") + + # Determine the first change on the branch + cmd = p4.run('changes', fmt('{depot}/project/{project}/{child}/...')) + try: + args.base = int(deque(cmd, maxlen=1).pop()['change']) + print(fmt("base={base}")) + except IndexError: + args.yes = False + args.base = args.changelevel + print(fmt("Branch {child} not populated: using base={base}")) + + def register(filespec, search, replace): + args.filespec = fmt(filespec) + if p4.contents(args.filespec).find(args.child) != -1: + print(fmt("{filespec} already updated: skipping.")) + return + client_spec = dict(View0=fmt('{filespec} //__CLIENT__/target')) + with p4.temp_client(client_spec) as (conn, client_root): + filename = os.path.join(client_root, 'target') + conn.do('sync', filename) + conn.do('edit', filename) + with open(filename, encoding='utf8') as f: + text = re.sub(search, fmt(replace), f.read(), 1) + with open(filename, 'w', encoding='utf8') as f: + f.write(text) + for result in conn.run('diff'): + if 'data' in result: + print(result['data']) + if args.yes: + conn.do('submit', '-d', fmt("Registering {child}."), filename) + else: + print(fmt("--yes omitted: skipping submit of {filespec}")) + + def p4read(filespec): + return ''.join([d['data'] for d in p4.run('print', filespec) + if d['code'] == 'text']) + + if not args.version: + # Task branch + register('{depot}/project/{project}/branch/index.html', + '(?=\n)', TASK_BRANCH_ENTRY) + args.git_name = fmt('{project}-{task}') + args.git_branch = fmt('dev/{date}/{task}') + elif args.version and not args.customer: + # Public version branch + register('{depot}/project/{project}/version/index.html', + '(?<=\n)', VERSION_BRANCH_ENTRY) + args.github = True + args.git_name = fmt('{project}-version-{version}') + args.git_branch = fmt('version/{version}') + else: + args.git_name = None + + if args.github and not args.git_name: + print(fmt("Don't know how to push {child} to GitHub: skipping.")) + elif args.github: + + # Invent a UUID to use as the section title for the branch in + # the Git Fusion configuration files. + args.uuid = uuid.uuid5(uuid.NAMESPACE_URL, args.child.encode('utf7')) + print(fmt("uuid={uuid}")) + + for repo in GF_REPOS: + config = '//.git-fusion/repos/{}/p4gf_config'.format(repo) + text = p4read(config) + if re.search(str(args.uuid), text): + print('Already registered in Git Fusion repo "{}": skipping.'.format(repo)) + else: + view = re.search(GF_VIEW_RE, text, re.MULTILINE | re.DOTALL).group(1) + args.gf_view = view.replace('/master/', fmt('/{child}/')) + print('Registering in Git Fusion repo "{}".'.format(repo)) + register(config, r"\n*\Z", GF_ENTRY) + + +if __name__ == '__main__': + main(sys.argv) + + +# A. REFERENCES +# +# [BRANCH-MERGE] Gareth Rees; "Memory Pool System branching and +# merging procedures"; Ravenbrook Limited; 2014-01-09. +# +# +# [VERSION-CREATE] Richard Kistruck; "Memory Pool System Version +# Create Procedure"; Ravenbrook Limited; 2008-10-29. +# +# +# +# B. DOCUMENT HISTORY +# +# 2014-03-18 GDR Created based on [BRANCH-MERGE] and [VERSION-CREATE]. +# +# 2016-02-13 RB Adapting to Git Fusion 2. +# +# +# C. COPYRIGHT AND LICENCE +# +# Copyright (c) 2014 Ravenbrook Ltd. All rights reserved. +# +# 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 REGENTS 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. +# +# +# $Id$ diff --git a/tool/gcovfmt.py b/tool/gcovfmt similarity index 96% rename from tool/gcovfmt.py rename to tool/gcovfmt index 0f17bd0a35..53a1081ab1 100755 --- a/tool/gcovfmt.py +++ b/tool/gcovfmt @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/bin/env python # # $Id$ -# Copyright (c) 2013 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. # # This program takes the output of gcov on standard input and writes a # human-readable table with a summary, to the file named on the @@ -70,7 +70,7 @@ def main(): # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2013 Ravenbrook Limited . +# Copyright (C) 2013-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/tool/index.rst b/tool/index.rst index 9d23b949e7..8edc44095a 100644 --- a/tool/index.rst +++ b/tool/index.rst @@ -25,23 +25,29 @@ This document is not confidential. 2. Tools -------- -================= ========================================================= -`gcovfmt.py`_ Formats the output of the ``gcov`` coverage tool into a - summary table. It runs only on OS X, where it is invoked - from the Xcode project after running the test suite for - the “Debug” configuration. +================= ========================================================== +`branch`_ Make a version or development branch. +`gcovfmt`_ Formats the output of the ``gcov`` coverage tool into a + summary table. +`release`_ Make a product release. +`testcoverage`_ Instrument the test suite for coverage, run it, and output + a coverage report. +`testopendylan`_ Download the latest version of Open Dylan and build it + against the MPS sources. `testrun.bat`_ Implements the ``testrun`` make target on Windows, where it is invoked from ``commpost.nmk``. `testrun.sh`_ Implements the ``testrun`` make target on FreeBSD and - Linux, it is invoked from ``comm.gmk``, and on OS X, - where it is invoked from the Xcode project. -`test-runner.py`_ Builds and runs tests. As of 2013-05-24 it is not used. -================= ========================================================= - -.. _gcovfmt.py: gcovfmt.py + Linux, it is invoked from ``comm.gmk``, and on OS X, where + it is invoked from the Xcode project. +================= ========================================================== + +.. _branch: branch +.. _gcovfmt: gcovfmt +.. _release: release +.. _testcoverage: testcoverage +.. _testopendylan: testopendylan .. _testrun.bat: testrun.bat .. _testrun.sh: testrun.sh -.. _test-runner.py: test-runner.py A. References @@ -57,6 +63,8 @@ B. Document History 2013-05-24 GDR_ Added ``gcovfmt.py`` and ``testrun.sh``. ``test-runner.py`` is no longer used. 2014-01-13 GDR_ Converted to reStructuredText. Added ``testrun.bat``. +2014-03-22 GDR_ Add ``branch``, ``release``, ``testcoverage``, and + ``testopendylan``. ========== ====== ======================================================== .. _GDR: mailto:gdr@ravenbrook.com diff --git a/tool/noaslr.c b/tool/noaslr.c new file mode 100644 index 0000000000..3ae73dc936 --- /dev/null +++ b/tool/noaslr.c @@ -0,0 +1,102 @@ +/* noaslr.c: Disable ASLR on OS X Mavericks + * + * $Id: //info.ravenbrook.com/project/mps/master/code/eventcnv.c#26 $ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * This is a command-line tool that runs another program with address + * space layout randomization (ASLR) disabled. + * + * The technique is taken from GDB via "How gdb disables ASLR in Mac + * OS X Lion" + * + * + * On OS X Mavericks, the _POSIX_SPAWN_DISABLE_ASLR constant is not + * defined in any header, but the LLDB sources reveal its value, and + * experimentally this value works. + * + */ + +#include +#include +#include +#include + +#ifndef _POSIX_SPAWN_DISABLE_ASLR +#define _POSIX_SPAWN_DISABLE_ASLR 0x100 +#endif + +int main(int argc, char **argv) +{ + extern char **environ; + pid_t pid; + posix_spawnattr_t attr; + int res, status = 1; + char *default_argv[] = {"/bin/sh", NULL}; + + if (argc >= 2) + ++ argv; + else + argv = default_argv; + + res = posix_spawnattr_init(&attr); + if (res != 0) + return res; + + res = posix_spawnattr_setflags(&attr, _POSIX_SPAWN_DISABLE_ASLR); + if (res != 0) + return res; + + res = posix_spawn(&pid, argv[0], NULL, &attr, argv, environ); + if (res != 0) + return res; + + if (waitpid(pid, &status, 0) == -1) + return 1; + + if (!WIFEXITED(status)) + return 1; + + return WEXITSTATUS(status); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/tool/p4-bisect b/tool/p4-bisect new file mode 100755 index 0000000000..10af68cfc0 --- /dev/null +++ b/tool/p4-bisect @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# +# +# Ravenbrook +# +# +# P4-BISECT -- FIND CHANGE THAT INTRODUCED A BUG +# +# Gareth Rees, Ravenbrook Limited, 2014-04-14 +# +# +# 1. INTRODUCTION +# +# This script automates (or partly automates) the process of finding, +# by binary search, the change that introduced a bug. +# +# The interface is modelled closely on git-bisect(1). + +import argparse +from functools import partial +import json +from os import unlink +import p4 +import subprocess +import sys + +BISECT_FILE = '.p4-bisect' + +def error(msg): + sys.stderr.write(msg) + sys.stderr.write('\n') + exit(1) + +def sync(*filespecs): + try: + p4.do('sync', *filespecs) + except p4.Error as e: + if 'file(s) up-to-date' not in e.args[0]: + raise + +class State(object): + def __init__(self, **d): + self.filespec = d['filespec'] + self.changes = d['changes'] + if 'current' in d: + self.current = d['current'] + + @classmethod + def load(cls): + try: + with open(BISECT_FILE, 'r') as f: + return cls(**json.load(f)) + except FileNotFoundError: + error("p4-bisect not in progress here.") + + def save(self): + with open(BISECT_FILE, 'w') as f: + json.dump(vars(self), f) + + def update(self): + n = len(self.changes) + if n == 0: + print("no changes remaining.".format(**vars(self))) + elif n == 1: + print("{} change remaining: {}.".format(n, self.changes[0])) + elif n == 2: + print("{} changes remaining: [{}, {}]." + .format(n, self.changes[0], self.changes[-1])) + else: + print("{} changes remaining: [{}, ..., {}]." + .format(n, self.changes[0], self.changes[-1])) + if n > 0: + self.current = self.changes[n // 2] + print("Syncing to changelevel {current}.".format(**vars(self))) + sync(*['{}@{}'.format(f, self.current) for f in self.filespec]) + self.save() + +def help(parser, args): + parser.print_help() + +def start(args): + args.filespec = args.filespec or ['...'] + changes = sorted(int(c['change']) for c in p4.run('changes', *args.filespec)) + if not changes: + error("No changes for {}".format(' '.join(args.filespec))) + if args.good is None: + args.good = changes[0] + if args.bad is None: + args.bad = changes[-1] + state = State(filespec=args.filespec, + changes=[c for c in changes if args.good <= c <= args.bad]) + state.update() + +def good(args): + state = State.load() + print("Change {current} good.".format(**vars(state))) + state.changes = [c for c in state.changes if c > state.current] + state.update() + +def bad(args): + state = State.load() + print("Change {current} bad.".format(**vars(state))) + state.changes = [c for c in state.changes if c < state.current] + state.update() + +def skip(args): + state = State.load() + print("Skipping change {current}.".format(**vars(state))) + state.changes.remove(state.current) + state.update() + +def reset(args): + state = State.load() + sync(*state.filespec) + unlink(BISECT_FILE) + +def run(args): + while True: + state = State.load() + if not state.changes: + break + result = subprocess.call([args.cmd] + args.args) + if result == 0: + good(None) + elif result == 125: + skip(None) + elif 0 < result < 128: + bad(None) + else: + exit(result) + +def main(argv): + parser = argparse.ArgumentParser( + prog='p4-bisect', epilog='For help on CMD, use p4-bisect CMD -h') + subparsers = parser.add_subparsers() + a = subparsers.add_parser + + help_parser = a('help', help='show this help message') + help_parser.set_defaults(func=partial(help, parser)) + + start_parser = a('start', help='start a p4-bisect session') + aa = start_parser.add_argument + start_parser.add_argument('-f', '--filespec', action='append', + help='filespec(s) to search') + start_parser.add_argument('good', nargs='?', type=int, + help='known good changelevel') + start_parser.add_argument('bad', nargs='?', type=int, + help='known bad changelevel') + start_parser.set_defaults(func=start) + + good_parser = a('good', help='declare current revision good') + good_parser.set_defaults(func=good) + + bad_parser = a('bad', help='declare current revision bad') + bad_parser.set_defaults(func=bad) + + skip_parser = a('skip', help='skip current revision') + skip_parser.set_defaults(func=skip) + + reset_parser = a('reset', help='finish p4-bisect session') + reset_parser.set_defaults(func=reset) + + run_parser = a('run', help='run p4-bisect session automatically') + run_parser.add_argument('cmd', + help='command that determines if current ' + 'changelevel is good or bad') + run_parser.add_argument('args', nargs=argparse.REMAINDER, + help='arguments to pass to cmd') + run_parser.set_defaults(func=run) + + args = parser.parse_args(argv[1:]) + args.func(args) + +if __name__ == '__main__': + main(sys.argv) diff --git a/tool/p4.py b/tool/p4.py new file mode 100644 index 0000000000..47466e5b6f --- /dev/null +++ b/tool/p4.py @@ -0,0 +1,257 @@ +# Ravenbrook +# +# +# P4.PY -- PYTHON INTERFACE TO PERFORCE +# +# Gareth Rees, Ravenbrook Limited, 2001-03-07 + +from collections import Iterator +from contextlib import contextmanager +import marshal +import os +import re +from subprocess import Popen, PIPE + +class Error(Exception): + pass + +class Command(Iterator): + """A Perforce command and its output. + + You can iterate over the Command object to yield the outputs of + the command as dictionaries. The keys and values in these output + dictionaries are decoded according to the connection's encoding. + + >>> conn = Connection(encoding='utf8') + >>> for change in conn.run('changes', '-m', '2'): + ... print("{change} {desc}".format(**change)) + 10021 Explaining how to use the autom + 10020 Archiving new mail + + Iteration raises Error if the output indicates that an error + occurred. + + >>> print(next(conn.run('help', 'xyzzy'))['data']) + Traceback (most recent call last): + ... + p4.Error: No help for xyzzy. + + Call the send() method to send input to commands like client -i. + The keys and values in these input dictionaries are encoded + according to encoding. + + >>> cmd = conn.run('client', '-i') + >>> cmd.send({'Client': 'abc', 'Root': '/'}) + >>> print(next(cmd)['data']) + 'Client abc saved.' + + Call the done() method to finish executing the Perforce command, + closing the input, discarding any output, and raising Error if it + failed. For example: + + >>> conn.run('edit', filespec).done() + + The send() method returns the Command object, to allow method calls to + be chained: + + >>> conn.run('client', '-i').send(client).done() + + In the common case where run() is immediately followed by done(), + use the do() method: + + >>> conn.do('submit', '-d', description) + + """ + def __init__(self, *args, **kwargs): + self.encoding = kwargs.pop('encoding', 'utf8') + self.pipe = Popen([a.encode('utf8') for a in args], + stdin = PIPE, stdout = PIPE) + + def __next__(self): + # Ensure that stdin is closed before attempting to read any + # output, to avoid deadlock. + self.pipe.stdin.close() + try: + data = marshal.load(self.pipe.stdout) + except EOFError: + raise StopIteration + def decode(s): + if isinstance(s, bytes): + return s.decode(self.encoding, 'replace') + else: + return s + data = {decode(k): decode(v) for k, v in data.items()} + if data.get('code') == 'error': + raise Error(data.get('data', '')) + return data + + next = __next__ # for compatibility with Python 2 + + def send(self, data): + def encode(s): + if isinstance(s, type(u'')): + return s.encode(self.encoding) + else: + return s + data = {encode(k): encode(v) for k, v in data.items()} + marshal.dump(data, self.pipe.stdin, 0) + return self + + def done(self): + for _ in self: + pass + +class Connection(object): + """A connection to a Perforce server. + + The constructor takes these keyword arguments: + + p4 -- the Perforce client executable (default 'p4') + port -- the server's listen address + client -- the client name + user -- the user name + encoding -- the encoding to use for text (default 'utf8') + + """ + def __init__(self, p4='p4', port=None, client=None, user=None, + encoding='utf8'): + self.args = [p4, '-G'] + if port: self.args.extend(['-p', port]) + if user: self.args.extend(['-u', user]) + if client: self.args.extend(['-c', client]) + self.encoding = encoding + self.kwargs = dict(p4=p4, port=port, client=client, user=user, + encoding=encoding) + + def run(self, *args): + """Run a Perforce command. + + >>> conn = Connection() + >>> conn.run('edit', filespec) + >>> conn.run('submit', '-d', description, filespec) + + Returns a Command object. + + """ + return Command(*(self.args + list(args)), encoding=self.encoding) + + def do(self, *args): + """Run a Perforce command and consume its output.""" + self.run(*args).done() + + def contents(self, filespec): + """Return the contents of the file whose specification is given as a + string. If the file does not exist, raise p4.Error. + + """ + return ''.join(t['data'] for t in self.run('print', filespec) + if 'data' in t) + + @contextmanager + def temp_client(self, client_spec): + """Return a context manager that creates a temporary client workspace + on entry and deletes it on exit. + + The client specification should omit the Client and Root keys: + these are added automatically. The workspace views should use + __CLIENT__ and this is replaced by the chosen client name. + + This context manager yields a tuple of the new Connection + object, and the root directory of the client workspace. + + """ + import shutil + import tempfile + import uuid + name = 'tmp-{}'.format(uuid.uuid4()) + root = tempfile.mkdtemp() + spec = {k: re.sub(r'__CLIENT__', name, v) + if re.match(r'View\d+$', k) else v + for k, v in client_spec.items()} + spec.update(Client=name, Root=root) + try: + conn = Connection(**dict(self.kwargs, client=name)) + conn.run('client', '-i').send(spec).done() + try: + yield conn, root + finally: + try: + conn.do('revert', '-k', '//...') + except Error: + pass + conn.do('client', '-d', name) + finally: + shutil.rmtree(root) + + +# Convenience interface for the default connection. +_conn = Connection() +run = _conn.run +do = _conn.do +contents = _conn.contents +temp_client = _conn.temp_client + + +# A. REFERENCES +# +# [SUBPROCESS] Python Standard Library: "subprocess -- Subprocess +# management"; . +# +# +# B. DOCUMENT HISTORY +# +# 2001-05-20 GDR Created. +# +# 2003-02-14 NB Changed os.wait to os.waitpid for Python 2.2. +# +# 2010-10-04 GDR Rewritten to use [SUBPROCESS] instead of os.pipe. +# +# 2010-10-05 GDR Raise an exception if Perforce returns an error. New +# function 'contents' for getting the contents of a file. +# +# 2010-10-06 GDR Move p4client, p4path, p4port, and p4user to global +# variables, to make testing easier. New function 'pipe' makes it +# possible to run the 'client -i' command. +# +# 2013-12-09 GDR Merge pipe and run functions into a Run class that +# also handles encoding and decoding. +# +# 2014-03-18 GDR Refactor into classes: Connection (holding the +# client/server configuration) and Command (a single command and its +# output). +# +# 2014-03-19 GDR New methods Connection.temp_client for creating a +# temporary client workspace, and Connection.do for encapsulating +# run().done(). +# +# +# C. COPYRIGHT AND LICENCE +# +# Copyright 2001-2014 Ravenbrook Ltd. All rights reserved. +# +# 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 REGENTS 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. +# +# +# $Id$ diff --git a/tool/release b/tool/release new file mode 100755 index 0000000000..d47fc36e78 --- /dev/null +++ b/tool/release @@ -0,0 +1,257 @@ +#!/usr/bin/env python +# +# RELEASE -- MAKE A RELEASE +# Gareth Rees, Ravenbrook Limited, 2014-03-18 +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. +# +# +# 1. INTRODUCTION +# +# This script automates the process of making a release, based on +# [RELEASE-BUILD]. +# +# This script is idempotent: that is, you can run it repeatedly and it +# will skip steps that have already been performed. + + +from __future__ import unicode_literals +import argparse +from contextlib import contextmanager +import datetime +import os +import re +import subprocess +import sys +import p4 + +class Error(Exception): pass + +@contextmanager +def pushdir(dir): + """Context manager that changes directory to dir for the body of the + with statement. + + """ + cwd = os.getcwd() + os.chdir(dir) + yield + os.chdir(cwd) + +DEPOT = '//info.ravenbrook.com' +PROJECT_RE = r'[a-z][a-z0-9.-]*' +PROJECT_FILESPEC_RE = r'{}/project/({})/'.format(re.escape(DEPOT), PROJECT_RE) +VERSION_RE = r'\d+\.\d+' +CUSTOMER_RE = r'[a-z][a-z0-9.-]*' +BRANCH_RE = (r'master|(?:custom/({})/)?(?:main|version/({}))' + .format(CUSTOMER_RE, VERSION_RE)) +BRANCH_FILESPEC_RE = r'{}({})(?:/|$)'.format(PROJECT_FILESPEC_RE, BRANCH_RE) + +RELEASE_ENTRY = ''' + + + {release} + {today}
+ {branch}/...@{changelevel} + + {description} + + + Known
+ Fixed + + +''' + +VERSION_ENTRY = ''' {release} +''' + +def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument('-P', '--project', + help='Name of the project.') + parser.add_argument('-b', '--branch', + help='Name of the branch to make the release from.') + parser.add_argument('-C', '--changelevel', type=int, + help='Changelevel at which to make the release.') + parser.add_argument('-d', '--description', + help='Description of the release.') + parser.add_argument('-y', '--yes', action='store_true', + help='Yes, really make the release.') + args = parser.parse_args(argv[1:]) + args.depot = DEPOT + args.today = datetime.date.today().strftime('%Y-%m-%d') + fmt = lambda s: s.format(**vars(args)) + + if not args.project: + # Deduce project from current directory. + filespec = next(p4.run('dirs', '.'))['dir'] + m = re.match(PROJECT_FILESPEC_RE, filespec) + if not m: + raise Error("Can't deduce project from current directory.") + args.project = m.group(1) + print(fmt("project={project}")) + + if not any(p4.run('dirs', fmt('{depot}/project/{project}'))): + raise Error(fmt("No such project: {project}")) + + if not args.branch: + # Deduce branch from current directory. + filespec = next(p4.run('dirs', '.'))['dir'] + m = re.match(BRANCH_FILESPEC_RE, filespec) + if not m: + raise Error("Can't deduce branch from {}".format(filespec)) + if args.project != m.group(1): + raise Error("Specified project={} but current directory belongs " + "to project={}.".format(args.project, m.group(1))) + args.branch = m.group(2) + print(fmt("branch={branch}")) + + m = re.match(BRANCH_RE, args.branch) + if not m: + raise Error(fmt("Invalid branch {branch}")) + args.customer = m.group(1) + args.version = m.group(2) + if args.customer: + print(fmt("customer={customer}")) + if args.version: + print(fmt("version={version}")) + + args.origin = fmt('{depot}/project/{project}/{branch}') + if not any(p4.run('dirs', args.origin)): + raise Error(fmt("No such branch: {branch}")) + + if not args.changelevel: + cmd = p4.run('changes', '-m', '1', fmt('{origin}/...')) + args.changelevel = int(next(cmd)['change']) + print(fmt("changelevel={changelevel}")) + + # Deduce release from code/version.c. + f = fmt('{origin}/code/version.c@{changelevel}') + m = re.search(r'^#define MPS_RELEASE "release/((\d+\.\d+)\.\d+)"$', + p4.contents(f), re.M) + if not m: + raise Error("Failed to extract release from {}.".format(f)) + args.release = m.group(1) + print(fmt("release={release}")) + if args.version and args.version != m.group(2): + raise Error(fmt("Version {version} does not match release {release}")) + if args.customer: + args.reldir = fmt('{depot}/project/{project}/custom/{customer}/release/{release}') + else: + args.reldir = fmt('{depot}/project/{project}/release/{release}') + + if not args.description: + args.description = fmt("Release {release}.") + print(fmt("description={description}")) + + args.kit = fmt('mps-kit-{release}') + client_spec = dict( + View0=fmt('{origin}/... //__CLIENT__/{kit}/...'), + View1=fmt('{reldir}/... //__CLIENT__/release/{release}/...')) + srcs = fmt('{origin}/...@{changelevel}') + for line_end, args.ext, cmd in (('local', 'tar.gz', ['tar', 'czf']), + ('win', 'zip', ['zip', '-r'])): + client_spec['LineEnd'] = line_end + archive = fmt('release/{release}/{kit}.{ext}') + with p4.temp_client(client_spec) as (conn, client_root): + try: + conn.do('files', fmt('{reldir}/{kit}.{ext}')) + except p4.Error as e: + print("Adding {}".format(archive)) + conn.do('sync', '-f', srcs) + with pushdir(client_root): + os.makedirs(fmt('release/{release}')) + subprocess.check_call(cmd + [archive, args.kit], + stdout=subprocess.DEVNULL) + if not args.yes: + print(fmt("--yes omitted: skipping submit of {kit}.{ext}")) + else: + conn.do('add', os.path.join(client_root, archive)) + desc = fmt("Adding the MPS Kit {ext} archive for " + "release {release}.") + conn.do('submit', '-d', desc) + else: + print("{} already exists: skipping.".format(archive)) + + def register(filespec, search, replace): + args.filespec = fmt(filespec) + if p4.contents(args.filespec).find(args.release) != -1: + print(fmt("{filespec} already updated: skipping.")) + return + client_spec = dict(View0=fmt('{filespec} //__CLIENT__/target')) + with p4.temp_client(client_spec) as (conn, client_root): + filename = os.path.join(client_root, 'target') + conn.do('sync', filename) + conn.do('edit', filename) + with open(filename, encoding='utf8') as f: + text = re.sub(search, fmt(replace), f.read(), 1) + with open(filename, 'w', encoding='utf8') as f: + f.write(text) + for result in conn.run('diff'): + if 'data' in result: + print(result['data']) + if args.yes: + conn.do('submit', '-d', fmt("Registering release {release}."), + filename) + else: + print(fmt("--yes omitted: skipping submit of {filespec}")) + + if not args.customer: + register('{depot}/project/{project}/release/index.html', + '(?<=\n)', RELEASE_ENTRY) + register('{depot}/project/{project}/version/index.html', + (r'(?<={0}\n \n)' + .format(re.escape(args.version), re.escape(args.project))), + VERSION_ENTRY) + register('{depot}/project/{project}/index.rst', + r'release/\d+\.\d+\.\d+', 'release/{release}') + + +if __name__ == '__main__': + main(sys.argv) + + +# A. REFERENCES +# +# [RELEASE-BUILD] Richard Brooksby; "Memory Pool System Release Build +# Procedure"; Ravenbrook Limited; 2002-06-17. +# +# +# +# B. DOCUMENT HISTORY +# +# 2014-03-18 GDR Created based on [RELEASE-BUILD]. +# +# +# C. COPYRIGHT AND LICENCE +# +# Copyright (c) 2014 Ravenbrook Ltd. All rights reserved. +# +# 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 REGENTS 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. +# +# +# $Id$ diff --git a/tool/testaslr.c b/tool/testaslr.c new file mode 100644 index 0000000000..f421565b7b --- /dev/null +++ b/tool/testaslr.c @@ -0,0 +1,62 @@ +/* testaslr.c: Simple test for ASLR + * + * $Id: //info.ravenbrook.com/project/mps/master/code/eventcnv.c#26 $ + * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. + * + * Run this program multiple times and see if gets different addresses. + */ + +#include +#include + +int data; + +int main() { + void *heap = malloc(4); + int stack = 0; + printf("data: %p text: %p stack: %p heap: %p\n", + &data, (void *)main, &stack, heap); + return 0; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2014 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/tool/testcases.txt b/tool/testcases.txt new file mode 100644 index 0000000000..0b45b8cc54 --- /dev/null +++ b/tool/testcases.txt @@ -0,0 +1,56 @@ +============= ================ ========================================== +Test case Flags Notes +============= ================ ========================================== +abqtest +airtest +amcss =P +amcsshe =P +amcssth =P =T +amsss =P +amssshe =P +apss +arenacv +awlut +awluthe +awlutth =T +btcv +bttest =N interactive +djbench =N benchmark +exposet0 =P +expt825 +finalcv =P +finaltest =P +fotest +gcbench =N benchmark +landtest +locbwcss +lockcov +lockut =T +locusss +locv +messtest +mpmss +mpsicv +mv2test +nailboardtest +poolncv +qs +sacss +segsmss +steptest =P +teletest =N interactive +walkt0 +zcoll =L +zmess +============= ================ ========================================== + +Key to flags +............ + + B -- known Bad + L -- Long runtime + N -- Not an automated test case + P -- relies on Polling or incremental collection + T -- multi-Threaded + W -- Windows-only + X -- Unix-only diff --git a/tool/testcoverage b/tool/testcoverage new file mode 100755 index 0000000000..56ee5789bb --- /dev/null +++ b/tool/testcoverage @@ -0,0 +1,83 @@ +#!/bin/sh +# +# TESTCOVERAGE -- TEST COVERAGE REPORT FOR THE MPS +# Gareth Rees, Ravenbrook Limited, 2014-03-21 +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. +# +# +# 1. INTRODUCTION +# +# This shell script runs the MPS test suite and prepares a test +# coverage report. +# +# Supported platforms: xc. + +ARCH=$(uname -m) +OS=$(uname -s) +PROJECT=mps +TOOL=$(dirname "$0") +CODE=$TOOL/../code +MPS_TELEMETRY_CONTROL=all +export MPS_TELEMETRY_CONTROL + +case "$ARCH-$OS" in + *-Darwin) + CONFIGURATION=Debug + ( + cd -- "$CODE" + xcrun xcodebuild -config "$CONFIGURATION" clean + xcrun xcodebuild -config "$CONFIGURATION" -target testrun \ + GCC_GENERATE_TEST_COVERAGE_FILES=YES \ + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES + ) + ( + cd -- "$CODE/xc/$PROJECT.build/$CONFIGURATION/$PROJECT.build/Objects-normal/$ARCH" + xcrun gcov mps.c 2> /dev/null + ) | "$TOOL/gcovfmt" + ;; + *) + echo "Platform $ARCH-$OS not supported." + ;; +esac + + +# A. REFERENCES +# +# +# B. DOCUMENT HISTORY +# +# 2014-03-21 GDR Created. +# +# +# C. COPYRIGHT AND LICENCE +# +# Copyright (c) 2014 Ravenbrook Ltd. All rights reserved. +# +# 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 REGENTS 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. +# +# +# $Id$ diff --git a/tool/testemscripten b/tool/testemscripten new file mode 100755 index 0000000000..b4c565440d --- /dev/null +++ b/tool/testemscripten @@ -0,0 +1,136 @@ +#!/bin/sh +# +# TESTEMSCRIPTEN -- TEST THE MPS WITH EMSCRIPTEN +# Gareth Rees, Ravenbrook Limited, 2014-04-17 +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. +# +# +# 1. INTRODUCTION +# +# This shell script pulls Emscripten from GitHub and uses it to build +# the MPS. +# +# Supported platforms: ?. +# +# +# 1.1. PREREQUISITES +# +# clang, curl, git, nodejs +# +# "python" needs to be Python 2 (otherwise configure fails), so you +# may need to run: +# +# port select --set python python27 +# +# You need to have a program "python2" on your path (which runs Python +# 2.7.3), so on OS X with MacPorts you need to run: +# +# ln -s /opt/local/bin/python2.7 /opt/local/bin/python2 + + +# 2. CONFIGURATION + +# Emscripten git repository +EMSCRIPTEN_REMOTE=https://github.com/kripken/emscripten.git + +# Fastcomp git repository +FASTCOMP_REMOTE=https://github.com/kripken/emscripten-fastcomp.git + +# Fastcomp clang git repository +CLANG_REMOTE=https://github.com/kripken/emscripten-fastcomp-clang + +# Directory to put everything in +TESTDIR="$PWD/.test" +mkdir -p -- "$TESTDIR" +cd -- "$TESTDIR" + + +# 3. UTILITIES + +# 3.1. checkout REPO REMOTE -- clone a git repository and pull + +checkout () { + REPO=$1 + REMOTE=$2 + if [ -d "$REPO" ]; then + echo "$REPO exists: skipping clone." + else + echo "cloning $REMOTE into $REPO." + git clone --recursive -- "$REMOTE" "$REPO" + fi + ( + cd -- "$REPO" + git pull + ) +} + + +# 4. PROCEDURE + +checkout emscripten "$EMSCRIPTEN_REMOTE" + + +# See [FASTCOMP]. + +checkout emscripten-fastcomp "$FASTCOMP_REMOTE" +( + cd emscripten-fastcomp + ( + cd tools + checkout clang "$CLANG_REMOTE"; + ) + mkdir -p build + ( + cd build + ../configure --enable-optimized --disable-assertions --enable-targets=host,js + make; + ) +) + + +# A. REFERENCES +# +# [EMPSCRIPTEN] "Emscripten SDK" +# +# +# [FASTCOMP] "LLVM Backend, aka fastcomp" +# +# +# +# B. DOCUMENT HISTORY +# +# 2014-04-17 GDR Created based on [EMSCRIPTEN] and [FASTCOMP]. +# +# +# C. COPYRIGHT AND LICENCE +# +# Copyright (c) 2014 Ravenbrook Ltd. All rights reserved. +# +# 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 REGENTS 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. +# +# +# $Id$ diff --git a/tool/testopendylan b/tool/testopendylan new file mode 100755 index 0000000000..4bd1488261 --- /dev/null +++ b/tool/testopendylan @@ -0,0 +1,176 @@ +#!/bin/sh +# +# TESTOPENDYLAN -- TEST THE MPS WITH OPENDYLAN +# Gareth Rees, Ravenbrook Limited, 2014-03-20 +# +# $Id$ +# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. +# +# +# 1. INTRODUCTION +# +# This shell script pulls Open Dylan from GitHub and builds it against +# the MPS. +# +# Supported platforms: lii3gc. +# +# Prerequisites: autoconf, bunzip2, curl, git. + + +# 2. CONFIGURATION + +# Check command-line argument +GC=$1 +case "$GC" in + mps) + # MPS sources we are testing against + CONFIGURE=--with-gc-path=$(cd -- "$(dirname "$0")/.." && pwd) + ;; + boehm) + CONFIGURE= + ;; + *) + echo "Backend '$GC' not supported: choose mps or boehm." + exit 1 +esac + + +# OpenDylan version for bootstrapping +VERSION=2013.2 + +# OpenDylan git repository +REMOTE=https://github.com/dylan-lang/opendylan.git + +# Directory to put everything in +TESTDIR="$PWD/.test/$GC" +mkdir -p -- "$TESTDIR" +cd -- "$TESTDIR" + + +# 3. PROCEDURE + +# 3.1. Clone the git repository and pull. See [OPENDYLAN]. + +REPO=opendylan + +if [ -d "$REPO" ]; then + echo "$REPO exists: skipping clone." +else + echo "cloning $REMOTE into $REPO." + git clone --recursive -- "$REMOTE" "$REPO" +fi +( + cd -- "$REPO" && + git pull; +) + + +# 3.2. Download the binary distribution, for bootstrapping. See [DOWNLOAD]. + +UNAME=$(uname -m)-$(uname -s) +case "$UNAME" in + i686-Linux) + PLATFORM=x86-linux ;; + *) + echo "Platform $UNAME not supported." + exit 1 +esac + +URL="http://opendylan.org/downloads/opendylan/$VERSION/opendylan-$VERSION-$PLATFORM.tar.bz2" +PACKAGE=$(basename "$URL") +PACKAGE_DIR="opendylan-$VERSION" + +if [ -f "$PACKAGE" ]; then + echo "$PACKAGE exists: skipping download." +else + echo "Downloading $URL..." + curl --url "$URL" --output "$PACKAGE" +fi + +if [ -d "$PACKAGE_DIR" ]; then + echo "$PACKAGE_DIR exists: skipping unpack." +else + echo "Unpacking $PACKAGE..." + bunzip2 --stdout "$PACKAGE" | tar xf - +fi + + +# 3.3. Set up PATH and check that the compiler runs. + +PATH="$PWD/$PACKAGE_DIR/bin:$PATH" +export PATH +COMPILER_VERSION=$(dylan-compiler -version) + +if [ "$COMPILER_VERSION" != "Version $VERSION" ]; then + echo "Unexpected version: $COMPILER_VERSION" + exit 1 +fi + + +# 3.4. Configure and build. See "Building" section of [WELCOME]. + +PREFIX="$PWD/prefix" + +if [ -f "$REPO/Makefile" ]; then + echo "$REPO/Makefile exists: skipping configure" +else ( + cd -- "$REPO" && + ./autogen.sh && + ./configure --with-gc="$GC" --prefix="$PREFIX" $CONFIGURE +) fi +( + cd -- "$REPO" && + make 3-stage-bootstrap +) + + +# A. REFERENCES +# +# [DOWNLOAD] "Open Dylan Downloads" +# +# +# [OPENDYLAN] "dylan-lang/opendylan project on GitHub" +# +# +# [WELCOME] "Welcome to Open Dylan!" +# +# +# +# B. DOCUMENT HISTORY +# +# 2014-03-20 GDR Created based on [WELCOME]. +# +# 2014-04-14 GDR Updated configure args based on revised build +# instructions [WELCOME]. +# +# +# C. COPYRIGHT AND LICENCE +# +# Copyright (c) 2014 Ravenbrook Ltd. All rights reserved. +# +# 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 REGENTS 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. +# +# +# $Id$ diff --git a/tool/testrun.bat b/tool/testrun.bat index a6fad0fa33..5e9c0606ab 100755 --- a/tool/testrun.bat +++ b/tool/testrun.bat @@ -1,47 +1,93 @@ -@rem $Id: //info.ravenbrook.com/project/mps/master/tool/testrun.sh#1 $ -@rem Copyright (c) 2013 Ravenbrook Limited. See end of file for license. +@rem $Id$ +@rem Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. +@rem +@rem This program runs a series of test cases, capturing the output of +@rem each one to a temporary file. In addition, the output of any test +@rem case that fails gets printed to standard output so that it is not +@rem lost when the test is running on a temporary build server (see +@rem job003489). Finally, it prints a summary of passes and failures, and +@rem if there were any failures, it exits with a non-zero status code. +@rem +@rem Usage: +@rem +@rem testrun.bat PLATFORM VARIETY ( SUITE | CASE1 CASE2 ... ) @echo off +@rem Find test case database in same directory as this script. +@rem The incantation %%~dpF% expands %%F to a drive letter and path only. +@rem See "help for" for more details. +for %%F in ("%0") do set TEST_CASE_DB=%%~dpF%testcases.txt + set PFM=%1 shift set VARIETY=%1 shift +set TESTSUITE=%1 +@rem Make a temporary output directory for the test logs. +set LOGDIR=%TMP%\mps-%PFM%-%VARIETY%-log +echo MPS test suite +echo Logging test output to %LOGDIR% +echo Test directory: %PFM%\%VARIETY% +if exist %LOGDIR% rmdir /q /s %LOGDIR% +mkdir %LOGDIR% + +@rem Determine which tests to run. +set EXCLUDE= +if "%TESTSUITE%"=="testrun" set EXCLUDE=LNX +if "%TESTSUITE%"=="testci" set EXCLUDE=BNX +if "%TESTSUITE%"=="testall" set EXCLUDE=NX +if "%TESTSUITE%"=="testansi" set EXCLUDE=LNTX +if "%TESTSUITE%"=="testpollnone" set EXCLUDE=LNPTX + +@rem Ensure that test cases don't pop up dialog box on abort() +set MPS_TESTLIB_NOABORT=true set TEST_COUNT=0 +set PASS_COUNT=0 set FAIL_COUNT=0 set SEPARATOR=---------------------------------------- -set LOGDIR=%TMP%\mps-%VARIETY%-log -echo Logging test output to %LOGDIR% -rmdir /q /s %LOGDIR% -mkdir %LOGDIR% -:loop -if "%1"=="" goto continue - set /a TEST_COUNT=%TEST_COUNT%+1 - echo Running %1 - %PFM%\%VARIETY%\%1 > %LOGDIR%\%1 - if "%errorlevel%"=="0" goto success - echo %SEPARATOR%%SEPARATOR% - type %LOGDIR%\%1 - echo %SEPARATOR%%SEPARATOR% - set /a FAIL_COUNT=%FAIL_COUNT%+1 - :success +if "%EXCLUDE%"=="" goto :args +for /f "tokens=1" %%T IN ('type %TEST_CASE_DB% ^|^ + findstr /b /r [abcdefghijklmnopqrstuvwxyz] ^|^ + findstr /v /r =[%EXCLUDE%]') do call :run_test %%T +goto :done + +:args +if "%1"=="" goto :done +call :run_test %1 shift -goto loop -:continue +goto :args -if "%FAIL_COUNT%"=="0" goto allpass - echo Tests: %TEST_COUNT% Failures: %FAIL_COUNT% - exit 1 +:done +if "%FAIL_COUNT%"=="0" ( + echo Tests: %TEST_COUNT%. All tests pass. + exit /b 0 +) else ( + echo Tests: %TEST_COUNT%. Passes: %PASS_COUNT%. Failures: %FAIL_COUNT%. + exit /b 1 +) -:allpass -echo Tests: %TEST_COUNT% All tests pass. +:run_test +set /a TEST_COUNT=%TEST_COUNT%+1 +set LOGTEST=%LOGDIR%\%TEST_COUNT%-%1 +echo Running %1 +%PFM%\%VARIETY%\%1 > %LOGTEST% +if "%errorlevel%"=="0" ( + set /a PASS_COUNT=%PASS_COUNT%+1 +) else ( + echo %SEPARATOR%%SEPARATOR% + type %LOGTEST% + echo %SEPARATOR%%SEPARATOR% + set /a FAIL_COUNT=%FAIL_COUNT%+1 +) +exit /b @rem C. COPYRIGHT AND LICENSE @rem -@rem Copyright (C) 2013 Ravenbrook Limited . +@rem Copyright (C) 2013-2014 Ravenbrook Limited . @rem All rights reserved. This is an open source license. Contact @rem Ravenbrook for commercial licensing options. @rem diff --git a/tool/testrun.sh b/tool/testrun.sh index ff01634b85..6ea7dfcb4d 100755 --- a/tool/testrun.sh +++ b/tool/testrun.sh @@ -1,7 +1,7 @@ #!/bin/sh # # $Id$ -# Copyright (c) 2013 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. # # This program runs a series of test cases, capturing the output of # each one to a temporary file. In addition, the output of any test @@ -12,31 +12,103 @@ # # Usage:: # -# testrun.sh case1 case2 ... +# testrun.sh [-s SUITE] [-r RUNNER] DIR [CASE1 CASE2 ...] +# +# You can use this program to run the same test many times, to get +# lots of random coverage. For example:: +# +# yes amcss | head -100 | xargs tool/testrun.sh code/xc/Debug +# +# This runs the AMC stress test 100 times from the code/xc/Debug +# directory, reporting all failures. + +echo "MPS test suite" + +TEST_RUNNER= +TEST_CASES= + +# Parse command-line arguments. +while [ $# -gt 0 ]; do + case "$1" in + -s) + TEST_SUITE=$2 + case "$TEST_SUITE" in + testrun) EXCLUDE="LNW" ;; + testci) EXCLUDE="BNW" ;; + testall) EXCLUDE="NW" ;; + testansi) EXCLUDE="LNTW" ;; + testpollnone) EXCLUDE="LNPTW" ;; + *) + echo "Test suite $TEST_SUITE not recognized." + exit 1 ;; + esac + echo "Test suite: $TEST_SUITE" + TEST_CASE_DB=$(dirname -- "$0")/testcases.txt + TEST_CASES=$(<"$TEST_CASE_DB" grep -e '^[a-z]' | + grep -v -e "=[$EXCLUDE]" | + cut -d' ' -f1) + shift 2 + ;; + -r) + TEST_RUNNER=$2 + shift 2 + ;; + -*) + echo "Unrecognized option $1" + exit 1 + ;; + *) + break + ;; + esac +done # Make a temporary output directory for the test logs. LOGDIR=$(mktemp -d /tmp/mps.log.XXXXXX) -echo "MPS test suite" echo "Logging test output to $LOGDIR" + +# Next argument is the directory containing the test cases. +TEST_DIR=$1 +shift +echo "Test directory: $TEST_DIR" + +# Determine which tests to run. +TEST_CASES="$TEST_CASES $*" -SEPARATOR="----------------------------------------" +SEPARATOR=---------------------------------------- TEST_COUNT=0 PASS_COUNT=0 FAIL_COUNT=0 -for TESTCASE in "$@"; do - TEST="$(basename "$TESTCASE")" - LOGTEST="$LOGDIR/$TEST" +for TESTCASE in $TEST_CASES; do + TEST=$(basename -- "$TESTCASE") + LOGTEST=$LOGDIR/$TEST_COUNT-$TEST + TELEMETRY=$LOGDIR/$TEST_COUNT-$TEST-io + MPS_TELEMETRY_FILENAME=$TELEMETRY.log + export MPS_TELEMETRY_FILENAME + echo "Running $TEST" - TEST_COUNT=$(expr $TEST_COUNT + 1) - if "$TESTCASE" > "$LOGTEST" 2>&1; then - PASS_COUNT=$(expr $PASS_COUNT + 1) + TEST_COUNT=$((TEST_COUNT + 1)) + if $TEST_RUNNER "$TEST_DIR/$TESTCASE" > "$LOGTEST" 2>&1; then + PASS_COUNT=$((PASS_COUNT + 1)) else echo "$TEST failed: log follows" echo ${SEPARATOR}${SEPARATOR} - cat "$LOGTEST" + cat -- "$LOGTEST" echo echo ${SEPARATOR}${SEPARATOR} - FAIL_COUNT=$(expr $FAIL_COUNT + 1) + FAIL_COUNT=$((FAIL_COUNT + 1)) + fi + + if [ -f "$MPS_TELEMETRY_FILENAME" ]; then + "$TEST_DIR/mpseventcnv" -f "$MPS_TELEMETRY_FILENAME" > "$TELEMETRY.cnv" + gzip "$MPS_TELEMETRY_FILENAME" + "$TEST_DIR/mpseventtxt" < "$TELEMETRY.cnv" > "$TELEMETRY.txt" + if [ -x "$TEST_DIR/mpseventsql" ]; then + MPS_TELEMETRY_DATABASE=$TELEMETRY.db + export MPS_TELEMETRY_DATABASE + "$TEST_DIR/mpseventsql" < "$TELEMETRY.cnv" >> "$LOGTEST" 2>&1 + fi + rm -f "$TELEMETRY.cnv" "$TELEMETRY.txt" "$TELEMETRY.db" fi done if [ $FAIL_COUNT = 0 ]; then @@ -49,7 +121,7 @@ fi # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2013 Ravenbrook Limited . +# Copyright (C) 2013-2014 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. #